aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mitmproxy/proxy/protocol/websocket.py47
1 files changed, 33 insertions, 14 deletions
diff --git a/mitmproxy/proxy/protocol/websocket.py b/mitmproxy/proxy/protocol/websocket.py
index c94763e0..31d734fd 100644
--- a/mitmproxy/proxy/protocol/websocket.py
+++ b/mitmproxy/proxy/protocol/websocket.py
@@ -59,28 +59,41 @@ class WebSocketLayer(base.Layer):
fb.append(frame)
if frame.header.fin:
+ payload = b''.join(f.payload for f in fb)
+ original_chunk_sizes = [len(f.payload) for f in fb]
+ fb.clear()
+
if frame.header.opcode == websockets.OPCODE.TEXT:
t = WebSocketTextMessage
else:
t = WebSocketBinaryMessage
- payload = b''.join(f.payload for f in fb)
- fb.clear()
-
websocket_message = t(self.flow, not is_server, payload)
+ length = len(websocket_message.content)
self.flow.messages.append(websocket_message)
self.channel.ask("websocket_message", self.flow)
- # chunk payload into multiple 10kB frames, and send them
- payload = websocket_message.content
- chunk_size = 10240 # 10kB
- chunks = range(0, len(payload), chunk_size)
+ def get_chunk(payload):
+ if len(payload) == length:
+ # message has the same length, we can reuse the same sizes
+ pos = 0
+ for s in original_chunk_sizes:
+ yield payload[pos:pos + s]
+ pos += s
+ else:
+ # just re-chunk everything into 10kB frames
+ chunk_size = 10240
+ chunks = range(0, len(payload), chunk_size)
+ for i in chunks:
+ yield payload[i:i + chunk_size]
+
frms = [
websockets.Frame(
- payload=payload[i:i + chunk_size],
+ payload=chunk,
opcode=frame.header.opcode,
mask=(False if is_server else 1),
- masking_key=(b'' if is_server else os.urandom(4))) for i in chunks
+ masking_key=(b'' if is_server else os.urandom(4)))
+ for chunk in get_chunk(websocket_message.content)
]
if len(frms) > 0:
@@ -113,7 +126,7 @@ class WebSocketLayer(base.Layer):
other_conn.send(bytes(frame))
- # close the connection
+ # initiate close handshake
return False
def _handle_unknown_frame(self, frame, source_conn, other_conn, is_server):
@@ -134,10 +147,11 @@ class WebSocketLayer(base.Layer):
client = self.client_conn.connection
server = self.server_conn.connection
conns = [client, server]
+ close_received = False
try:
while not self.channel.should_exit.is_set():
- r = tcp.ssl_read_select(conns, 1)
+ r = tcp.ssl_read_select(conns, 0.5)
for conn in r:
source_conn = self.client_conn if conn == client else self.server_conn
other_conn = self.server_conn if conn == client else self.client_conn
@@ -145,10 +159,15 @@ class WebSocketLayer(base.Layer):
frame = websockets.Frame.from_file(source_conn.rfile)
- if not self._handle_frame(frame, source_conn, other_conn, is_server):
- return
+ cont = self._handle_frame(frame, source_conn, other_conn, is_server)
+ if not cont:
+ if close_received:
+ return
+ else:
+ close_received = True
except (socket.error, exceptions.TcpException, SSL.Error) as e:
- self.flow.error = flow.Error("WebSocket connection closed unexpectedly: {}".format(repr(e)))
+ s = 'server' if is_server else 'client'
+ self.flow.error = flow.Error("WebSocket connection closed unexpectedly by {}: {}".format(s, repr(e)))
self.channel.tell("websocket_error", self.flow)
finally:
self.channel.tell("websocket_end", self.flow)