Beispiel #1
0
            public override void OnStreamRemoved(IHttp2Stream stream)
            {
                Http2Decompressor decompressor = _frameListener.Decompressor(stream);

                if (decompressor is object)
                {
                    Cleanup(decompressor);
                }
            }
Beispiel #2
0
        /// <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);
                }
            }
        }
Beispiel #3
0
            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));
                }
            }
Beispiel #4
0
        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));
            }
        }
Beispiel #5
0
 /// <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();
 }