We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Sticky cookies are improperly formatted.
##### Steps to reproduce the problem:

1. Go to http://www.html-kit.com/tools/cookietester/
2. Click 'Set Test Cookie'
3. Observe that one cookie is sent to the server.
4. Remove the cookie.
5. launch mitmproxy with `mitmproxy -t html-kit\.com` and tell your browser to use it as a proxy
6. Reload the page.
7. Click 'Set Test Cookie'
8. Observe that two 'cookies' are sent to the server.

##### Any other comments? What have you tried so far?
There appears to be a comma in the output of mitmproxy, even though it is surrounded by quotes. It's possible, then that this is a parsing fail on the tool's end caused by a difference in what's sent back for the format of the date. Still, should it really be changing that?

##### System information
Arch Linux, freshly updated.

Mitmproxy version: 2.0.1 (release version) 
Python version: 3.6.0
Platform: Linux-4.10.6-1-ARCH-x86_64-with-glibc2.3.4
SSL version: OpenSSL 1.0.2k  26 Jan 2017


--- 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/addons/stickycookie.py`
Content:
```
1 import collections
2 from http import cookiejar
3 
4 from mitmproxy.net.http import cookies
5 
6 from mitmproxy import exceptions
7 from mitmproxy import flowfilter
8 from mitmproxy import ctx
9 
10 
11 def ckey(attrs, f):
12     """
13         Returns a (domain, port, path) tuple.
14     """
15     domain = f.request.host
16     path = "/"
17     if "domain" in attrs:
18         domain = attrs["domain"]
19     if "path" in attrs:
20         path = attrs["path"]
21     return (domain, f.request.port, path)
22 
23 
24 def domain_match(a, b):
25     if cookiejar.domain_match(a, b):
26         return True
27     elif cookiejar.domain_match(a, b.strip(".")):
28         return True
29     return False
30 
31 
32 class StickyCookie:
33     def __init__(self):
34         self.jar = collections.defaultdict(dict)
35         self.flt = None
36 
37     def configure(self, updated):
38         if "stickycookie" in updated:
39             if ctx.options.stickycookie:
40                 flt = flowfilter.parse(ctx.options.stickycookie)
41                 if not flt:
42                     raise exceptions.OptionsError(
43                         "stickycookie: invalid filter expression: %s" % ctx.options.stickycookie
44                     )
45                 self.flt = flt
46             else:
47                 self.flt = None
48 
49     def response(self, flow):
50         if self.flt:
51             for name, (value, attrs) in flow.response.cookies.items(multi=True):
52                 # FIXME: We now know that Cookie.py screws up some cookies with
53                 # valid RFC 822/1123 datetime specifications for expiry. Sigh.
54                 dom_port_path = ckey(attrs, flow)
55 
56                 if domain_match(flow.request.host, dom_port_path[0]):
57                     if cookies.is_expired(attrs):
58                         # Remove the cookie from jar
59                         self.jar[dom_port_path].pop(name, None)
60 
61                         # If all cookies of a dom_port_path have been removed
62                         # then remove it from the jar itself
63                         if not self.jar[dom_port_path]:
64                             self.jar.pop(dom_port_path, None)
65                     else:
66                         b = attrs.copy()
67                         b.insert(0, name, value)
68                         self.jar[dom_port_path][name] = b
69 
70     def request(self, flow):
71         if self.flt:
72             l = []
73             if flowfilter.match(self.flt, flow):
74                 for domain, port, path in self.jar.keys():
75                     match = [
76                         domain_match(flow.request.host, domain),
77                         flow.request.port == port,
78                         flow.request.path.startswith(path)
79                     ]
80                     if all(match):
81                         c = self.jar[(domain, port, path)]
82                         l.extend([cookies.format_cookie_header(c[name].items(multi=True)) for name in c.keys()])
83             if l:
84                 # FIXME: we need to formalise this...
85                 flow.request.stickycookie = True
86                 flow.request.headers["cookie"] = "; ".join(l)
87
```
--- 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/addons/stickycookie.py b/mitmproxy/addons/stickycookie.py
--- a/mitmproxy/addons/stickycookie.py
+++ b/mitmproxy/addons/stickycookie.py
@@ -1,14 +1,14 @@
 import collections
 from http import cookiejar
+from typing import List, Tuple, Dict, Optional  # noqa
 
+from mitmproxy import http, flowfilter, ctx, exceptions
 from mitmproxy.net.http import cookies
 
-from mitmproxy import exceptions
-from mitmproxy import flowfilter
-from mitmproxy import ctx
+TOrigin = Tuple[str, int, str]
 
 
-def ckey(attrs, f):
+def ckey(attrs: Dict[str, str], f: http.HTTPFlow) -> TOrigin:
     """
         Returns a (domain, port, path) tuple.
     """
@@ -21,18 +21,18 @@
     return (domain, f.request.port, path)
 
 
-def domain_match(a, b):
-    if cookiejar.domain_match(a, b):
+def domain_match(a: str, b: str) -> bool:
+    if cookiejar.domain_match(a, b):  # type: ignore
         return True
-    elif cookiejar.domain_match(a, b.strip(".")):
+    elif cookiejar.domain_match(a, b.strip(".")):  # type: ignore
         return True
     return False
 
 
 class StickyCookie:
     def __init__(self):
-        self.jar = collections.defaultdict(dict)
-        self.flt = None
+        self.jar = collections.defaultdict(dict)  # type: Dict[TOrigin, Dict[str, str]]
+        self.flt = None  # type: Optional[flowfilter.TFilter]
 
     def configure(self, updated):
         if "stickycookie" in updated:
@@ -46,7 +46,7 @@
             else:
                 self.flt = None
 
-    def response(self, flow):
+    def response(self, flow: http.HTTPFlow):
         if self.flt:
             for name, (value, attrs) in flow.response.cookies.items(multi=True):
                 # FIXME: We now know that Cookie.py screws up some cookies with
@@ -63,24 +63,21 @@
                         if not self.jar[dom_port_path]:
                             self.jar.pop(dom_port_path, None)
                     else:
-                        b = attrs.copy()
-                        b.insert(0, name, value)
-                        self.jar[dom_port_path][name] = b
+                        self.jar[dom_port_path][name] = value
 
-    def request(self, flow):
+    def request(self, flow: http.HTTPFlow):
         if self.flt:
-            l = []
+            cookie_list = []  # type: List[Tuple[str,str]]
             if flowfilter.match(self.flt, flow):
-                for domain, port, path in self.jar.keys():
+                for (domain, port, path), c in self.jar.items():
                     match = [
                         domain_match(flow.request.host, domain),
                         flow.request.port == port,
                         flow.request.path.startswith(path)
                     ]
                     if all(match):
-                        c = self.jar[(domain, port, path)]
-                        l.extend([cookies.format_cookie_header(c[name].items(multi=True)) for name in c.keys()])
-            if l:
+                        cookie_list.extend(c.items())
+            if cookie_list:
                 # FIXME: we need to formalise this...
-                flow.request.stickycookie = True
-                flow.request.headers["cookie"] = "; ".join(l)
+                flow.metadata["stickycookie"] = True
+                flow.request.headers["cookie"] = cookies.format_cookie_header(cookie_list)

{"golden_diff": "diff --git a/mitmproxy/addons/stickycookie.py b/mitmproxy/addons/stickycookie.py\n--- a/mitmproxy/addons/stickycookie.py\n+++ b/mitmproxy/addons/stickycookie.py\n@@ -1,14 +1,14 @@\n import collections\n from http import cookiejar\n+from typing import List, Tuple, Dict, Optional  # noqa\n \n+from mitmproxy import http, flowfilter, ctx, exceptions\n from mitmproxy.net.http import cookies\n \n-from mitmproxy import exceptions\n-from mitmproxy import flowfilter\n-from mitmproxy import ctx\n+TOrigin = Tuple[str, int, str]\n \n \n-def ckey(attrs, f):\n+def ckey(attrs: Dict[str, str], f: http.HTTPFlow) -> TOrigin:\n     \"\"\"\n         Returns a (domain, port, path) tuple.\n     \"\"\"\n@@ -21,18 +21,18 @@\n     return (domain, f.request.port, path)\n \n \n-def domain_match(a, b):\n-    if cookiejar.domain_match(a, b):\n+def domain_match(a: str, b: str) -> bool:\n+    if cookiejar.domain_match(a, b):  # type: ignore\n         return True\n-    elif cookiejar.domain_match(a, b.strip(\".\")):\n+    elif cookiejar.domain_match(a, b.strip(\".\")):  # type: ignore\n         return True\n     return False\n \n \n class StickyCookie:\n     def __init__(self):\n-        self.jar = collections.defaultdict(dict)\n-        self.flt = None\n+        self.jar = collections.defaultdict(dict)  # type: Dict[TOrigin, Dict[str, str]]\n+        self.flt = None  # type: Optional[flowfilter.TFilter]\n \n     def configure(self, updated):\n         if \"stickycookie\" in updated:\n@@ -46,7 +46,7 @@\n             else:\n                 self.flt = None\n \n-    def response(self, flow):\n+    def response(self, flow: http.HTTPFlow):\n         if self.flt:\n             for name, (value, attrs) in flow.response.cookies.items(multi=True):\n                 # FIXME: We now know that Cookie.py screws up some cookies with\n@@ -63,24 +63,21 @@\n                         if not self.jar[dom_port_path]:\n                             self.jar.pop(dom_port_path, None)\n                     else:\n-                        b = attrs.copy()\n-                        b.insert(0, name, value)\n-                        self.jar[dom_port_path][name] = b\n+                        self.jar[dom_port_path][name] = value\n \n-    def request(self, flow):\n+    def request(self, flow: http.HTTPFlow):\n         if self.flt:\n-            l = []\n+            cookie_list = []  # type: List[Tuple[str,str]]\n             if flowfilter.match(self.flt, flow):\n-                for domain, port, path in self.jar.keys():\n+                for (domain, port, path), c in self.jar.items():\n                     match = [\n                         domain_match(flow.request.host, domain),\n                         flow.request.port == port,\n                         flow.request.path.startswith(path)\n                     ]\n                     if all(match):\n-                        c = self.jar[(domain, port, path)]\n-                        l.extend([cookies.format_cookie_header(c[name].items(multi=True)) for name in c.keys()])\n-            if l:\n+                        cookie_list.extend(c.items())\n+            if cookie_list:\n                 # FIXME: we need to formalise this...\n-                flow.request.stickycookie = True\n-                flow.request.headers[\"cookie\"] = \"; \".join(l)\n+                flow.metadata[\"stickycookie\"] = True\n+                flow.request.headers[\"cookie\"] = cookies.format_cookie_header(cookie_list)\n", "issue": "Sticky cookies are improperly formatted.\n##### Steps to reproduce the problem:\r\n\r\n1. Go to http://www.html-kit.com/tools/cookietester/\r\n2. Click 'Set Test Cookie'\r\n3. Observe that one cookie is sent to the server.\r\n4. Remove the cookie.\r\n5. launch mitmproxy with `mitmproxy -t html-kit\\.com` and tell your browser to use it as a proxy\r\n6. Reload the page.\r\n7. Click 'Set Test Cookie'\r\n8. Observe that two 'cookies' are sent to the server.\r\n\r\n##### Any other comments? What have you tried so far?\r\nThere appears to be a comma in the output of mitmproxy, even though it is surrounded by quotes. It's possible, then that this is a parsing fail on the tool's end caused by a difference in what's sent back for the format of the date. Still, should it really be changing that?\r\n\r\n##### System information\r\nArch Linux, freshly updated.\r\n\r\nMitmproxy version: 2.0.1 (release version) \r\nPython version: 3.6.0\r\nPlatform: Linux-4.10.6-1-ARCH-x86_64-with-glibc2.3.4\r\nSSL version: OpenSSL 1.0.2k  26 Jan 2017\r\n\n", "before_files": [{"content": "import collections\nfrom http import cookiejar\n\nfrom mitmproxy.net.http import cookies\n\nfrom mitmproxy import exceptions\nfrom mitmproxy import flowfilter\nfrom mitmproxy import ctx\n\n\ndef ckey(attrs, f):\n    \"\"\"\n        Returns a (domain, port, path) tuple.\n    \"\"\"\n    domain = f.request.host\n    path = \"/\"\n    if \"domain\" in attrs:\n        domain = attrs[\"domain\"]\n    if \"path\" in attrs:\n        path = attrs[\"path\"]\n    return (domain, f.request.port, path)\n\n\ndef domain_match(a, b):\n    if cookiejar.domain_match(a, b):\n        return True\n    elif cookiejar.domain_match(a, b.strip(\".\")):\n        return True\n    return False\n\n\nclass StickyCookie:\n    def __init__(self):\n        self.jar = collections.defaultdict(dict)\n        self.flt = None\n\n    def configure(self, updated):\n        if \"stickycookie\" in updated:\n            if ctx.options.stickycookie:\n                flt = flowfilter.parse(ctx.options.stickycookie)\n                if not flt:\n                    raise exceptions.OptionsError(\n                        \"stickycookie: invalid filter expression: %s\" % ctx.options.stickycookie\n                    )\n                self.flt = flt\n            else:\n                self.flt = None\n\n    def response(self, flow):\n        if self.flt:\n            for name, (value, attrs) in flow.response.cookies.items(multi=True):\n                # FIXME: We now know that Cookie.py screws up some cookies with\n                # valid RFC 822/1123 datetime specifications for expiry. Sigh.\n                dom_port_path = ckey(attrs, flow)\n\n                if domain_match(flow.request.host, dom_port_path[0]):\n                    if cookies.is_expired(attrs):\n                        # Remove the cookie from jar\n                        self.jar[dom_port_path].pop(name, None)\n\n                        # If all cookies of a dom_port_path have been removed\n                        # then remove it from the jar itself\n                        if not self.jar[dom_port_path]:\n                            self.jar.pop(dom_port_path, None)\n                    else:\n                        b = attrs.copy()\n                        b.insert(0, name, value)\n                        self.jar[dom_port_path][name] = b\n\n    def request(self, flow):\n        if self.flt:\n            l = []\n            if flowfilter.match(self.flt, flow):\n                for domain, port, path in self.jar.keys():\n                    match = [\n                        domain_match(flow.request.host, domain),\n                        flow.request.port == port,\n                        flow.request.path.startswith(path)\n                    ]\n                    if all(match):\n                        c = self.jar[(domain, port, path)]\n                        l.extend([cookies.format_cookie_header(c[name].items(multi=True)) for name in c.keys()])\n            if l:\n                # FIXME: we need to formalise this...\n                flow.request.stickycookie = True\n                flow.request.headers[\"cookie\"] = \"; \".join(l)\n", "path": "mitmproxy/addons/stickycookie.py"}], "after_files": [{"content": "import collections\nfrom http import cookiejar\nfrom typing import List, Tuple, Dict, Optional  # noqa\n\nfrom mitmproxy import http, flowfilter, ctx, exceptions\nfrom mitmproxy.net.http import cookies\n\nTOrigin = Tuple[str, int, str]\n\n\ndef ckey(attrs: Dict[str, str], f: http.HTTPFlow) -> TOrigin:\n    \"\"\"\n        Returns a (domain, port, path) tuple.\n    \"\"\"\n    domain = f.request.host\n    path = \"/\"\n    if \"domain\" in attrs:\n        domain = attrs[\"domain\"]\n    if \"path\" in attrs:\n        path = attrs[\"path\"]\n    return (domain, f.request.port, path)\n\n\ndef domain_match(a: str, b: str) -> bool:\n    if cookiejar.domain_match(a, b):  # type: ignore\n        return True\n    elif cookiejar.domain_match(a, b.strip(\".\")):  # type: ignore\n        return True\n    return False\n\n\nclass StickyCookie:\n    def __init__(self):\n        self.jar = collections.defaultdict(dict)  # type: Dict[TOrigin, Dict[str, str]]\n        self.flt = None  # type: Optional[flowfilter.TFilter]\n\n    def configure(self, updated):\n        if \"stickycookie\" in updated:\n            if ctx.options.stickycookie:\n                flt = flowfilter.parse(ctx.options.stickycookie)\n                if not flt:\n                    raise exceptions.OptionsError(\n                        \"stickycookie: invalid filter expression: %s\" % ctx.options.stickycookie\n                    )\n                self.flt = flt\n            else:\n                self.flt = None\n\n    def response(self, flow: http.HTTPFlow):\n        if self.flt:\n            for name, (value, attrs) in flow.response.cookies.items(multi=True):\n                # FIXME: We now know that Cookie.py screws up some cookies with\n                # valid RFC 822/1123 datetime specifications for expiry. Sigh.\n                dom_port_path = ckey(attrs, flow)\n\n                if domain_match(flow.request.host, dom_port_path[0]):\n                    if cookies.is_expired(attrs):\n                        # Remove the cookie from jar\n                        self.jar[dom_port_path].pop(name, None)\n\n                        # If all cookies of a dom_port_path have been removed\n                        # then remove it from the jar itself\n                        if not self.jar[dom_port_path]:\n                            self.jar.pop(dom_port_path, None)\n                    else:\n                        self.jar[dom_port_path][name] = value\n\n    def request(self, flow: http.HTTPFlow):\n        if self.flt:\n            cookie_list = []  # type: List[Tuple[str,str]]\n            if flowfilter.match(self.flt, flow):\n                for (domain, port, path), c in self.jar.items():\n                    match = [\n                        domain_match(flow.request.host, domain),\n                        flow.request.port == port,\n                        flow.request.path.startswith(path)\n                    ]\n                    if all(match):\n                        cookie_list.extend(c.items())\n            if cookie_list:\n                # FIXME: we need to formalise this...\n                flow.metadata[\"stickycookie\"] = True\n                flow.request.headers[\"cookie\"] = cookies.format_cookie_header(cookie_list)\n", "path": "mitmproxy/addons/stickycookie.py"}]}