public KnownHeader(string name, HttpHeaderType headerType, HttpHeaderParser?parser, string[]?knownValues = null, int?http2StaticTableIndex = null, int?http3StaticTableIndex = null) { Debug.Assert(!string.IsNullOrEmpty(name)); Debug.Assert(name[0] == ':' || HttpRuleParser.GetTokenLength(name, 0) == name.Length); Name = name; HeaderType = headerType; Parser = parser; KnownValues = knownValues; Http2EncodedName = http2StaticTableIndex.HasValue ? HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexingToAllocatedArray(http2StaticTableIndex.GetValueOrDefault()) : HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexingNewNameToAllocatedArray(name); Http3EncodedName = http3StaticTableIndex.HasValue ? QPack.QPackEncoder.EncodeLiteralHeaderFieldWithStaticNameReferenceToArray(http3StaticTableIndex.GetValueOrDefault()) : QPack.QPackEncoder.EncodeLiteralHeaderFieldWithoutNameReferenceToArray(name); var asciiBytesWithColonSpace = new byte[name.Length + 2]; // + 2 for ':' and ' ' int asciiBytes = Encoding.ASCII.GetBytes(name, asciiBytesWithColonSpace); Debug.Assert(asciiBytes == name.Length); asciiBytesWithColonSpace[asciiBytesWithColonSpace.Length - 2] = (byte)':'; asciiBytesWithColonSpace[asciiBytesWithColonSpace.Length - 1] = (byte)' '; AsciiBytesWithColonSpace = asciiBytesWithColonSpace; }
public void BeginEncodeHeaders_ExcludedHeaders_NotAddedToTable(string headerName, bool neverIndex) { Span <byte> buffer = new byte[1024 * 16]; var headers = new HttpResponseHeaders(); headers.Append(headerName, "1"); var enumerator = new Http2HeadersEnumerator(); enumerator.Initialize(headers); var hpackEncoder = new HPackEncoder(maxHeaderTableSize: Http2PeerSettings.DefaultHeaderTableSize); Assert.True(HPackHeaderWriter.BeginEncodeHeaders(hpackEncoder, enumerator, buffer, out _)); if (neverIndex) { Assert.Equal(0x10, buffer[0] & 0x10); } else { Assert.Equal(0, buffer[0] & 0x40); } Assert.Empty(GetHeaderEntries(hpackEncoder)); }
public void GlobalSetup() { _memoryPool = SlabMemoryPoolFactory.Create(); var options = new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); _pipe = new Pipe(options); _httpRequestHeaders = new HttpRequestHeaders(); _httpRequestHeaders.Append(HeaderNames.Method, new StringValues("GET")); _httpRequestHeaders.Append(HeaderNames.Path, new StringValues("/")); _httpRequestHeaders.Append(HeaderNames.Scheme, new StringValues("http")); _httpRequestHeaders.Append(HeaderNames.Authority, new StringValues("localhost:80")); _hpackEncoder = new HPackEncoder(); _headersBuffer = new byte[1024 * 16]; var serviceContext = new ServiceContext { DateHeaderValueManager = new DateHeaderValueManager(), ServerOptions = new KestrelServerOptions(), Log = new KestrelTrace(NullLogger.Instance), SystemClock = new MockSystemClock() }; serviceContext.ServerOptions.Limits.Http2.MaxStreamsPerConnection = int.MaxValue; serviceContext.DateHeaderValueManager.OnHeartbeat(default);
public virtual void GlobalSetup() { _memoryPool = SlabMemoryPoolFactory.Create(); _httpFrame = new Http2Frame(); var options = new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); _connectionPair = DuplexPipe.CreateConnectionPair(options, options); _httpRequestHeaders = new HttpRequestHeaders(); _httpRequestHeaders.HeaderMethod = new StringValues("GET"); _httpRequestHeaders.HeaderPath = new StringValues("/"); _httpRequestHeaders.HeaderScheme = new StringValues("http"); _httpRequestHeaders.HeaderAuthority = new StringValues("localhost:80"); _headersBuffer = new byte[1024 * 16]; _hpackEncoder = new HPackEncoder(); var serviceContext = TestContextFactory.CreateServiceContext( serverOptions: new KestrelServerOptions(), dateHeaderValueManager: new DateHeaderValueManager(), systemClock: new MockSystemClock(), log: new MockTrace()); serviceContext.DateHeaderValueManager.OnHeartbeat(default);
public Http2FrameWriter( PipeWriter outputPipeWriter, ConnectionContext connectionContext, Http2Connection http2Connection, OutputFlowControl connectionOutputFlowControl, ITimeoutControl timeoutControl, MinDataRate minResponseDataRate, string connectionId, MemoryPool <byte> memoryPool, ServiceContext serviceContext) { // Allow appending more data to the PipeWriter when a flush is pending. _outputWriter = new ConcurrentPipeWriter(outputPipeWriter, memoryPool, _writeLock); _connectionContext = connectionContext; _http2Connection = http2Connection; _connectionOutputFlowControl = connectionOutputFlowControl; _connectionId = connectionId; _log = serviceContext.Log; _timeoutControl = timeoutControl; _minResponseDataRate = minResponseDataRate; _flusher = new TimingPipeFlusher(_outputWriter, timeoutControl, serviceContext.Log); _outgoingFrame = new Http2Frame(); _headerEncodingBuffer = new byte[_maxFrameSize]; _hpackEncoder = new HPackEncoder(serviceContext.ServerOptions.AllowResponseHeaderCompression); }
/// <summary> /// Begin encoding headers in the first HEADERS frame. /// </summary> public static bool BeginEncodeHeaders(int statusCode, HPackEncoder hpackEncoder, Http2HeadersEnumerator headersEnumerator, Span <byte> buffer, out int length) { length = 0; if (!hpackEncoder.EnsureDynamicTableSizeUpdate(buffer, out var sizeUpdateLength)) { throw new HPackEncodingException(SR.net_http_hpack_encode_failure); } length += sizeUpdateLength; if (!EncodeStatusHeader(statusCode, hpackEncoder, buffer.Slice(length), out var statusCodeLength)) { throw new HPackEncodingException(SR.net_http_hpack_encode_failure); } length += statusCodeLength; if (!headersEnumerator.MoveNext()) { return(true); } // We're ok with not throwing if no headers were encoded because we've already encoded the status. // There is a small chance that the header will encode if there is no other content in the next HEADERS frame. var done = EncodeHeadersCore(hpackEncoder, headersEnumerator, buffer.Slice(length), throwIfNoneEncoded: false, out var headersLength); length += headersLength; return(done); }
public void GlobalSetup() { _http2HeadersEnumerator = new Http2HeadersEnumerator(); _hpackEncoder = new HPackEncoder(); _buffer = new byte[1024 * 1024]; _knownResponseHeaders = new HttpResponseHeaders { HeaderServer = "Kestrel", HeaderContentType = "application/json", HeaderDate = "Date!", HeaderContentLength = "0", HeaderAcceptRanges = "Ranges!", HeaderTransferEncoding = "Encoding!", HeaderVia = "Via!", HeaderVary = "Vary!", HeaderWWWAuthenticate = "Authenticate!", HeaderLastModified = "Modified!", HeaderExpires = "Expires!", HeaderAge = "Age!" }; _unknownResponseHeaders = new HttpResponseHeaders(); for (var i = 0; i < 10; i++) { _unknownResponseHeaders.Append("Unknown" + i, "Value" + i); } }
private bool EncodeDynamicHeader(Span <byte> buffer, int staticTableIndex, string name, string value, int headerLength, Encoding?valueEncoding, out int bytesWritten) { EncoderHeaderEntry?headerField = GetEntry(name, value); if (headerField != null) { // Already exists in dynamic table. Write index. int index = CalculateDynamicTableIndex(headerField.Index); return(HPackEncoder.EncodeIndexedHeaderField(index, buffer, out bytesWritten)); } else { // Doesn't exist in dynamic table. Add new entry to dynamic table. int index = ResolveDynamicTableIndex(staticTableIndex, name); bool success = index == -1 ? HPackEncoder.EncodeLiteralHeaderFieldIndexingNewName(name, value, valueEncoding, buffer, out bytesWritten) : HPackEncoder.EncodeLiteralHeaderFieldIndexing(index, value, valueEncoding, buffer, out bytesWritten); if (success) { uint headerSize = (uint)headerLength; EnsureCapacity(headerSize); AddHeaderEntry(name, value, headerSize); } return(success); } }
private void WriteHeader(ref HeaderEncodingState state, string name, string value) { // TODO: ISSUE 31307: Use static table for known headers int bytesWritten; while (!HPackEncoder.EncodeHeader(name, value, _requestBuffer.AvailableSpan, out bytesWritten)) { GrowWriteBuffer(); } _requestBuffer.Commit(bytesWritten); while (_requestBuffer.ActiveSpan.Slice(state.CurrentFrameOffset).Length > FrameHeader.Size + FrameHeader.MaxLength) { // We've exceeded the frame size limit. // Fill in the current frame header. WriteCurrentFrameHeader(ref state, FrameHeader.MaxLength, false); state.IsFirstFrame = false; state.CurrentFrameOffset += FrameHeader.Size + FrameHeader.MaxLength; // Reserve space for new frame header _requestBuffer.Commit(FrameHeader.Size); Span <byte> currentFrameSpan = _requestBuffer.ActiveSpan.Slice(state.CurrentFrameOffset); // Shift the remainder down to make room for the new frame header. // We'll fill this in when the frame is complete. currentFrameSpan.Slice(0, currentFrameSpan.Length - FrameHeader.Size).CopyTo(currentFrameSpan.Slice(FrameHeader.Size)); } }
public void BeginEncodeHeaders_MaxHeaderTableSizeUpdated_SizeUpdateInHeaders() { Span <byte> buffer = new byte[1024 * 16]; var hpackEncoder = new HPackEncoder(); hpackEncoder.UpdateMaxHeaderTableSize(100); var enumerator = new Http2HeadersEnumerator(); // First request enumerator.Initialize(new Dictionary <string, StringValues>()); Assert.True(HPackHeaderWriter.BeginEncodeHeaders(hpackEncoder, enumerator, buffer, out var length)); Assert.Equal(2, length); const byte DynamicTableSizeUpdateMask = 0xe0; var integerDecoder = new IntegerDecoder(); Assert.False(integerDecoder.BeginTryDecode((byte)(buffer[0] & ~DynamicTableSizeUpdateMask), prefixLength: 5, out _)); Assert.True(integerDecoder.TryDecode(buffer[1], out var result)); Assert.Equal(100, result); // Second request enumerator.Initialize(new Dictionary <string, StringValues>()); Assert.True(HPackHeaderWriter.BeginEncodeHeaders(hpackEncoder, enumerator, buffer, out length)); Assert.Equal(0, length); }
public void BeginEncodeHeaders_CacheControlPrivate_NewIndexValue() { Span <byte> buffer = new byte[1024 * 16]; var headers = new HttpResponseHeaders(); headers.HeaderCacheControl = "private"; var enumerator = new Http2HeadersEnumerator(); enumerator.Initialize(headers); var hpackEncoder = new HPackEncoder(); Assert.True(HPackHeaderWriter.BeginEncodeHeaders(302, hpackEncoder, enumerator, buffer, out var length)); var result = buffer.Slice(5, length - 5).ToArray(); var hex = BitConverter.ToString(result); Assert.Equal("58-07-70-72-69-76-61-74-65", hex); var statusHeader = GetHeaderEntry(hpackEncoder, 0); Assert.Equal("Cache-Control", statusHeader.Name); Assert.Equal("private", statusHeader.Value); }
private EncoderHeaderEntry GetHeaderEntry(HPackEncoder encoder, int index) { var entry = encoder.Head; while (index-- >= 0) { entry = entry.Before; } return(entry); }
partial void Initialize(int?http2StaticTableIndex, int?http3StaticTableIndex) { Http2EncodedName = http2StaticTableIndex.HasValue ? HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexingToAllocatedArray(http2StaticTableIndex.GetValueOrDefault()) : HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexingNewNameToAllocatedArray(Name); Http3EncodedName = http3StaticTableIndex.HasValue ? QPack.QPackEncoder.EncodeLiteralHeaderFieldWithStaticNameReferenceToArray(http3StaticTableIndex.GetValueOrDefault()) : QPack.QPackEncoder.EncodeLiteralHeaderFieldWithoutNameReferenceToArray(Name); }
public bool EnsureDynamicTableSizeUpdate(Span <byte> buffer, out int length) { // Check if there is a table size update that should be encoded if (_pendingTableSizeUpdate) { bool success = HPackEncoder.EncodeDynamicTableSizeUpdate((int)_maxHeaderTableSize, buffer, out length); _pendingTableSizeUpdate = false; return(success); } length = 0; return(true); }
private static bool EncodeHeader(KnownHeaderType knownHeaderType, string name, string value, Span <byte> buffer, out int length) { var hPackStaticTableId = GetResponseHeaderStaticTableId(knownHeaderType); if (hPackStaticTableId == -1) { return(HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexingNewName(name, value, buffer, out length)); } else { return(HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexing(hPackStaticTableId, value, buffer, out length)); } }
private static bool EncodeStatusHeader(int statusCode, DynamicHPackEncoder hpackEncoder, Span <byte> buffer, out int length) { if (H2StaticTable.TryGetStatusIndex(statusCode, out var index)) { // Status codes which exist in the HTTP/2 StaticTable. return(HPackEncoder.EncodeIndexedHeaderField(index, buffer, out length)); } else { const string name = ":status"; var value = StatusCodes.ToStatusString(statusCode); return(hpackEncoder.EncodeHeader(buffer, H2StaticTable.Status200, HeaderEncodingHint.Index, name, value, valueEncoding: null, out length)); } }
private List <EncoderHeaderEntry> GetHeaderEntries(HPackEncoder encoder) { var headers = new List <EncoderHeaderEntry>(); var entry = encoder.Head; while (entry.Before != encoder.Head) { entry = entry.Before; headers.Add(entry); } ; return(headers); }
public void BeginEncodeHeaders_HeaderExceedHeaderTableSize_NoIndexAndNoHeaderEntry() { Span <byte> buffer = new byte[1024 * 16]; var headers = new HttpResponseHeaders(); headers.Append("x-Custom", new string('!', (int)Http2PeerSettings.DefaultHeaderTableSize)); var enumerator = new Http2HeadersEnumerator(); enumerator.Initialize(headers); var hpackEncoder = new HPackEncoder(); Assert.True(HPackHeaderWriter.BeginEncodeHeaders(200, hpackEncoder, enumerator, buffer, out var length)); Assert.Empty(GetHeaderEntries(hpackEncoder)); }
private static bool EncodeStatusHeader(int statusCode, HPackEncoder hpackEncoder, Span <byte> buffer, out int length) { switch (statusCode) { case 200: case 204: case 206: case 304: case 400: case 404: case 500: // Status codes which exist in the HTTP/2 StaticTable. return(HPackEncoder.EncodeIndexedHeaderField(H2StaticTable.GetStatusIndex(statusCode), buffer, out length)); default: const string name = ":status"; var value = StatusCodes.ToStatusString(statusCode); return(hpackEncoder.EncodeHeader(buffer, H2StaticTable.Status200, HeaderEncodingHint.Index, name, value, out length)); } }
/// <summary> /// Begin encoding headers in the first HEADERS frame. /// </summary> public static bool BeginEncodeHeaders(HPackEncoder hpackEncoder, Http2HeadersEnumerator headersEnumerator, Span <byte> buffer, out int length) { length = 0; if (!hpackEncoder.EnsureDynamicTableSizeUpdate(buffer, out var sizeUpdateLength)) { throw new HPackEncodingException(SR.net_http_hpack_encode_failure); } length += sizeUpdateLength; if (!headersEnumerator.MoveNext()) { return(true); } var done = EncodeHeadersCore(hpackEncoder, headersEnumerator, buffer.Slice(length), throwIfNoneEncoded: true, out var headersLength); length += headersLength; return(done); }
/// <summary> /// Begin encoding headers in the first HEADERS frame. /// </summary> public static bool BeginEncodeHeaders(int statusCode, IEnumerator <KeyValuePair <string, string> > headersEnumerator, Span <byte> buffer, out int length) { if (!HPackEncoder.EncodeStatusHeader(statusCode, buffer, out var statusCodeLength)) { throw new HPackEncodingException(SR.net_http_hpack_encode_failure); } if (!headersEnumerator.MoveNext()) { length = statusCodeLength; return(true); } // We're ok with not throwing if no headers were encoded because we've already encoded the status. // There is a small chance that the header will encode if there is no other content in the next HEADERS frame. var done = EncodeHeaders(headersEnumerator, buffer.Slice(statusCodeLength), throwIfNoneEncoded: false, out var headersLength); length = statusCodeLength + headersLength; return(done); }
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 static void WriteStartStream(this PipeWriter writer, int streamId, HPackEncoder hpackEncoder, Http2HeadersEnumerator headers, byte[] headerEncodingBuffer, bool endStream, Http2Frame frame = null) { frame ??= new Http2Frame(); frame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId); var buffer = headerEncodingBuffer.AsSpan(); var done = HPackHeaderWriter.BeginEncodeHeaders(hpackEncoder, 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 = HPackHeaderWriter.ContinueEncodeHeaders(hpackEncoder, headers, buffer, out length); frame.PayloadLength = length; if (done) { frame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS; } Http2FrameWriter.WriteHeader(frame, writer); writer.Write(buffer.Slice(0, length)); } }
public bool EncodeHeader(Span <byte> buffer, int staticTableIndex, HeaderEncodingHint encodingHint, string name, string value, Encoding?valueEncoding, out int bytesWritten) { Debug.Assert(!_pendingTableSizeUpdate, "Dynamic table size update should be encoded before headers."); // Never index sensitive value. if (encodingHint == HeaderEncodingHint.NeverIndex) { int index = ResolveDynamicTableIndex(staticTableIndex, name); return(index == -1 ? HPackEncoder.EncodeLiteralHeaderFieldNeverIndexingNewName(name, value, valueEncoding, buffer, out bytesWritten) : HPackEncoder.EncodeLiteralHeaderFieldNeverIndexing(index, value, valueEncoding, buffer, out bytesWritten)); } // No dynamic table. Only use the static table. if (!_allowDynamicCompression || _maxHeaderTableSize == 0 || encodingHint == HeaderEncodingHint.IgnoreIndex) { return(staticTableIndex == -1 ? HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexingNewName(name, value, valueEncoding, buffer, out bytesWritten) : HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexing(staticTableIndex, value, valueEncoding, buffer, out bytesWritten)); } // Header is greater than the maximum table size. // Don't attempt to add dynamic header as all existing dynamic headers will be removed. var headerLength = HeaderField.GetLength(name.Length, valueEncoding?.GetByteCount(value) ?? value.Length); if (headerLength > _maxHeaderTableSize) { int index = ResolveDynamicTableIndex(staticTableIndex, name); return(index == -1 ? HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexingNewName(name, value, valueEncoding, buffer, out bytesWritten) : HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexing(index, value, valueEncoding, buffer, out bytesWritten)); } return(EncodeDynamicHeader(buffer, staticTableIndex, name, value, headerLength, valueEncoding, out bytesWritten)); }
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)); }
public void BeginEncodeHeaders_Status302_NewIndexValue() { Span <byte> buffer = new byte[1024 * 16]; var headers = new HttpResponseHeaders(); var enumerator = new Http2HeadersEnumerator(); enumerator.Initialize(headers); var hpackEncoder = new HPackEncoder(); Assert.True(HPackHeaderWriter.BeginEncodeHeaders(302, hpackEncoder, enumerator, buffer, out var length)); var result = buffer.Slice(0, length).ToArray(); var hex = BitConverter.ToString(result); Assert.Equal("48-03-33-30-32", hex); var statusHeader = GetHeaderEntry(hpackEncoder, 0); Assert.Equal(":status", statusHeader.Name); Assert.Equal("302", statusHeader.Value); }
private static bool EncodeHeadersCore(HPackEncoder hpackEncoder, Http2HeadersEnumerator headersEnumerator, Span <byte> buffer, bool throwIfNoneEncoded, out int length) { var currentLength = 0; do { var staticTableId = headersEnumerator.HPackStaticTableId; var name = headersEnumerator.Current.Key; var value = headersEnumerator.Current.Value; var hint = ResolveHeaderEncodingHint(staticTableId, name); if (!hpackEncoder.EncodeHeader( buffer.Slice(currentLength), staticTableId, hint, name, value, out var headerLength)) { // If the header wasn't written, and no headers have been written, then the header is too large. // Throw an error to avoid an infinite loop of attempting to write large header. if (currentLength == 0 && throwIfNoneEncoded) { throw new HPackEncodingException(SR.net_http_hpack_encode_failure); } length = currentLength; return(false); } currentLength += headerLength; }while (headersEnumerator.MoveNext()); length = currentLength; return(true); }
/// <summary> /// Continue encoding headers in the next HEADERS frame. The enumerator should already have a current value. /// </summary> public static bool ContinueEncodeHeaders(HPackEncoder hpackEncoder, Http2HeadersEnumerator headersEnumerator, Span <byte> buffer, out int length) { return(EncodeHeadersCore(hpackEncoder, headersEnumerator, buffer, throwIfNoneEncoded: true, out length)); }
private static bool EncodeHeader(string name, string value, Span <byte> buffer, out int length) { return(HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexingNewName(name, value, valueEncoding: null, buffer, out length)); }
// adapted from Header serialization code in Http2Connection.cs private static Memory <byte> HPackEncode(HttpHeaders headers) { var buffer = new ArrayBuffer(4); FillAvailableSpaceWithOnes(buffer); foreach (KeyValuePair <HeaderDescriptor, string[]> header in headers.GetHeaderDescriptorsAndValues()) { KnownHeader knownHeader = header.Key.KnownHeader; if (knownHeader != null) { // For all other known headers, send them via their pre-encoded name and the associated value. WriteBytes(knownHeader.Http2EncodedName); string separator = null; if (header.Value.Length > 1) { HttpHeaderParser parser = header.Key.Parser; if (parser != null && parser.SupportsMultipleValues) { separator = parser.Separator; } else { separator = HttpHeaderParser.DefaultSeparator; } } WriteLiteralHeaderValues(header.Value, separator); } else { // The header is not known: fall back to just encoding the header name and value(s). WriteLiteralHeader(header.Key.Name, header.Value); } } return(buffer.ActiveMemory); void WriteBytes(ReadOnlySpan <byte> bytes) { if (bytes.Length > buffer.AvailableLength) { buffer.EnsureAvailableSpace(bytes.Length); FillAvailableSpaceWithOnes(buffer); } bytes.CopyTo(buffer.AvailableSpan); buffer.Commit(bytes.Length); } void WriteLiteralHeaderValues(string[] values, string separator) { int bytesWritten; while (!HPackEncoder.EncodeStringLiterals(values, separator, buffer.AvailableSpan, out bytesWritten)) { buffer.EnsureAvailableSpace(buffer.AvailableLength + 1); FillAvailableSpaceWithOnes(buffer); } buffer.Commit(bytesWritten); } void WriteLiteralHeader(string name, string[] values) { int bytesWritten; while (!HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexingNewName(name, values, HttpHeaderParser.DefaultSeparator, buffer.AvailableSpan, out bytesWritten)) { buffer.EnsureAvailableSpace(buffer.AvailableLength + 1); FillAvailableSpaceWithOnes(buffer); } buffer.Commit(bytesWritten); } // force issues related to buffer not being zeroed out void FillAvailableSpaceWithOnes(ArrayBuffer buffer) => buffer.AvailableSpan.Fill(0xff); }