We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
url with Chinese query string return 400
use IE11,url with Chinese query string,return 400.

1. Mitmproxy version: 3.0.0 (release version)
    Python version: 3.5.3
    Platform: Windows-10-10.0.14393-SP0
    SSL version: OpenSSL 1.1.0e 16 Feb 2017 
    Windows version: 10 10.0.14393 SP0 Multiprocessor Free
2. chrome+mitmdump is fine.
3. but use IE11+mitmdump is error.  
4. use IE11 + burpsuite is fine.
5. mitmdump --listen-host 127.0.0.1 --listen-port 8080

Mitmproxy was no hint error, but query string **lc_name** was submitted to the charset difference.
return HTTP 400.
html charset is gb2312.

IE11 developer tools see url   http://wlpx.tax-edu.net/jsp/portal/PortalControl?flag=selectCourse&lc_id=42825&lc_name=é��ç�¨ç�¥è¯�å��è�½å��

chrome developer tools see  url http://wlpx.tax-edu.net/jsp/portal/PortalControl?flag=selectCourse&lc_id=42825&lc_name=%CD%A8%D3%C3%D6%AA%CA%B6%BA%CD%C4%DC%C1%A6

![image](https://cloud.githubusercontent.com/assets/1232444/25463418/abe2ef0e-2b28-11e7-827c-ea73b525213c.png)




--- 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/net/http/url.py`
Content:
```
1 import urllib.parse
2 from typing import Sequence
3 from typing import Tuple
4 
5 from mitmproxy.net import check
6 
7 
8 def parse(url):
9     """
10         URL-parsing function that checks that
11             - port is an integer 0-65535
12             - host is a valid IDNA-encoded hostname with no null-bytes
13             - path is valid ASCII
14 
15         Args:
16             A URL (as bytes or as unicode)
17 
18         Returns:
19             A (scheme, host, port, path) tuple
20 
21         Raises:
22             ValueError, if the URL is not properly formatted.
23     """
24     parsed = urllib.parse.urlparse(url)
25 
26     if not parsed.hostname:
27         raise ValueError("No hostname given")
28 
29     if isinstance(url, bytes):
30         host = parsed.hostname
31 
32         # this should not raise a ValueError,
33         # but we try to be very forgiving here and accept just everything.
34     else:
35         host = parsed.hostname.encode("idna")
36         if isinstance(parsed, urllib.parse.ParseResult):
37             parsed = parsed.encode("ascii")
38 
39     port = parsed.port  # Returns None if port number invalid in Py3.5. Will throw ValueError in Py3.6
40     if not port:
41         port = 443 if parsed.scheme == b"https" else 80
42 
43     full_path = urllib.parse.urlunparse(
44         (b"", b"", parsed.path, parsed.params, parsed.query, parsed.fragment)
45     )
46     if not full_path.startswith(b"/"):
47         full_path = b"/" + full_path
48 
49     if not check.is_valid_host(host):
50         raise ValueError("Invalid Host")
51 
52     return parsed.scheme, host, port, full_path
53 
54 
55 def unparse(scheme, host, port, path=""):
56     """
57     Returns a URL string, constructed from the specified components.
58 
59     Args:
60         All args must be str.
61     """
62     if path == "*":
63         path = ""
64     return "%s://%s%s" % (scheme, hostport(scheme, host, port), path)
65 
66 
67 def encode(s: Sequence[Tuple[str, str]], similar_to: str=None) -> str:
68     """
69         Takes a list of (key, value) tuples and returns a urlencoded string.
70         If similar_to is passed, the output is formatted similar to the provided urlencoded string.
71     """
72 
73     remove_trailing_equal = False
74     if similar_to:
75         remove_trailing_equal = any("=" not in param for param in similar_to.split("&"))
76 
77     encoded = urllib.parse.urlencode(s, False, errors="surrogateescape")
78 
79     if encoded and remove_trailing_equal:
80         encoded = encoded.replace("=&", "&")
81         if encoded[-1] == '=':
82             encoded = encoded[:-1]
83 
84     return encoded
85 
86 
87 def decode(s):
88     """
89         Takes a urlencoded string and returns a list of surrogate-escaped (key, value) tuples.
90     """
91     return urllib.parse.parse_qsl(s, keep_blank_values=True, errors='surrogateescape')
92 
93 
94 def quote(b: str, safe: str="/") -> str:
95     """
96     Returns:
97         An ascii-encodable str.
98     """
99     return urllib.parse.quote(b, safe=safe, errors="surrogateescape")
100 
101 
102 def unquote(s: str) -> str:
103     """
104     Args:
105         s: A surrogate-escaped str
106     Returns:
107         A surrogate-escaped str
108     """
109     return urllib.parse.unquote(s, errors="surrogateescape")
110 
111 
112 def hostport(scheme, host, port):
113     """
114         Returns the host component, with a port specifcation if needed.
115     """
116     if (port, scheme) in [(80, "http"), (443, "https"), (80, b"http"), (443, b"https")]:
117         return host
118     else:
119         if isinstance(host, bytes):
120             return b"%s:%d" % (host, port)
121         else:
122             return "%s:%d" % (host, port)
123
```
--- 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/net/http/url.py b/mitmproxy/net/http/url.py
--- a/mitmproxy/net/http/url.py
+++ b/mitmproxy/net/http/url.py
@@ -21,16 +21,25 @@
         Raises:
             ValueError, if the URL is not properly formatted.
     """
-    parsed = urllib.parse.urlparse(url)
+    # Size of Ascii character after encoding is 1 byte which is same as its size
+    # But non-Ascii character's size after encoding will be more than its size
+    def ascii_check(l):
+        if len(l) == len(str(l).encode()):
+            return True
+        return False
+
+    if isinstance(url, bytes):
+        url = url.decode()
+        if not ascii_check(url):
+            url = urllib.parse.urlsplit(url)
+            url = list(url)
+            url[3] = urllib.parse.quote(url[3])
+            url = urllib.parse.urlunsplit(url)
 
+    parsed = urllib.parse.urlparse(url)
     if not parsed.hostname:
         raise ValueError("No hostname given")
 
-    if isinstance(url, bytes):
-        host = parsed.hostname
-
-        # this should not raise a ValueError,
-        # but we try to be very forgiving here and accept just everything.
     else:
         host = parsed.hostname.encode("idna")
         if isinstance(parsed, urllib.parse.ParseResult):

{"golden_diff": "diff --git a/mitmproxy/net/http/url.py b/mitmproxy/net/http/url.py\n--- a/mitmproxy/net/http/url.py\n+++ b/mitmproxy/net/http/url.py\n@@ -21,16 +21,25 @@\n         Raises:\n             ValueError, if the URL is not properly formatted.\n     \"\"\"\n-    parsed = urllib.parse.urlparse(url)\n+    # Size of Ascii character after encoding is 1 byte which is same as its size\n+    # But non-Ascii character's size after encoding will be more than its size\n+    def ascii_check(l):\n+        if len(l) == len(str(l).encode()):\n+            return True\n+        return False\n+\n+    if isinstance(url, bytes):\n+        url = url.decode()\n+        if not ascii_check(url):\n+            url = urllib.parse.urlsplit(url)\n+            url = list(url)\n+            url[3] = urllib.parse.quote(url[3])\n+            url = urllib.parse.urlunsplit(url)\n \n+    parsed = urllib.parse.urlparse(url)\n     if not parsed.hostname:\n         raise ValueError(\"No hostname given\")\n \n-    if isinstance(url, bytes):\n-        host = parsed.hostname\n-\n-        # this should not raise a ValueError,\n-        # but we try to be very forgiving here and accept just everything.\n     else:\n         host = parsed.hostname.encode(\"idna\")\n         if isinstance(parsed, urllib.parse.ParseResult):\n", "issue": "url with Chinese query string return 400\nuse IE11,url with Chinese query string,return 400.\r\n\r\n1. Mitmproxy version: 3.0.0 (release version)\r\n    Python version: 3.5.3\r\n    Platform: Windows-10-10.0.14393-SP0\r\n    SSL version: OpenSSL 1.1.0e 16 Feb 2017 \r\n    Windows version: 10 10.0.14393 SP0 Multiprocessor Free\r\n2. chrome+mitmdump is fine.\r\n3. but use IE11+mitmdump is error.  \r\n4. use IE11 + burpsuite is fine.\r\n5. mitmdump --listen-host 127.0.0.1 --listen-port 8080\r\n\r\nMitmproxy was no hint error, but query string **lc_name** was submitted to the charset difference.\r\nreturn HTTP 400.\r\nhtml charset is gb2312.\r\n\r\nIE11 developer tools see url   http://wlpx.tax-edu.net/jsp/portal/PortalControl?flag=selectCourse&lc_id=42825&lc_name=\u00e9\ufffd\ufffd\u00e7\ufffd\u00a8\u00e7\ufffd\u00a5\u00e8\u00af\ufffd\u00e5\ufffd\ufffd\u00e8\ufffd\u00bd\u00e5\ufffd\ufffd\r\n\r\nchrome developer tools see  url http://wlpx.tax-edu.net/jsp/portal/PortalControl?flag=selectCourse&lc_id=42825&lc_name=%CD%A8%D3%C3%D6%AA%CA%B6%BA%CD%C4%DC%C1%A6\r\n\r\n![image](https://cloud.githubusercontent.com/assets/1232444/25463418/abe2ef0e-2b28-11e7-827c-ea73b525213c.png)\r\n\r\n\r\n\n", "before_files": [{"content": "import urllib.parse\nfrom typing import Sequence\nfrom typing import Tuple\n\nfrom mitmproxy.net import check\n\n\ndef parse(url):\n    \"\"\"\n        URL-parsing function that checks that\n            - port is an integer 0-65535\n            - host is a valid IDNA-encoded hostname with no null-bytes\n            - path is valid ASCII\n\n        Args:\n            A URL (as bytes or as unicode)\n\n        Returns:\n            A (scheme, host, port, path) tuple\n\n        Raises:\n            ValueError, if the URL is not properly formatted.\n    \"\"\"\n    parsed = urllib.parse.urlparse(url)\n\n    if not parsed.hostname:\n        raise ValueError(\"No hostname given\")\n\n    if isinstance(url, bytes):\n        host = parsed.hostname\n\n        # this should not raise a ValueError,\n        # but we try to be very forgiving here and accept just everything.\n    else:\n        host = parsed.hostname.encode(\"idna\")\n        if isinstance(parsed, urllib.parse.ParseResult):\n            parsed = parsed.encode(\"ascii\")\n\n    port = parsed.port  # Returns None if port number invalid in Py3.5. Will throw ValueError in Py3.6\n    if not port:\n        port = 443 if parsed.scheme == b\"https\" else 80\n\n    full_path = urllib.parse.urlunparse(\n        (b\"\", b\"\", parsed.path, parsed.params, parsed.query, parsed.fragment)\n    )\n    if not full_path.startswith(b\"/\"):\n        full_path = b\"/\" + full_path\n\n    if not check.is_valid_host(host):\n        raise ValueError(\"Invalid Host\")\n\n    return parsed.scheme, host, port, full_path\n\n\ndef unparse(scheme, host, port, path=\"\"):\n    \"\"\"\n    Returns a URL string, constructed from the specified components.\n\n    Args:\n        All args must be str.\n    \"\"\"\n    if path == \"*\":\n        path = \"\"\n    return \"%s://%s%s\" % (scheme, hostport(scheme, host, port), path)\n\n\ndef encode(s: Sequence[Tuple[str, str]], similar_to: str=None) -> str:\n    \"\"\"\n        Takes a list of (key, value) tuples and returns a urlencoded string.\n        If similar_to is passed, the output is formatted similar to the provided urlencoded string.\n    \"\"\"\n\n    remove_trailing_equal = False\n    if similar_to:\n        remove_trailing_equal = any(\"=\" not in param for param in similar_to.split(\"&\"))\n\n    encoded = urllib.parse.urlencode(s, False, errors=\"surrogateescape\")\n\n    if encoded and remove_trailing_equal:\n        encoded = encoded.replace(\"=&\", \"&\")\n        if encoded[-1] == '=':\n            encoded = encoded[:-1]\n\n    return encoded\n\n\ndef decode(s):\n    \"\"\"\n        Takes a urlencoded string and returns a list of surrogate-escaped (key, value) tuples.\n    \"\"\"\n    return urllib.parse.parse_qsl(s, keep_blank_values=True, errors='surrogateescape')\n\n\ndef quote(b: str, safe: str=\"/\") -> str:\n    \"\"\"\n    Returns:\n        An ascii-encodable str.\n    \"\"\"\n    return urllib.parse.quote(b, safe=safe, errors=\"surrogateescape\")\n\n\ndef unquote(s: str) -> str:\n    \"\"\"\n    Args:\n        s: A surrogate-escaped str\n    Returns:\n        A surrogate-escaped str\n    \"\"\"\n    return urllib.parse.unquote(s, errors=\"surrogateescape\")\n\n\ndef hostport(scheme, host, port):\n    \"\"\"\n        Returns the host component, with a port specifcation if needed.\n    \"\"\"\n    if (port, scheme) in [(80, \"http\"), (443, \"https\"), (80, b\"http\"), (443, b\"https\")]:\n        return host\n    else:\n        if isinstance(host, bytes):\n            return b\"%s:%d\" % (host, port)\n        else:\n            return \"%s:%d\" % (host, port)\n", "path": "mitmproxy/net/http/url.py"}], "after_files": [{"content": "import urllib.parse\nfrom typing import Sequence\nfrom typing import Tuple\n\nfrom mitmproxy.net import check\n\n\ndef parse(url):\n    \"\"\"\n        URL-parsing function that checks that\n            - port is an integer 0-65535\n            - host is a valid IDNA-encoded hostname with no null-bytes\n            - path is valid ASCII\n\n        Args:\n            A URL (as bytes or as unicode)\n\n        Returns:\n            A (scheme, host, port, path) tuple\n\n        Raises:\n            ValueError, if the URL is not properly formatted.\n    \"\"\"\n    # Size of Ascii character after encoding is 1 byte which is same as its size\n    # But non-Ascii character's size after encoding will be more than its size\n    def ascii_check(l):\n        if len(l) == len(str(l).encode()):\n            return True\n        return False\n\n    if isinstance(url, bytes):\n        url = url.decode()\n        if not ascii_check(url):\n            url = urllib.parse.urlsplit(url)\n            url = list(url)\n            url[3] = urllib.parse.quote(url[3])\n            url = urllib.parse.urlunsplit(url)\n\n    parsed = urllib.parse.urlparse(url)\n    if not parsed.hostname:\n        raise ValueError(\"No hostname given\")\n\n    else:\n        host = parsed.hostname.encode(\"idna\")\n        if isinstance(parsed, urllib.parse.ParseResult):\n            parsed = parsed.encode(\"ascii\")\n\n    port = parsed.port  # Returns None if port number invalid in Py3.5. Will throw ValueError in Py3.6\n    if not port:\n        port = 443 if parsed.scheme == b\"https\" else 80\n\n    full_path = urllib.parse.urlunparse(\n        (b\"\", b\"\", parsed.path, parsed.params, parsed.query, parsed.fragment)\n    )\n    if not full_path.startswith(b\"/\"):\n        full_path = b\"/\" + full_path\n\n    if not check.is_valid_host(host):\n        raise ValueError(\"Invalid Host\")\n\n    return parsed.scheme, host, port, full_path\n\n\ndef unparse(scheme, host, port, path=\"\"):\n    \"\"\"\n    Returns a URL string, constructed from the specified components.\n\n    Args:\n        All args must be str.\n    \"\"\"\n    if path == \"*\":\n        path = \"\"\n    return \"%s://%s%s\" % (scheme, hostport(scheme, host, port), path)\n\n\ndef encode(s: Sequence[Tuple[str, str]], similar_to: str=None) -> str:\n    \"\"\"\n        Takes a list of (key, value) tuples and returns a urlencoded string.\n        If similar_to is passed, the output is formatted similar to the provided urlencoded string.\n    \"\"\"\n\n    remove_trailing_equal = False\n    if similar_to:\n        remove_trailing_equal = any(\"=\" not in param for param in similar_to.split(\"&\"))\n\n    encoded = urllib.parse.urlencode(s, False, errors=\"surrogateescape\")\n\n    if encoded and remove_trailing_equal:\n        encoded = encoded.replace(\"=&\", \"&\")\n        if encoded[-1] == '=':\n            encoded = encoded[:-1]\n\n    return encoded\n\n\ndef decode(s):\n    \"\"\"\n        Takes a urlencoded string and returns a list of surrogate-escaped (key, value) tuples.\n    \"\"\"\n    return urllib.parse.parse_qsl(s, keep_blank_values=True, errors='surrogateescape')\n\n\ndef quote(b: str, safe: str=\"/\") -> str:\n    \"\"\"\n    Returns:\n        An ascii-encodable str.\n    \"\"\"\n    return urllib.parse.quote(b, safe=safe, errors=\"surrogateescape\")\n\n\ndef unquote(s: str) -> str:\n    \"\"\"\n    Args:\n        s: A surrogate-escaped str\n    Returns:\n        A surrogate-escaped str\n    \"\"\"\n    return urllib.parse.unquote(s, errors=\"surrogateescape\")\n\n\ndef hostport(scheme, host, port):\n    \"\"\"\n        Returns the host component, with a port specifcation if needed.\n    \"\"\"\n    if (port, scheme) in [(80, \"http\"), (443, \"https\"), (80, b\"http\"), (443, b\"https\")]:\n        return host\n    else:\n        if isinstance(host, bytes):\n            return b\"%s:%d\" % (host, port)\n        else:\n            return \"%s:%d\" % (host, port)\n", "path": "mitmproxy/net/http/url.py"}]}