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

Reply via email to