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)); }
// 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()); }