/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Servers/Kestrel/shared/HPackHeaderWriter.cs
201 строка
8 KB
Jiri Cincura ↹
Fix typos in code. (#67428)
02 июл 2026, 13:29
Не верифицирован
02 июл 2026, 13:29
fa8126f
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Net.Http; using System.Net.Http.HPack; #if !(IS_TESTS || IS_BENCHMARKS) namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2; #else namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests; #endif internal enum HeaderWriteResult : byte { // Not all headers written. MoreHeaders = 0, // All headers written. Done = 1, // Oversized header for the given buffer. BufferTooSmall = 2, } // This file is used by Kestrel to write response headers and tests to write request headers. // To avoid adding test code to Kestrel this file is shared. Test specific code is excluded from Kestrel by ifdefs. internal static class HPackHeaderWriter { /// <summary> /// Begin encoding headers in the first HEADERS frame. /// </summary> public static HeaderWriteResult BeginEncodeHeaders(int statusCode, DynamicHPackEncoder 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 HeaderWriteResult.Done; } // Since we've already encoded the status, we know we didn't start with an empty buffer. We don't need to increase it immediately because // 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), canRequestLargerBuffer: false, out var headersLength); length += headersLength; return done; } /// <summary> /// Begin encoding headers in the first HEADERS frame. /// </summary> public static HeaderWriteResult BeginEncodeHeaders(DynamicHPackEncoder hpackEncoder, Http2HeadersEnumerator headersEnumerator, Span<byte> buffer, out int length) => BeginEncodeHeaders(hpackEncoder, headersEnumerator, buffer, iterateBeforeFirstElement: true, out length); /// <summary> /// Begin encoding headers in the first HEADERS frame without stepping the iterator. /// </summary> public static HeaderWriteResult RetryBeginEncodeHeaders(DynamicHPackEncoder hpackEncoder, Http2HeadersEnumerator headersEnumerator, Span<byte> buffer, out int length) => BeginEncodeHeaders(hpackEncoder, headersEnumerator, buffer, iterateBeforeFirstElement: false, out length); /// <summary> /// Begin encoding headers in the first HEADERS frame. /// </summary> private static HeaderWriteResult BeginEncodeHeaders(DynamicHPackEncoder hpackEncoder, Http2HeadersEnumerator headersEnumerator, Span<byte> buffer, bool iterateBeforeFirstElement, out int length) { length = 0; if (!hpackEncoder.EnsureDynamicTableSizeUpdate(buffer, out var sizeUpdateLength)) { throw new HPackEncodingException(SR.net_http_hpack_encode_failure); } length += sizeUpdateLength; if (iterateBeforeFirstElement && !headersEnumerator.MoveNext()) { return HeaderWriteResult.Done; } var done = EncodeHeadersCore(hpackEncoder, headersEnumerator, buffer.Slice(length), canRequestLargerBuffer: true, out var headersLength); length += headersLength; return done; } /// <summary> /// Continue encoding headers in the next HEADERS frame. The enumerator should already have a current value. /// </summary> public static HeaderWriteResult ContinueEncodeHeaders(DynamicHPackEncoder hpackEncoder, Http2HeadersEnumerator headersEnumerator, Span<byte> buffer, out int length) { return EncodeHeadersCore(hpackEncoder, headersEnumerator, buffer, canRequestLargerBuffer: true, 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 static HeaderWriteResult EncodeHeadersCore(DynamicHPackEncoder hpackEncoder, Http2HeadersEnumerator headersEnumerator, Span<byte> buffer, bool canRequestLargerBuffer, out int length) { var currentLength = 0; do { var staticTableId = headersEnumerator.HPackStaticTableId; var name = headersEnumerator.Current.Key; var value = headersEnumerator.Current.Value; var valueEncoding = ReferenceEquals(headersEnumerator.EncodingSelector, KestrelServerOptions.DefaultHeaderEncodingSelector) ? null : headersEnumerator.EncodingSelector(name); var hint = ResolveHeaderEncodingHint(staticTableId, name); if (!hpackEncoder.EncodeHeader( buffer.Slice(currentLength), staticTableId, hint, name, value, valueEncoding, out var headerLength)) { // If the header wasn't written, and no headers have been written, then the header is too large. // Request for a larger buffer to write large header. if (currentLength == 0 && canRequestLargerBuffer) { length = 0; return HeaderWriteResult.BufferTooSmall; } length = currentLength; return HeaderWriteResult.MoreHeaders; } currentLength += headerLength; } while (headersEnumerator.MoveNext()); length = currentLength; return HeaderWriteResult.Done; } private static HeaderEncodingHint ResolveHeaderEncodingHint(int staticTableId, string name) { HeaderEncodingHint hint; if (IsSensitive(staticTableId, name)) { hint = HeaderEncodingHint.NeverIndex; } else if (IsNotDynamicallyIndexed(staticTableId)) { hint = HeaderEncodingHint.IgnoreIndex; } else { hint = HeaderEncodingHint.Index; } return hint; } private static bool IsSensitive(int staticTableIndex, string name) { // Set-Cookie could contain sensitive data. switch (staticTableIndex) { case H2StaticTable.SetCookie: case H2StaticTable.ContentDisposition: return true; case -1: // Content-Disposition currently isn't a known header so a // static index probably won't be specified. return string.Equals(name, "Content-Disposition", StringComparison.OrdinalIgnoreCase); } return false; } private static bool IsNotDynamicallyIndexed(int staticTableIndex) { // Content-Length is added to static content. Content length is different for each // file, and is unlikely to be reused because of browser caching. return staticTableIndex == H2StaticTable.ContentLength; } }