@@ -39,14 +39,19 @@ internal static async Task SendHttp2(Stream clientStream, Stream serverStream, i
39
39
CancellationTokenSource cancellationTokenSource , Guid connectionId ,
40
40
ExceptionHandler exceptionFunc )
41
41
{
42
+ var clientSettings = new Http2Settings ( ) ;
43
+ var serverSettings = new Http2Settings ( ) ;
44
+ ;
42
45
var sessions = new ConcurrentDictionary < int , SessionEventArgs > ( ) ;
43
46
44
47
// Now async relay all server=>client & client=>server data
45
48
var sendRelay =
46
- copyHttp2FrameAsync ( clientStream , serverStream , onDataSend , sessionFactory , sessions , onBeforeRequest ,
49
+ copyHttp2FrameAsync ( clientStream , serverStream , onDataSend , clientSettings , serverSettings ,
50
+ sessionFactory , sessions , onBeforeRequest ,
47
51
bufferSize , connectionId , true , cancellationTokenSource . Token , exceptionFunc ) ;
48
52
var receiveRelay =
49
- copyHttp2FrameAsync ( serverStream , clientStream , onDataReceive , sessionFactory , sessions , onBeforeResponse ,
53
+ copyHttp2FrameAsync ( serverStream , clientStream , onDataReceive , serverSettings , clientSettings ,
54
+ sessionFactory , sessions , onBeforeResponse ,
50
55
bufferSize , connectionId , false , cancellationTokenSource . Token , exceptionFunc ) ;
51
56
52
57
await Task . WhenAny ( sendRelay , receiveRelay ) ;
@@ -56,15 +61,17 @@ internal static async Task SendHttp2(Stream clientStream, Stream serverStream, i
56
61
}
57
62
58
63
private static async Task copyHttp2FrameAsync ( Stream input , Stream output , Action < byte [ ] , int , int > onCopy ,
64
+ Http2Settings localSettings , Http2Settings remoteSettings ,
59
65
Func < SessionEventArgs > sessionFactory , ConcurrentDictionary < int , SessionEventArgs > sessions ,
60
66
Func < SessionEventArgs , Task > onBeforeRequestResponse ,
61
67
int bufferSize , Guid connectionId , bool isClient , CancellationToken cancellationToken ,
62
68
ExceptionHandler exceptionFunc )
63
69
{
64
- var decoder = new Decoder ( 8192 , 4096 * 16 ) ;
70
+ int headerTableSize = 0 ;
71
+ Decoder decoder = null ;
65
72
66
73
var headerBuffer = new byte [ 9 ] ;
67
- var buffer = new byte [ 32768 ] ;
74
+ byte [ ] buffer = null ;
68
75
while ( true )
69
76
{
70
77
int read = await forceRead ( input , headerBuffer , 0 , 9 , cancellationToken ) ;
@@ -80,6 +87,11 @@ private static async Task copyHttp2FrameAsync(Stream input, Stream output, Actio
80
87
int streamId = ( ( headerBuffer [ 5 ] & 0x7f ) << 24 ) + ( headerBuffer [ 6 ] << 16 ) + ( headerBuffer [ 7 ] << 8 ) +
81
88
headerBuffer [ 8 ] ;
82
89
90
+ if ( buffer == null || buffer . Length < localSettings . MaxFrameSize )
91
+ {
92
+ buffer = new byte [ localSettings . MaxFrameSize ] ;
93
+ }
94
+
83
95
read = await forceRead ( input , buffer , 0 , length , cancellationToken ) ;
84
96
onCopy ( buffer , 0 , read ) ;
85
97
if ( read != length )
@@ -145,7 +157,7 @@ private static async Task copyHttp2FrameAsync(Stream input, Stream output, Actio
145
157
}
146
158
}
147
159
}
148
- else if ( type == 1 /*headers*/ )
160
+ else if ( type == 1 /* headers */ )
149
161
{
150
162
bool endHeaders = ( flags & ( int ) Http2FrameFlag . EndHeaders ) != 0 ;
151
163
bool padded = ( flags & ( int ) Http2FrameFlag . Padded ) != 0 ;
@@ -181,13 +193,18 @@ private static async Task copyHttp2FrameAsync(Stream input, Stream output, Actio
181
193
} ) ;
182
194
try
183
195
{
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 )
185
199
{
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 ) ;
189
202
}
190
203
204
+ decoder . Decode ( new BinaryReader ( new MemoryStream ( buffer , offset , dataLength ) ) ,
205
+ headerListener ) ;
206
+ decoder . EndHeaderBlock ( ) ;
207
+
191
208
if ( isClient )
192
209
{
193
210
var request = args . HttpClient . Request ;
@@ -225,6 +242,53 @@ private static async Task copyHttp2FrameAsync(Stream input, Stream output, Actio
225
242
rr . Locked = true ;
226
243
}
227
244
}
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
+ }
228
292
229
293
if ( ! isClient && endStream )
230
294
{
@@ -269,6 +333,14 @@ private static async Task<int> forceRead(Stream input, byte[] buffer, int offset
269
333
return totalRead ;
270
334
}
271
335
336
+
337
+ class Http2Settings
338
+ {
339
+ public int HeaderTableSize { get ; set ; } = 4096 ;
340
+
341
+ public int MaxFrameSize { get ; set ; } = 16384 ;
342
+ }
343
+
272
344
class MyHeaderListener : IHeaderListener
273
345
{
274
346
private readonly Action < string , string > addHeaderFunc ;
0 commit comments