don't emit WS CONT. frames when the peer does not send any, fix #4701 (#4719)

This commit is contained in:
Maximilian Hils 2021-08-02 14:23:56 +02:00 committed by GitHub
parent 9d02eb91c4
commit e865484c45
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 29 additions and 3 deletions

View File

@ -65,7 +65,7 @@ class WebsocketConnection(wsproto.Connection):
def __init__(self, *args, conn: connection.Connection, **kwargs): def __init__(self, *args, conn: connection.Connection, **kwargs):
super(WebsocketConnection, self).__init__(*args, **kwargs) super(WebsocketConnection, self).__init__(*args, **kwargs)
self.conn = conn self.conn = conn
self.frame_buf = [] self.frame_buf = [b""]
def send2(self, event: wsproto.events.Event) -> commands.SendData: def send2(self, event: wsproto.events.Event) -> commands.SendData:
data = self.send(event) data = self.send(event)
@ -155,10 +155,10 @@ class WebsocketLayer(layer.Layer):
is_text = isinstance(ws_event.data, str) is_text = isinstance(ws_event.data, str)
if is_text: if is_text:
typ = Opcode.TEXT typ = Opcode.TEXT
src_ws.frame_buf.append(ws_event.data.encode()) src_ws.frame_buf[-1] += ws_event.data.encode()
else: else:
typ = Opcode.BINARY typ = Opcode.BINARY
src_ws.frame_buf.append(ws_event.data) src_ws.frame_buf[-1] += ws_event.data
if ws_event.message_finished: if ws_event.message_finished:
content = b"".join(src_ws.frame_buf) content = b"".join(src_ws.frame_buf)
@ -174,6 +174,9 @@ class WebsocketLayer(layer.Layer):
for msg in fragmentizer(message.content): for msg in fragmentizer(message.content):
yield dst_ws.send2(msg) yield dst_ws.send2(msg)
elif ws_event.frame_finished:
src_ws.frame_buf.append(b"")
elif isinstance(ws_event, (wsproto.events.Ping, wsproto.events.Pong)): elif isinstance(ws_event, (wsproto.events.Ping, wsproto.events.Pong)):
yield commands.Log( yield commands.Log(
f"Received WebSocket {ws_event.__class__.__name__.lower()} from {from_str} " f"Received WebSocket {ws_event.__class__.__name__.lower()} from {from_str} "
@ -209,8 +212,11 @@ class Fragmentizer:
Practice: Practice:
Some WebSocket servers reject large payload sizes. Some WebSocket servers reject large payload sizes.
Other WebSocket servers reject CONTINUATION frames.
As a workaround, we either retain the original chunking or, if the payload has been modified, use ~4kB chunks. As a workaround, we either retain the original chunking or, if the payload has been modified, use ~4kB chunks.
If one deals with web servers that do not support CONTINUATION frames, addons need to monkeypatch FRAGMENT_SIZE
if they need to modify the message.
""" """
# A bit less than 4kb to accommodate for headers. # A bit less than 4kb to accommodate for headers.
FRAGMENT_SIZE = 4000 FRAGMENT_SIZE = 4000

View File

@ -193,6 +193,26 @@ def test_fragmented(ws_testdata):
assert flow.websocket.messages[-1].content == b"foobar" assert flow.websocket.messages[-1].content == b"foobar"
def test_unfragmented(ws_testdata):
tctx, playbook, flow = ws_testdata
assert (
playbook
<< websocket.WebsocketStartHook(flow)
>> reply()
>> DataReceived(tctx.server, b"\x81\x06foo")
)
# This already triggers wsproto to emit a wsproto.events.Message, see
# https://github.com/mitmproxy/mitmproxy/issues/4701
assert(
playbook
>> DataReceived(tctx.server, b"bar")
<< websocket.WebsocketMessageHook(flow)
>> reply()
<< SendData(tctx.client, b"\x81\x06foobar")
)
assert flow.websocket.messages[-1].content == b"foobar"
def test_protocol_error(ws_testdata): def test_protocol_error(ws_testdata):
tctx, playbook, flow = ws_testdata tctx, playbook, flow = ws_testdata
assert ( assert (