12
12
from ..packages .hyperframe .frame import (
13
13
FRAMES , DataFrame , HeadersFrame , PushPromiseFrame , RstStreamFrame ,
14
14
SettingsFrame , Frame , WindowUpdateFrame , GoAwayFrame , PingFrame ,
15
- BlockedFrame
15
+ BlockedFrame , FRAME_MAX_LEN , FRAME_MAX_ALLOWED_LEN
16
16
)
17
17
from ..packages .hpack .hpack_compat import Encoder , Decoder
18
18
from .stream import Stream
@@ -123,6 +123,7 @@ def __init_state(self):
123
123
# Values for the settings used on an HTTP/2 connection.
124
124
self ._settings = {
125
125
SettingsFrame .INITIAL_WINDOW_SIZE : 65535 ,
126
+ SettingsFrame .SETTINGS_MAX_FRAME_SIZE : FRAME_MAX_LEN ,
126
127
}
127
128
128
129
# The socket used to send data.
@@ -247,16 +248,17 @@ def _send_preamble(self):
247
248
# The server will also send an initial settings frame, so get it.
248
249
self ._recv_cb ()
249
250
250
- def close (self ):
251
+ def close (self , error_code = None ):
251
252
"""
252
253
Close the connection to the server.
253
254
255
+ :param error_code: (optional) The error code to reset all streams with.
254
256
:returns: Nothing.
255
257
"""
256
258
# Close all streams
257
259
for stream in list (self .streams .values ()):
258
260
log .debug ("Close stream %d" % stream .stream_id )
259
- stream .close ()
261
+ stream .close (error_code )
260
262
261
263
# Send GoAway frame to the server
262
264
try :
@@ -388,10 +390,14 @@ def receive_frame(self, frame):
388
390
if 'ACK' not in frame .flags :
389
391
self ._update_settings (frame )
390
392
391
- # Need to return an ack.
392
- f = SettingsFrame (0 )
393
- f .flags .add ('ACK' )
394
- self ._send_cb (f )
393
+ # When the setting containing the max frame size value is out
394
+ # of range, the spec dictates to tear down the connection.
395
+ # Therefore we make sure the socket is still alive before
396
+ # returning the ack.
397
+ if self ._sock is not None :
398
+ f = SettingsFrame (0 )
399
+ f .flags .add ('ACK' )
400
+ self ._send_cb (f )
395
401
elif frame .type == GoAwayFrame .type :
396
402
# If we get GoAway with error code zero, we are doing a graceful
397
403
# shutdown and all is well. Otherwise, throw an exception.
@@ -449,6 +455,20 @@ def _update_settings(self, frame):
449
455
450
456
self ._settings [SettingsFrame .INITIAL_WINDOW_SIZE ] = newsize
451
457
458
+ if SettingsFrame .SETTINGS_MAX_FRAME_SIZE in frame .settings :
459
+ new_size = frame .settings [SettingsFrame .SETTINGS_MAX_FRAME_SIZE ]
460
+ if FRAME_MAX_LEN <= new_size <= FRAME_MAX_ALLOWED_LEN :
461
+ self ._settings [SettingsFrame .SETTINGS_MAX_FRAME_SIZE ] = new_size
462
+ else :
463
+ log .warning (
464
+ "Frame size %d is outside of allowed range" ,
465
+ new_size )
466
+ # Tear the connection down with error code PROTOCOL_ERROR
467
+ self .close (1 )
468
+ error_string = ("Advertised frame size %d is outside of range" %
469
+ (new_size ))
470
+ raise ConnectionError (error_string )
471
+
452
472
def _new_stream (self , stream_id = None , local_closed = False ):
453
473
"""
454
474
Returns a new stream object for this connection.
@@ -469,12 +489,7 @@ def _close_stream(self, stream_id, error_code=None):
469
489
"""
470
490
Called by a stream when it would like to be 'closed'.
471
491
"""
472
- if error_code is not None :
473
- f = RstStreamFrame (stream_id )
474
- f .error_code = error_code
475
- self ._send_cb (f )
476
-
477
- del self .streams [stream_id ]
492
+ self ._send_rst_frame (stream_id , error_code )
478
493
479
494
def _send_cb (self , frame , tolerate_peer_gone = False ):
480
495
"""
@@ -495,6 +510,13 @@ def _send_cb(self, frame, tolerate_peer_gone=False):
495
510
496
511
data = frame .serialize ()
497
512
513
+ if frame .body_len > self ._settings [SettingsFrame .SETTINGS_MAX_FRAME_SIZE ]:
514
+ raise ValueError (
515
+ "Frame size %d exceeds maximum frame size setting %d" %
516
+ (frame .body_len ,
517
+ self ._settings [SettingsFrame .SETTINGS_MAX_FRAME_SIZE ])
518
+ )
519
+
498
520
log .info (
499
521
"Sending frame %s on stream %d" ,
500
522
frame .__class__ .__name__ ,
@@ -535,6 +557,15 @@ def _consume_single_frame(self):
535
557
# Parse the header. We can use the returned memoryview directly here.
536
558
frame , length = Frame .parse_frame_header (header )
537
559
560
+ if (length > FRAME_MAX_LEN ):
561
+ log .warning (
562
+ "Frame size exceeded on stream %d (received: %d, max: %d)" ,
563
+ frame .stream_id ,
564
+ length ,
565
+ FRAME_MAX_LEN
566
+ )
567
+ self ._send_rst_frame (frame .stream_id , 6 ) # 6 = FRAME_SIZE_ERROR
568
+
538
569
# Read the remaining data from the socket.
539
570
data = self ._recv_payload (length )
540
571
self ._consume_frame_payload (frame , data )
@@ -595,9 +626,7 @@ def _consume_frame_payload(self, frame, data):
595
626
# the ENABLE_PUSH setting is 0, but the spec leaves the client
596
627
# action undefined when they do it anyway. So we just refuse
597
628
# the stream and go about our business.
598
- f = RstStreamFrame (frame .promised_stream_id )
599
- f .error_code = 7 # REFUSED_STREAM
600
- self ._send_cb (f )
629
+ self ._send_rst_frame (frame .promised_stream_id , 7 )
601
630
602
631
# Work out to whom this frame should go.
603
632
if frame .stream_id != 0 :
@@ -606,9 +635,7 @@ def _consume_frame_payload(self, frame, data):
606
635
except KeyError :
607
636
# If we receive an unexpected stream identifier then we
608
637
# cancel the stream with an error of type PROTOCOL_ERROR
609
- f = RstStreamFrame (frame .stream_id )
610
- f .error_code = 1 # PROTOCOL_ERROR
611
- self ._send_cb (f )
638
+ self ._send_rst_frame (frame .stream_id , 1 )
612
639
log .warning (
613
640
"Unexpected stream identifier %d" % (frame .stream_id )
614
641
)
@@ -637,6 +664,20 @@ def _recv_cb(self):
637
664
except ConnectionResetError :
638
665
break
639
666
667
+ def _send_rst_frame (self , stream_id , error_code ):
668
+ """
669
+ Send reset stream frame with error code and remove stream from map.
670
+ """
671
+ f = RstStreamFrame (stream_id )
672
+ f .error_code = error_code
673
+ self ._send_cb (f )
674
+
675
+ try :
676
+ del self .streams [stream_id ]
677
+ except KeyError as e : # pragma: no cover
678
+ log .warn (
679
+ "Stream with id %d does not exist: %s" ,
680
+ stream_id , e )
640
681
641
682
# The following two methods are the implementation of the context manager
642
683
# protocol.
0 commit comments