public override void OnStreamRemoved(IHttp2Stream stream) { Http2Decompressor decompressor = _frameListener.Decompressor(stream); if (decompressor is object) { Cleanup(decompressor); } }
/// <summary> /// Checks if a new decompressor object is needed for the stream identified by <paramref name="streamId"/>. /// This method will modify the <c>content-encoding</c> header contained in <paramref name="headers"/>. /// </summary> /// <param name="ctx">The context</param> /// <param name="streamId">The identifier for the headers inside <paramref name="headers"/></param> /// <param name="headers">Object representing headers which have been read</param> /// <param name="endOfStream">Indicates if the stream has ended</param> /// <exception cref="Http2Exception">If the <c>content-encoding</c> is not supported</exception> private void InitDecompressor(IChannelHandlerContext ctx, int streamId, IHttp2Headers headers, bool endOfStream) { var stream = _connection.Stream(streamId); if (stream is null) { return; } Http2Decompressor decompressor = Decompressor(stream); if (decompressor is null && !endOfStream) { // Determine the content encoding. if (!headers.TryGet(HttpHeaderNames.ContentEncoding, out var contentEncoding)) { contentEncoding = HttpHeaderValues.Identity; } EmbeddedChannel channel = NewContentDecompressor(ctx, contentEncoding); if (channel is object) { decompressor = new Http2Decompressor(channel); _ = stream.SetProperty(_propertyKey, decompressor); // Decode the content and remove or replace the existing headers // so that the message looks like a decoded message. var targetContentEncoding = GetTargetContentEncoding(contentEncoding); if (HttpHeaderValues.Identity.ContentEqualsIgnoreCase(targetContentEncoding)) { _ = headers.Remove(HttpHeaderNames.ContentEncoding); } else { _ = headers.Set(HttpHeaderNames.ContentEncoding, targetContentEncoding); } } } if (decompressor is object) { // The content length will be for the compressed data. Since we will decompress the data // this content-length will not be correct. Instead of queuing messages or delaying sending // header frames...just remove the content-length header _ = headers.Remove(HttpHeaderNames.ContentLength); // The first time that we initialize a decompressor, decorate the local flow controller to // properly convert consumed bytes. if (!_flowControllerInitialized) { _flowControllerInitialized = true; var localEndpoint = _connection.Local; localEndpoint.FlowController = new ConsumedBytesConverter(this, localEndpoint.FlowController); } } }
public bool ConsumeBytes(IHttp2Stream stream, int numBytes) { Http2Decompressor decompressor = _frameListener.Decompressor(stream); if (decompressor is object) { // Convert the decompressed bytes to compressed (on the wire) bytes. numBytes = decompressor.ConsumeBytes(stream.Id, numBytes); } try { return(_flowController.ConsumeBytes(stream, numBytes)); } catch (Http2Exception) { throw; } catch (Exception t) { // The stream should be closed at this point. We have already changed our state tracking the compressed // bytes, and there is no guarantee we can recover if the underlying flow controller throws. return(ThrowHelper.ThrowStreamError_ErrorWhileReturningBytesToFlowControlWindow(stream.Id, t)); } }
public override int OnDataRead(IChannelHandlerContext ctx, int streamId, IByteBuffer data, int padding, bool endOfStream) { IHttp2Stream stream = _connection.Stream(streamId); Http2Decompressor decompressor = Decompressor(stream); if (decompressor is null) { // The decompressor may be null if no compatible encoding type was found in this stream's headers return(_listener.OnDataRead(ctx, streamId, data, padding, endOfStream)); } EmbeddedChannel channel = decompressor.Decompressor; int compressedBytes = data.ReadableBytes + padding; decompressor.IncrementCompressedBytes(compressedBytes); try { // call retain here as it will call release after its written to the channel _ = channel.WriteInbound(data.Retain()); var buf = NextReadableBuf(channel); if (buf is null && endOfStream && channel.Finish()) { buf = NextReadableBuf(channel); } if (buf is null) { if (endOfStream) { _ = _listener.OnDataRead(ctx, streamId, Unpooled.Empty, padding, true); } // No new decompressed data was extracted from the compressed data. This means the application could // not be provided with data and thus could not return how many bytes were processed. We will assume // there is more data coming which will complete the decompression block. To allow for more data we // return all bytes to the flow control window (so the peer can send more data). decompressor.IncrementDecompressedBytes(compressedBytes); return(compressedBytes); } try { IHttp2LocalFlowController flowController = _connection.Local.FlowController; decompressor.IncrementDecompressedBytes(padding); while (true) { var nextBuf = NextReadableBuf(channel); var decompressedEndOfStream = nextBuf is null && endOfStream; if (decompressedEndOfStream && channel.Finish()) { nextBuf = NextReadableBuf(channel); decompressedEndOfStream = nextBuf is null; } decompressor.IncrementDecompressedBytes(buf.ReadableBytes); // Immediately return the bytes back to the flow controller. ConsumedBytesConverter will convert // from the decompressed amount which the user knows about to the compressed amount which flow // control knows about. _ = flowController.ConsumeBytes(stream, _listener.OnDataRead(ctx, streamId, buf, padding, decompressedEndOfStream)); if (nextBuf is null) { break; } padding = 0; // Padding is only communicated once on the first iteration. _ = buf.Release(); buf = nextBuf; } // We consume bytes each time we call the listener to ensure if multiple frames are decompressed // that the bytes are accounted for immediately. Otherwise the user may see an inconsistent state of // flow control. return(0); } finally { _ = buf.Release(); } } catch (Http2Exception) { throw; } catch (Exception t) { return(ThrowHelper.ThrowStreamError_DecompressorErrorDetectedWhileDelegatingDataReadOnStream(stream.Id, t)); } }
/// <summary> /// Release remaining content from the <see cref="EmbeddedChannel"/>. /// </summary> /// <param name="decompressor">The decompressor for <c>stream</c></param> private static void Cleanup(Http2Decompressor decompressor) { _ = decompressor.Decompressor.FinishAndReleaseAll(); }