Exemple #1
0
        public void WriteResponseHeaders(int streamId, int statusCode, IHeaderDictionary headers)
        {
            lock (_writeLock)
            {
                _outgoingFrame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId);

                var done = _hpackEncoder.BeginEncode(statusCode, EnumerateHeaders(headers), _outgoingFrame.Payload, out var payloadLength);
                _outgoingFrame.Length = payloadLength;

                if (done)
                {
                    _outgoingFrame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS;
                }

                Append(_outgoingFrame.Raw);

                while (!done)
                {
                    _outgoingFrame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId);

                    done = _hpackEncoder.Encode(_outgoingFrame.Payload, out var length);
                    _outgoingFrame.Length = length;

                    if (done)
                    {
                        _outgoingFrame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS;
                    }

                    Append(_outgoingFrame.Raw);
                }
            }
        }
        protected async Task StartStreamAsync(int streamId, IEnumerable <KeyValuePair <string, string> > headers, bool endStream)
        {
            var tcs = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously);

            _runningStreams[streamId] = tcs;

            var frame = new Http2Frame(Http2Limits.MinAllowedMaxFrameSize);

            frame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId);
            var done = _hpackEncoder.BeginEncode(headers, frame.HeadersPayload, out var length);

            frame.PayloadLength = length;

            if (done)
            {
                frame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS;
            }

            if (endStream)
            {
                frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
            }

            await SendAsync(frame.Raw);

            while (!done)
            {
                frame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId);
                done = _hpackEncoder.Encode(frame.HeadersPayload, out length);
                frame.PayloadLength = length;

                if (done)
                {
                    frame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS;
                }

                await SendAsync(frame.Raw);
            }
        }
        public void EncodesHeadersInSinglePayloadWhenSpaceAvailable(KeyValuePair <string, string>[] headers, byte[] expectedPayload, int?statusCode)
        {
            var encoder = new HPackEncoder();
            var payload = new byte[1024];
            var length  = 0;

            if (statusCode.HasValue)
            {
                Assert.True(encoder.BeginEncode(statusCode.Value, headers, payload, out length));
            }
            else
            {
                Assert.True(encoder.BeginEncode(headers, payload, out length));
            }
            Assert.Equal(expectedPayload.Length, length);

            for (var i = 0; i < length; i++)
            {
                Assert.True(expectedPayload[i] == payload[i], $"{expectedPayload[i]} != {payload[i]} at {i} (len {length})");
            }

            Assert.Equal(expectedPayload, new ArraySegment <byte>(payload, 0, length));
        }
Exemple #4
0
        // Optional header fields for padding and priority are not implemented.

        /* https://tools.ietf.org/html/rfc7540#section-6.2
         +---------------+
         |Pad Length? (8)|
         +-+-------------+-----------------------------------------------+
         |E|                 Stream Dependency? (31)                     |
         +-+-------------+-----------------------------------------------+
         |  Weight? (8)  |
         +-+-------------+-----------------------------------------------+
         |                   Header Block Fragment (*)                 ...
         +---------------------------------------------------------------+
         |                           Padding (*)                       ...
         +---------------------------------------------------------------+
         */
        public void WriteResponseHeaders(int streamId, int statusCode, IHeaderDictionary headers)
        {
            lock (_writeLock)
            {
                if (_completed)
                {
                    return;
                }

                try
                {
                    _outgoingFrame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId);
                    var buffer = _headerEncodingBuffer.AsSpan();
                    var done   = _hpackEncoder.BeginEncode(statusCode, EnumerateHeaders(headers), buffer, out var payloadLength);

                    _outgoingFrame.PayloadLength = payloadLength;

                    if (done)
                    {
                        _outgoingFrame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS;
                    }

                    WriteHeaderUnsynchronized();
                    _outputWriter.Write(buffer.Slice(0, payloadLength));

                    while (!done)
                    {
                        _outgoingFrame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId);

                        done = _hpackEncoder.Encode(buffer, out payloadLength);
                        _outgoingFrame.PayloadLength = payloadLength;

                        if (done)
                        {
                            _outgoingFrame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS;
                        }

                        WriteHeaderUnsynchronized();
                        _outputWriter.Write(buffer.Slice(0, payloadLength));
                    }
                }
                catch (HPackEncodingException hex)
                {
                    // Header errors are fatal to the connection. We don't have a direct way to signal this to the Http2Connection.
                    _connectionContext.Abort(new ConnectionAbortedException("", hex));
                    throw new InvalidOperationException("", hex); // Report the error to the user if this was the first write.
                }
            }
        }
        // Optional header fields for padding and priority are not implemented.

        /* https://tools.ietf.org/html/rfc7540#section-6.2
         +---------------+
         |Pad Length? (8)|
         +-+-------------+-----------------------------------------------+
         |E|                 Stream Dependency? (31)                     |
         +-+-------------+-----------------------------------------------+
         |  Weight? (8)  |
         +-+-------------+-----------------------------------------------+
         |                   Header Block Fragment (*)                 ...
         +---------------------------------------------------------------+
         |                           Padding (*)                       ...
         +---------------------------------------------------------------+
         */
        public void WriteResponseHeaders(int streamId, int statusCode, IHeaderDictionary headers)
        {
            lock (_writeLock)
            {
                if (_completed)
                {
                    return;
                }

                try
                {
                    _outgoingFrame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId);
                    var buffer = _headerEncodingBuffer.AsSpan();
                    var done   = _hpackEncoder.BeginEncode(statusCode, EnumerateHeaders(headers), buffer, out var payloadLength);
                    FinishWritingHeaders(streamId, payloadLength, done);
                }
                catch (HPackEncodingException hex)
                {
                    _log.HPackEncodingError(_connectionId, streamId, hex);
                    _http2Connection.Abort(new ConnectionAbortedException(hex.Message, hex));
                    throw new InvalidOperationException(hex.Message, hex); // Report the error to the user if this was the first write.
                }
            }
        }
        public static void WriteStartStream(this PipeWriter writer, int streamId, Http2HeadersEnumerator headers, HPackEncoder hpackEncoder, byte[] headerEncodingBuffer, bool endStream)
        {
            var frame = new Http2Frame();

            frame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId);

            var buffer = headerEncodingBuffer.AsSpan();
            var done   = hpackEncoder.BeginEncode(headers, buffer, out var length);

            frame.PayloadLength = length;

            if (done)
            {
                frame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS;
            }

            if (endStream)
            {
                frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
            }

            Http2FrameWriter.WriteHeader(frame, writer);
            writer.Write(buffer.Slice(0, length));

            while (!done)
            {
                frame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId);

                done = hpackEncoder.Encode(buffer, out length);
                frame.PayloadLength = length;

                if (done)
                {
                    frame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS;
                }

                Http2FrameWriter.WriteHeader(frame, writer);
                writer.Write(buffer.Slice(0, length));
            }
        }
        public void EncodesHeadersInSinglePayloadWhenSpaceAvailable()
        {
            var encoder = new HPackEncoder();

            var statusCode = 200;
            var headers    = new []
            {
                new KeyValuePair <string, string>("date", "Mon, 24 Jul 2017 19:22:30 GMT"),
                new KeyValuePair <string, string>("content-type", "text/html; charset=utf-8"),
                new KeyValuePair <string, string>("server", "Kestrel")
            };

            var expectedPayload = new byte[]
            {
                0x88, 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x1d,
                0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x34, 0x20,
                0x4a, 0x75, 0x6c, 0x20, 0x32, 0x30, 0x31, 0x37,
                0x20, 0x31, 0x39, 0x3a, 0x32, 0x32, 0x3a, 0x33,
                0x30, 0x20, 0x47, 0x4d, 0x54, 0x00, 0x0c, 0x63,
                0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x74,
                0x79, 0x70, 0x65, 0x18, 0x74, 0x65, 0x78, 0x74,
                0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3b, 0x20, 0x63,
                0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x75,
                0x74, 0x66, 0x2d, 0x38, 0x00, 0x06, 0x73, 0x65,
                0x72, 0x76, 0x65, 0x72, 0x07, 0x4b, 0x65, 0x73,
                0x74, 0x72, 0x65, 0x6c
            };

            var payload = new byte[1024];

            Assert.True(encoder.BeginEncode(statusCode, headers, payload, out var length));
            Assert.Equal(expectedPayload.Length, length);

            for (var i = 0; i < length; i++)
            {
                Assert.True(expectedPayload[i] == payload[i], $"{expectedPayload[i]} != {payload[i]} at {i} (len {length})");
            }

            Assert.Equal(expectedPayload, new ArraySegment <byte>(payload, 0, length));
        }
        public Task StartStreamAsync(int streamId, IEnumerable <KeyValuePair <string, string> > headers, bool endStream)
        {
            var writableBuffer = _pair.Application.Output;
            var tcs            = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously);

            var frame = new Http2Frame();

            frame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId);

            var buffer = _headerEncodingBuffer.AsSpan();
            var done   = _hpackEncoder.BeginEncode(headers, buffer, out var length);

            frame.PayloadLength = length;

            if (done)
            {
                frame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS;
            }

            if (endStream)
            {
                frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
            }

            WriteHeader(frame, writableBuffer);
            writableBuffer.Write(buffer.Slice(0, length));

            while (!done)
            {
                frame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId);

                done = _hpackEncoder.Encode(buffer, out length);
                frame.PayloadLength = length;

                if (done)
                {
                    frame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS;
                }

                WriteHeader(frame, writableBuffer);
                writableBuffer.Write(buffer.Slice(0, length));
            }

            return(FlushAsync(writableBuffer));
        }
        public void EncodesHeadersInMultiplePayloadsWhenSpaceNotAvailable(bool exactSize)
        {
            var encoder = new HPackEncoder();

            var statusCode = 200;
            var headers    = new []
            {
                new KeyValuePair <string, string>("date", "Mon, 24 Jul 2017 19:22:30 GMT"),
                new KeyValuePair <string, string>("content-type", "text/html; charset=utf-8"),
                new KeyValuePair <string, string>("server", "Kestrel")
            };

            var expectedStatusCodePayload = new byte[]
            {
                0x88
            };

            var expectedDateHeaderPayload = new byte[]
            {
                0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x1d, 0x4d,
                0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x34, 0x20, 0x4a,
                0x75, 0x6c, 0x20, 0x32, 0x30, 0x31, 0x37, 0x20,
                0x31, 0x39, 0x3a, 0x32, 0x32, 0x3a, 0x33, 0x30,
                0x20, 0x47, 0x4d, 0x54
            };

            var expectedContentTypeHeaderPayload = new byte[]
            {
                0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
                0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x18, 0x74,
                0x65, 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c,
                0x3b, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,
                0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38
            };

            var expectedServerHeaderPayload = new byte[]
            {
                0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
                0x07, 0x4b, 0x65, 0x73, 0x74, 0x72, 0x65, 0x6c
            };

            Span <byte> payload = new byte[1024];
            var         offset  = 0;

            // When !exactSize, slices are one byte short of fitting the next header
            var sliceLength = expectedStatusCodePayload.Length + (exactSize ? 0 : expectedDateHeaderPayload.Length - 1);

            Assert.False(encoder.BeginEncode(statusCode, headers, payload.Slice(offset, sliceLength), out var length));
            Assert.Equal(expectedStatusCodePayload.Length, length);
            Assert.Equal(expectedStatusCodePayload, payload.Slice(0, length).ToArray());

            offset += length;

            sliceLength = expectedDateHeaderPayload.Length + (exactSize ? 0 : expectedContentTypeHeaderPayload.Length - 1);
            Assert.False(encoder.Encode(payload.Slice(offset, sliceLength), out length));
            Assert.Equal(expectedDateHeaderPayload.Length, length);
            Assert.Equal(expectedDateHeaderPayload, payload.Slice(offset, length).ToArray());

            offset += length;

            sliceLength = expectedContentTypeHeaderPayload.Length + (exactSize ? 0 : expectedServerHeaderPayload.Length - 1);
            Assert.False(encoder.Encode(payload.Slice(offset, sliceLength), out length));
            Assert.Equal(expectedContentTypeHeaderPayload.Length, length);
            Assert.Equal(expectedContentTypeHeaderPayload, payload.Slice(offset, length).ToArray());

            offset += length;

            sliceLength = expectedServerHeaderPayload.Length;
            Assert.True(encoder.Encode(payload.Slice(offset, sliceLength), out length));
            Assert.Equal(expectedServerHeaderPayload.Length, length);
            Assert.Equal(expectedServerHeaderPayload, payload.Slice(offset, length).ToArray());
        }