From 147b7166314f6d599eb5dd262d472668232c2e4b Mon Sep 17 00:00:00 2001 From: cypherpotato Date: Thu, 31 Oct 2024 16:03:56 -0300 Subject: [PATCH] wip --- extensions/Sisk.SslProxy/Constants.cs | 3 + extensions/Sisk.SslProxy/HttpConnection.cs | 323 ++++++++++++++++++ .../HttpSerializer/HttpRequestReader.cs | 14 +- .../HttpSerializer/HttpResponseReader.cs | 15 +- extensions/Sisk.SslProxy/SerializerUtils.cs | 2 +- extensions/Sisk.SslProxy/SslProxy.cs | 267 +-------------- src/Http/HttpResponseExtensions.cs | 10 + src/Http/HttpServer__Core.cs | 2 +- src/Http/RequestContext.cs | 14 + 9 files changed, 372 insertions(+), 278 deletions(-) create mode 100644 extensions/Sisk.SslProxy/HttpConnection.cs create mode 100644 src/Http/RequestContext.cs diff --git a/extensions/Sisk.SslProxy/Constants.cs b/extensions/Sisk.SslProxy/Constants.cs index 926fac4..00cfbc0 100644 --- a/extensions/Sisk.SslProxy/Constants.cs +++ b/extensions/Sisk.SslProxy/Constants.cs @@ -20,6 +20,9 @@ static class Constants public const string Server = "siskproxy/0.1"; + // pre-allocate 32 headers for each request + public const int HEADER_LINE_ALLOCATION = 32; + public static readonly byte[] CHUNKED_EOF = [ 0x30, // ascii 0 CH_RETURN, CH_LINEFEED, diff --git a/extensions/Sisk.SslProxy/HttpConnection.cs b/extensions/Sisk.SslProxy/HttpConnection.cs new file mode 100644 index 0000000..0b96e22 --- /dev/null +++ b/extensions/Sisk.SslProxy/HttpConnection.cs @@ -0,0 +1,323 @@ +// The Sisk Framework source code +// Copyright (c) 2023 PROJECT PRINCIPIUM +// +// The code below is licensed under the MIT license as +// of the date of its publication, available at +// +// File name: HttpConnection.cs +// Repository: https://github.com/sisk-http/core + +using System.Net; +using System.Net.Security; +using System.Net.Sockets; +using Sisk.Core.Http; +using Sisk.Ssl.HttpSerializer; + +namespace Sisk.Ssl; + +internal class HttpConnection : IDisposable +{ + private static readonly Random clientIdRngGenerator = new Random(); + private int connectionCloseState = -1; + private bool disposedValue; + private int clientId = clientIdRngGenerator.Next(); + private int iteration = 0; + private DateTime createdAt = DateTime.Now; + + // do not dispose parent on Dispose () + public SslProxy Parent { get; } + public TcpClient Client { get; } + + public DateTime CreatedAt { get => this.createdAt; } + public int ClientId { get => this.clientId; } + public int Iteration { get => this.iteration; } + public int CloseState { get => this.connectionCloseState; } + + public HttpConnection(SslProxy parent, TcpClient client) + { + this.Client = client; + this.Parent = parent; + } + + public void HandleConnection() + { + this.Client.NoDelay = true; + + if (this.disposedValue) + return; + + Logger.LogInformation($"#{this.clientId} open"); + + try + { + using (var tcpStream = this.Client.GetStream()) + using (var sslStream = new SslStream(tcpStream, true)) + using (var gatewayClient = new TcpClient()) + { + try + { + gatewayClient.Connect(this.Parent.GatewayEndpoint); + gatewayClient.NoDelay = true; + gatewayClient.SendTimeout = (int)(this.Parent.GatewayTimeout.TotalSeconds); + gatewayClient.ReceiveTimeout = (int)(this.Parent.GatewayTimeout.TotalSeconds); + } + catch (Exception ex) + { + this.connectionCloseState = 1; + Logger.LogInformation($"#{this.clientId}: Gateway connect exception: {ex.Message}"); + + HttpResponseWriter.TryWriteHttp1DefaultResponse( + new HttpStatusInformation(502), + "The host service ins't working.", + sslStream); + + return; + } + + // used by header values + Span secHXl1 = stackalloc byte[4096]; + // used by request path + Span secHXl2 = stackalloc byte[2048]; + // used by header name + Span secHLg1 = stackalloc byte[512]; + // used by response reason message + Span secHL21 = stackalloc byte[256]; + // used by request method and response status code + Span secHSm1 = stackalloc byte[8]; + // used by request and respones protocol + Span secHSm2 = stackalloc byte[8]; + + HttpRequestReaderSpan reqReaderMemory = new HttpRequestReaderSpan() + { + MethodBuffer = secHSm1, + PathBuffer = secHXl2, + ProtocolBuffer = secHSm2, + PsHeaderName = secHLg1, + PsHeaderValue = secHXl1 + }; + + HttpResponseReaderSpan resReaderMemory = new HttpResponseReaderSpan() + { + ProtocolBuffer = secHSm2, + StatusCodeBuffer = secHSm1, + StatusReasonBuffer = secHL21, + PsHeaderName = secHLg1, + PsHeaderValue = secHXl1 + }; + + using (var gatewayStream = gatewayClient.GetStream()) + { + gatewayStream.ReadTimeout = (int)(this.Parent.GatewayTimeout.TotalSeconds); + gatewayStream.WriteTimeout = (int)(this.Parent.GatewayTimeout.TotalSeconds); + + try + { + sslStream.AuthenticateAsServer( + serverCertificate: this.Parent.ServerCertificate, + clientCertificateRequired: this.Parent.ClientCertificateRequired, + enabledSslProtocols: this.Parent.AllowedProtocols, + checkCertificateRevocation: this.Parent.CheckCertificateRevocation); + } + catch (Exception ex) + { + Logger.LogInformation($"#{this.clientId}: SslAuthentication failed: {ex.Message}"); + this.connectionCloseState = 2; + + // write an error on the http stream + HttpResponseWriter.TryWriteHttp1DefaultResponse( + new HttpStatusInformation(400), + "The plain HTTP request was sent to an HTTPS port.", + sslStream); + + return; + } + + while (this.Client.Connected && !this.disposedValue) + { + try + { + if (!HttpRequestReader.TryReadHttp1Request( + this.clientId, + sslStream, + reqReaderMemory, + this.Parent.GatewayHostname, + this.Client, + out var method, + out var path, + out var proto, + out var forwardedFor, + out var reqContentLength, + out var headers, + out var expectContinue)) + { + Logger.LogInformation($"#{this.clientId}: couldn't read request"); + this.connectionCloseState = 9; + + HttpResponseWriter.TryWriteHttp1DefaultResponse( + new HttpStatusInformation(400), + "The server received an invalid HTTP message.", + sslStream); + + return; + } + + Logger.LogInformation($"#{this.clientId} >> {method} {path}"); + + if (this.Parent.ProxyAuthorization is not null) + { + headers.Add((HttpKnownHeaderNames.ProxyAuthorization, this.Parent.ProxyAuthorization.ToString())); + } + + string clientIpAddress = ((IPEndPoint)this.Client.Client.LocalEndPoint!).Address.ToString(); + if (forwardedFor is not null) + { + headers.Add((HttpKnownHeaderNames.XForwardedFor, forwardedFor + ", " + clientIpAddress)); + } + else + { + headers.Add((HttpKnownHeaderNames.XForwardedFor, clientIpAddress)); + } + + if (!HttpRequestWriter.TryWriteHttpV1Request(this.clientId, gatewayStream, method, path, headers)) + { + HttpResponseWriter.TryWriteHttp1DefaultResponse( + new HttpStatusInformation(502), + "The host service ins't working.", + sslStream); + + this.connectionCloseState = 3; + return; + } + + if (expectContinue) + { + goto readGatewayResponse; + } + + redirClientContent: + if (reqContentLength > 0) + { + if (!SerializerUtils.CopyStream(sslStream, gatewayStream, reqContentLength, CancellationToken.None)) + { + // client couldn't send the full content + break; + } + } + + readGatewayResponse: + if (!HttpResponseReader.TryReadHttp1Response( + this.clientId, + gatewayStream, + resReaderMemory, + out var resStatusCode, + out var resStatusDescr, + out var resHeaders, + out var resContentLength, + out var isChunked, + out var gatewayAllowsKeepAlive, + out var isWebSocket)) + { + HttpResponseWriter.TryWriteHttp1DefaultResponse( + new HttpStatusInformation(502), + "The host service ins't working.", + sslStream); + + this.connectionCloseState = 4; + return; + } + + Logger.LogInformation($"#{this.clientId} << {resStatusCode} {resStatusDescr}"); + + // TODO: check if client wants to keep alive + if (gatewayAllowsKeepAlive && this.Parent.KeepAliveEnabled) + { + ; + } + else + { + resHeaders.Add(("Connection", "close")); + } +#if VERBOSE + if (!expectContinue) + resHeaders.Add(("X-Debug-Connection-Id", this.clientId.ToString())); +#endif + + HttpResponseWriter.TryWriteHttp1Response(this.clientId, sslStream, resStatusCode, resStatusDescr, resHeaders); + + if (expectContinue && resStatusCode == "100") + { + expectContinue = false; + goto redirClientContent; + } + + if (isWebSocket) + { + AutoResetEvent waitEvent = new AutoResetEvent(false); + + Logger.LogInformation($"#{this.clientId} << entering ws"); + + SerializerUtils.CopyBlocking(gatewayStream, sslStream, waitEvent); + SerializerUtils.CopyBlocking(sslStream, gatewayStream, waitEvent); + + waitEvent.WaitOne(); + } + else if (resContentLength > 0) + { + Logger.LogInformation($"#{this.clientId} << {resContentLength} bytes written"); + SerializerUtils.CopyStream(gatewayStream, sslStream, resContentLength, CancellationToken.None); + } + else if (isChunked) + { + AutoResetEvent waitEvent = new AutoResetEvent(false); + Logger.LogInformation($"#{this.clientId} << entering chunked data"); + SerializerUtils.CopyUntilBlocking(gatewayStream, sslStream, Constants.CHUNKED_EOF, waitEvent); + waitEvent.WaitOne(); + } + + tcpStream.Flush(); + + if (!gatewayAllowsKeepAlive || !this.Parent.KeepAliveEnabled) + { + this.connectionCloseState = 5; + break; + } + } + catch + { + this.connectionCloseState = 6; + return; + } + finally + { + this.iteration++; + } + } + } + } + } + finally + { + Logger.LogInformation($"#{this.clientId} closed. State = {this.connectionCloseState}"); + } + } + + protected virtual void Dispose(bool disposing) + { + if (!this.disposedValue) + { + if (disposing) + { + this.Client.Close(); + } + + this.disposedValue = true; + } + } + + public void Dispose() + { + // Não altere este código. Coloque o código de limpeza no método 'Dispose(bool disposing)' + this.Dispose(disposing: true); + GC.SuppressFinalize(this); + } +} diff --git a/extensions/Sisk.SslProxy/HttpSerializer/HttpRequestReader.cs b/extensions/Sisk.SslProxy/HttpSerializer/HttpRequestReader.cs index 2c411ad..30b1371 100644 --- a/extensions/Sisk.SslProxy/HttpSerializer/HttpRequestReader.cs +++ b/extensions/Sisk.SslProxy/HttpSerializer/HttpRequestReader.cs @@ -7,9 +7,9 @@ // File name: HttpRequestReader.cs // Repository: https://github.com/sisk-http/core -using Sisk.Core.Http; using System.Diagnostics.CodeAnalysis; using System.Net.Sockets; +using Sisk.Core.Http; namespace Sisk.Ssl.HttpSerializer; @@ -44,21 +44,21 @@ public static bool TryReadHttp1Request( try { - Span _method = SerializerUtils.ReadUntil(readMemory.MethodBuffer, inboundStream, Constants.CH_SPACE); + ReadOnlySpan _method = SerializerUtils.ReadUntil(readMemory.MethodBuffer, inboundStream, Constants.CH_SPACE); if (_method.Length == 0) goto ret; method = SerializerUtils.DecodeString(_method); - Span _path = SerializerUtils.ReadUntil(readMemory.PathBuffer, inboundStream, Constants.CH_SPACE); + ReadOnlySpan _path = SerializerUtils.ReadUntil(readMemory.PathBuffer, inboundStream, Constants.CH_SPACE); if (_path.Length == 0) goto ret; path = SerializerUtils.DecodeString(_path); - Span _protocol = SerializerUtils.ReadUntil(readMemory.ProtocolBuffer, inboundStream, Constants.CH_RETURN); + ReadOnlySpan _protocol = SerializerUtils.ReadUntil(readMemory.ProtocolBuffer, inboundStream, Constants.CH_RETURN); if (_protocol.Length == 0) goto ret; proto = SerializerUtils.DecodeString(_protocol); inboundStream.ReadByte(); // \n - List<(string, string)> headerList = new List<(string, string)>(32); + List<(string, string)> headerList = new List<(string, string)>(Constants.HEADER_LINE_ALLOCATION); while (inboundStream.CanRead) { char? firstReadChar; @@ -75,10 +75,10 @@ public static bool TryReadHttp1Request( bool fwHeader = true; - Span headerName = SerializerUtils.ReadUntil(readMemory.PsHeaderName, inboundStream, Constants.CH_HSEP); + ReadOnlySpan headerName = SerializerUtils.ReadUntil(readMemory.PsHeaderName, inboundStream, Constants.CH_HSEP); if (headerName.Length == 0) goto ret; - Span headerValue = SerializerUtils.ReadUntil(readMemory.PsHeaderValue, inboundStream, Constants.CH_RETURN); + ReadOnlySpan headerValue = SerializerUtils.ReadUntil(readMemory.PsHeaderValue, inboundStream, Constants.CH_RETURN); if (headerValue.Length == 0) goto ret; inboundStream.ReadByte(); // \n diff --git a/extensions/Sisk.SslProxy/HttpSerializer/HttpResponseReader.cs b/extensions/Sisk.SslProxy/HttpSerializer/HttpResponseReader.cs index 8f36b3d..25ddf81 100644 --- a/extensions/Sisk.SslProxy/HttpSerializer/HttpResponseReader.cs +++ b/extensions/Sisk.SslProxy/HttpSerializer/HttpResponseReader.cs @@ -7,8 +7,8 @@ // File name: HttpResponseReader.cs // Repository: https://github.com/sisk-http/core -using Sisk.Core.Http; using System.Diagnostics.CodeAnalysis; +using Sisk.Core.Http; namespace Sisk.Ssl.HttpSerializer; @@ -42,21 +42,21 @@ public static bool TryReadHttp1Response( try { - Span _proto = SerializerUtils.ReadUntil(memory.ProtocolBuffer, inboundStream, Constants.CH_SPACE); + ReadOnlySpan _proto = SerializerUtils.ReadUntil(memory.ProtocolBuffer, inboundStream, Constants.CH_SPACE); if (_proto.Length == 0) goto ret; _ = SerializerUtils.DecodeString(_proto); //protocol - Span _statusCode = SerializerUtils.ReadUntil(memory.StatusCodeBuffer, inboundStream, Constants.CH_SPACE); + ReadOnlySpan _statusCode = SerializerUtils.ReadUntil(memory.StatusCodeBuffer, inboundStream, Constants.CH_SPACE); if (_statusCode.Length == 0) goto ret; statusCode = SerializerUtils.DecodeString(_statusCode); - Span _reason = SerializerUtils.ReadUntil(memory.StatusReasonBuffer, inboundStream, Constants.CH_RETURN); + ReadOnlySpan _reason = SerializerUtils.ReadUntil(memory.StatusReasonBuffer, inboundStream, Constants.CH_RETURN); if (_reason.Length == 0) goto ret; statusDescription = SerializerUtils.DecodeString(_reason); inboundStream.ReadByte(); // \n - List<(string, string)> headerList = new List<(string, string)>(32); + List<(string, string)> headerList = new List<(string, string)>(Constants.HEADER_LINE_ALLOCATION); while (inboundStream.CanRead) { char? firstReadChar; @@ -71,9 +71,10 @@ public static bool TryReadHttp1Response( firstReadChar = (char)c; } - Span headerName = SerializerUtils.ReadUntil(memory.PsHeaderName, inboundStream, Constants.CH_HSEP); + ReadOnlySpan headerName = SerializerUtils.ReadUntil(memory.PsHeaderName, inboundStream, Constants.CH_HSEP); if (headerName.Length == 0) goto ret; - Span headerValue = SerializerUtils.ReadUntil(memory.PsHeaderValue, inboundStream, Constants.CH_RETURN); + + ReadOnlySpan headerValue = SerializerUtils.ReadUntil(memory.PsHeaderValue, inboundStream, Constants.CH_RETURN); if (headerValue.Length == 0) goto ret; inboundStream.ReadByte(); // \n diff --git a/extensions/Sisk.SslProxy/SerializerUtils.cs b/extensions/Sisk.SslProxy/SerializerUtils.cs index de3b258..dcc2e8b 100644 --- a/extensions/Sisk.SslProxy/SerializerUtils.cs +++ b/extensions/Sisk.SslProxy/SerializerUtils.cs @@ -29,7 +29,7 @@ public static bool WaitUntilHasData(Stream stream, TimeSpan timeout) return true; } - public static Span ReadUntil(Span buffer, Stream inputStream, int intercept) + public static ReadOnlySpan ReadUntil(Span buffer, Stream inputStream, int intercept) { int current, size = 0; while (inputStream.CanRead && ((current = inputStream.ReadByte()) >= 0)) diff --git a/extensions/Sisk.SslProxy/SslProxy.cs b/extensions/Sisk.SslProxy/SslProxy.cs index adca82b..54fe456 100644 --- a/extensions/Sisk.SslProxy/SslProxy.cs +++ b/extensions/Sisk.SslProxy/SslProxy.cs @@ -7,14 +7,12 @@ // File name: SslProxy.cs // Repository: https://github.com/sisk-http/core -using Sisk.Core.Http; -using Sisk.Ssl.HttpSerializer; using System.Net; -using System.Net.Security; using System.Net.Sockets; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Threading.Channels; +using Sisk.Ssl.HttpSerializer; namespace Sisk.Ssl; @@ -160,272 +158,17 @@ async void ConsumerJobThread() { while (reader.TryRead(out var client)) { - new Thread(() => this.HandleTcpClient(client)) - .Start(); + new Thread(delegate () { this.HandleTcpClient(client); }).Start(); } } } void HandleTcpClient(TcpClient client) { - client.NoDelay = true; - bool keepAliveEnabled = this.KeepAliveEnabled; - int clientId = clientIdGenerator.Next(); - int connectionCloseState = 0; - int iteration = 0; - - if (this.disposedValue) - return; - - Logger.LogInformation($"#{clientId} open"); - - try - { - using (var tcpStream = client.GetStream()) - using (var sslStream = new SslStream(tcpStream, true)) - using (var gatewayClient = new TcpClient()) - { - try - { - gatewayClient.Connect(this.remoteEndpoint); - gatewayClient.NoDelay = true; - gatewayClient.SendTimeout = (int)(this.GatewayTimeout.TotalSeconds); - gatewayClient.ReceiveTimeout = (int)(this.GatewayTimeout.TotalSeconds); - } - catch (Exception ex) - { - connectionCloseState = 1; - Logger.LogInformation($"#{clientId}: Gateway connect exception: {ex.Message}"); - - HttpResponseWriter.TryWriteHttp1DefaultResponse( - new HttpStatusInformation(502), - "The host service ins't working.", - sslStream); - - return; - } - - // used by header values - Span secHXl1 = stackalloc byte[4096]; - // used by request path - Span secHXl2 = stackalloc byte[2048]; - // used by header name - Span secHLg1 = stackalloc byte[512]; - // used by response reason message - Span secHL21 = stackalloc byte[256]; - // used by request method and response status code - Span secHSm1 = stackalloc byte[8]; - // used by request and respones protocol - Span secHSm2 = stackalloc byte[8]; - - HttpRequestReaderSpan reqReaderMemory = new HttpRequestReaderSpan() - { - MethodBuffer = secHSm1, - PathBuffer = secHXl2, - ProtocolBuffer = secHSm2, - PsHeaderName = secHLg1, - PsHeaderValue = secHXl1 - }; - - HttpResponseReaderSpan resReaderMemory = new HttpResponseReaderSpan() - { - ProtocolBuffer = secHSm2, - StatusCodeBuffer = secHSm1, - StatusReasonBuffer = secHL21, - PsHeaderName = secHLg1, - PsHeaderValue = secHXl1 - }; - - using (var gatewayStream = gatewayClient.GetStream()) - { - gatewayStream.ReadTimeout = (int)(this.GatewayTimeout.TotalSeconds); - gatewayStream.WriteTimeout = (int)(this.GatewayTimeout.TotalSeconds); - - try - { - sslStream.AuthenticateAsServer(this.ServerCertificate, this.ClientCertificateRequired, this.AllowedProtocols, this.CheckCertificateRevocation); - } - catch (Exception ex) - { - Logger.LogInformation($"#{clientId}: SslAuthentication failed: {ex.Message}"); - connectionCloseState = 2; - - // write an error on the http stream - HttpResponseWriter.TryWriteHttp1DefaultResponse( - new HttpStatusInformation(400), - "The plain HTTP request was sent to an HTTPS port.", - sslStream); - - return; - } + using var gatewayClient = new TcpClient(); + using var connection = new HttpConnection(this, client); - while (client.Connected && !this.disposedValue) - { - try - { - if (!HttpRequestReader.TryReadHttp1Request( - clientId, - sslStream, - reqReaderMemory, - this.GatewayHostname, - client, - out var method, - out var path, - out var proto, - out var forwardedFor, - out var reqContentLength, - out var headers, - out var expectContinue)) - { - Logger.LogInformation($"#{clientId}: couldn't read request"); - connectionCloseState = 9; - - HttpResponseWriter.TryWriteHttp1DefaultResponse( - new HttpStatusInformation(400), - "The server received an invalid HTTP message.", - sslStream); - - return; - } - - Logger.LogInformation($"#{clientId} >> {method} {path}"); - - if (this.ProxyAuthorization is not null) - { - headers.Add((HttpKnownHeaderNames.ProxyAuthorization, this.ProxyAuthorization.ToString())); - } - - string clientIpAddress = ((IPEndPoint)client.Client.LocalEndPoint!).Address.ToString(); - if (forwardedFor is not null) - { - headers.Add((HttpKnownHeaderNames.XForwardedFor, forwardedFor + ", " + clientIpAddress)); - } - else - { - headers.Add((HttpKnownHeaderNames.XForwardedFor, clientIpAddress)); - } - - if (!HttpRequestWriter.TryWriteHttpV1Request(clientId, gatewayStream, method, path, headers)) - { - HttpResponseWriter.TryWriteHttp1DefaultResponse( - new HttpStatusInformation(502), - "The host service ins't working.", - sslStream); - - connectionCloseState = 3; - return; - } - - if (expectContinue) - { - goto readGatewayResponse; - } - - redirClientContent: - if (reqContentLength > 0) - { - if (!SerializerUtils.CopyStream(sslStream, gatewayStream, reqContentLength, CancellationToken.None)) - { - // client couldn't send the full content - break; - } - } - - readGatewayResponse: - if (!HttpResponseReader.TryReadHttp1Response( - clientId, - gatewayStream, - resReaderMemory, - out var resStatusCode, - out var resStatusDescr, - out var resHeaders, - out var resContentLength, - out var isChunked, - out var gatewayAllowsKeepAlive, - out var isWebSocket)) - { - HttpResponseWriter.TryWriteHttp1DefaultResponse( - new HttpStatusInformation(502), - "The host service ins't working.", - sslStream); - - connectionCloseState = 4; - return; - } - - Logger.LogInformation($"#{clientId} << {resStatusCode} {resStatusDescr}"); - - // TODO: check if client wants to keep alive - if (gatewayAllowsKeepAlive && keepAliveEnabled) - { - ; - } - else - { - resHeaders.Add(("Connection", "close")); - } -#if VERBOSE - if (!expectContinue) - resHeaders.Add(("X-Debug-Connection-Id", clientId.ToString())); -#endif - - HttpResponseWriter.TryWriteHttp1Response(clientId, sslStream, resStatusCode, resStatusDescr, resHeaders); - - if (expectContinue && resStatusCode == "100") - { - expectContinue = false; - goto redirClientContent; - } - - if (isWebSocket) - { - AutoResetEvent waitEvent = new AutoResetEvent(false); - - Logger.LogInformation($"#{clientId} << entering ws"); - - SerializerUtils.CopyBlocking(gatewayStream, sslStream, waitEvent); - SerializerUtils.CopyBlocking(sslStream, gatewayStream, waitEvent); - - waitEvent.WaitOne(); - } - else if (resContentLength > 0) - { - Logger.LogInformation($"#{clientId} << {resContentLength} bytes written"); - SerializerUtils.CopyStream(gatewayStream, sslStream, resContentLength, CancellationToken.None); - } - else if (isChunked) - { - AutoResetEvent waitEvent = new AutoResetEvent(false); - Logger.LogInformation($"#{clientId} << entering chunked data"); - SerializerUtils.CopyUntilBlocking(gatewayStream, sslStream, Constants.CHUNKED_EOF, waitEvent); - waitEvent.WaitOne(); - } - - tcpStream.Flush(); - - if (!gatewayAllowsKeepAlive || !keepAliveEnabled) - { - connectionCloseState = 5; - break; - } - } - catch - { - connectionCloseState = 6; - return; - } - finally - { - iteration++; - } - } - } - } - } - finally - { - Logger.LogInformation($"#{clientId} closed. State = {connectionCloseState}"); - } + connection.HandleConnection(); } private void Dispose(bool disposing) diff --git a/src/Http/HttpResponseExtensions.cs b/src/Http/HttpResponseExtensions.cs index 39fb467..b85f10d 100644 --- a/src/Http/HttpResponseExtensions.cs +++ b/src/Http/HttpResponseExtensions.cs @@ -167,4 +167,14 @@ public static THttpResponse WithCookie(this THttpResponse respons response.SetCookie(cookie); return response; } + + /// + /// Gets a completed from the given as the result. + /// + /// The type which implements . + /// The object. + public static Task AsTask(this THttpResponse response) where THttpResponse : HttpResponse + { + return Task.FromResult(response); + } } diff --git a/src/Http/HttpServer__Core.cs b/src/Http/HttpServer__Core.cs index a500568..7d311cd 100644 --- a/src/Http/HttpServer__Core.cs +++ b/src/Http/HttpServer__Core.cs @@ -60,7 +60,6 @@ internal static void ApplyHttpContentHeaders(HttpListenerResponse response, Http if (contentHeaders.ContentEncoding.Count > 0 && response.Headers.GetValues(HttpKnownHeaderNames.ContentEncoding)?.Length is 0 or null) response.AppendHeader(HttpKnownHeaderNames.ContentEncoding, string.Join(", ", contentHeaders.ContentEncoding)); - } internal static void SetCorsHeaders(HttpListenerRequest baseRequest, CrossOriginResourceSharingHeaders cors, HttpListenerResponse baseResponse) @@ -134,6 +133,7 @@ private void ListenerCallback(IAsyncResult result) this.httpListener.BeginGetContext(this.ListenerCallback, null); HttpListenerContext context = this.httpListener.EndGetContext(result); + this.ProcessRequest(context); } diff --git a/src/Http/RequestContext.cs b/src/Http/RequestContext.cs new file mode 100644 index 0000000..f220f12 --- /dev/null +++ b/src/Http/RequestContext.cs @@ -0,0 +1,14 @@ +// The Sisk Framework source code +// Copyright (c) 2024 PROJECT PRINCIPIUM +// +// The code below is licensed under the MIT license as +// of the date of its publication, available at +// +// File name: RequestContext.cs +// Repository: https://github.com/sisk-http/core + +namespace Sisk.Core.Http; + +public static class RequestContext +{ +}