using System; using System.IO; using System.Runtime.InteropServices; using System.Text; namespace MSCO { /// /// HTTPS-Abruf über Windows (WinHTTP). Unitys WWW in My Summer Car (Unity 5.0) kann nur TLS 1.0, /// heutige Server verlangen TLS 1.2+. WinHTTP nutzt die TLS-Version des Betriebssystems /// (unter Proton/Wine über GnuTLS). Blockierend - nur in einem Hintergrund-Thread aufrufen. /// public static class WinHttp { public static string Get(string url, int timeoutMs, out string error) { error = null; IntPtr session = IntPtr.Zero, connect = IntPtr.Zero, request = IntPtr.Zero; try { var uri = new Uri(url); bool secure = uri.Scheme == "https"; session = WinHttpOpen("MSCO/" + MSCO.ModVersion, AccessTypeDefaultProxy, null, null, 0); if (session == IntPtr.Zero) return Fail(out error, "WinHttpOpen"); WinHttpSetTimeouts(session, timeoutMs, timeoutMs, timeoutMs, timeoutMs); // Ältere Windows-Versionen haben TLS 1.2 nicht standardmäßig an - ausdrücklich erlauben int protocols = ProtocolTls12 | ProtocolTls13; if (!WinHttpSetOption(session, OptionSecureProtocols, ref protocols, 4)) { protocols = ProtocolTls12; WinHttpSetOption(session, OptionSecureProtocols, ref protocols, 4); } connect = WinHttpConnect(session, uri.Host, (ushort)uri.Port, 0); if (connect == IntPtr.Zero) return Fail(out error, "WinHttpConnect"); request = WinHttpOpenRequest(connect, "GET", uri.PathAndQuery, null, null, IntPtr.Zero, secure ? FlagSecure : 0); if (request == IntPtr.Zero) return Fail(out error, "WinHttpOpenRequest"); if (!WinHttpSendRequest(request, null, 0, IntPtr.Zero, 0, 0, IntPtr.Zero)) return Fail(out error, "WinHttpSendRequest"); if (!WinHttpReceiveResponse(request, IntPtr.Zero)) return Fail(out error, "WinHttpReceiveResponse"); int status = 0, size = 4; WinHttpQueryHeaders(request, QueryStatusCode | QueryFlagNumber, null, ref status, ref size, IntPtr.Zero); if (status != 0 && status != 200) { error = "HTTP " + status; return null; } using (var body = new MemoryStream()) { var buffer = new byte[16 * 1024]; while (true) { int read; if (!WinHttpReadData(request, buffer, buffer.Length, out read)) return Fail(out error, "WinHttpReadData"); if (read == 0) break; body.Write(buffer, 0, read); if (body.Length > 4 * 1024 * 1024) { error = "Response too large"; return null; } } return Encoding.UTF8.GetString(body.ToArray()); } } catch (Exception e) { error = e.Message; return null; } finally { if (request != IntPtr.Zero) WinHttpCloseHandle(request); if (connect != IntPtr.Zero) WinHttpCloseHandle(connect); if (session != IntPtr.Zero) WinHttpCloseHandle(session); } } static string Fail(out string error, string step) { int code = Marshal.GetLastWin32Error(); error = step + " failed (" + code + (code == 12175 ? ", secure connection failed" : code == 12002 ? ", timeout" : code == 12029 ? ", cannot connect" : "") + ")"; return null; } const int AccessTypeDefaultProxy = 0; const int FlagSecure = 0x00800000; const int OptionSecureProtocols = 84; const int ProtocolTls12 = 0x00000800; const int ProtocolTls13 = 0x00002000; const int QueryStatusCode = 19; const int QueryFlagNumber = 0x20000000; [DllImport("winhttp.dll", SetLastError = true, CharSet = CharSet.Unicode)] static extern IntPtr WinHttpOpen(string agent, int accessType, string proxy, string bypass, int flags); [DllImport("winhttp.dll", SetLastError = true)] static extern bool WinHttpSetTimeouts(IntPtr handle, int resolve, int connect, int send, int receive); [DllImport("winhttp.dll", SetLastError = true)] static extern bool WinHttpSetOption(IntPtr handle, int option, ref int value, int length); [DllImport("winhttp.dll", SetLastError = true, CharSet = CharSet.Unicode)] static extern IntPtr WinHttpConnect(IntPtr session, string server, ushort port, int reserved); [DllImport("winhttp.dll", SetLastError = true, CharSet = CharSet.Unicode)] static extern IntPtr WinHttpOpenRequest(IntPtr connect, string verb, string path, string version, string referrer, IntPtr acceptTypes, int flags); [DllImport("winhttp.dll", SetLastError = true, CharSet = CharSet.Unicode)] static extern bool WinHttpSendRequest(IntPtr request, string headers, int headersLength, IntPtr optional, int optionalLength, int totalLength, IntPtr context); [DllImport("winhttp.dll", SetLastError = true)] static extern bool WinHttpReceiveResponse(IntPtr request, IntPtr reserved); [DllImport("winhttp.dll", SetLastError = true, CharSet = CharSet.Unicode)] static extern bool WinHttpQueryHeaders(IntPtr request, int infoLevel, string name, ref int buffer, ref int bufferLength, IntPtr index); [DllImport("winhttp.dll", SetLastError = true)] static extern bool WinHttpReadData(IntPtr request, byte[] buffer, int toRead, out int read); [DllImport("winhttp.dll", SetLastError = true)] static extern bool WinHttpCloseHandle(IntPtr handle); } }