diff options
Diffstat (limited to 'lib/Net.Http/src')
-rw-r--r-- | lib/Net.Http/src/Core/Compression/IHttpCompressorManager.cs | 7 | ||||
-rw-r--r-- | lib/Net.Http/src/Core/Compression/IResponseCompressor.cs | 10 | ||||
-rw-r--r-- | lib/Net.Http/src/Core/Compression/ManagedHttpCompressor.cs | 34 | ||||
-rw-r--r-- | lib/Net.Http/src/Core/Response/ResponseWriter.cs | 40 | ||||
-rw-r--r-- | lib/Net.Http/src/IMemoryResponseReader.cs (renamed from lib/Net.Http/src/IMemoryResponseEntity.cs) | 6 |
5 files changed, 71 insertions, 26 deletions
diff --git a/lib/Net.Http/src/Core/Compression/IHttpCompressorManager.cs b/lib/Net.Http/src/Core/Compression/IHttpCompressorManager.cs index 982fa28..80763f8 100644 --- a/lib/Net.Http/src/Core/Compression/IHttpCompressorManager.cs +++ b/lib/Net.Http/src/Core/Compression/IHttpCompressorManager.cs @@ -72,6 +72,13 @@ namespace VNLib.Net.Http ReadOnlyMemory<byte> CompressBlock(object compressorState, ReadOnlyMemory<byte> input, bool finalBlock); /// <summary> + /// Flushes any stored compressor data that still needs to be sent to the client. + /// </summary> + /// <param name="compressorState">The compressor state instance</param> + /// <returns>The remaining data stored in the compressor state, may be empty if no data is pending</returns> + ReadOnlyMemory<byte> Flush(object compressorState); + + /// <summary> /// Initializes the compressor state for a compression operation /// </summary> /// <param name="compressorState">The user-defined compression state</param> diff --git a/lib/Net.Http/src/Core/Compression/IResponseCompressor.cs b/lib/Net.Http/src/Core/Compression/IResponseCompressor.cs index 0beea28..a68a838 100644 --- a/lib/Net.Http/src/Core/Compression/IResponseCompressor.cs +++ b/lib/Net.Http/src/Core/Compression/IResponseCompressor.cs @@ -50,10 +50,16 @@ namespace VNLib.Net.Http.Core.Compression /// </summary> /// <param name="compMethod">The compression mode to use</param> /// <param name="output">The stream to write compressed data to</param> - void Init(Stream output, CompressionMethod compMethod); + void Init(Stream output, CompressionMethod compMethod); /// <summary> - /// Compresses a block of data and writes it to the output stream + /// Gets a value that indicates if the compressor requires more flushing to occur + /// </summary> + bool IsFlushRequired(); + + /// <summary> + /// Compresses a block of data and writes it to the output stream. If an empty flush is + /// commited, then the input buffer will be empty. /// </summary> /// <param name="buffer">The block of memory to write to compress</param> /// <param name="finalBlock">A value that indicates if this block is the final block</param> diff --git a/lib/Net.Http/src/Core/Compression/ManagedHttpCompressor.cs b/lib/Net.Http/src/Core/Compression/ManagedHttpCompressor.cs index e580b2d..b3f971a 100644 --- a/lib/Net.Http/src/Core/Compression/ManagedHttpCompressor.cs +++ b/lib/Net.Http/src/Core/Compression/ManagedHttpCompressor.cs @@ -29,7 +29,6 @@ using System.Threading.Tasks; namespace VNLib.Net.Http.Core.Compression { - internal sealed class ManagedHttpCompressor : IResponseCompressor { //Store the compressor @@ -49,13 +48,31 @@ namespace VNLib.Net.Http.Core.Compression private object? _compressor; private Stream? _stream; + private ReadOnlyMemory<byte> _lastFlush; + private bool initialized; ///<inheritdoc/> public int BlockSize { get; private set; } + public bool IsFlushRequired() + { + //See if a flush is required + _lastFlush = _provider.Flush(_compressor!); + return _lastFlush.Length > 0; + } + ///<inheritdoc/> public ValueTask CompressBlockAsync(ReadOnlyMemory<byte> buffer, bool finalBlock) { + /* + * If input buffer is empty and flush data is available, + * write the last flush data to the stream + */ + if(buffer.Length == 0 && _lastFlush.Length > 0) + { + return _stream!.WriteAsync(_lastFlush); + } + //Compress the block ReadOnlyMemory<byte> result = _provider.CompressBlock(_compressor!, buffer, finalBlock); @@ -68,7 +85,14 @@ namespace VNLib.Net.Http.Core.Compression { //Remove stream ref and de-init the compressor _stream = null; - _provider.DeinitCompressor(_compressor!); + _lastFlush = default; + + //Deinit compressor if initialized + if (initialized) + { + _provider.DeinitCompressor(_compressor!); + initialized = false; + } } ///<inheritdoc/> @@ -76,10 +100,12 @@ namespace VNLib.Net.Http.Core.Compression { //Defer alloc the compressor _compressor ??= _provider.AllocCompressor(); + + //Init the compressor and get the block size + BlockSize = _provider.InitCompressor(_compressor, compMethod); - //Store the stream and init the compressor _stream = output; - BlockSize = _provider.InitCompressor(_compressor, compMethod); + initialized = true; } } }
\ No newline at end of file diff --git a/lib/Net.Http/src/Core/Response/ResponseWriter.cs b/lib/Net.Http/src/Core/Response/ResponseWriter.cs index 7a448a1..77dc619 100644 --- a/lib/Net.Http/src/Core/Response/ResponseWriter.cs +++ b/lib/Net.Http/src/Core/Response/ResponseWriter.cs @@ -91,11 +91,12 @@ namespace VNLib.Net.Http.Core #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task + ReadOnlyMemory<byte> _readSegment; + ///<inheritdoc/> async Task IHttpResponseBody.WriteEntityAsync(Stream dest, long count, Memory<byte> buffer) { int remaining; - ReadOnlyMemory<byte> segment; //Write a sliding window response if (_memoryResponse != null) @@ -107,16 +108,16 @@ namespace VNLib.Net.Http.Core while (remaining > 0) { //Get remaining segment - segment = _memoryResponse.GetRemainingConstrained(remaining); + _readSegment = _memoryResponse.GetRemainingConstrained(remaining); //Write segment to output stream - await dest.WriteAsync(segment); + await dest.WriteAsync(_readSegment); //Advance by the written ammount - _memoryResponse.Advance(segment.Length); + _memoryResponse.Advance(_readSegment.Length); //Update remaining - remaining -= segment.Length; + remaining -= _readSegment.Length; } } else @@ -135,8 +136,6 @@ namespace VNLib.Net.Http.Core ///<inheritdoc/> async Task IHttpResponseBody.WriteEntityAsync(Stream dest, Memory<byte> buffer) { - ReadOnlyMemory<byte> segment; - //Write a sliding window response if (_memoryResponse != null) { @@ -144,13 +143,13 @@ namespace VNLib.Net.Http.Core while (_memoryResponse.Remaining > 0) { //Get remaining segment - segment = _memoryResponse.GetMemory(); + _readSegment = _memoryResponse.GetMemory(); //Write segment to output stream - await dest.WriteAsync(segment); + await dest.WriteAsync(_readSegment); //Advance by the written ammount - _memoryResponse.Advance(segment.Length); + _memoryResponse.Advance(_readSegment.Length); } } else @@ -172,7 +171,6 @@ namespace VNLib.Net.Http.Core //Locals bool remaining; int read; - ReadOnlyMemory<byte> segment; //Write a sliding window response if (_memoryResponse != null) @@ -197,16 +195,16 @@ namespace VNLib.Net.Http.Core //Write response body from memory do { - segment = _memoryResponse.GetRemainingConstrained(dest.BlockSize); + _readSegment = _memoryResponse.GetRemainingConstrained(dest.BlockSize); //Advance by the trimmed segment length - _memoryResponse.Advance(segment.Length); + _memoryResponse.Advance(_readSegment.Length); //Check if data is remaining after an advance remaining = _memoryResponse.Remaining > 0; //Compress the trimmed block - await dest.CompressBlockAsync(segment, !remaining); + await dest.CompressBlockAsync(_readSegment, !remaining); } while (remaining); } @@ -214,16 +212,16 @@ namespace VNLib.Net.Http.Core { do { - segment = _memoryResponse.GetMemory(); + _readSegment = _memoryResponse.GetMemory(); //Advance by the segment length, this should be safe even if its zero - _memoryResponse.Advance(segment.Length); + _memoryResponse.Advance(_readSegment.Length); //Check if data is remaining after an advance remaining = _memoryResponse.Remaining > 0; //Write to output - await dest.CompressBlockAsync(segment, !remaining); + await dest.CompressBlockAsync(_readSegment, !remaining); } while (remaining); } @@ -264,6 +262,13 @@ namespace VNLib.Net.Http.Core //remove ref so its not disposed again _streamResponse = null; } + + //Continue flusing flushing the compressor if required + while(dest.IsFlushRequired()) + { + //Flush the compressor + await dest.CompressBlockAsync(ReadOnlyMemory<byte>.Empty, true); + } } #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task @@ -274,6 +279,7 @@ namespace VNLib.Net.Http.Core //Clear has data flag HasData = false; Length = 0; + _readSegment = default; //Clear rseponse containers _streamResponse?.Dispose(); diff --git a/lib/Net.Http/src/IMemoryResponseEntity.cs b/lib/Net.Http/src/IMemoryResponseReader.cs index aa77f58..a54cec7 100644 --- a/lib/Net.Http/src/IMemoryResponseEntity.cs +++ b/lib/Net.Http/src/IMemoryResponseReader.cs @@ -1,11 +1,11 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Net.Http -* File: IMemoryResponseEntity.cs +* File: IMemoryResponseReader.cs * -* IMemoryResponseEntity.cs is part of VNLib.Net.Http which is part of the larger +* IMemoryResponseReader.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 |