@@ -77,8 +77,10 @@ class Protocol:
7777 Args:
7878 side: :attr:`~Side.CLIENT` or :attr:`~Side.SERVER`.
7979 state: Initial state of the WebSocket connection.
80- max_size: Maximum size of incoming messages in bytes;
81- :obj:`None` disables the limit.
80+ max_size: Maximum size of incoming messages in bytes.
81+ :obj:`None` disables the limit. You may pass a ``(max_message_size,
82+ max_fragment_size)`` tuple to set different limits for messages and
83+ fragments when you expect long messages sent in short fragments.
8284 logger: Logger for this connection; depending on ``side``,
8385 defaults to ``logging.getLogger("websockets.client")``
8486 or ``logging.getLogger("websockets.server")``;
@@ -91,7 +93,7 @@ def __init__(
9193 side : Side ,
9294 * ,
9395 state : State = OPEN ,
94- max_size : int | None = 2 ** 20 ,
96+ max_size : tuple [ int | None , int | None ] | int | None = 2 ** 20 ,
9597 logger : LoggerLike | None = None ,
9698 ) -> None :
9799 # Unique identifier. For logs.
@@ -114,11 +116,14 @@ def __init__(
114116 self .state = state
115117
116118 # Maximum size of incoming messages in bytes.
117- self .max_size = max_size
119+ if isinstance (max_size , int ) or max_size is None :
120+ self .max_message_size , self .max_fragment_size = max_size , None
121+ else :
122+ self .max_message_size , self .max_fragment_size = max_size
118123
119124 # Current size of incoming message in bytes. Only set while reading a
120125 # fragmented message i.e. a data frames with the FIN bit not set.
121- self .cur_size : int | None = None
126+ self .current_size : int | None = None
122127
123128 # True while sending a fragmented message i.e. a data frames with the
124129 # FIN bit not set.
@@ -578,12 +583,19 @@ def parse(self) -> Generator[None]:
578583 # connection isn't closed cleanly.
579584 raise EOFError ("unexpected end of stream" )
580585
581- if self .max_size is None :
582- max_size = None
583- elif self .cur_size is None :
584- max_size = self .max_size
585- else :
586- max_size = self .max_size - self .cur_size
586+ max_size = None
587+
588+ if self .max_message_size is not None :
589+ if self .current_size is None :
590+ max_size = self .max_message_size
591+ else :
592+ max_size = self .max_message_size - self .current_size
593+
594+ if self .max_fragment_size is not None :
595+ if max_size is None :
596+ max_size = self .max_fragment_size
597+ else :
598+ max_size = min (max_size , self .max_fragment_size )
587599
588600 # During a normal closure, execution ends here on the next
589601 # iteration of the loop after receiving a close frame. At
@@ -613,7 +625,7 @@ def parse(self) -> Generator[None]:
613625 self .parser_exc = exc
614626
615627 except PayloadTooBig as exc :
616- exc .set_current_size (self .cur_size )
628+ exc .set_current_size (self .current_size )
617629 self .fail (CloseCode .MESSAGE_TOO_BIG , str (exc ))
618630 self .parser_exc = exc
619631
@@ -664,18 +676,18 @@ def recv_frame(self, frame: Frame) -> None:
664676
665677 """
666678 if frame .opcode is OP_TEXT or frame .opcode is OP_BINARY :
667- if self .cur_size is not None :
679+ if self .current_size is not None :
668680 raise ProtocolError ("expected a continuation frame" )
669681 if not frame .fin :
670- self .cur_size = len (frame .data )
682+ self .current_size = len (frame .data )
671683
672684 elif frame .opcode is OP_CONT :
673- if self .cur_size is None :
685+ if self .current_size is None :
674686 raise ProtocolError ("unexpected continuation frame" )
675687 if frame .fin :
676- self .cur_size = None
688+ self .current_size = None
677689 else :
678- self .cur_size += len (frame .data )
690+ self .current_size += len (frame .data )
679691
680692 elif frame .opcode is OP_PING :
681693 # 5.5.2. Ping: "Upon receipt of a Ping frame, an endpoint MUST
@@ -696,7 +708,7 @@ def recv_frame(self, frame: Frame) -> None:
696708 assert self .close_sent is not None
697709 self .close_rcvd_then_sent = False
698710
699- if self .cur_size is not None :
711+ if self .current_size is not None :
700712 raise ProtocolError ("incomplete fragmented message" )
701713
702714 # 5.5.1 Close: "If an endpoint receives a Close frame and did
0 commit comments