We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
MIT incompatible with GPL3
I was pleased to see mitmproxy is licensed under MIT. At least one dependency, html2text, is licensed under GPL3. This is a problem as per https://opensource.stackexchange.com/questions/1640/if-im-using-a-gpl-3-library-in-my-project-can-i-license-my-project-under-mit-l. Can we resolve this incompatible licensing issue? html2text is barely used. I think it could be removed.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mitmproxy/contentviews/html_outline.py`
Content:
```
1 import html2text
2 
3 from mitmproxy.contentviews import base
4 
5 
6 class ViewHTMLOutline(base.View):
7     name = "HTML Outline"
8     prompt = ("html outline", "o")
9     content_types = ["text/html"]
10 
11     def __call__(self, data, **metadata):
12         data = data.decode("utf-8", "replace")
13         h = html2text.HTML2Text(baseurl="")
14         h.ignore_images = True
15         h.body_width = 0
16         outline = h.handle(data)
17         return "HTML Outline", base.format_text(outline)
18
```

Path: `setup.py`
Content:
```
1 import os
2 import runpy
3 from codecs import open
4 
5 from setuptools import setup, find_packages
6 
7 # Based on https://github.com/pypa/sampleproject/blob/master/setup.py
8 # and https://python-packaging-user-guide.readthedocs.org/
9 
10 here = os.path.abspath(os.path.dirname(__file__))
11 
12 with open(os.path.join(here, 'README.rst'), encoding='utf-8') as f:
13     long_description = f.read()
14 
15 VERSION = runpy.run_path(os.path.join(here, "mitmproxy", "version.py"))["VERSION"]
16 
17 setup(
18     name="mitmproxy",
19     version=VERSION,
20     description="An interactive, SSL-capable, man-in-the-middle HTTP proxy for penetration testers and software developers.",
21     long_description=long_description,
22     url="http://mitmproxy.org",
23     author="Aldo Cortesi",
24     author_email="aldo@corte.si",
25     license="MIT",
26     classifiers=[
27         "License :: OSI Approved :: MIT License",
28         "Development Status :: 5 - Production/Stable",
29         "Environment :: Console",
30         "Environment :: Console :: Curses",
31         "Operating System :: MacOS :: MacOS X",
32         "Operating System :: POSIX",
33         "Operating System :: Microsoft :: Windows",
34         "Programming Language :: Python",
35         "Programming Language :: Python :: 3",
36         "Programming Language :: Python :: 3 :: Only",
37         "Programming Language :: Python :: 3.5",
38         "Programming Language :: Python :: 3.6",
39         "Programming Language :: Python :: Implementation :: CPython",
40         "Topic :: Security",
41         "Topic :: Internet",
42         "Topic :: Internet :: WWW/HTTP",
43         "Topic :: Internet :: Proxy Servers",
44         "Topic :: Software Development :: Testing"
45     ],
46     packages=find_packages(include=[
47         "mitmproxy", "mitmproxy.*",
48         "pathod", "pathod.*",
49     ]),
50     include_package_data=True,
51     entry_points={
52         'console_scripts': [
53             "mitmproxy = mitmproxy.tools.main:mitmproxy",
54             "mitmdump = mitmproxy.tools.main:mitmdump",
55             "mitmweb = mitmproxy.tools.main:mitmweb",
56             "pathod = pathod.pathod_cmdline:go_pathod",
57             "pathoc = pathod.pathoc_cmdline:go_pathoc"
58         ]
59     },
60     # https://packaging.python.org/en/latest/requirements/#install-requires
61     # It is not considered best practice to use install_requires to pin dependencies to specific versions.
62     install_requires=[
63         "blinker>=1.4, <1.5",
64         "brotlipy>=0.5.1, <0.8",
65         "certifi>=2015.11.20.1",  # no semver here - this should always be on the last release!
66         "click>=6.2, <7",
67         "cryptography>=2.0,<2.1",
68         "h2>=3.0, <4",
69         "html2text>=2016.1.8, <=2016.9.19",
70         "hyperframe>=5.0, <6",
71         "kaitaistruct>=0.7, <0.8",
72         "ldap3>=2.2.0, <2.4",
73         "passlib>=1.6.5, <1.8",
74         "pyasn1>=0.3.1, <0.4",
75         "pyOpenSSL>=17.3,<17.4",
76         "pyparsing>=2.1.3, <2.3",
77         "pyperclip>=1.5.22, <1.6",
78         "requests>=2.9.1, <3",
79         "ruamel.yaml>=0.13.2, <0.16",
80         "sortedcontainers>=1.5.4, <1.6",
81         "tornado>=4.3, <4.6",
82         "urwid>=1.3.1, <1.4",
83     ],
84     extras_require={
85         ':sys_platform == "win32"': [
86             "pydivert>=2.0.3, <2.1",
87         ],
88         'dev': [
89             "flake8>=3.2.1, <3.5",
90             "Flask>=0.10.1, <0.13",
91             "mypy>=0.521,<0.522",
92             "pytest-cov>=2.2.1, <3",
93             "pytest-faulthandler>=1.3.0, <2",
94             "pytest-timeout>=1.0.0, <2",
95             "pytest-xdist>=1.14, <2",
96             "pytest>=3.1, <4",
97             "rstcheck>=2.2, <4.0",
98             "sphinx_rtd_theme>=0.1.9, <0.3",
99             "sphinx-autobuild>=0.5.2, <0.8",
100             "sphinx>=1.3.5, <1.7",
101             "sphinxcontrib-documentedlist>=0.5.0, <0.7",
102             "tox>=2.3, <3",
103         ],
104         'examples': [
105             "beautifulsoup4>=4.4.1, <4.7",
106             "Pillow>=3.2,<4.3",
107         ]
108     }
109 )
110
```

Path: `mitmproxy/contentviews/__init__.py`
Content:
```
1 """
2 Mitmproxy Content Views
3 =======================
4 
5 mitmproxy includes a set of content views which can be used to
6 format/decode/highlight data. While they are currently used for HTTP message
7 bodies only, the may be used in other contexts in the future, e.g. to decode
8 protobuf messages sent as WebSocket frames.
9 
10 Thus, the View API is very minimalistic. The only arguments are `data` and
11 `**metadata`, where `data` is the actual content (as bytes). The contents on
12 metadata depend on the protocol in use. For HTTP, the message headers are
13 passed as the ``headers`` keyword argument. For HTTP requests, the query
14 parameters are passed as the ``query`` keyword argument.
15 """
16 import traceback
17 from typing import Dict, Optional  # noqa
18 from typing import List  # noqa
19 from typing import Tuple  # noqa
20 
21 from mitmproxy import exceptions
22 from mitmproxy.net import http
23 from mitmproxy.utils import strutils
24 from . import (
25     auto, raw, hex, json, xml_html, html_outline, wbxml, javascript, css,
26     urlencoded, multipart, image, query, protobuf
27 )
28 from .base import View, VIEW_CUTOFF, KEY_MAX, format_text, format_dict, TViewResult
29 
30 views = []  # type: List[View]
31 content_types_map = {}  # type: Dict[str, List[View]]
32 view_prompts = []  # type: List[Tuple[str, str]]
33 
34 
35 def get(name: str) -> Optional[View]:
36     for i in views:
37         if i.name.lower() == name.lower():
38             return i
39     return None
40 
41 
42 def get_by_shortcut(c: str) -> Optional[View]:
43     for i in views:
44         if i.prompt[1] == c:
45             return i
46     return None
47 
48 
49 def add(view: View) -> None:
50     # TODO: auto-select a different name (append an integer?)
51     for i in views:
52         if i.name == view.name:
53             raise exceptions.ContentViewException("Duplicate view: " + view.name)
54 
55     # TODO: the UI should auto-prompt for a replacement shortcut
56     for prompt in view_prompts:
57         if prompt[1] == view.prompt[1]:
58             raise exceptions.ContentViewException("Duplicate view shortcut: " + view.prompt[1])
59 
60     views.append(view)
61 
62     for ct in view.content_types:
63         l = content_types_map.setdefault(ct, [])
64         l.append(view)
65 
66     view_prompts.append(view.prompt)
67 
68 
69 def remove(view: View) -> None:
70     for ct in view.content_types:
71         l = content_types_map.setdefault(ct, [])
72         l.remove(view)
73 
74         if not len(l):
75             del content_types_map[ct]
76 
77     view_prompts.remove(view.prompt)
78     views.remove(view)
79 
80 
81 def safe_to_print(lines, encoding="utf8"):
82     """
83     Wraps a content generator so that each text portion is a *safe to print* unicode string.
84     """
85     for line in lines:
86         clean_line = []
87         for (style, text) in line:
88             if isinstance(text, bytes):
89                 text = text.decode(encoding, "replace")
90             text = strutils.escape_control_characters(text)
91             clean_line.append((style, text))
92         yield clean_line
93 
94 
95 def get_message_content_view(viewname, message):
96     """
97     Like get_content_view, but also handles message encoding.
98     """
99     viewmode = get(viewname)
100     if not viewmode:
101         viewmode = get("auto")
102     try:
103         content = message.content
104     except ValueError:
105         content = message.raw_content
106         enc = "[cannot decode]"
107     else:
108         if isinstance(message, http.Message) and content != message.raw_content:
109             enc = "[decoded {}]".format(
110                 message.headers.get("content-encoding")
111             )
112         else:
113             enc = None
114 
115     if content is None:
116         return "", iter([[("error", "content missing")]]), None
117 
118     metadata = {}
119     if isinstance(message, http.Request):
120         metadata["query"] = message.query
121     if isinstance(message, http.Message):
122         metadata["headers"] = message.headers
123 
124     description, lines, error = get_content_view(
125         viewmode, content, **metadata
126     )
127 
128     if enc:
129         description = "{} {}".format(enc, description)
130 
131     return description, lines, error
132 
133 
134 def get_content_view(viewmode: View, data: bytes, **metadata):
135     """
136         Args:
137             viewmode: the view to use.
138             data, **metadata: arguments passed to View instance.
139 
140         Returns:
141             A (description, content generator, error) tuple.
142             If the content view raised an exception generating the view,
143             the exception is returned in error and the flow is formatted in raw mode.
144             In contrast to calling the views directly, text is always safe-to-print unicode.
145     """
146     try:
147         ret = viewmode(data, **metadata)
148         if ret is None:
149             ret = "Couldn't parse: falling back to Raw", get("Raw")(data, **metadata)[1]
150         desc, content = ret
151         error = None
152     # Third-party viewers can fail in unexpected ways...
153     except Exception:
154         desc = "Couldn't parse: falling back to Raw"
155         _, content = get("Raw")(data, **metadata)
156         error = "{} Content viewer failed: \n{}".format(
157             getattr(viewmode, "name"),
158             traceback.format_exc()
159         )
160 
161     return desc, safe_to_print(content), error
162 
163 
164 # The order in which ContentViews are added is important!
165 add(auto.ViewAuto())
166 add(raw.ViewRaw())
167 add(hex.ViewHex())
168 add(json.ViewJSON())
169 add(xml_html.ViewXmlHtml())
170 add(wbxml.ViewWBXML())
171 add(html_outline.ViewHTMLOutline())
172 add(javascript.ViewJavaScript())
173 add(css.ViewCSS())
174 add(urlencoded.ViewURLEncoded())
175 add(multipart.ViewMultipart())
176 add(image.ViewImage())
177 add(query.ViewQuery())
178 add(protobuf.ViewProtobuf())
179 
180 __all__ = [
181     "View", "VIEW_CUTOFF", "KEY_MAX", "format_text", "format_dict", "TViewResult",
182     "get", "get_by_shortcut", "add", "remove",
183     "get_content_view", "get_message_content_view",
184 ]
185
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mitmproxy/contentviews/__init__.py b/mitmproxy/contentviews/__init__.py
--- a/mitmproxy/contentviews/__init__.py
+++ b/mitmproxy/contentviews/__init__.py
@@ -22,7 +22,7 @@
 from mitmproxy.net import http
 from mitmproxy.utils import strutils
 from . import (
-    auto, raw, hex, json, xml_html, html_outline, wbxml, javascript, css,
+    auto, raw, hex, json, xml_html, wbxml, javascript, css,
     urlencoded, multipart, image, query, protobuf
 )
 from .base import View, VIEW_CUTOFF, KEY_MAX, format_text, format_dict, TViewResult
@@ -168,7 +168,6 @@
 add(json.ViewJSON())
 add(xml_html.ViewXmlHtml())
 add(wbxml.ViewWBXML())
-add(html_outline.ViewHTMLOutline())
 add(javascript.ViewJavaScript())
 add(css.ViewCSS())
 add(urlencoded.ViewURLEncoded())
diff --git a/mitmproxy/contentviews/html_outline.py b/mitmproxy/contentviews/html_outline.py
deleted file mode 100644
--- a/mitmproxy/contentviews/html_outline.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import html2text
-
-from mitmproxy.contentviews import base
-
-
-class ViewHTMLOutline(base.View):
-    name = "HTML Outline"
-    prompt = ("html outline", "o")
-    content_types = ["text/html"]
-
-    def __call__(self, data, **metadata):
-        data = data.decode("utf-8", "replace")
-        h = html2text.HTML2Text(baseurl="")
-        h.ignore_images = True
-        h.body_width = 0
-        outline = h.handle(data)
-        return "HTML Outline", base.format_text(outline)
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -66,7 +66,6 @@
         "click>=6.2, <7",
         "cryptography>=2.0,<2.1",
         "h2>=3.0, <4",
-        "html2text>=2016.1.8, <=2016.9.19",
         "hyperframe>=5.0, <6",
         "kaitaistruct>=0.7, <0.8",
         "ldap3>=2.2.0, <2.4",

{"golden_diff": "diff --git a/mitmproxy/contentviews/__init__.py b/mitmproxy/contentviews/__init__.py\n--- a/mitmproxy/contentviews/__init__.py\n+++ b/mitmproxy/contentviews/__init__.py\n@@ -22,7 +22,7 @@\n from mitmproxy.net import http\n from mitmproxy.utils import strutils\n from . import (\n-    auto, raw, hex, json, xml_html, html_outline, wbxml, javascript, css,\n+    auto, raw, hex, json, xml_html, wbxml, javascript, css,\n     urlencoded, multipart, image, query, protobuf\n )\n from .base import View, VIEW_CUTOFF, KEY_MAX, format_text, format_dict, TViewResult\n@@ -168,7 +168,6 @@\n add(json.ViewJSON())\n add(xml_html.ViewXmlHtml())\n add(wbxml.ViewWBXML())\n-add(html_outline.ViewHTMLOutline())\n add(javascript.ViewJavaScript())\n add(css.ViewCSS())\n add(urlencoded.ViewURLEncoded())\ndiff --git a/mitmproxy/contentviews/html_outline.py b/mitmproxy/contentviews/html_outline.py\ndeleted file mode 100644\n--- a/mitmproxy/contentviews/html_outline.py\n+++ /dev/null\n@@ -1,17 +0,0 @@\n-import html2text\n-\n-from mitmproxy.contentviews import base\n-\n-\n-class ViewHTMLOutline(base.View):\n-    name = \"HTML Outline\"\n-    prompt = (\"html outline\", \"o\")\n-    content_types = [\"text/html\"]\n-\n-    def __call__(self, data, **metadata):\n-        data = data.decode(\"utf-8\", \"replace\")\n-        h = html2text.HTML2Text(baseurl=\"\")\n-        h.ignore_images = True\n-        h.body_width = 0\n-        outline = h.handle(data)\n-        return \"HTML Outline\", base.format_text(outline)\ndiff --git a/setup.py b/setup.py\n--- a/setup.py\n+++ b/setup.py\n@@ -66,7 +66,6 @@\n         \"click>=6.2, <7\",\n         \"cryptography>=2.0,<2.1\",\n         \"h2>=3.0, <4\",\n-        \"html2text>=2016.1.8, <=2016.9.19\",\n         \"hyperframe>=5.0, <6\",\n         \"kaitaistruct>=0.7, <0.8\",\n         \"ldap3>=2.2.0, <2.4\",\n", "issue": "MIT incompatible with GPL3\nI was pleased to see mitmproxy is licensed under MIT. At least one dependency, html2text, is licensed under GPL3. This is a problem as per https://opensource.stackexchange.com/questions/1640/if-im-using-a-gpl-3-library-in-my-project-can-i-license-my-project-under-mit-l. Can we resolve this incompatible licensing issue? html2text is barely used. I think it could be removed.\n", "before_files": [{"content": "import html2text\n\nfrom mitmproxy.contentviews import base\n\n\nclass ViewHTMLOutline(base.View):\n    name = \"HTML Outline\"\n    prompt = (\"html outline\", \"o\")\n    content_types = [\"text/html\"]\n\n    def __call__(self, data, **metadata):\n        data = data.decode(\"utf-8\", \"replace\")\n        h = html2text.HTML2Text(baseurl=\"\")\n        h.ignore_images = True\n        h.body_width = 0\n        outline = h.handle(data)\n        return \"HTML Outline\", base.format_text(outline)\n", "path": "mitmproxy/contentviews/html_outline.py"}, {"content": "import os\nimport runpy\nfrom codecs import open\n\nfrom setuptools import setup, find_packages\n\n# Based on https://github.com/pypa/sampleproject/blob/master/setup.py\n# and https://python-packaging-user-guide.readthedocs.org/\n\nhere = os.path.abspath(os.path.dirname(__file__))\n\nwith open(os.path.join(here, 'README.rst'), encoding='utf-8') as f:\n    long_description = f.read()\n\nVERSION = runpy.run_path(os.path.join(here, \"mitmproxy\", \"version.py\"))[\"VERSION\"]\n\nsetup(\n    name=\"mitmproxy\",\n    version=VERSION,\n    description=\"An interactive, SSL-capable, man-in-the-middle HTTP proxy for penetration testers and software developers.\",\n    long_description=long_description,\n    url=\"http://mitmproxy.org\",\n    author=\"Aldo Cortesi\",\n    author_email=\"aldo@corte.si\",\n    license=\"MIT\",\n    classifiers=[\n        \"License :: OSI Approved :: MIT License\",\n        \"Development Status :: 5 - Production/Stable\",\n        \"Environment :: Console\",\n        \"Environment :: Console :: Curses\",\n        \"Operating System :: MacOS :: MacOS X\",\n        \"Operating System :: POSIX\",\n        \"Operating System :: Microsoft :: Windows\",\n        \"Programming Language :: Python\",\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3 :: Only\",\n        \"Programming Language :: Python :: 3.5\",\n        \"Programming Language :: Python :: 3.6\",\n        \"Programming Language :: Python :: Implementation :: CPython\",\n        \"Topic :: Security\",\n        \"Topic :: Internet\",\n        \"Topic :: Internet :: WWW/HTTP\",\n        \"Topic :: Internet :: Proxy Servers\",\n        \"Topic :: Software Development :: Testing\"\n    ],\n    packages=find_packages(include=[\n        \"mitmproxy\", \"mitmproxy.*\",\n        \"pathod\", \"pathod.*\",\n    ]),\n    include_package_data=True,\n    entry_points={\n        'console_scripts': [\n            \"mitmproxy = mitmproxy.tools.main:mitmproxy\",\n            \"mitmdump = mitmproxy.tools.main:mitmdump\",\n            \"mitmweb = mitmproxy.tools.main:mitmweb\",\n            \"pathod = pathod.pathod_cmdline:go_pathod\",\n            \"pathoc = pathod.pathoc_cmdline:go_pathoc\"\n        ]\n    },\n    # https://packaging.python.org/en/latest/requirements/#install-requires\n    # It is not considered best practice to use install_requires to pin dependencies to specific versions.\n    install_requires=[\n        \"blinker>=1.4, <1.5\",\n        \"brotlipy>=0.5.1, <0.8\",\n        \"certifi>=2015.11.20.1\",  # no semver here - this should always be on the last release!\n        \"click>=6.2, <7\",\n        \"cryptography>=2.0,<2.1\",\n        \"h2>=3.0, <4\",\n        \"html2text>=2016.1.8, <=2016.9.19\",\n        \"hyperframe>=5.0, <6\",\n        \"kaitaistruct>=0.7, <0.8\",\n        \"ldap3>=2.2.0, <2.4\",\n        \"passlib>=1.6.5, <1.8\",\n        \"pyasn1>=0.3.1, <0.4\",\n        \"pyOpenSSL>=17.3,<17.4\",\n        \"pyparsing>=2.1.3, <2.3\",\n        \"pyperclip>=1.5.22, <1.6\",\n        \"requests>=2.9.1, <3\",\n        \"ruamel.yaml>=0.13.2, <0.16\",\n        \"sortedcontainers>=1.5.4, <1.6\",\n        \"tornado>=4.3, <4.6\",\n        \"urwid>=1.3.1, <1.4\",\n    ],\n    extras_require={\n        ':sys_platform == \"win32\"': [\n            \"pydivert>=2.0.3, <2.1\",\n        ],\n        'dev': [\n            \"flake8>=3.2.1, <3.5\",\n            \"Flask>=0.10.1, <0.13\",\n            \"mypy>=0.521,<0.522\",\n            \"pytest-cov>=2.2.1, <3\",\n            \"pytest-faulthandler>=1.3.0, <2\",\n            \"pytest-timeout>=1.0.0, <2\",\n            \"pytest-xdist>=1.14, <2\",\n            \"pytest>=3.1, <4\",\n            \"rstcheck>=2.2, <4.0\",\n            \"sphinx_rtd_theme>=0.1.9, <0.3\",\n            \"sphinx-autobuild>=0.5.2, <0.8\",\n            \"sphinx>=1.3.5, <1.7\",\n            \"sphinxcontrib-documentedlist>=0.5.0, <0.7\",\n            \"tox>=2.3, <3\",\n        ],\n        'examples': [\n            \"beautifulsoup4>=4.4.1, <4.7\",\n            \"Pillow>=3.2,<4.3\",\n        ]\n    }\n)\n", "path": "setup.py"}, {"content": "\"\"\"\nMitmproxy Content Views\n=======================\n\nmitmproxy includes a set of content views which can be used to\nformat/decode/highlight data. While they are currently used for HTTP message\nbodies only, the may be used in other contexts in the future, e.g. to decode\nprotobuf messages sent as WebSocket frames.\n\nThus, the View API is very minimalistic. The only arguments are `data` and\n`**metadata`, where `data` is the actual content (as bytes). The contents on\nmetadata depend on the protocol in use. For HTTP, the message headers are\npassed as the ``headers`` keyword argument. For HTTP requests, the query\nparameters are passed as the ``query`` keyword argument.\n\"\"\"\nimport traceback\nfrom typing import Dict, Optional  # noqa\nfrom typing import List  # noqa\nfrom typing import Tuple  # noqa\n\nfrom mitmproxy import exceptions\nfrom mitmproxy.net import http\nfrom mitmproxy.utils import strutils\nfrom . import (\n    auto, raw, hex, json, xml_html, html_outline, wbxml, javascript, css,\n    urlencoded, multipart, image, query, protobuf\n)\nfrom .base import View, VIEW_CUTOFF, KEY_MAX, format_text, format_dict, TViewResult\n\nviews = []  # type: List[View]\ncontent_types_map = {}  # type: Dict[str, List[View]]\nview_prompts = []  # type: List[Tuple[str, str]]\n\n\ndef get(name: str) -> Optional[View]:\n    for i in views:\n        if i.name.lower() == name.lower():\n            return i\n    return None\n\n\ndef get_by_shortcut(c: str) -> Optional[View]:\n    for i in views:\n        if i.prompt[1] == c:\n            return i\n    return None\n\n\ndef add(view: View) -> None:\n    # TODO: auto-select a different name (append an integer?)\n    for i in views:\n        if i.name == view.name:\n            raise exceptions.ContentViewException(\"Duplicate view: \" + view.name)\n\n    # TODO: the UI should auto-prompt for a replacement shortcut\n    for prompt in view_prompts:\n        if prompt[1] == view.prompt[1]:\n            raise exceptions.ContentViewException(\"Duplicate view shortcut: \" + view.prompt[1])\n\n    views.append(view)\n\n    for ct in view.content_types:\n        l = content_types_map.setdefault(ct, [])\n        l.append(view)\n\n    view_prompts.append(view.prompt)\n\n\ndef remove(view: View) -> None:\n    for ct in view.content_types:\n        l = content_types_map.setdefault(ct, [])\n        l.remove(view)\n\n        if not len(l):\n            del content_types_map[ct]\n\n    view_prompts.remove(view.prompt)\n    views.remove(view)\n\n\ndef safe_to_print(lines, encoding=\"utf8\"):\n    \"\"\"\n    Wraps a content generator so that each text portion is a *safe to print* unicode string.\n    \"\"\"\n    for line in lines:\n        clean_line = []\n        for (style, text) in line:\n            if isinstance(text, bytes):\n                text = text.decode(encoding, \"replace\")\n            text = strutils.escape_control_characters(text)\n            clean_line.append((style, text))\n        yield clean_line\n\n\ndef get_message_content_view(viewname, message):\n    \"\"\"\n    Like get_content_view, but also handles message encoding.\n    \"\"\"\n    viewmode = get(viewname)\n    if not viewmode:\n        viewmode = get(\"auto\")\n    try:\n        content = message.content\n    except ValueError:\n        content = message.raw_content\n        enc = \"[cannot decode]\"\n    else:\n        if isinstance(message, http.Message) and content != message.raw_content:\n            enc = \"[decoded {}]\".format(\n                message.headers.get(\"content-encoding\")\n            )\n        else:\n            enc = None\n\n    if content is None:\n        return \"\", iter([[(\"error\", \"content missing\")]]), None\n\n    metadata = {}\n    if isinstance(message, http.Request):\n        metadata[\"query\"] = message.query\n    if isinstance(message, http.Message):\n        metadata[\"headers\"] = message.headers\n\n    description, lines, error = get_content_view(\n        viewmode, content, **metadata\n    )\n\n    if enc:\n        description = \"{} {}\".format(enc, description)\n\n    return description, lines, error\n\n\ndef get_content_view(viewmode: View, data: bytes, **metadata):\n    \"\"\"\n        Args:\n            viewmode: the view to use.\n            data, **metadata: arguments passed to View instance.\n\n        Returns:\n            A (description, content generator, error) tuple.\n            If the content view raised an exception generating the view,\n            the exception is returned in error and the flow is formatted in raw mode.\n            In contrast to calling the views directly, text is always safe-to-print unicode.\n    \"\"\"\n    try:\n        ret = viewmode(data, **metadata)\n        if ret is None:\n            ret = \"Couldn't parse: falling back to Raw\", get(\"Raw\")(data, **metadata)[1]\n        desc, content = ret\n        error = None\n    # Third-party viewers can fail in unexpected ways...\n    except Exception:\n        desc = \"Couldn't parse: falling back to Raw\"\n        _, content = get(\"Raw\")(data, **metadata)\n        error = \"{} Content viewer failed: \\n{}\".format(\n            getattr(viewmode, \"name\"),\n            traceback.format_exc()\n        )\n\n    return desc, safe_to_print(content), error\n\n\n# The order in which ContentViews are added is important!\nadd(auto.ViewAuto())\nadd(raw.ViewRaw())\nadd(hex.ViewHex())\nadd(json.ViewJSON())\nadd(xml_html.ViewXmlHtml())\nadd(wbxml.ViewWBXML())\nadd(html_outline.ViewHTMLOutline())\nadd(javascript.ViewJavaScript())\nadd(css.ViewCSS())\nadd(urlencoded.ViewURLEncoded())\nadd(multipart.ViewMultipart())\nadd(image.ViewImage())\nadd(query.ViewQuery())\nadd(protobuf.ViewProtobuf())\n\n__all__ = [\n    \"View\", \"VIEW_CUTOFF\", \"KEY_MAX\", \"format_text\", \"format_dict\", \"TViewResult\",\n    \"get\", \"get_by_shortcut\", \"add\", \"remove\",\n    \"get_content_view\", \"get_message_content_view\",\n]\n", "path": "mitmproxy/contentviews/__init__.py"}], "after_files": [{"content": null, "path": "mitmproxy/contentviews/html_outline.py"}, {"content": "import os\nimport runpy\nfrom codecs import open\n\nfrom setuptools import setup, find_packages\n\n# Based on https://github.com/pypa/sampleproject/blob/master/setup.py\n# and https://python-packaging-user-guide.readthedocs.org/\n\nhere = os.path.abspath(os.path.dirname(__file__))\n\nwith open(os.path.join(here, 'README.rst'), encoding='utf-8') as f:\n    long_description = f.read()\n\nVERSION = runpy.run_path(os.path.join(here, \"mitmproxy\", \"version.py\"))[\"VERSION\"]\n\nsetup(\n    name=\"mitmproxy\",\n    version=VERSION,\n    description=\"An interactive, SSL-capable, man-in-the-middle HTTP proxy for penetration testers and software developers.\",\n    long_description=long_description,\n    url=\"http://mitmproxy.org\",\n    author=\"Aldo Cortesi\",\n    author_email=\"aldo@corte.si\",\n    license=\"MIT\",\n    classifiers=[\n        \"License :: OSI Approved :: MIT License\",\n        \"Development Status :: 5 - Production/Stable\",\n        \"Environment :: Console\",\n        \"Environment :: Console :: Curses\",\n        \"Operating System :: MacOS :: MacOS X\",\n        \"Operating System :: POSIX\",\n        \"Operating System :: Microsoft :: Windows\",\n        \"Programming Language :: Python\",\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3 :: Only\",\n        \"Programming Language :: Python :: 3.5\",\n        \"Programming Language :: Python :: 3.6\",\n        \"Programming Language :: Python :: Implementation :: CPython\",\n        \"Topic :: Security\",\n        \"Topic :: Internet\",\n        \"Topic :: Internet :: WWW/HTTP\",\n        \"Topic :: Internet :: Proxy Servers\",\n        \"Topic :: Software Development :: Testing\"\n    ],\n    packages=find_packages(include=[\n        \"mitmproxy\", \"mitmproxy.*\",\n        \"pathod\", \"pathod.*\",\n    ]),\n    include_package_data=True,\n    entry_points={\n        'console_scripts': [\n            \"mitmproxy = mitmproxy.tools.main:mitmproxy\",\n            \"mitmdump = mitmproxy.tools.main:mitmdump\",\n            \"mitmweb = mitmproxy.tools.main:mitmweb\",\n            \"pathod = pathod.pathod_cmdline:go_pathod\",\n            \"pathoc = pathod.pathoc_cmdline:go_pathoc\"\n        ]\n    },\n    # https://packaging.python.org/en/latest/requirements/#install-requires\n    # It is not considered best practice to use install_requires to pin dependencies to specific versions.\n    install_requires=[\n        \"blinker>=1.4, <1.5\",\n        \"brotlipy>=0.5.1, <0.8\",\n        \"certifi>=2015.11.20.1\",  # no semver here - this should always be on the last release!\n        \"click>=6.2, <7\",\n        \"cryptography>=2.0,<2.1\",\n        \"h2>=3.0, <4\",\n        \"hyperframe>=5.0, <6\",\n        \"kaitaistruct>=0.7, <0.8\",\n        \"ldap3>=2.2.0, <2.4\",\n        \"passlib>=1.6.5, <1.8\",\n        \"pyasn1>=0.3.1, <0.4\",\n        \"pyOpenSSL>=17.3,<17.4\",\n        \"pyparsing>=2.1.3, <2.3\",\n        \"pyperclip>=1.5.22, <1.6\",\n        \"requests>=2.9.1, <3\",\n        \"ruamel.yaml>=0.13.2, <0.16\",\n        \"sortedcontainers>=1.5.4, <1.6\",\n        \"tornado>=4.3, <4.6\",\n        \"urwid>=1.3.1, <1.4\",\n    ],\n    extras_require={\n        ':sys_platform == \"win32\"': [\n            \"pydivert>=2.0.3, <2.1\",\n        ],\n        'dev': [\n            \"flake8>=3.2.1, <3.5\",\n            \"Flask>=0.10.1, <0.13\",\n            \"mypy>=0.521,<0.522\",\n            \"pytest-cov>=2.2.1, <3\",\n            \"pytest-faulthandler>=1.3.0, <2\",\n            \"pytest-timeout>=1.0.0, <2\",\n            \"pytest-xdist>=1.14, <2\",\n            \"pytest>=3.1, <4\",\n            \"rstcheck>=2.2, <4.0\",\n            \"sphinx_rtd_theme>=0.1.9, <0.3\",\n            \"sphinx-autobuild>=0.5.2, <0.8\",\n            \"sphinx>=1.3.5, <1.7\",\n            \"sphinxcontrib-documentedlist>=0.5.0, <0.7\",\n            \"tox>=2.3, <3\",\n        ],\n        'examples': [\n            \"beautifulsoup4>=4.4.1, <4.7\",\n            \"Pillow>=3.2,<4.3\",\n        ]\n    }\n)\n", "path": "setup.py"}, {"content": "\"\"\"\nMitmproxy Content Views\n=======================\n\nmitmproxy includes a set of content views which can be used to\nformat/decode/highlight data. While they are currently used for HTTP message\nbodies only, the may be used in other contexts in the future, e.g. to decode\nprotobuf messages sent as WebSocket frames.\n\nThus, the View API is very minimalistic. The only arguments are `data` and\n`**metadata`, where `data` is the actual content (as bytes). The contents on\nmetadata depend on the protocol in use. For HTTP, the message headers are\npassed as the ``headers`` keyword argument. For HTTP requests, the query\nparameters are passed as the ``query`` keyword argument.\n\"\"\"\nimport traceback\nfrom typing import Dict, Optional  # noqa\nfrom typing import List  # noqa\nfrom typing import Tuple  # noqa\n\nfrom mitmproxy import exceptions\nfrom mitmproxy.net import http\nfrom mitmproxy.utils import strutils\nfrom . import (\n    auto, raw, hex, json, xml_html, wbxml, javascript, css,\n    urlencoded, multipart, image, query, protobuf\n)\nfrom .base import View, VIEW_CUTOFF, KEY_MAX, format_text, format_dict, TViewResult\n\nviews = []  # type: List[View]\ncontent_types_map = {}  # type: Dict[str, List[View]]\nview_prompts = []  # type: List[Tuple[str, str]]\n\n\ndef get(name: str) -> Optional[View]:\n    for i in views:\n        if i.name.lower() == name.lower():\n            return i\n    return None\n\n\ndef get_by_shortcut(c: str) -> Optional[View]:\n    for i in views:\n        if i.prompt[1] == c:\n            return i\n    return None\n\n\ndef add(view: View) -> None:\n    # TODO: auto-select a different name (append an integer?)\n    for i in views:\n        if i.name == view.name:\n            raise exceptions.ContentViewException(\"Duplicate view: \" + view.name)\n\n    # TODO: the UI should auto-prompt for a replacement shortcut\n    for prompt in view_prompts:\n        if prompt[1] == view.prompt[1]:\n            raise exceptions.ContentViewException(\"Duplicate view shortcut: \" + view.prompt[1])\n\n    views.append(view)\n\n    for ct in view.content_types:\n        l = content_types_map.setdefault(ct, [])\n        l.append(view)\n\n    view_prompts.append(view.prompt)\n\n\ndef remove(view: View) -> None:\n    for ct in view.content_types:\n        l = content_types_map.setdefault(ct, [])\n        l.remove(view)\n\n        if not len(l):\n            del content_types_map[ct]\n\n    view_prompts.remove(view.prompt)\n    views.remove(view)\n\n\ndef safe_to_print(lines, encoding=\"utf8\"):\n    \"\"\"\n    Wraps a content generator so that each text portion is a *safe to print* unicode string.\n    \"\"\"\n    for line in lines:\n        clean_line = []\n        for (style, text) in line:\n            if isinstance(text, bytes):\n                text = text.decode(encoding, \"replace\")\n            text = strutils.escape_control_characters(text)\n            clean_line.append((style, text))\n        yield clean_line\n\n\ndef get_message_content_view(viewname, message):\n    \"\"\"\n    Like get_content_view, but also handles message encoding.\n    \"\"\"\n    viewmode = get(viewname)\n    if not viewmode:\n        viewmode = get(\"auto\")\n    try:\n        content = message.content\n    except ValueError:\n        content = message.raw_content\n        enc = \"[cannot decode]\"\n    else:\n        if isinstance(message, http.Message) and content != message.raw_content:\n            enc = \"[decoded {}]\".format(\n                message.headers.get(\"content-encoding\")\n            )\n        else:\n            enc = None\n\n    if content is None:\n        return \"\", iter([[(\"error\", \"content missing\")]]), None\n\n    metadata = {}\n    if isinstance(message, http.Request):\n        metadata[\"query\"] = message.query\n    if isinstance(message, http.Message):\n        metadata[\"headers\"] = message.headers\n\n    description, lines, error = get_content_view(\n        viewmode, content, **metadata\n    )\n\n    if enc:\n        description = \"{} {}\".format(enc, description)\n\n    return description, lines, error\n\n\ndef get_content_view(viewmode: View, data: bytes, **metadata):\n    \"\"\"\n        Args:\n            viewmode: the view to use.\n            data, **metadata: arguments passed to View instance.\n\n        Returns:\n            A (description, content generator, error) tuple.\n            If the content view raised an exception generating the view,\n            the exception is returned in error and the flow is formatted in raw mode.\n            In contrast to calling the views directly, text is always safe-to-print unicode.\n    \"\"\"\n    try:\n        ret = viewmode(data, **metadata)\n        if ret is None:\n            ret = \"Couldn't parse: falling back to Raw\", get(\"Raw\")(data, **metadata)[1]\n        desc, content = ret\n        error = None\n    # Third-party viewers can fail in unexpected ways...\n    except Exception:\n        desc = \"Couldn't parse: falling back to Raw\"\n        _, content = get(\"Raw\")(data, **metadata)\n        error = \"{} Content viewer failed: \\n{}\".format(\n            getattr(viewmode, \"name\"),\n            traceback.format_exc()\n        )\n\n    return desc, safe_to_print(content), error\n\n\n# The order in which ContentViews are added is important!\nadd(auto.ViewAuto())\nadd(raw.ViewRaw())\nadd(hex.ViewHex())\nadd(json.ViewJSON())\nadd(xml_html.ViewXmlHtml())\nadd(wbxml.ViewWBXML())\nadd(javascript.ViewJavaScript())\nadd(css.ViewCSS())\nadd(urlencoded.ViewURLEncoded())\nadd(multipart.ViewMultipart())\nadd(image.ViewImage())\nadd(query.ViewQuery())\nadd(protobuf.ViewProtobuf())\n\n__all__ = [\n    \"View\", \"VIEW_CUTOFF\", \"KEY_MAX\", \"format_text\", \"format_dict\", \"TViewResult\",\n    \"get\", \"get_by_shortcut\", \"add\", \"remove\",\n    \"get_content_view\", \"get_message_content_view\",\n]\n", "path": "mitmproxy/contentviews/__init__.py"}]}