# Buggy code
def flow_to_json(flow: mitmproxy.flow.Flow) -> dict:
    """
    Remove flow message content and cert to save transmission space.

    Args:
        flow: The original flow.
    """
    f = {
        "id": flow.id,
        "intercepted": flow.intercepted,
        "client_conn": flow.client_conn.get_state(),
        "server_conn": flow.server_conn.get_state(),
        "type": flow.type,
        "modified": flow.modified(),
        "marked": flow.marked,
    }
    # .alpn_proto_negotiated is bytes, we need to decode that.
    for conn in "client_conn", "server_conn":
        if f[conn]["alpn_proto_negotiated"] is None:
            continue
        f[conn]["alpn_proto_negotiated"] = f[conn]["alpn_proto_negotiated"].decode(
            errors="backslashreplace"
        )
    if flow.error:
        f["error"] = flow.error.get_state()

    if isinstance(flow, http.HTTPFlow):
        if flow.request:
            if flow.request.raw_content:
                content_length = len(flow.request.raw_content)
                content_hash = hashlib.sha256(flow.request.raw_content).hexdigest()
            else:
                content_length = None
                content_hash = None
            f["request"] = {
                "method": flow.request.method,
                "scheme": flow.request.scheme,
                "host": flow.request.host,
                "port": flow.request.port,
                "path": flow.request.path,
                "http_version": flow.request.http_version,
                "headers": tuple(flow.request.headers.items(True)),
                "contentLength": content_length,
                "contentHash": content_hash,
                "timestamp_start": flow.request.timestamp_start,
                "timestamp_end": flow.request.timestamp_end,
                "is_replay": flow.request.is_replay,
            }
        if flow.response:
            if flow.response.raw_content:
                content_length = len(flow.response.raw_content)
                content_hash = hashlib.sha256(flow.response.raw_content).hexdigest()
            else:
                content_length = None
                content_hash = None
            f["response"] = {
                "http_version": flow.response.http_version,
                "status_code": flow.response.status_code,
                "reason": flow.response.reason,
                "headers": tuple(flow.response.headers.items(True)),
                "contentLength": content_length,
                "contentHash": content_hash,
                "timestamp_start": flow.response.timestamp_start,
                "timestamp_end": flow.response.timestamp_end,
                "is_replay": flow.response.is_replay,
            }
    f.get("server_conn", {}).pop("cert", None)

    return f


# Traceback (TypeError)
ERROR:tornado.application:Exception in callback <function WebMaster.run.<locals>.<lambda> at 0x7f8871ebb378>
Traceback (most recent call last):
File "/home/matthew/Hack/mitmproxy/venv3.5/lib/python3.5/site-packages/tornado/ioloop.py", line 1041, in _run
return self.callback()
File "/home/matthew/Hack/mitmproxy/mitmproxy/tools/web/master.py", line 109, in <lambda>
tornado.ioloop.PeriodicCallback(lambda: self.tick(timeout=0), 5).start()
File "/home/matthew/Hack/mitmproxy/mitmproxy/master.py", line 109, in tick
handle_func(obj)
File "/home/matthew/Hack/mitmproxy/mitmproxy/controller.py", line 70, in wrapper
master.addons(f.__name__, message)
File "/home/matthew/Hack/mitmproxy/mitmproxy/addonmanager.py", line 90, in __call__
self.invoke(i, name, *args, **kwargs)
File "/home/matthew/Hack/mitmproxy/mitmproxy/addonmanager.py", line 85, in invoke
func(*args, **kwargs)
File "/home/matthew/Hack/mitmproxy/mitmproxy/addons/view.py", line 327, in request
self.add(f)
File "/home/matthew/Hack/mitmproxy/mitmproxy/addons/view.py", line 255, in add
self.sig_view_add.send(self, flow=f)
File "/home/matthew/Hack/mitmproxy/venv3.5/lib/python3.5/site-packages/blinker/base.py", line 267, in send
for receiver in self.receivers_for(sender)]
File "/home/matthew/Hack/mitmproxy/venv3.5/lib/python3.5/site-packages/blinker/base.py", line 267, in <listcomp>
for receiver in self.receivers_for(sender)]
File "/home/matthew/Hack/mitmproxy/mitmproxy/tools/web/master.py", line 58, in _sig_view_add
data=app.flow_to_json(flow)
File "/home/matthew/Hack/mitmproxy/mitmproxy/tools/web/app.py", line 197, in broadcast
message = json.dumps(kwargs, ensure_ascii=False).encode("utf8", "surrogateescape")
File "/usr/lib/python3.5/json/__init__.py", line 237, in dumps
**kw).encode(obj)
File "/usr/lib/python3.5/json/encoder.py", line 198, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib/python3.5/json/encoder.py", line 256, in iterencode
return _iterencode(o, 0)
File "/usr/lib/python3.5/json/encoder.py", line 179, in default
raise TypeError(repr(o) + " is not JSON serializable")
TypeError: b'-----BEGIN CERTIFICATE-----\nMIIC5jCCAc6gAwIBAgIGDYj0HL5MMA0GCSqGSIb3DQEBCwUAMCgxEjAQBgNVBAMM\nCW1pdG1wcm94eTESMBAGA1UECgwJbWl0bXByb3h5MB4XDTE3MDIyNTA5MDM0M1oX\nDTIwMDIyNzA5MDM0M1owFTETMBEGA1UEAwwKZ2l0aHViLmNvbTCCASIwDQYJKoZI\nhvcNAQEBBQADggEPADCCAQoCggEBAMTLqdlVNA4h2xzkX5XhLO1wtqZX0X0JpsXC\nHUO+KE3Pf2IBHWFAzeB3SVuaTSIa55UvRUDgZm+gYpl/qswf3MpPB8rkosLtwSJt\ns7ziAYF0JlrwYW+ZBaH/baQZ4JmgpY3qFzrkNhXwrVW+Wg3uO47w/9GaIuUNVv5t\nElfbCDBO0wvWt9tgEuaFNLVwOnibN4LEioQw/xnnUZu4JU6u+16rWasARxU7vlGs\no+CB6wgoK62W4VnSK7aQv6PMAOR49tyzhLXO6LKHQtZA4DG34zXWTYfXhuTC7rnA\nQ6haZ9qyVyeYclIXpJkmf10q2eJTjQbj8ff4Cj3LYlVmBtC2qbsCAwEAAaMpMCcw\nJQYDVR0RBB4wHIIKZ2l0aHViLmNvbYIOd3d3LmdpdGh1Yi5jb20wDQYJKoZIhvcN\nAQELBQADggEBABRJcH+lDB6ec343S+tNYDtr+wWgSiGw7WggKcUpMawBuqY61K4L\nLoxous98ie5XFfLbZI2rW/sIbMEuhjjamEMNmt83ZmZxo/YzMTXO/HlmHZYm+Vjw\nTdhGxe5cGTxjCwXhygRHX+IupDjanniwmh2jfg/0SlW7S4YE/MQJ1mcbGyzppwkg\n4hZ6sEcGe+RC7Sn1tJWlVpA3V8a6udZE8ejlaZV0/PYbJUWyRxAl00PlvRG2sPu5\nEJM7Xbd0TxtqVX7oagImBhqlhf0CyJfRMq0DU34j0oeUqtV/0FaapMumOODcnloI\nJeldz1QeX2hHksE1hYeVjZNFNKQLtzvEpgg=\n-----END CERTIFICATE-----\n' is not JSON serializable

# Fixed code
def flow_to_json(flow: mitmproxy.flow.Flow) -> dict:
    """
    Remove flow message content and cert to save transmission space.

    Args:
        flow: The original flow.
    """
    f = {
        "id": flow.id,
        "intercepted": flow.intercepted,
        "client_conn": flow.client_conn.get_state(),
        "server_conn": flow.server_conn.get_state(),
        "type": flow.type,
        "modified": flow.modified(),
        "marked": flow.marked,
    }
    # .alpn_proto_negotiated is bytes, we need to decode that.
    for conn in "client_conn", "server_conn":
        if f[conn]["alpn_proto_negotiated"] is None:
            continue
        f[conn]["alpn_proto_negotiated"] = f[conn]["alpn_proto_negotiated"].decode(
            errors="backslashreplace"
        )
    if flow.error:
        f["error"] = flow.error.get_state()

    if isinstance(flow, http.HTTPFlow):
        if flow.request:
            if flow.request.raw_content:
                content_length = len(flow.request.raw_content)
                content_hash = hashlib.sha256(flow.request.raw_content).hexdigest()
            else:
                content_length = None
                content_hash = None
            f["request"] = {
                "method": flow.request.method,
                "scheme": flow.request.scheme,
                "host": flow.request.host,
                "port": flow.request.port,
                "path": flow.request.path,
                "http_version": flow.request.http_version,
                "headers": tuple(flow.request.headers.items(True)),
                "contentLength": content_length,
                "contentHash": content_hash,
                "timestamp_start": flow.request.timestamp_start,
                "timestamp_end": flow.request.timestamp_end,
                "is_replay": flow.request.is_replay,
            }
        if flow.response:
            if flow.response.raw_content:
                content_length = len(flow.response.raw_content)
                content_hash = hashlib.sha256(flow.response.raw_content).hexdigest()
            else:
                content_length = None
                content_hash = None
            f["response"] = {
                "http_version": flow.response.http_version,
                "status_code": flow.response.status_code,
                "reason": flow.response.reason,
                "headers": tuple(flow.response.headers.items(True)),
                "contentLength": content_length,
                "contentHash": content_hash,
                "timestamp_start": flow.response.timestamp_start,
                "timestamp_end": flow.response.timestamp_end,
                "is_replay": flow.response.is_replay,
            }
    f.get("server_conn", {}).pop("cert", None)
    f.get("client_conn", {}).pop("mitmcert", None)

    return f