# Buggy code
def __call__(self):
    self.flow = WebSocketFlow(
        self.client_conn, self.server_conn, self.handshake_flow, self
    )
    self.flow.metadata["websocket_handshake"] = self.handshake_flow
    self.handshake_flow.metadata["websocket_flow"] = self.flow
    self.channel.ask("websocket_start", self.flow)

    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, 0.1)
            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
                is_server = conn == self.server_conn.connection

                frame = websockets.Frame.from_file(source_conn.rfile)

                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:
        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)


# Traceback (ValueError)
Traceback (most recent call last):
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/mitmproxy/tools/console/master.py", line 281, in run
self.loop.run()
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/urwid/main_loop.py", line 278, in run
self._run()
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/urwid/main_loop.py", line 376, in _run
self.event_loop.run()
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/urwid/main_loop.py", line 682, in run
self._loop()
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/urwid/main_loop.py", line 719, in _loop
self._watch_files[fd]()
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/urwid/raw_display.py", line 393, in <lambda>
event_loop, callback, self.get_available_raw_input())
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/urwid/raw_display.py", line 493, in parse_input
callback(processed, processed_codes)
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/urwid/main_loop.py", line 403, in _update
self.process_input(keys)
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/urwid/main_loop.py", line 503, in process_input
k = self._topmost_widget.keypress(self.screen_size, k)
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/mitmproxy/tools/console/window.py", line 84, in keypress
k = super().keypress(size, k)
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/urwid/container.py", line 1116, in keypress
return self.footer.keypress((maxcol,),key)
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/mitmproxy/tools/console/statusbar.py", line 155, in keypress
return self.master.ab.keypress(*args, **kwargs)
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/mitmproxy/tools/console/statusbar.py", line 110, in keypress
self.prompt_execute(self._w.get_edit_text())
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/mitmproxy/tools/console/statusbar.py", line 133, in prompt_execute
msg = p(txt)
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/mitmproxy/tools/console/statusbar.py", line 21, in __call__
return self.callback(pth, *self.args)
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/mitmproxy/tools/console/master.py", line 388, in save_flows
return self._write_flows(path, self.view)
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/mitmproxy/tools/console/master.py", line 382, in _write_flows
fw.add(i)
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/mitmproxy/io.py", line 27, in add
tnetstring.dump(d, self.fo)
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/mitmproxy/contrib/tnetstring.py", line 66, in dump
file_handle.write(dumps(value))
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/mitmproxy/contrib/tnetstring.py", line 57, in dumps
_rdumpq(q, 0, value)
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/mitmproxy/contrib/tnetstring.py", line 142, in _rdumpq
size = _rdumpq(q, size, v)
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/mitmproxy/contrib/tnetstring.py", line 142, in _rdumpq
size = _rdumpq(q, size, v)
File "/usr/local/Cellar/mitmproxy/2.0.0/libexec/lib/python3.6/site-packages/mitmproxy/contrib/tnetstring.py", line 149, in _rdumpq
raise ValueError("unserializable object: {} ({})".format(value, type(value)))
ValueError: unserializable object: <WebSocketFlow (69 messages)> (<class 'mitmproxy.websocket.WebSocketFlow'>)

mitmproxy has crashed!
Please lodge a bug report at:
https://github.com/mitmproxy/mitmproxy
Shutting down...

# Fixed code
def __call__(self):
    self.flow = WebSocketFlow(
        self.client_conn, self.server_conn, self.handshake_flow, self
    )
    self.flow.metadata["websocket_handshake"] = self.handshake_flow.id
    self.handshake_flow.metadata["websocket_flow"] = self.flow.id
    self.channel.ask("websocket_start", self.flow)

    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, 0.1)
            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
                is_server = conn == self.server_conn.connection

                frame = websockets.Frame.from_file(source_conn.rfile)

                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:
        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)