Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 02e872e

Browse files
authored
Implement Header encoding selectors on SocketsHttpHandler (dotnet#39468)
* Expose HeaderEncodingSelectors on SocketsHttpHandler * Implement header encoding selectors in SocketsHttpHandler * Add header encoding tests * Add summaries for new APIs * Use Stream.Write(byte[], int, int) overloads for framework compat * Add dummy API implementation to browser target * HPack/QPack fixes * Move HeaderEncodingSelector delegate to namespace, add TContext * Encoding fixes * Remove unused using * Simplify test * HttpConnection PR feedback * Simplify fast-path detection
1 parent c205ed0 commit 02e872e

25 files changed

+656
-168
lines changed

src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackEncoder.cs

Lines changed: 95 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#nullable enable
55
using System.Collections.Generic;
66
using System.Diagnostics;
7+
using System.Text;
78

89
namespace System.Net.Http.HPack
910
{
@@ -96,7 +97,7 @@ public static bool EncodeLiteralHeaderFieldWithoutIndexing(int index, string val
9697
if (IntegerEncoder.Encode(index, 4, destination, out int indexLength))
9798
{
9899
Debug.Assert(indexLength >= 1);
99-
if (EncodeStringLiteral(value, destination.Slice(indexLength), out int nameLength))
100+
if (EncodeStringLiteral(value, valueEncoding: null, destination.Slice(indexLength), out int nameLength))
100101
{
101102
bytesWritten = indexLength + nameLength;
102103
return true;
@@ -128,7 +129,7 @@ public static bool EncodeLiteralHeaderFieldNeverIndexing(int index, string value
128129
if (IntegerEncoder.Encode(index, 4, destination, out int indexLength))
129130
{
130131
Debug.Assert(indexLength >= 1);
131-
if (EncodeStringLiteral(value, destination.Slice(indexLength), out int nameLength))
132+
if (EncodeStringLiteral(value, valueEncoding: null, destination.Slice(indexLength), out int nameLength))
132133
{
133134
bytesWritten = indexLength + nameLength;
134135
return true;
@@ -160,7 +161,7 @@ public static bool EncodeLiteralHeaderFieldIndexing(int index, string value, Spa
160161
if (IntegerEncoder.Encode(index, 6, destination, out int indexLength))
161162
{
162163
Debug.Assert(indexLength >= 1);
163-
if (EncodeStringLiteral(value, destination.Slice(indexLength), out int nameLength))
164+
if (EncodeStringLiteral(value, valueEncoding: null, destination.Slice(indexLength), out int nameLength))
164165
{
165166
bytesWritten = indexLength + nameLength;
166167
return true;
@@ -276,7 +277,7 @@ private static bool EncodeLiteralHeaderNewNameCore(byte mask, string name, strin
276277
{
277278
destination[0] = mask;
278279
if (EncodeLiteralHeaderName(name, destination.Slice(1), out int nameLength) &&
279-
EncodeStringLiteral(value, destination.Slice(1 + nameLength), out int valueLength))
280+
EncodeStringLiteral(value, valueEncoding: null, destination.Slice(1 + nameLength), out int valueLength))
280281
{
281282
bytesWritten = 1 + nameLength + valueLength;
282283
return true;
@@ -289,6 +290,11 @@ private static bool EncodeLiteralHeaderNewNameCore(byte mask, string name, strin
289290

290291
/// <summary>Encodes a "Literal Header Field without Indexing - New Name".</summary>
291292
public static bool EncodeLiteralHeaderFieldWithoutIndexingNewName(string name, ReadOnlySpan<string> values, string separator, Span<byte> destination, out int bytesWritten)
293+
{
294+
return EncodeLiteralHeaderFieldWithoutIndexingNewName(name, values, separator, valueEncoding: null, destination, out bytesWritten);
295+
}
296+
297+
public static bool EncodeLiteralHeaderFieldWithoutIndexingNewName(string name, ReadOnlySpan<string> values, string separator, Encoding? valueEncoding, Span<byte> destination, out int bytesWritten)
292298
{
293299
// From https://tools.ietf.org/html/rfc7541#section-6.2.2
294300
// ------------------------------------------------------
@@ -309,7 +315,7 @@ public static bool EncodeLiteralHeaderFieldWithoutIndexingNewName(string name, R
309315
{
310316
destination[0] = 0;
311317
if (EncodeLiteralHeaderName(name, destination.Slice(1), out int nameLength) &&
312-
EncodeStringLiterals(values, separator, destination.Slice(1 + nameLength), out int valueLength))
318+
EncodeStringLiterals(values, separator, valueEncoding, destination.Slice(1 + nameLength), out int valueLength))
313319
{
314320
bytesWritten = 1 + nameLength + valueLength;
315321
return true;
@@ -395,27 +401,20 @@ private static bool EncodeLiteralHeaderName(string value, Span<byte> destination
395401
return false;
396402
}
397403

398-
private static bool EncodeStringLiteralValue(string value, Span<byte> destination, out int bytesWritten)
404+
private static void EncodeValueStringPart(string value, Span<byte> destination)
399405
{
400-
if (value.Length <= destination.Length)
406+
Debug.Assert(destination.Length >= value.Length);
407+
408+
for (int i = 0; i < value.Length; i++)
401409
{
402-
for (int i = 0; i < value.Length; i++)
410+
char c = value[i];
411+
if ((c & 0xFF80) != 0)
403412
{
404-
char c = value[i];
405-
if ((c & 0xFF80) != 0)
406-
{
407-
throw new HttpRequestException(SR.net_http_request_invalid_char_encoding);
408-
}
409-
410-
destination[i] = (byte)c;
413+
throw new HttpRequestException(SR.net_http_request_invalid_char_encoding);
411414
}
412415

413-
bytesWritten = value.Length;
414-
return true;
416+
destination[i] = (byte)c;
415417
}
416-
417-
bytesWritten = 0;
418-
return false;
419418
}
420419

421420
public static bool EncodeStringLiteral(ReadOnlySpan<byte> value, Span<byte> destination, out int bytesWritten)
@@ -453,6 +452,11 @@ public static bool EncodeStringLiteral(ReadOnlySpan<byte> value, Span<byte> dest
453452
}
454453

455454
public static bool EncodeStringLiteral(string value, Span<byte> destination, out int bytesWritten)
455+
{
456+
return EncodeStringLiteral(value, valueEncoding: null, destination, out bytesWritten);
457+
}
458+
459+
public static bool EncodeStringLiteral(string value, Encoding? valueEncoding, Span<byte> destination, out int bytesWritten)
456460
{
457461
// From https://tools.ietf.org/html/rfc7541#section-5.2
458462
// ------------------------------------------------------
@@ -466,13 +470,28 @@ public static bool EncodeStringLiteral(string value, Span<byte> destination, out
466470
if (destination.Length != 0)
467471
{
468472
destination[0] = 0; // TODO: Use Huffman encoding
469-
if (IntegerEncoder.Encode(value.Length, 7, destination, out int integerLength))
473+
474+
int encodedStringLength = valueEncoding is null || ReferenceEquals(valueEncoding, Encoding.Latin1)
475+
? value.Length
476+
: valueEncoding.GetByteCount(value);
477+
478+
if (IntegerEncoder.Encode(encodedStringLength, 7, destination, out int integerLength))
470479
{
471480
Debug.Assert(integerLength >= 1);
472-
473-
if (EncodeStringLiteralValue(value, destination.Slice(integerLength), out int valueLength))
481+
destination = destination.Slice(integerLength);
482+
if (encodedStringLength <= destination.Length)
474483
{
475-
bytesWritten = integerLength + valueLength;
484+
if (valueEncoding is null)
485+
{
486+
EncodeValueStringPart(value, destination);
487+
}
488+
else
489+
{
490+
int written = valueEncoding.GetBytes(value, destination);
491+
Debug.Assert(written == encodedStringLength);
492+
}
493+
494+
bytesWritten = integerLength + encodedStringLength;
476495
return true;
477496
}
478497
}
@@ -502,56 +521,87 @@ public static bool EncodeDynamicTableSizeUpdate(int value, Span<byte> destinatio
502521
}
503522

504523
public static bool EncodeStringLiterals(ReadOnlySpan<string> values, string? separator, Span<byte> destination, out int bytesWritten)
524+
{
525+
return EncodeStringLiterals(values, separator, valueEncoding: null, destination, out bytesWritten);
526+
}
527+
528+
public static bool EncodeStringLiterals(ReadOnlySpan<string> values, string? separator, Encoding? valueEncoding, Span<byte> destination, out int bytesWritten)
505529
{
506530
bytesWritten = 0;
507531

508532
if (values.Length == 0)
509533
{
510-
return EncodeStringLiteral("", destination, out bytesWritten);
534+
return EncodeStringLiteral("", valueEncoding: null, destination, out bytesWritten);
511535
}
512536
else if (values.Length == 1)
513537
{
514-
return EncodeStringLiteral(values[0], destination, out bytesWritten);
538+
return EncodeStringLiteral(values[0], valueEncoding, destination, out bytesWritten);
515539
}
516540

517541
if (destination.Length != 0)
518542
{
519-
int valueLength = 0;
543+
Debug.Assert(separator != null);
544+
int valueLength;
520545

521546
// Calculate length of all parts and separators.
522-
foreach (string part in values)
547+
if (valueEncoding is null || ReferenceEquals(valueEncoding, Encoding.Latin1))
523548
{
524-
valueLength = checked((int)(valueLength + part.Length));
549+
valueLength = checked((int)(values.Length - 1) * separator.Length);
550+
foreach (string part in values)
551+
{
552+
valueLength = checked((int)(valueLength + part.Length));
553+
}
554+
}
555+
else
556+
{
557+
valueLength = checked((int)(values.Length - 1) * valueEncoding.GetByteCount(separator));
558+
foreach (string part in values)
559+
{
560+
valueLength = checked((int)(valueLength + valueEncoding.GetByteCount(part)));
561+
}
525562
}
526-
527-
Debug.Assert(separator != null);
528-
valueLength = checked((int)(valueLength + (values.Length - 1) * separator.Length));
529563

530564
destination[0] = 0;
531565
if (IntegerEncoder.Encode(valueLength, 7, destination, out int integerLength))
532566
{
533567
Debug.Assert(integerLength >= 1);
534-
535-
int encodedLength = 0;
536-
for (int j = 0; j < values.Length; j++)
568+
destination = destination.Slice(integerLength);
569+
if (destination.Length >= valueLength)
537570
{
538-
if (j != 0 && !EncodeStringLiteralValue(separator, destination.Slice(integerLength), out encodedLength))
571+
if (valueEncoding is null)
539572
{
540-
return false;
573+
string value = values[0];
574+
EncodeValueStringPart(value, destination);
575+
destination = destination.Slice(value.Length);
576+
577+
for (int i = 1; i < values.Length; i++)
578+
{
579+
EncodeValueStringPart(separator, destination);
580+
destination = destination.Slice(separator.Length);
581+
582+
value = values[i];
583+
EncodeValueStringPart(value, destination);
584+
destination = destination.Slice(value.Length);
585+
}
541586
}
587+
else
588+
{
589+
int written = valueEncoding.GetBytes(values[0], destination);
590+
destination = destination.Slice(written);
542591

543-
integerLength += encodedLength;
592+
for (int i = 1; i < values.Length; i++)
593+
{
594+
written = valueEncoding.GetBytes(separator, destination);
595+
destination = destination.Slice(written);
544596

545-
if (!EncodeStringLiteralValue(values[j], destination.Slice(integerLength), out encodedLength))
546-
{
547-
return false;
597+
written = valueEncoding.GetBytes(values[i], destination);
598+
destination = destination.Slice(written);
599+
}
548600
}
549601

550-
integerLength += encodedLength;
602+
bytesWritten = integerLength + valueLength;
603+
return true;
551604
}
552-
553-
bytesWritten = integerLength;
554-
return true;
555605
}
556606
}
557607

0 commit comments

Comments
 (0)