From e96e3f480b92e4691d21828efc07c3cc8619d9d8 Mon Sep 17 00:00:00 2001 From: ParantezTech <12572227+par274@users.noreply.github.com> Date: Wed, 7 Oct 2026 02:43:10 +0300 Subject: [PATCH] [Network] Track Http2 template and request options Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com> --- src/SharpEmu.Libs/Network/Http2Exports.cs | 168 +++++++++++++++++++++- 1 file changed, 163 insertions(+), 5 deletions(-) diff --git a/src/SharpEmu.Libs/Network/Http2Exports.cs b/src/SharpEmu.Libs/Network/Http2Exports.cs index 4502745..76e87a9 100644 --- a/src/SharpEmu.Libs/Network/Http2Exports.cs +++ b/src/SharpEmu.Libs/Network/Http2Exports.cs @@ -12,29 +12,82 @@ public static class Http2Exports private const int Http2ErrorInvalidVersion = unchecked((int)0x817B106A); private const int Http2ErrorInvalidId = unchecked((int)0x817B1100); private const int Http2ErrorInvalidValue = unchecked((int)0x817B11FE); + private const int Http2ErrorNullPointer = unchecked((int)0x817B1225); private const int Http2ErrorProhibited = unchecked((int)0x817B5224); private static readonly ConcurrentDictionary Contexts = new(); private static readonly ConcurrentDictionary Templates = new(); private static readonly ConcurrentDictionary Requests = new(); + private static readonly ConcurrentDictionary CookieBoxes = new(); private static int _nextContextId; private static int _nextTemplateId = 0x1000; private static int _nextRequestId = 0x2000; + private static int _nextCookieBoxId = 0x3000; private sealed record Http2Context(int NetId, int SslId, ulong PoolSize, int MaxRequests); - private sealed record Http2Template(int ContextId, string UserAgent, int HttpVersion, bool AutoProxyConfig); + private sealed record Http2CookieBox; - private sealed record Http2Request(int TemplateId, string Method, string Url, ulong ContentLength); + private sealed record Http2Options( + uint MinimumSslVersion, + uint SslOptions, + ulong RedirectCallback, + ulong RedirectUserArgument, + bool AutoRedirect, + int CookieBoxId, + uint ConnectTimeoutMicroseconds, + uint ReceiveTimeoutMicroseconds, + uint SendTimeoutMicroseconds, + bool AuthEnabled, + ulong SslCallback, + ulong SslUserArgument) + { + public static readonly Http2Options Default = new(0, 0, 0, 0, true, 0, 0, 0, 0, false, 0, 0); + } + + private sealed record Http2Header(string Name, string Value, uint Mode); + + private sealed record Http2Template( + int ContextId, + string UserAgent, + int HttpVersion, + bool AutoProxyConfig, + Http2Options Options); + + private sealed record Http2Request( + int TemplateId, + string Method, + string Url, + ulong ContentLength, + Http2Options Options, + Http2Header[] Headers); + + internal readonly record struct Http2OptionsSnapshot( + uint MinimumSslVersion, + uint SslOptions, + ulong RedirectCallback, + ulong RedirectUserArgument, + bool AutoRedirect, + int CookieBoxId, + uint ConnectTimeoutMicroseconds, + uint ReceiveTimeoutMicroseconds, + uint SendTimeoutMicroseconds, + bool AuthEnabled, + ulong SslCallback, + ulong SslUserArgument); + + internal readonly record struct Http2HeaderSnapshot(string Name, string Value, uint Mode); public static void ResetRuntimeState() { Contexts.Clear(); Templates.Clear(); Requests.Clear(); + CookieBoxes.Clear(); _nextContextId = 0; _nextTemplateId = 0x1000; _nextRequestId = 0x2000; + _nextCookieBoxId = 0x3000; } [SysAbiExport( @@ -119,7 +172,12 @@ public static class Http2Exports } var id = Interlocked.Increment(ref _nextTemplateId); - Templates[id] = new Http2Template(contextId, userAgent, httpVersion, autoProxyConfig != 0); + Templates[id] = new Http2Template( + contextId, + userAgent, + httpVersion, + autoProxyConfig != 0, + Http2Options.Default); TraceHttp2("create_template", id, unchecked((ulong)contextId), ctx[CpuRegister.Rsi], unchecked((ulong)httpVersion), unchecked((ulong)autoProxyConfig)); return ctx.SetReturn(id); } @@ -147,7 +205,7 @@ public static class Http2Exports public static int Http2CreateRequestWithUrl(CpuContext ctx) { var templateId = unchecked((int)ctx[CpuRegister.Rdi]); - if (!Templates.ContainsKey(templateId)) + if (!Templates.TryGetValue(templateId, out var template)) { return ctx.SetReturn(Http2ErrorInvalidId); } @@ -159,7 +217,13 @@ public static class Http2Exports } var id = Interlocked.Increment(ref _nextRequestId); - Requests[id] = new Http2Request(templateId, method, url, ctx[CpuRegister.Rcx]); + Requests[id] = new Http2Request( + templateId, + method, + url, + ctx[CpuRegister.Rcx], + template.Options, + []); TraceHttp2("create_request", id, unchecked((ulong)templateId), ctx[CpuRegister.Rsi], ctx[CpuRegister.Rdx], ctx[CpuRegister.Rcx]); return ctx.SetReturn(id); } @@ -202,6 +266,100 @@ public static class Http2Exports } } + private static int SetTimeout( + CpuContext ctx, + Func update, + string timeoutName) + { + var id = unchecked((int)ctx[CpuRegister.Rdi]); + var microseconds = unchecked((uint)ctx[CpuRegister.Rsi]); + return TryUpdateOptions(id, options => update(options, microseconds)) + ? TraceAndReturn(ctx, $"set_{timeoutName}_timeout", id, microseconds, 0, 0, 0) + : ctx.SetReturn(Http2ErrorInvalidId); + } + + private static bool TryUpdateOptions(int id, Func update) + { + while (Templates.TryGetValue(id, out var template)) + { + if (Templates.TryUpdate(id, template with { Options = update(template.Options) }, template)) + { + return true; + } + } + + while (Requests.TryGetValue(id, out var request)) + { + if (Requests.TryUpdate(id, request with { Options = update(request.Options) }, request)) + { + return true; + } + } + + return false; + } + + internal static bool TryGetOptionsForTests(int id, out Http2OptionsSnapshot snapshot) + { + var options = Templates.TryGetValue(id, out var template) + ? template.Options + : Requests.TryGetValue(id, out var request) + ? request.Options + : null; + if (options is null) + { + snapshot = default; + return false; + } + + snapshot = new Http2OptionsSnapshot( + options.MinimumSslVersion, + options.SslOptions, + options.RedirectCallback, + options.RedirectUserArgument, + options.AutoRedirect, + options.CookieBoxId, + options.ConnectTimeoutMicroseconds, + options.ReceiveTimeoutMicroseconds, + options.SendTimeoutMicroseconds, + options.AuthEnabled, + options.SslCallback, + options.SslUserArgument); + return true; + } + + internal static Http2HeaderSnapshot[] GetRequestHeadersForTests(int requestId) => + Requests.TryGetValue(requestId, out var request) + ? request.Headers + .Select(static header => new Http2HeaderSnapshot(header.Name, header.Value, header.Mode)) + .ToArray() + : []; + + internal static bool TryGetRequestContentLengthForTests(int requestId, out ulong contentLength) + { + if (Requests.TryGetValue(requestId, out var request)) + { + contentLength = request.ContentLength; + return true; + } + + contentLength = 0; + return false; + } + + private static int TraceAndReturn( + CpuContext ctx, + string operation, + int id, + ulong arg0, + ulong arg1, + ulong arg2, + ulong arg3) + { + TraceHttp2(operation, id, arg0, arg1, arg2, arg3); + return ctx.SetReturn(0); + } + private static bool TryReadUtf8Z(CpuContext ctx, ulong address, int maxLength, out string value) { value = string.Empty;