On Sun, 05 Jan 2020 11:27:14 +0100 Adrian Vollmer
<debian...@vollmer.syss.de> wrote:
Package: mitmproxy
Version: 4.0.4-6
Severity: grave
Tags: newcomer
Justification: renders package unusable
After upgrading python3-wsproto to version 0.15.0-2, mitmproxy fails
immediately with this error message:
ImportError: cannot import name 'WSConnection' from 'wsproto.connection'
(/usr/lib/python3/dist-packages/wsproto/connection.py)
As stated in the setup.py of mitmproxy, it requires the Python
module wsproto to be at least 0.14.0 and < 0.15.0 [1]:
"wsproto>=0.14.0,<0.15.0",
Downgrading the package with `apt install python3-wsproto=0.11.0-3` solves
this issue temporarily (this version is shown below).
[1]
https://github.com/mitmproxy/mitmproxy/blob/7b638f1b6b543ec5e23170217a42ca0e5c421992/setup.py#L85
Uploading an NMU to DELAYED/0, including the upstream fix and two
clean-up commits from the Janitor.
--
Cheers,
Andrej
diff --git a/debian/changelog b/debian/changelog
index 24243762..37c4ef68 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,16 @@
+mitmproxy (4.0.4-6.1) unstable; urgency=medium
+
+ * Non-maintainer upload.
+
+ [ Debian Janitor ]
+ * Update standards version, no changes needed.
+ * Bump debhelper from old 9 to 12.
+
+ [ Andrej Shadura ]
+ * Apply an upstream patch for wsproto 0.13 compatibility (Closes: #948206)
+
+ -- Andrej Shadura <andre...@debian.org> Wed, 08 Apr 2020 16:47:13 +0200
+
mitmproxy (4.0.4-6) unstable; urgency=medium
* Fix symlinks to fontawesome (Closes: #928749)
diff --git a/debian/compat b/debian/compat
deleted file mode 100644
index ec635144..00000000
--- a/debian/compat
+++ /dev/null
@@ -1 +0,0 @@
-9
diff --git a/debian/control b/debian/control
index d6b82dc9..0e09f57f 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,7 @@ Source: mitmproxy
Section: net
Priority: optional
Maintainer: Sebastien Delafond <s...@debian.org>
-Build-Depends: debhelper (>= 9), dh-python, python3, python3-setuptools,
+Build-Depends: dh-python, python3, python3-setuptools,
pandoc,
python3-blinker (>= 1.4),
python3-brotli (>= 0.7.0),
@@ -27,8 +27,9 @@ Build-Depends: debhelper (>= 9), dh-python, python3,
python3-setuptools,
python3-sortedcontainers (>= 1.5.4),
python3-tornado (>= 4.3),
python3-urwid (>= 2.0.1),
- python3-wsproto (>= 0.11.0)
-Standards-Version: 4.3.0
+ python3-wsproto (>= 0.13.0),
+ debhelper-compat (= 12)
+Standards-Version: 4.4.0
Vcs-Git: https://salsa.debian.org/debian/mitmproxy.git
Vcs-Browser: https://salsa.debian.org/debian/mitmproxy
Homepage: https://mitmproxy.org
diff --git a/debian/patches/0006-update-to-wsproto-0.13.patch
b/debian/patches/0006-update-to-wsproto-0.13.patch
new file mode 100644
index 00000000..15e71c27
--- /dev/null
+++ b/debian/patches/0006-update-to-wsproto-0.13.patch
@@ -0,0 +1,191 @@
+From: rpigott <rpig...@berkeley.edu>
+Date: Sun, 27 Jan 2019 00:59:26 -0800
+Subject: update to wsproto 0.13
+
+Origin: upstream, https://github.com/mitmproxy/mitmproxy/commit/106948d99
+Bug-Debian: http://bugs.debian.org/948206
+Applied-Upstream: yes
+
+---
+ mitmproxy/proxy/protocol/websocket.py | 87 +++++++++++++++++------------------
+ setup.py | 2 +-
+ 2 files changed, 44 insertions(+), 45 deletions(-)
+
+diff --git a/mitmproxy/proxy/protocol/websocket.py
b/mitmproxy/proxy/protocol/websocket.py
+index 0d1964a..591bae7 100644
+--- a/mitmproxy/proxy/protocol/websocket.py
++++ b/mitmproxy/proxy/protocol/websocket.py
+@@ -4,8 +4,9 @@ from OpenSSL import SSL
+
+
+ import wsproto
+-from wsproto import events
+-from wsproto.connection import ConnectionType, WSConnection
++from wsproto import events, WSConnection
++from wsproto.connection import ConnectionType
++from wsproto.events import AcceptConnection, CloseConnection, Message, Ping,
Request
+ from wsproto.extensions import PerMessageDeflate
+
+ from mitmproxy import exceptions
+@@ -56,47 +57,44 @@ class WebSocketLayer(base.Layer):
+ if 'Sec-WebSocket-Extensions' in handshake_flow.response.headers:
+ if PerMessageDeflate.name in
handshake_flow.response.headers['Sec-WebSocket-Extensions']:
+ extensions = [PerMessageDeflate()]
+- self.connections[self.client_conn] =
WSConnection(ConnectionType.SERVER,
+-
extensions=extensions)
+- self.connections[self.server_conn] =
WSConnection(ConnectionType.CLIENT,
+-
host=handshake_flow.request.host,
+-
resource=handshake_flow.request.path,
+-
extensions=extensions)
++ self.connections[self.client_conn] =
WSConnection(ConnectionType.SERVER)
++ self.connections[self.server_conn] =
WSConnection(ConnectionType.CLIENT)
++
+ if extensions:
+- for conn in self.connections.values():
+- conn.extensions[0].finalize(conn,
handshake_flow.response.headers['Sec-WebSocket-Extensions'])
++
extensions[0].finalize(handshake_flow.response.headers['Sec-WebSocket-Extensions'])
+
+- data = self.connections[self.server_conn].bytes_to_send()
+- self.connections[self.client_conn].receive_bytes(data)
++ request = Request(extensions = extensions, host =
handshake_flow.request.host, target = handshake_flow.request.path)
++ data = self.connections[self.server_conn].send(request)
++ self.connections[self.client_conn].receive_data(data)
+
+ event = next(self.connections[self.client_conn].events())
+- assert isinstance(event, events.ConnectionRequested)
++ assert isinstance(event, events.Request)
+
+- self.connections[self.client_conn].accept(event)
+-
self.connections[self.server_conn].receive_bytes(self.connections[self.client_conn].bytes_to_send())
+- assert isinstance(next(self.connections[self.server_conn].events()),
events.ConnectionEstablished)
++ data =
self.connections[self.client_conn].send(AcceptConnection(extensions=extensions))
++ self.connections[self.server_conn].receive_data(data)
++ assert isinstance(next(self.connections[self.server_conn].events()),
events.AcceptConnection)
+
+ def _handle_event(self, event, source_conn, other_conn, is_server):
+- if isinstance(event, events.DataReceived):
+- return self._handle_data_received(event, source_conn, other_conn,
is_server)
+- elif isinstance(event, events.PingReceived):
+- return self._handle_ping_received(event, source_conn, other_conn,
is_server)
+- elif isinstance(event, events.PongReceived):
+- return self._handle_pong_received(event, source_conn, other_conn,
is_server)
+- elif isinstance(event, events.ConnectionClosed):
+- return self._handle_connection_closed(event, source_conn,
other_conn, is_server)
++ if isinstance(event, events.Message):
++ return self._handle_message(event, source_conn, other_conn,
is_server)
++ elif isinstance(event, events.Ping):
++ return self._handle_ping(event, source_conn, other_conn,
is_server)
++ elif isinstance(event, events.Pong):
++ return self._handle_pong(event, source_conn, other_conn,
is_server)
++ elif isinstance(event, events.CloseConnection):
++ return self._handle_close_connection(event, source_conn,
other_conn, is_server)
+
+ # fail-safe for unhandled events
+ return True # pragma: no cover
+
+- def _handle_data_received(self, event, source_conn, other_conn,
is_server):
++ def _handle_message(self, event, source_conn, other_conn, is_server):
+ fb = self.server_frame_buffer if is_server else
self.client_frame_buffer
+ fb.append(event.data)
+
+ if event.message_finished:
+ original_chunk_sizes = [len(f) for f in fb]
+
+- if isinstance(event, events.TextReceived):
++ if isinstance(event, events.TextMessage):
+ message_type = wsproto.frame_protocol.Opcode.TEXT
+ payload = ''.join(fb)
+ else:
+@@ -127,19 +125,20 @@ class WebSocketLayer(base.Layer):
+ yield (payload[i:i + chunk_size], True if i +
chunk_size >= len(payload) else False)
+
+ for chunk, final in get_chunk(websocket_message.content):
+- self.connections[other_conn].send_data(chunk, final)
+-
other_conn.send(self.connections[other_conn].bytes_to_send())
++ data = self.connections[other_conn].send(Message(data =
chunk, message_finished = final))
++ other_conn.send(data)
+
+ if self.flow.stream:
+- self.connections[other_conn].send_data(event.data,
event.message_finished)
+- other_conn.send(self.connections[other_conn].bytes_to_send())
++ data = self.connections[other_conn].send(Message(data =
event.data, message_finished = event.message_finished))
++ other_conn.send(data)
+ return True
+
+- def _handle_ping_received(self, event, source_conn, other_conn,
is_server):
+- # PING is automatically answered with a PONG by wsproto
+- self.connections[other_conn].ping()
+- other_conn.send(self.connections[other_conn].bytes_to_send())
+- source_conn.send(self.connections[source_conn].bytes_to_send())
++ def _handle_ping(self, event, source_conn, other_conn, is_server):
++ # Use event.response to create the approprate Pong response
++ data = self.connections[other_conn].send(Ping())
++ other_conn.send(data)
++ data = self.connections[source_conn].send(event.response())
++ source_conn.send(data)
+ self.log(
+ "Ping Received from {}".format("server" if is_server else
"client"),
+ "info",
+@@ -147,7 +146,7 @@ class WebSocketLayer(base.Layer):
+ )
+ return True
+
+- def _handle_pong_received(self, event, source_conn, other_conn,
is_server):
++ def _handle_pong(self, event, source_conn, other_conn, is_server):
+ self.log(
+ "Pong Received from {}".format("server" if is_server else
"client"),
+ "info",
+@@ -155,14 +154,15 @@ class WebSocketLayer(base.Layer):
+ )
+ return True
+
+- def _handle_connection_closed(self, event, source_conn, other_conn,
is_server):
++ def _handle_close_connection(self, event, source_conn, other_conn,
is_server):
+ self.flow.close_sender = "server" if is_server else "client"
+ self.flow.close_code = event.code
+ self.flow.close_reason = event.reason
+
+- self.connections[other_conn].close(event.code, event.reason)
+- other_conn.send(self.connections[other_conn].bytes_to_send())
+- source_conn.send(self.connections[source_conn].bytes_to_send())
++ data =
self.connections[other_conn].send(CloseConnection(code=event.code,
reason=event.reason))
++ other_conn.send(data)
++ data = self.connections[source_conn].send(event.response())
++ source_conn.send(data)
+
+ return False
+
+@@ -170,8 +170,7 @@ class WebSocketLayer(base.Layer):
+ while True:
+ try:
+ payload = message_queue.get_nowait()
+- self.connections[endpoint].send_data(payload, final=True)
+- data = self.connections[endpoint].bytes_to_send()
++ data = self.connections[endpoint].send(Message(data =
payload, message_finished = True))
+ endpoint.send(data)
+ except queue.Empty:
+ break
+@@ -197,8 +196,8 @@ class WebSocketLayer(base.Layer):
+ is_server = (source_conn == self.server_conn)
+
+ frame = websockets.Frame.from_file(source_conn.rfile)
+- self.connections[source_conn].receive_bytes(bytes(frame))
+-
source_conn.send(self.connections[source_conn].bytes_to_send())
++ data =
self.connections[source_conn].receive_data(bytes(frame))
++ source_conn.send(data)
+
+ if close_received:
+ return
+diff --git a/setup.py b/setup.py
+index 2c2cb5f..656530c 100644
+--- a/setup.py
++++ b/setup.py
+@@ -78,7 +78,7 @@ setup(
+ "sortedcontainers>=1.5.4",
+ "tornado>=4.3",
+ "urwid>=2.0.1",
+- "wsproto>=0.11.0",
++ "wsproto>=0.13.0",
+ ],
+ extras_require={
+ ':sys_platform == "win32"': [
diff --git a/debian/patches/series b/debian/patches/series
index 5c8eb68b..d39b9453 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -3,3 +3,4 @@
0003-Remove-test_xss_scanner.py.patch
0004-Remove-test_cibuild.py.patch
0005-Remove-test_readfile.py.patch
+0006-update-to-wsproto-0.13.patch