From 21130c889bd8564b201aa16c8f645abdf85d374a Mon Sep 17 00:00:00 2001 From: vnugent Date: Sun, 31 Mar 2024 17:00:45 -0400 Subject: Squashed commit of the following: commit 448a93bb1d18d032087afe2476ffccb98634a54c Author: vnugent Date: Sun Mar 31 16:56:51 2024 -0400 ci: fix third-party dir cleanup commit 9afed1427472da1ea13079f98dbe27339e55ee7d Author: vnugent Date: Sun Mar 31 16:43:15 2024 -0400 perf: Deprecate unsafememoryhandle span extensions commit 3ff90da4f02af47ea6d233fdd4445337ebe36452 Author: vnugent Date: Sat Mar 30 21:36:18 2024 -0400 refactor: Updates, advanced tracing, http optimizations commit 8d6b79b5ae309b36f265ba81529bcef8bfcd7414 Merge: 6c1667b 5585915 Author: vnugent Date: Sun Mar 24 21:01:31 2024 -0400 Merge branch 'main' into develop commit 6c1667be23597513537f8190e2f55d65eb9b7c7a Author: vnugent Date: Fri Mar 22 12:01:53 2024 -0400 refactor: Overhauled native library loading and lazy init commit ebf688f2f974295beabf7b5def7e6f6f150551d0 Author: vnugent Date: Wed Mar 20 22:16:17 2024 -0400 refactor: Update compression header files and macros + Ci build commit 9c7b564911080ccd5cbbb9851a0757b05e1e9047 Author: vnugent Date: Tue Mar 19 21:54:49 2024 -0400 refactor: JWK overhaul & add length getter to FileUpload commit 6d8c3444e09561e5957491b3cc1ae858e0abdd14 Author: vnugent Date: Mon Mar 18 16:13:20 2024 -0400 feat: Add FNV1a software checksum and basic correction tests commit 00d182088cecefc08ca80b1faee9bed3f215f40b Author: vnugent Date: Fri Mar 15 01:05:27 2024 -0400 chore: #6 Use utils filewatcher instead of built-in commit d513c10d9895c6693519ef1d459c6a5a76929436 Author: vnugent Date: Sun Mar 10 21:58:14 2024 -0400 source tree project location updated --- lib/Net.Http/src/Core/HttpContext.cs | 6 +- lib/Net.Http/src/Core/HttpEvent.cs | 15 ++-- lib/Net.Http/src/Core/HttpServerProcessing.cs | 47 ++++++++----- lib/Net.Http/src/Core/IHttpLifeCycle.cs | 7 +- lib/Net.Http/src/Core/InitDataBuffer.cs | 36 ++++++++-- lib/Net.Http/src/Core/Request/HttpInputStream.cs | 81 ++++++++++++++-------- lib/Net.Http/src/Core/Request/HttpRequest.cs | 3 - lib/Net.Http/src/Core/Request/HttpRequestBody.cs | 70 ------------------- .../src/Core/Request/HttpRequestExtensions.cs | 71 +++++++++++-------- .../src/Core/RequestParse/Http11ParseExtensions.cs | 12 ++-- lib/Net.Http/src/Core/Response/HttpResponse.cs | 18 +++-- .../src/Core/Response/HttpStreamResponse.cs | 7 -- .../src/Core/Response/HttpstreamResponse.cs | 7 -- lib/Net.Http/src/Helpers/HttpRange.cs | 9 ++- lib/Net.Http/src/HttpBufferConfig.cs | 8 ++- 15 files changed, 194 insertions(+), 203 deletions(-) delete mode 100644 lib/Net.Http/src/Core/Request/HttpRequestBody.cs (limited to 'lib/Net.Http') diff --git a/lib/Net.Http/src/Core/HttpContext.cs b/lib/Net.Http/src/Core/HttpContext.cs index 8cecf30..f742e97 100644 --- a/lib/Net.Http/src/Core/HttpContext.cs +++ b/lib/Net.Http/src/Core/HttpContext.cs @@ -96,7 +96,7 @@ namespace VNLib.Net.Http.Core */ if (supportedMethods != CompressionMethod.None) { - Debug.Assert(server.Config.CompressorManager != null, "Expected non-null provider"); + Debug.Assert(server.Config.CompressorManager != null, "Expected non-null compressor manager"); _compressor = new ManagedHttpCompressor(server.Config.CompressorManager); } else @@ -153,7 +153,7 @@ namespace VNLib.Net.Http.Core Buffers.AllocateBuffer(ParentServer.Config.MemoryPool); //Init new connection - Response.OnNewConnection(); + Response.OnNewConnection(ctx.ConnectionStream); } /// @@ -199,7 +199,7 @@ namespace VNLib.Net.Http.Core Response.OnRelease(); //Free buffers - Buffers.FreeAll(true); + Buffers.FreeAll(ParentServer.Config.BufferConfig.ZeroBuffersOnDisconnect); return true; } diff --git a/lib/Net.Http/src/Core/HttpEvent.cs b/lib/Net.Http/src/Core/HttpEvent.cs index 8867a12..37c5ab5 100644 --- a/lib/Net.Http/src/Core/HttpEvent.cs +++ b/lib/Net.Http/src/Core/HttpEvent.cs @@ -33,18 +33,11 @@ using VNLib.Net.Http.Core.Response; namespace VNLib.Net.Http { - internal sealed class HttpEvent : MarshalByRefObject, IHttpEvent + internal sealed class HttpEvent(HttpContext ctx) : MarshalByRefObject, IHttpEvent { - private HttpContext Context; - private ConnectionInfo _ci; - private FileUpload[] _uploads; - - internal HttpEvent(HttpContext ctx) - { - Context = ctx; - _ci = new ConnectionInfo(ctx); - _uploads = ctx.Request.CopyUploads(); - } + private HttpContext Context = ctx; + private ConnectionInfo _ci = new(ctx); + private FileUpload[] _uploads = ctx.Request.CopyUploads(); /// IConnectionInfo IHttpEvent.Server => _ci; diff --git a/lib/Net.Http/src/Core/HttpServerProcessing.cs b/lib/Net.Http/src/Core/HttpServerProcessing.cs index 8a9ca07..7770ad7 100644 --- a/lib/Net.Http/src/Core/HttpServerProcessing.cs +++ b/lib/Net.Http/src/Core/HttpServerProcessing.cs @@ -181,8 +181,16 @@ namespace VNLib.Net.Http return false; } + bool keepalive = true; + + //Handle an error parsing the request + if(!PreProcessRequest(context, (HttpStatusCode)status, ref keepalive)) + { + return false; + } + //process the request - bool keepalive = await ProcessRequestAsync(context, (HttpStatusCode)status); + bool processSuccess = await ProcessRequestAsync(context); #if DEBUG static void WriteConnectionDebugLog(HttpServer server, HttpContext context) @@ -210,18 +218,17 @@ namespace VNLib.Net.Http WriteConnectionDebugLog(this, context); } #endif - - //Close the response + await context.WriteResponseAsync(); - - //Flush the stream before returning + await context.FlushTransportAsync(); /* * If an alternate protocol was specified, we need to break the keepalive loop + * the handler will manage the alternate protocol */ - return keepalive & context.AlternateProtocol == null; + return processSuccess & keepalive & context.AlternateProtocol == null; } finally { @@ -259,8 +266,7 @@ namespace VNLib.Net.Http //Get the parse buffer IHttpHeaderParseBuffer parseBuffer = ctx.Buffers.RequestHeaderParseBuffer; - - //Init parser + TransportReader reader = new (ctx.GetTransport(), parseBuffer, _config.HttpEncoding, HeaderLineTermination); HttpStatusCode code; @@ -278,13 +284,13 @@ namespace VNLib.Net.Http } //Parse the headers - if ((code = ctx.Request.Http1ParseHeaders(ref parseState, ref reader, Config, lineBuf)) > 0) + if ((code = ctx.Request.Http1ParseHeaders(ref parseState, ref reader, in _config, lineBuf)) > 0) { return code; } //Prepare entity body for request - if ((code = ctx.Request.Http1PrepareEntityBody(ref parseState, ref reader, Config)) > 0) + if ((code = ctx.Request.Http1PrepareEntityBody(ref parseState, ref reader, in _config)) > 0) { return code; } @@ -303,8 +309,7 @@ namespace VNLib.Net.Http } } - [MethodImpl(MethodImplOptions.AggressiveOptimization)] - private async Task ProcessRequestAsync(HttpContext context, HttpStatusCode status) + private bool PreProcessRequest(HttpContext context, HttpStatusCode status, ref bool keepalive) { //Check status if (status != 0) @@ -340,12 +345,12 @@ namespace VNLib.Net.Http context.Respond(HttpStatusCode.ServiceUnavailable); return false; } - + //Store keepalive value from request, and check if keepalives are enabled by the configuration - bool keepalive = context.Request.State.KeepAlive & _config.ConnectionKeepAlive > TimeSpan.Zero; - + keepalive = context.Request.State.KeepAlive & _config.ConnectionKeepAlive > TimeSpan.Zero; + //Set connection header (only for http1.1) - + if (keepalive) { /* @@ -363,6 +368,12 @@ namespace VNLib.Net.Http context.Response.Headers.Set(HttpResponseHeader.Connection, "closed"); } + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + private async Task ProcessRequestAsync(HttpContext context) + { //Get the server root for the specified location or fallback to a wildcard host if one is selected IWebRoot? root = ServerRoots!.GetValueOrDefault(context.Request.State.Location.DnsSafeHost, _wildcardRoot); @@ -370,7 +381,7 @@ namespace VNLib.Net.Http { context.Respond(HttpStatusCode.NotFound); //make sure control leaves - return keepalive; + return true; } //Check the expect header and return an early status code @@ -449,7 +460,7 @@ namespace VNLib.Net.Http * * For now I will allow it. */ - return keepalive; + return true; } } diff --git a/lib/Net.Http/src/Core/IHttpLifeCycle.cs b/lib/Net.Http/src/Core/IHttpLifeCycle.cs index 9ba5ff1..12a1f3f 100644 --- a/lib/Net.Http/src/Core/IHttpLifeCycle.cs +++ b/lib/Net.Http/src/Core/IHttpLifeCycle.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023 Vaughn Nugent +* Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Net.Http @@ -58,10 +58,5 @@ namespace VNLib.Net.Http.Core /// method should not throw exceptions /// void OnComplete(); - - /// - /// Raised when a new connection is established on the current context - /// - void OnNewConnection(); } } \ No newline at end of file diff --git a/lib/Net.Http/src/Core/InitDataBuffer.cs b/lib/Net.Http/src/Core/InitDataBuffer.cs index 6a400bb..6d559cd 100644 --- a/lib/Net.Http/src/Core/InitDataBuffer.cs +++ b/lib/Net.Http/src/Core/InitDataBuffer.cs @@ -24,11 +24,13 @@ using System; using System.Buffers; +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using VNLib.Utils; using VNLib.Utils.Extensions; +using VNLib.Utils.Memory; namespace VNLib.Net.Http.Core { @@ -84,6 +86,14 @@ namespace VNLib.Net.Http.Core set => MemoryMarshal.Write(_positionSegment, in value); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private readonly int GetDataPosition() + { + Debug.Assert(Position >= 0 && Position <= _dataSize, "Invalid position value"); + //Points to the first byte of the data segment to read from + return POSITION_SEG_SIZE + Position; + } + /// /// Get the amount of data remaining in the data buffer /// @@ -93,6 +103,19 @@ namespace VNLib.Net.Http.Core get => _dataSize - Position; } + /// + /// Performs a discard in a single operation by setting the + /// position to the end of the data buffer + /// + /// The number of bytes that were remaining in the buffer before the discard + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal readonly int DiscardRemaining() + { + int remaining = Remaining; + Position = _dataSize; + return remaining; + } + /// /// Reads data from the internal buffer into the supplied buffer /// @@ -103,11 +126,14 @@ namespace VNLib.Net.Http.Core { //Calc how many bytes can be read into the output buffer int bytesToRead = Math.Min(Remaining, buffer.Length); - - Span btr = DataSegment.Slice(Position, bytesToRead); - - //Write data to output buffer - btr.CopyTo(buffer); + + MemoryUtil.Memmove( + ref MemoryMarshal.GetArrayDataReference(_buffer), + (nuint)GetDataPosition(), + ref MemoryMarshal.GetReference(buffer), + 0, + (nuint)bytesToRead + ); //Update position pointer Position += bytesToRead; diff --git a/lib/Net.Http/src/Core/Request/HttpInputStream.cs b/lib/Net.Http/src/Core/Request/HttpInputStream.cs index e36d1e4..ccaa336 100644 --- a/lib/Net.Http/src/Core/Request/HttpInputStream.cs +++ b/lib/Net.Http/src/Core/Request/HttpInputStream.cs @@ -40,14 +40,18 @@ namespace VNLib.Net.Http.Core /// internal sealed class HttpInputStream(IHttpContextInformation ContextInfo) : Stream { - - private long ContentLength; - private Stream? InputStream; - private long _position; - + private StreamState _state; private InitDataBuffer? _initalData; - private long Remaining => Math.Max(ContentLength - _position, 0); + private long Remaining + { + get + { + long remaining = _state.ContentLength - _state.Position; + Debug.Assert(remaining >= 0, "Input stream overrun. Read more data than was available for the connection"); + return remaining; + } + } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void OnComplete() @@ -59,12 +63,7 @@ namespace VNLib.Net.Http.Core _initalData = null; } - //Remove stream cache copy - InputStream = null; - //Reset position - _position = 0; - //reset content length - ContentLength = 0; + _state = default; } /// @@ -74,9 +73,8 @@ namespace VNLib.Net.Http.Core /// The number of bytes to allow being read from the transport or initial buffer internal ref InitDataBuffer? Prepare(long contentLength) { - ContentLength = contentLength; - //Cache transport stream - InputStream = ContextInfo.GetTransport(); + _state.ContentLength = contentLength; + _state.InputStream = ContextInfo.GetTransport(); return ref _initalData; } @@ -105,13 +103,13 @@ namespace VNLib.Net.Http.Core /// /// Gets the total size of the entity body (aka Content-Length) /// - public override long Length => ContentLength; + public override long Length => _state.ContentLength; /// /// Gets the number of bytes currently read from the entity body, setting the /// position is a NOOP /// - public override long Position { get => _position; set { } } + public override long Position { get => _state.Position; set { } } /// /// NOOP @@ -149,19 +147,19 @@ namespace VNLib.Net.Http.Core writer.Advance(read); //Update position - _position += read; + _state.Position += read; } //See if data is still remaining to be read from transport (reamining size is also the amount of data that can be read) if (writer.RemainingSize > 0) { //Read from transport - ERRNO read = InputStream!.Read(writer.Remaining); + ERRNO read = _state.InputStream!.Read(writer.Remaining); //Update writer position writer.Advance(read); - _position += read; + _state.Position += read; } //Return number of bytes written to the buffer @@ -196,19 +194,19 @@ namespace VNLib.Net.Http.Core writer.Advance(read); //Update position - _position += read; + _state.Position += read; } //See if data is still remaining to be read from transport (reamining size is also the amount of data that can be read) if (writer.RemainingSize > 0) { //Read from transport - int read = await InputStream!.ReadAsync(writer.Remaining, cancellationToken).ConfigureAwait(true); + int read = await _state.InputStream!.ReadAsync(writer.Remaining, cancellationToken).ConfigureAwait(true); //Update writer position writer.Advance(read); - _position += read; + _state.Position += read; } //Return number of bytes written to the buffer @@ -232,7 +230,7 @@ namespace VNLib.Net.Http.Core if(_initalData.HasValue && remaining <= _initalData.Value.Remaining) { //All data has been buffred, so just clear the buffer - _position = Length; + _state.Position = Length; return ValueTask.CompletedTask; } //We must actaully disacrd data from the stream @@ -244,14 +242,31 @@ namespace VNLib.Net.Http.Core private async ValueTask DiscardStreamDataAsync() { - int read; - do + DiscardInternalBuffer(); + + int read, bytesToRead = (int)Math.Min(HttpServer.WriteOnlyScratchBuffer.Length, Remaining); + + while (bytesToRead > 0) { //Read data to the discard buffer until reading is completed (read == 0) - read = await ReadAsync(HttpServer.WriteOnlyScratchBuffer, CancellationToken.None) + read = await _state.InputStream!.ReadAsync(HttpServer.WriteOnlyScratchBuffer.Slice(0, bytesToRead), CancellationToken.None) .ConfigureAwait(true); - } while (read > 0); + //Update position + _state.Position += read; + + //Recalculate the number of bytes to read + bytesToRead = (int)Math.Min(HttpServer.WriteOnlyScratchBuffer.Length, Remaining); + } + } + + private void DiscardInternalBuffer() + { + if (_initalData.HasValue) + { + //Update the stream position with remaining data + _state.Position += _initalData.Value.DiscardRemaining(); + } } /// @@ -260,12 +275,20 @@ namespace VNLib.Net.Http.Core /// /// /// - public override long Seek(long offset, SeekOrigin origin) => _position; + public override long Seek(long offset, SeekOrigin origin) => _state.Position; /// public override void SetLength(long value) => throw new NotSupportedException(); /// public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + + + private struct StreamState + { + public Stream? InputStream; + public long Position; + public long ContentLength; + } } } \ No newline at end of file diff --git a/lib/Net.Http/src/Core/Request/HttpRequest.cs b/lib/Net.Http/src/Core/Request/HttpRequest.cs index ce8257f..3ebf0d4 100644 --- a/lib/Net.Http/src/Core/Request/HttpRequest.cs +++ b/lib/Net.Http/src/Core/Request/HttpRequest.cs @@ -81,9 +81,6 @@ namespace VNLib.Net.Http.Core void IHttpLifeCycle.OnRelease() { } - - void IHttpLifeCycle.OnNewConnection() - { } void IHttpLifeCycle.OnNewRequest() { } diff --git a/lib/Net.Http/src/Core/Request/HttpRequestBody.cs b/lib/Net.Http/src/Core/Request/HttpRequestBody.cs deleted file mode 100644 index e39a35c..0000000 --- a/lib/Net.Http/src/Core/Request/HttpRequestBody.cs +++ /dev/null @@ -1,70 +0,0 @@ -/* -* Copyright (c) 2023 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Net.Http -* File: HttpRequestBody.cs -* -* HttpRequestBody.cs is part of VNLib.Net.Http which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Net.Http is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Net.Http is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; -using System.Collections.Generic; - -namespace VNLib.Net.Http.Core -{ - /// - /// Represents a higher-level request entity body (query arguments, request body etc) - /// that has been parsed and captured - /// - internal class HttpRequestBody - { - public readonly List Uploads; - public readonly Dictionary RequestArgs; - public readonly Dictionary QueryArgs; - - public HttpRequestBody() - { - Uploads = new(1); - - //Request/query args should not be case sensitive - RequestArgs = new(StringComparer.OrdinalIgnoreCase); - QueryArgs = new(StringComparer.OrdinalIgnoreCase); - } - - /// - /// Releases all resources used by the current instance - /// - public void OnComplete() - { - //Only enumerate/clear if file uplaods are present - if (Uploads.Count > 0) - { - //Dispose all initialized files - for (int i = 0; i < Uploads.Count; i++) - { - Uploads[i].Free(); - } - //Emtpy list - Uploads.Clear(); - } - //Clear request args and file uplaods - RequestArgs.Clear(); - QueryArgs.Clear(); - } - } -} \ No newline at end of file diff --git a/lib/Net.Http/src/Core/Request/HttpRequestExtensions.cs b/lib/Net.Http/src/Core/Request/HttpRequestExtensions.cs index 69bd2af..878622e 100644 --- a/lib/Net.Http/src/Core/Request/HttpRequestExtensions.cs +++ b/lib/Net.Http/src/Core/Request/HttpRequestExtensions.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023 Vaughn Nugent +* Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Net.Http @@ -91,9 +91,17 @@ namespace VNLib.Net.Http.Core [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsCrossOrigin(this HttpRequest Request) { - return Request.State.Origin != null - && (!Request.State.Origin.Authority.Equals(Request.State.Location.Authority, StringComparison.Ordinal) - || !Request.State.Origin.Scheme.Equals(Request.State.Location.Scheme, StringComparison.Ordinal)); + if(Request.State.Origin is null) + { + return false; + } + + //Get the origin string components for comparison (allocs new strings :( ) + string locOrigin = Request.State.Location.GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped); + string reqOrigin = Request.State.Origin.GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped); + + //If origin components are not equal, this is a cross origin request + return !string.Equals(locOrigin, reqOrigin, StringComparison.OrdinalIgnoreCase); } /// @@ -136,18 +144,6 @@ namespace VNLib.Net.Http.Core { HttpRequest request = context.Request; IHttpContextInformation info = context; - IHttpMemoryPool pool = context.ParentServer.Config.MemoryPool; - - //Gets the max form data buffer size to help calculate the initial char buffer size - int maxBufferSize = context.ParentServer.Config.BufferConfig.FormDataBufferSize; - - //Calculate a largest available buffer to read the entire stream or up to the maximum buffer size - int bufferSize = (int)Math.Min(request.InputStream.Length, maxBufferSize); - - //Get the form data buffer (should be cost free) - Memory formBuffer = context.Buffers.GetFormDataBuffer(); - - Debug.Assert(!formBuffer.IsEmpty, "GetFormDataBuffer() returned an empty memory buffer"); switch (request.State.ContentType) { @@ -157,10 +153,9 @@ namespace VNLib.Net.Http.Core case ContentType.UrlEncoded: { //Alloc the form data character buffer, this will need to grow if the form data is larger than the buffer - using IResizeableMemoryHandle urlbody = pool.AllocFormDataBuffer(bufferSize); - - //Load char buffer from stream - int chars = await BufferInputStream(request.InputStream, urlbody, formBuffer, info.Encoding); + using IResizeableMemoryHandle urlbody = AllocFdBuffer(context); + + int chars = await BufferInputStreamAsChars(request.InputStream, urlbody, GetFdBuffer(context), info.Encoding); //Get the body as a span, and split the 'string' at the & character ((ReadOnlySpan)urlbody.AsSpan(0, chars)) @@ -175,12 +170,10 @@ namespace VNLib.Net.Http.Core { break; } - - //Alloc the form data buffer - using IResizeableMemoryHandle formBody = pool.AllocFormDataBuffer(bufferSize); - - //Load char buffer from stream - int chars = await BufferInputStream(request.InputStream, formBody, formBuffer, info.Encoding); + + using IResizeableMemoryHandle formBody = AllocFdBuffer(context); + + int chars = await BufferInputStreamAsChars(request.InputStream, formBody, GetFdBuffer(context), info.Encoding); //Split the body as a span at the boundries ((ReadOnlySpan)formBody.AsSpan(0, chars)) @@ -194,6 +187,25 @@ namespace VNLib.Net.Http.Core request.AddFileUpload(new(request.InputStream, false, request.State.ContentType, null)); break; } + + + static IResizeableMemoryHandle AllocFdBuffer(HttpContext context) + { + //Gets the max form data buffer size to help calculate the initial char buffer size + int maxBufferSize = context.ParentServer.Config.BufferConfig.FormDataBufferSize; + + //Calculate a largest available buffer to read the entire stream or up to the maximum buffer size + int buffersize = (int)Math.Min(context.Request.InputStream.Length, maxBufferSize); + + return context.ParentServer.Config.MemoryPool.AllocFormDataBuffer(buffersize); + } + + static Memory GetFdBuffer(HttpContext context) + { + Memory formBuffer = context.Buffers.GetFormDataBuffer(); + Debug.Assert(!formBuffer.IsEmpty, "GetFormDataBuffer() returned an empty memory buffer"); + return formBuffer; + } } /* @@ -203,7 +215,12 @@ namespace VNLib.Net.Http.Core * We assume the parsing method checked the size of the input stream so we can assume its safe to read * all of it into memory. */ - private static async ValueTask BufferInputStream(Stream stream, IResizeableMemoryHandle charBuffer, Memory binBuffer, Encoding encoding) + private static async ValueTask BufferInputStreamAsChars( + Stream stream, + IResizeableMemoryHandle charBuffer, + Memory binBuffer, + Encoding encoding + ) { int length = 0; do diff --git a/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs b/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs index 86535c3..cabb723 100644 --- a/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs +++ b/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs @@ -117,7 +117,6 @@ namespace VNLib.Net.Http.Core //Try to parse the requested http version, only supported versions if ((reqState.HttpVersion = HttpHelpers.ParseHttpVersion(requestLine[endloc..])) == HttpVersion.None) { - //Return not supported return HttpStatusCode.HttpVersionNotSupported; } @@ -450,18 +449,18 @@ namespace VNLib.Net.Http.Core } //Set full http range - reqState.Range = new(startRangeValue, endRangeValue, HttpRangeType.FullRange); + reqState.Range = HttpRange.FullRange(startRangeValue, endRangeValue); } else { //From-end range - reqState.Range = new(0, endRangeValue, HttpRangeType.FromEnd); + reqState.Range = HttpRange.FromEnd(endRangeValue); } } else if(hasStartRange) { //Valid start range only, so from start range - reqState.Range = new(startRangeValue, 0, HttpRangeType.FromStart); + reqState.Range = HttpRange.FromStart(startRangeValue); } //No valid range values } @@ -554,7 +553,10 @@ namespace VNLib.Net.Http.Core //Bad format to include a message body with a GET, HEAD, or TRACE request if (parseState.ContentLength > 0) { - Config.ServerLog.Debug("Message body received from {ip} with GET, HEAD, or TRACE request, was considered an error and the request was dropped", reqState.RemoteEndPoint); + Config.ServerLog.Debug( + "Message body received from {ip} with GET, HEAD, or TRACE request, was considered an error and the request was dropped", + reqState.RemoteEndPoint + ); return HttpStatusCode.BadRequest; } else diff --git a/lib/Net.Http/src/Core/Response/HttpResponse.cs b/lib/Net.Http/src/Core/Response/HttpResponse.cs index ec9879b..06f114c 100644 --- a/lib/Net.Http/src/Core/Response/HttpResponse.cs +++ b/lib/Net.Http/src/Core/Response/HttpResponse.cs @@ -119,10 +119,11 @@ namespace VNLib.Net.Http.Core.Response //Write headers for (int i = 0; i < Headers.Count; i++) { - writer.Append(Headers.Keys[i]); //Write header key - writer.Append(": "); //Write separator - writer.Append(Headers[i]); //Write the header value - writer.Append(HttpHelpers.CRLF); //Crlf + //: \r\n + writer.Append(Headers.Keys[i]); + writer.Append(": "); + writer.Append(Headers[i]); + writer.Append(HttpHelpers.CRLF); } //Remove writen headers @@ -131,7 +132,6 @@ namespace VNLib.Net.Http.Core.Response //Write cookies if any are set if (Cookies.Count > 0) { - //Enumerate and write foreach (HttpCookie cookie in Cookies) { writer.Append("Set-Cookie: "); @@ -141,8 +141,7 @@ namespace VNLib.Net.Http.Core.Response writer.Append(HttpHelpers.CRLF); } - - //Clear all current cookies + Cookies.Clear(); } @@ -302,10 +301,9 @@ namespace VNLib.Net.Http.Core.Response } /// - public void OnNewConnection() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void OnNewConnection(Stream transport) { - //Get the transport stream and init streams - Stream transport = ContextInfo.GetTransport(); ReusableChunkedStream.OnNewConnection(transport); ReusableDirectStream.OnNewConnection(transport); } diff --git a/lib/Net.Http/src/Core/Response/HttpStreamResponse.cs b/lib/Net.Http/src/Core/Response/HttpStreamResponse.cs index b08d2ab..679d8fe 100644 --- a/lib/Net.Http/src/Core/Response/HttpStreamResponse.cs +++ b/lib/Net.Http/src/Core/Response/HttpStreamResponse.cs @@ -22,13 +22,6 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ -/* - * This file handles response entity processing. It handles in-memory response - * processing, as well as stream response processing. It handles constraints - * such as content-range limits. I tried to eliminate or reduce the amount of - * memory copying required to process the response entity. - */ - using System; using System.IO; using System.Threading; diff --git a/lib/Net.Http/src/Core/Response/HttpstreamResponse.cs b/lib/Net.Http/src/Core/Response/HttpstreamResponse.cs index b08d2ab..679d8fe 100644 --- a/lib/Net.Http/src/Core/Response/HttpstreamResponse.cs +++ b/lib/Net.Http/src/Core/Response/HttpstreamResponse.cs @@ -22,13 +22,6 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ -/* - * This file handles response entity processing. It handles in-memory response - * processing, as well as stream response processing. It handles constraints - * such as content-range limits. I tried to eliminate or reduce the amount of - * memory copying required to process the response entity. - */ - using System; using System.IO; using System.Threading; diff --git a/lib/Net.Http/src/Helpers/HttpRange.cs b/lib/Net.Http/src/Helpers/HttpRange.cs index cedcc40..af8c8a8 100644 --- a/lib/Net.Http/src/Helpers/HttpRange.cs +++ b/lib/Net.Http/src/Helpers/HttpRange.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023 Vaughn Nugent +* Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Net.Http @@ -40,5 +40,12 @@ namespace VNLib.Net.Http /// The ending range value /// True if the range values are valid, false otherwise public static bool IsValidRangeValue(ulong start, ulong end) => start <= end; + + + internal static HttpRange FromStart(ulong start) => new(start, 0, HttpRangeType.FromStart); + + internal static HttpRange FromEnd(ulong end) => new(0, end, HttpRangeType.FromEnd); + + internal static HttpRange FullRange(ulong start, ulong end) => new(start, end, HttpRangeType.FullRange); } } \ No newline at end of file diff --git a/lib/Net.Http/src/HttpBufferConfig.cs b/lib/Net.Http/src/HttpBufferConfig.cs index fa3ad21..81fea12 100644 --- a/lib/Net.Http/src/HttpBufferConfig.cs +++ b/lib/Net.Http/src/HttpBufferConfig.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023 Vaughn Nugent +* Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Net.Http @@ -74,5 +74,11 @@ namespace VNLib.Net.Http /// May be set to 0 when is set to null (compression is disabled). /// public readonly int ChunkedResponseAccumulatorSize { get; init; } = 64 * 1024; + + /// + /// When a transport connection is closed, dedicated buffers are released back to the + /// heap. This setting controls whether the buffers are all zeroed before being released. + /// + public readonly bool ZeroBuffersOnDisconnect { get; init; } = true; } } \ No newline at end of file -- cgit