Skip to content
This repository was archived by the owner on Jul 9, 2023. It is now read-only.

Commit 60feeb0

Browse files
committed
http/2: using some default settings from specification, process the settings frame
process the rst_stream frame
1 parent 66c4113 commit 60feeb0

File tree

1 file changed

+81
-9
lines changed

1 file changed

+81
-9
lines changed

src/Titanium.Web.Proxy/Http2/Http2Helper.cs

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,19 @@ internal static async Task SendHttp2(Stream clientStream, Stream serverStream, i
3939
CancellationTokenSource cancellationTokenSource, Guid connectionId,
4040
ExceptionHandler exceptionFunc)
4141
{
42+
var clientSettings = new Http2Settings();
43+
var serverSettings = new Http2Settings();
44+
;
4245
var sessions = new ConcurrentDictionary<int, SessionEventArgs>();
4346

4447
// Now async relay all server=>client & client=>server data
4548
var sendRelay =
46-
copyHttp2FrameAsync(clientStream, serverStream, onDataSend, sessionFactory, sessions, onBeforeRequest,
49+
copyHttp2FrameAsync(clientStream, serverStream, onDataSend, clientSettings, serverSettings,
50+
sessionFactory, sessions, onBeforeRequest,
4751
bufferSize, connectionId, true, cancellationTokenSource.Token, exceptionFunc);
4852
var receiveRelay =
49-
copyHttp2FrameAsync(serverStream, clientStream, onDataReceive, sessionFactory, sessions, onBeforeResponse,
53+
copyHttp2FrameAsync(serverStream, clientStream, onDataReceive, serverSettings, clientSettings,
54+
sessionFactory, sessions, onBeforeResponse,
5055
bufferSize, connectionId, false, cancellationTokenSource.Token, exceptionFunc);
5156

5257
await Task.WhenAny(sendRelay, receiveRelay);
@@ -56,15 +61,17 @@ internal static async Task SendHttp2(Stream clientStream, Stream serverStream, i
5661
}
5762

5863
private static async Task copyHttp2FrameAsync(Stream input, Stream output, Action<byte[], int, int> onCopy,
64+
Http2Settings localSettings, Http2Settings remoteSettings,
5965
Func<SessionEventArgs> sessionFactory, ConcurrentDictionary<int, SessionEventArgs> sessions,
6066
Func<SessionEventArgs, Task> onBeforeRequestResponse,
6167
int bufferSize, Guid connectionId, bool isClient, CancellationToken cancellationToken,
6268
ExceptionHandler exceptionFunc)
6369
{
64-
var decoder = new Decoder(8192, 4096 * 16);
70+
int headerTableSize = 0;
71+
Decoder decoder = null;
6572

6673
var headerBuffer = new byte[9];
67-
var buffer = new byte[32768];
74+
byte[] buffer = null;
6875
while (true)
6976
{
7077
int read = await forceRead(input, headerBuffer, 0, 9, cancellationToken);
@@ -80,6 +87,11 @@ private static async Task copyHttp2FrameAsync(Stream input, Stream output, Actio
8087
int streamId = ((headerBuffer[5] & 0x7f) << 24) + (headerBuffer[6] << 16) + (headerBuffer[7] << 8) +
8188
headerBuffer[8];
8289

90+
if (buffer == null || buffer.Length < localSettings.MaxFrameSize)
91+
{
92+
buffer = new byte[localSettings.MaxFrameSize];
93+
}
94+
8395
read = await forceRead(input, buffer, 0, length, cancellationToken);
8496
onCopy(buffer, 0, read);
8597
if (read != length)
@@ -145,7 +157,7 @@ private static async Task copyHttp2FrameAsync(Stream input, Stream output, Actio
145157
}
146158
}
147159
}
148-
else if (type == 1 /*headers*/)
160+
else if (type == 1 /* headers */)
149161
{
150162
bool endHeaders = (flags & (int)Http2FrameFlag.EndHeaders) != 0;
151163
bool padded = (flags & (int)Http2FrameFlag.Padded) != 0;
@@ -181,13 +193,18 @@ private static async Task copyHttp2FrameAsync(Stream input, Stream output, Actio
181193
});
182194
try
183195
{
184-
lock (decoder)
196+
// recreate the decoder when new value is bigger
197+
// should we recreate when smaller, too?
198+
if (decoder == null || headerTableSize < localSettings.HeaderTableSize)
185199
{
186-
decoder.Decode(new BinaryReader(new MemoryStream(buffer, offset, dataLength)),
187-
headerListener);
188-
decoder.EndHeaderBlock();
200+
headerTableSize = localSettings.HeaderTableSize;
201+
decoder = new Decoder(8192, headerTableSize);
189202
}
190203

204+
decoder.Decode(new BinaryReader(new MemoryStream(buffer, offset, dataLength)),
205+
headerListener);
206+
decoder.EndHeaderBlock();
207+
191208
if (isClient)
192209
{
193210
var request = args.HttpClient.Request;
@@ -225,6 +242,53 @@ private static async Task copyHttp2FrameAsync(Stream input, Stream output, Actio
225242
rr.Locked = true;
226243
}
227244
}
245+
else if (type == 4 /* settings */)
246+
{
247+
if (length % 6 != 0)
248+
{
249+
// https://httpwg.org/specs/rfc7540.html#SETTINGS
250+
// 6.5. SETTINGS
251+
// A SETTINGS frame with a length other than a multiple of 6 octets MUST be treated as a connection error (Section 5.4.1) of type FRAME_SIZE_ERROR
252+
throw new ProxyHttpException("Invalid settings length", null, null);
253+
}
254+
255+
int pos = 0;
256+
while (pos < length)
257+
{
258+
int identifier = (buffer[pos++] << 8) + buffer[pos++];
259+
int value = (buffer[pos++] << 24) + (buffer[pos++] << 16) + (buffer[pos++] << 8) + buffer[pos++];
260+
if (identifier == 1 /*SETTINGS_HEADER_TABLE_SIZE*/)
261+
{
262+
//System.Diagnostics.Debug.WriteLine("HEADER SIZE CONN: " + connectionId + ", CLIENT: " + isClient + ", value: " + value);
263+
remoteSettings.HeaderTableSize = value;
264+
}
265+
else if (identifier == 5 /*SETTINGS_MAX_FRAME_SIZE*/)
266+
{
267+
remoteSettings.MaxFrameSize = value;
268+
}
269+
}
270+
}
271+
272+
if (type == 3 /* rst_stream */)
273+
{
274+
int errorCode = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
275+
if (streamId == 0)
276+
{
277+
// connection error
278+
exceptionFunc(new ProxyHttpException("HTTP/2 connection error. Error code: " + errorCode, null, args));
279+
return;
280+
}
281+
else
282+
{
283+
// stream error
284+
sessions.TryRemove(streamId, out _);
285+
286+
if (errorCode != 8 /*cancel*/)
287+
{
288+
exceptionFunc(new ProxyHttpException("HTTP/2 stream error. Error code: " + errorCode, null, args));
289+
}
290+
}
291+
}
228292

229293
if (!isClient && endStream)
230294
{
@@ -269,6 +333,14 @@ private static async Task<int> forceRead(Stream input, byte[] buffer, int offset
269333
return totalRead;
270334
}
271335

336+
337+
class Http2Settings
338+
{
339+
public int HeaderTableSize { get; set; } = 4096;
340+
341+
public int MaxFrameSize { get; set; } = 16384;
342+
}
343+
272344
class MyHeaderListener : IHeaderListener
273345
{
274346
private readonly Action<string, string> addHeaderFunc;

0 commit comments

Comments
 (0)