Exemple #1
0
        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);
Exemple #4
0
        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);
        }
Exemple #6
0
        /// <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);
            }
        }
Exemple #8
0
    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);
        }
    }
Exemple #9
0
            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);
        }
Exemple #13
0
        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);
        }
Exemple #14
0
    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);
    }
Exemple #15
0
        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));
        }
Exemple #19
0
        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));
            }
        }
Exemple #20
0
        /// <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);
        }
Exemple #21
0
        /// <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));
            }
        }
Exemple #24
0
    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);
        }
Exemple #27
0
        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);
        }
Exemple #28
0
 /// <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));
 }
Exemple #29
0
 private static bool EncodeHeader(string name, string value, Span <byte> buffer, out int length)
 {
     return(HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexingNewName(name, value, valueEncoding: null, buffer, out length));
 }
Exemple #30
0
        // 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);
        }