Beta Release
This commit is contained in:
+127
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace MSCO
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user