We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[tests actually pass] Remove harextractor example
If nobody objects, this PR (temporarily) removes the harextractor example, which depends on harparser, a Python2-only library. It's probably worth the time to rewrite this without any dependencies, but first we want to get a Python 3 release out of the door.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `examples/har_extractor.py`
Content:
```
1 """
2     This inline script utilizes harparser.HAR from
3     https://github.com/JustusW/harparser to generate a HAR log object.
4 """
5 import six
6 import sys
7 import pytz
8 from harparser import HAR
9 
10 from datetime import datetime
11 
12 
13 class _HARLog(HAR.log):
14     # The attributes need to be registered here for them to actually be
15     # available later via self. This is due to HAREncodable linking __getattr__
16     # to __getitem__. Anything that is set only in __init__ will just be added
17     # as key/value pair to self.__classes__.
18     __page_list__ = []
19     __page_count__ = 0
20     __page_ref__ = {}
21 
22     def __init__(self, page_list=[]):
23         self.__page_list__ = page_list
24         self.__page_count__ = 0
25         self.__page_ref__ = {}
26 
27         HAR.log.__init__(self, {"version": "1.2",
28                                 "creator": {"name": "MITMPROXY HARExtractor",
29                                             "version": "0.1",
30                                             "comment": ""},
31                                 "pages": [],
32                                 "entries": []})
33 
34     def reset(self):
35         self.__init__(self.__page_list__)
36 
37     def add(self, obj):
38         if isinstance(obj, HAR.pages):
39             self['pages'].append(obj)
40         if isinstance(obj, HAR.entries):
41             self['entries'].append(obj)
42 
43     def create_page_id(self):
44         self.__page_count__ += 1
45         return "autopage_%s" % str(self.__page_count__)
46 
47     def set_page_ref(self, page, ref):
48         self.__page_ref__[page] = ref
49 
50     def get_page_ref(self, page):
51         return self.__page_ref__.get(page, None)
52 
53     def get_page_list(self):
54         return self.__page_list__
55 
56 
57 def start(context):
58     """
59         On start we create a HARLog instance. You will have to adapt this to
60         suit your actual needs of HAR generation. As it will probably be
61         necessary to cluster logs by IPs or reset them from time to time.
62     """
63     context.dump_file = None
64     if len(sys.argv) > 1:
65         context.dump_file = sys.argv[1]
66     else:
67         raise ValueError(
68             'Usage: -s "har_extractor.py filename" '
69             '(- will output to stdout, filenames ending with .zhar '
70             'will result in compressed har)'
71         )
72     context.HARLog = _HARLog()
73     context.seen_server = set()
74 
75 
76 def response(context, flow):
77     """
78        Called when a server response has been received. At the time of this
79        message both a request and a response are present and completely done.
80     """
81     # Values are converted from float seconds to int milliseconds later.
82     ssl_time = -.001
83     connect_time = -.001
84     if flow.server_conn not in context.seen_server:
85         # Calculate the connect_time for this server_conn. Afterwards add it to
86         # seen list, in order to avoid the connect_time being present in entries
87         # that use an existing connection.
88         connect_time = (flow.server_conn.timestamp_tcp_setup -
89                         flow.server_conn.timestamp_start)
90         context.seen_server.add(flow.server_conn)
91 
92         if flow.server_conn.timestamp_ssl_setup is not None:
93             # Get the ssl_time for this server_conn as the difference between
94             # the start of the successful tcp setup and the successful ssl
95             # setup. If no ssl setup has been made it is left as -1 since it
96             # doesn't apply to this connection.
97             ssl_time = (flow.server_conn.timestamp_ssl_setup -
98                         flow.server_conn.timestamp_tcp_setup)
99 
100     # Calculate the raw timings from the different timestamps present in the
101     # request and response object. For lack of a way to measure it dns timings
102     # can not be calculated. The same goes for HAR blocked: MITMProxy will open
103     # a server connection as soon as it receives the host and port from the
104     # client connection. So the time spent waiting is actually spent waiting
105     # between request.timestamp_end and response.timestamp_start thus it
106     # correlates to HAR wait instead.
107     timings_raw = {
108         'send': flow.request.timestamp_end - flow.request.timestamp_start,
109         'wait': flow.response.timestamp_start - flow.request.timestamp_end,
110         'receive': flow.response.timestamp_end - flow.response.timestamp_start,
111         'connect': connect_time,
112         'ssl': ssl_time
113     }
114 
115     # HAR timings are integers in ms, so we have to re-encode the raw timings to
116     # that format.
117     timings = dict([(k, int(1000 * v)) for k, v in six.iteritems(timings_raw)])
118 
119     # The full_time is the sum of all timings.
120     # Timings set to -1 will be ignored as per spec.
121     full_time = sum(v for v in timings.values() if v > -1)
122 
123     started_date_time = datetime.utcfromtimestamp(
124         flow.request.timestamp_start).replace(tzinfo=pytz.timezone("UTC")).isoformat()
125 
126     request_query_string = [{"name": k, "value": v}
127                             for k, v in flow.request.query or {}]
128 
129     response_body_size = len(flow.response.content)
130     response_body_decoded_size = len(flow.response.get_decoded_content())
131     response_body_compression = response_body_decoded_size - response_body_size
132 
133     entry = HAR.entries({
134         "startedDateTime": started_date_time,
135         "time": full_time,
136         "request": {
137             "method": flow.request.method,
138             "url": flow.request.url,
139             "httpVersion": flow.request.http_version,
140             "cookies": format_cookies(flow.request.cookies),
141             "headers": format_headers(flow.request.headers),
142             "queryString": request_query_string,
143             "headersSize": len(str(flow.request.headers)),
144             "bodySize": len(flow.request.content),
145         },
146         "response": {
147             "status": flow.response.status_code,
148             "statusText": flow.response.reason,
149             "httpVersion": flow.response.http_version,
150             "cookies": format_cookies(flow.response.cookies),
151             "headers": format_headers(flow.response.headers),
152             "content": {
153                 "size": response_body_size,
154                 "compression": response_body_compression,
155                 "mimeType": flow.response.headers.get('Content-Type', '')
156             },
157             "redirectURL": flow.response.headers.get('Location', ''),
158             "headersSize": len(str(flow.response.headers)),
159             "bodySize": response_body_size,
160         },
161         "cache": {},
162         "timings": timings,
163     })
164 
165     # If the current url is in the page list of context.HARLog or
166     # does not have a referrer, we add it as a new pages object.
167     is_new_page = (
168         flow.request.url in context.HARLog.get_page_list() or
169         flow.request.headers.get('Referer') is None
170     )
171     if is_new_page:
172         page_id = context.HARLog.create_page_id()
173         context.HARLog.add(
174             HAR.pages({
175                 "startedDateTime": entry['startedDateTime'],
176                 "id": page_id,
177                 "title": flow.request.url,
178                 "pageTimings": {}
179             })
180         )
181         context.HARLog.set_page_ref(flow.request.url, page_id)
182         entry['pageref'] = page_id
183 
184     # Lookup the referer in the page_ref of context.HARLog to point this entries
185     # pageref attribute to the right pages object, then set it as a new
186     # reference to build a reference tree.
187     elif context.HARLog.get_page_ref(flow.request.headers.get('Referer')) is not None:
188         entry['pageref'] = context.HARLog.get_page_ref(
189             flow.request.headers['Referer']
190         )
191         context.HARLog.set_page_ref(
192             flow.request.headers['Referer'], entry['pageref']
193         )
194 
195     context.HARLog.add(entry)
196 
197 
198 def done(context):
199     """
200         Called once on script shutdown, after any other events.
201     """
202     import pprint
203     import json
204 
205     json_dump = context.HARLog.json()
206     compressed_json_dump = context.HARLog.compress()
207 
208     if context.dump_file == '-':
209         context.log(pprint.pformat(json.loads(json_dump)))
210     elif context.dump_file.endswith('.zhar'):
211         file(context.dump_file, "w").write(compressed_json_dump)
212     else:
213         file(context.dump_file, "w").write(json_dump)
214     context.log(
215         "HAR log finished with %s bytes (%s bytes compressed)" % (
216             len(json_dump), len(compressed_json_dump)
217         )
218     )
219     context.log(
220         "Compression rate is %s%%" % str(
221             100. * len(compressed_json_dump) / len(json_dump)
222         )
223     )
224 
225 
226 def format_cookies(obj):
227     if obj:
228         return [{"name": k.strip(), "value": v[0]} for k, v in obj.items()]
229     return ""
230 
231 
232 def format_headers(obj):
233     if obj:
234         return [{"name": k, "value": v} for k, v in obj.fields]
235     return ""
236 
237 
238 def print_attributes(obj, filter_string=None, hide_privates=False):
239     """
240         Useful helper method to quickly get all attributes of an object and its
241         values.
242     """
243     for attr in dir(obj):
244         if hide_privates and "__" in attr:
245             continue
246         if filter_string is not None and filter_string not in attr:
247             continue
248         value = getattr(obj, attr)
249         print("%s.%s" % ('obj', attr), value, type(value))
250
```
--- 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/examples/har_extractor.py b/examples/har_extractor.py
--- a/examples/har_extractor.py
+++ b/examples/har_extractor.py
@@ -60,6 +60,12 @@
         suit your actual needs of HAR generation. As it will probably be
         necessary to cluster logs by IPs or reset them from time to time.
     """
+    if sys.version_info >= (3, 0):
+        raise RuntimeError(
+            "har_extractor.py does not work on Python 3. "
+            "Please check out https://github.com/mitmproxy/mitmproxy/issues/1320 "
+            "if you want to help making this work again."
+        )
     context.dump_file = None
     if len(sys.argv) > 1:
         context.dump_file = sys.argv[1]

{"golden_diff": "diff --git a/examples/har_extractor.py b/examples/har_extractor.py\n--- a/examples/har_extractor.py\n+++ b/examples/har_extractor.py\n@@ -60,6 +60,12 @@\n         suit your actual needs of HAR generation. As it will probably be\n         necessary to cluster logs by IPs or reset them from time to time.\n     \"\"\"\n+    if sys.version_info >= (3, 0):\n+        raise RuntimeError(\n+            \"har_extractor.py does not work on Python 3. \"\n+            \"Please check out https://github.com/mitmproxy/mitmproxy/issues/1320 \"\n+            \"if you want to help making this work again.\"\n+        )\n     context.dump_file = None\n     if len(sys.argv) > 1:\n         context.dump_file = sys.argv[1]\n", "issue": "[tests actually pass] Remove harextractor example\nIf nobody objects, this PR (temporarily) removes the harextractor example, which depends on harparser, a Python2-only library. It's probably worth the time to rewrite this without any dependencies, but first we want to get a Python 3 release out of the door.\n\n", "before_files": [{"content": "\"\"\"\n    This inline script utilizes harparser.HAR from\n    https://github.com/JustusW/harparser to generate a HAR log object.\n\"\"\"\nimport six\nimport sys\nimport pytz\nfrom harparser import HAR\n\nfrom datetime import datetime\n\n\nclass _HARLog(HAR.log):\n    # The attributes need to be registered here for them to actually be\n    # available later via self. This is due to HAREncodable linking __getattr__\n    # to __getitem__. Anything that is set only in __init__ will just be added\n    # as key/value pair to self.__classes__.\n    __page_list__ = []\n    __page_count__ = 0\n    __page_ref__ = {}\n\n    def __init__(self, page_list=[]):\n        self.__page_list__ = page_list\n        self.__page_count__ = 0\n        self.__page_ref__ = {}\n\n        HAR.log.__init__(self, {\"version\": \"1.2\",\n                                \"creator\": {\"name\": \"MITMPROXY HARExtractor\",\n                                            \"version\": \"0.1\",\n                                            \"comment\": \"\"},\n                                \"pages\": [],\n                                \"entries\": []})\n\n    def reset(self):\n        self.__init__(self.__page_list__)\n\n    def add(self, obj):\n        if isinstance(obj, HAR.pages):\n            self['pages'].append(obj)\n        if isinstance(obj, HAR.entries):\n            self['entries'].append(obj)\n\n    def create_page_id(self):\n        self.__page_count__ += 1\n        return \"autopage_%s\" % str(self.__page_count__)\n\n    def set_page_ref(self, page, ref):\n        self.__page_ref__[page] = ref\n\n    def get_page_ref(self, page):\n        return self.__page_ref__.get(page, None)\n\n    def get_page_list(self):\n        return self.__page_list__\n\n\ndef start(context):\n    \"\"\"\n        On start we create a HARLog instance. You will have to adapt this to\n        suit your actual needs of HAR generation. As it will probably be\n        necessary to cluster logs by IPs or reset them from time to time.\n    \"\"\"\n    context.dump_file = None\n    if len(sys.argv) > 1:\n        context.dump_file = sys.argv[1]\n    else:\n        raise ValueError(\n            'Usage: -s \"har_extractor.py filename\" '\n            '(- will output to stdout, filenames ending with .zhar '\n            'will result in compressed har)'\n        )\n    context.HARLog = _HARLog()\n    context.seen_server = set()\n\n\ndef response(context, flow):\n    \"\"\"\n       Called when a server response has been received. At the time of this\n       message both a request and a response are present and completely done.\n    \"\"\"\n    # Values are converted from float seconds to int milliseconds later.\n    ssl_time = -.001\n    connect_time = -.001\n    if flow.server_conn not in context.seen_server:\n        # Calculate the connect_time for this server_conn. Afterwards add it to\n        # seen list, in order to avoid the connect_time being present in entries\n        # that use an existing connection.\n        connect_time = (flow.server_conn.timestamp_tcp_setup -\n                        flow.server_conn.timestamp_start)\n        context.seen_server.add(flow.server_conn)\n\n        if flow.server_conn.timestamp_ssl_setup is not None:\n            # Get the ssl_time for this server_conn as the difference between\n            # the start of the successful tcp setup and the successful ssl\n            # setup. If no ssl setup has been made it is left as -1 since it\n            # doesn't apply to this connection.\n            ssl_time = (flow.server_conn.timestamp_ssl_setup -\n                        flow.server_conn.timestamp_tcp_setup)\n\n    # Calculate the raw timings from the different timestamps present in the\n    # request and response object. For lack of a way to measure it dns timings\n    # can not be calculated. The same goes for HAR blocked: MITMProxy will open\n    # a server connection as soon as it receives the host and port from the\n    # client connection. So the time spent waiting is actually spent waiting\n    # between request.timestamp_end and response.timestamp_start thus it\n    # correlates to HAR wait instead.\n    timings_raw = {\n        'send': flow.request.timestamp_end - flow.request.timestamp_start,\n        'wait': flow.response.timestamp_start - flow.request.timestamp_end,\n        'receive': flow.response.timestamp_end - flow.response.timestamp_start,\n        'connect': connect_time,\n        'ssl': ssl_time\n    }\n\n    # HAR timings are integers in ms, so we have to re-encode the raw timings to\n    # that format.\n    timings = dict([(k, int(1000 * v)) for k, v in six.iteritems(timings_raw)])\n\n    # The full_time is the sum of all timings.\n    # Timings set to -1 will be ignored as per spec.\n    full_time = sum(v for v in timings.values() if v > -1)\n\n    started_date_time = datetime.utcfromtimestamp(\n        flow.request.timestamp_start).replace(tzinfo=pytz.timezone(\"UTC\")).isoformat()\n\n    request_query_string = [{\"name\": k, \"value\": v}\n                            for k, v in flow.request.query or {}]\n\n    response_body_size = len(flow.response.content)\n    response_body_decoded_size = len(flow.response.get_decoded_content())\n    response_body_compression = response_body_decoded_size - response_body_size\n\n    entry = HAR.entries({\n        \"startedDateTime\": started_date_time,\n        \"time\": full_time,\n        \"request\": {\n            \"method\": flow.request.method,\n            \"url\": flow.request.url,\n            \"httpVersion\": flow.request.http_version,\n            \"cookies\": format_cookies(flow.request.cookies),\n            \"headers\": format_headers(flow.request.headers),\n            \"queryString\": request_query_string,\n            \"headersSize\": len(str(flow.request.headers)),\n            \"bodySize\": len(flow.request.content),\n        },\n        \"response\": {\n            \"status\": flow.response.status_code,\n            \"statusText\": flow.response.reason,\n            \"httpVersion\": flow.response.http_version,\n            \"cookies\": format_cookies(flow.response.cookies),\n            \"headers\": format_headers(flow.response.headers),\n            \"content\": {\n                \"size\": response_body_size,\n                \"compression\": response_body_compression,\n                \"mimeType\": flow.response.headers.get('Content-Type', '')\n            },\n            \"redirectURL\": flow.response.headers.get('Location', ''),\n            \"headersSize\": len(str(flow.response.headers)),\n            \"bodySize\": response_body_size,\n        },\n        \"cache\": {},\n        \"timings\": timings,\n    })\n\n    # If the current url is in the page list of context.HARLog or\n    # does not have a referrer, we add it as a new pages object.\n    is_new_page = (\n        flow.request.url in context.HARLog.get_page_list() or\n        flow.request.headers.get('Referer') is None\n    )\n    if is_new_page:\n        page_id = context.HARLog.create_page_id()\n        context.HARLog.add(\n            HAR.pages({\n                \"startedDateTime\": entry['startedDateTime'],\n                \"id\": page_id,\n                \"title\": flow.request.url,\n                \"pageTimings\": {}\n            })\n        )\n        context.HARLog.set_page_ref(flow.request.url, page_id)\n        entry['pageref'] = page_id\n\n    # Lookup the referer in the page_ref of context.HARLog to point this entries\n    # pageref attribute to the right pages object, then set it as a new\n    # reference to build a reference tree.\n    elif context.HARLog.get_page_ref(flow.request.headers.get('Referer')) is not None:\n        entry['pageref'] = context.HARLog.get_page_ref(\n            flow.request.headers['Referer']\n        )\n        context.HARLog.set_page_ref(\n            flow.request.headers['Referer'], entry['pageref']\n        )\n\n    context.HARLog.add(entry)\n\n\ndef done(context):\n    \"\"\"\n        Called once on script shutdown, after any other events.\n    \"\"\"\n    import pprint\n    import json\n\n    json_dump = context.HARLog.json()\n    compressed_json_dump = context.HARLog.compress()\n\n    if context.dump_file == '-':\n        context.log(pprint.pformat(json.loads(json_dump)))\n    elif context.dump_file.endswith('.zhar'):\n        file(context.dump_file, \"w\").write(compressed_json_dump)\n    else:\n        file(context.dump_file, \"w\").write(json_dump)\n    context.log(\n        \"HAR log finished with %s bytes (%s bytes compressed)\" % (\n            len(json_dump), len(compressed_json_dump)\n        )\n    )\n    context.log(\n        \"Compression rate is %s%%\" % str(\n            100. * len(compressed_json_dump) / len(json_dump)\n        )\n    )\n\n\ndef format_cookies(obj):\n    if obj:\n        return [{\"name\": k.strip(), \"value\": v[0]} for k, v in obj.items()]\n    return \"\"\n\n\ndef format_headers(obj):\n    if obj:\n        return [{\"name\": k, \"value\": v} for k, v in obj.fields]\n    return \"\"\n\n\ndef print_attributes(obj, filter_string=None, hide_privates=False):\n    \"\"\"\n        Useful helper method to quickly get all attributes of an object and its\n        values.\n    \"\"\"\n    for attr in dir(obj):\n        if hide_privates and \"__\" in attr:\n            continue\n        if filter_string is not None and filter_string not in attr:\n            continue\n        value = getattr(obj, attr)\n        print(\"%s.%s\" % ('obj', attr), value, type(value))\n", "path": "examples/har_extractor.py"}], "after_files": [{"content": "\"\"\"\n    This inline script utilizes harparser.HAR from\n    https://github.com/JustusW/harparser to generate a HAR log object.\n\"\"\"\nimport six\nimport sys\nimport pytz\nfrom harparser import HAR\n\nfrom datetime import datetime\n\n\nclass _HARLog(HAR.log):\n    # The attributes need to be registered here for them to actually be\n    # available later via self. This is due to HAREncodable linking __getattr__\n    # to __getitem__. Anything that is set only in __init__ will just be added\n    # as key/value pair to self.__classes__.\n    __page_list__ = []\n    __page_count__ = 0\n    __page_ref__ = {}\n\n    def __init__(self, page_list=[]):\n        self.__page_list__ = page_list\n        self.__page_count__ = 0\n        self.__page_ref__ = {}\n\n        HAR.log.__init__(self, {\"version\": \"1.2\",\n                                \"creator\": {\"name\": \"MITMPROXY HARExtractor\",\n                                            \"version\": \"0.1\",\n                                            \"comment\": \"\"},\n                                \"pages\": [],\n                                \"entries\": []})\n\n    def reset(self):\n        self.__init__(self.__page_list__)\n\n    def add(self, obj):\n        if isinstance(obj, HAR.pages):\n            self['pages'].append(obj)\n        if isinstance(obj, HAR.entries):\n            self['entries'].append(obj)\n\n    def create_page_id(self):\n        self.__page_count__ += 1\n        return \"autopage_%s\" % str(self.__page_count__)\n\n    def set_page_ref(self, page, ref):\n        self.__page_ref__[page] = ref\n\n    def get_page_ref(self, page):\n        return self.__page_ref__.get(page, None)\n\n    def get_page_list(self):\n        return self.__page_list__\n\n\ndef start(context):\n    \"\"\"\n        On start we create a HARLog instance. You will have to adapt this to\n        suit your actual needs of HAR generation. As it will probably be\n        necessary to cluster logs by IPs or reset them from time to time.\n    \"\"\"\n    if sys.version_info >= (3, 0):\n        raise RuntimeError(\n            \"har_extractor.py does not work on Python 3. \"\n            \"Please check out https://github.com/mitmproxy/mitmproxy/issues/1320 \"\n            \"if you want to help making this work again.\"\n        )\n    context.dump_file = None\n    if len(sys.argv) > 1:\n        context.dump_file = sys.argv[1]\n    else:\n        raise ValueError(\n            'Usage: -s \"har_extractor.py filename\" '\n            '(- will output to stdout, filenames ending with .zhar '\n            'will result in compressed har)'\n        )\n    context.HARLog = _HARLog()\n    context.seen_server = set()\n\n\ndef response(context, flow):\n    \"\"\"\n       Called when a server response has been received. At the time of this\n       message both a request and a response are present and completely done.\n    \"\"\"\n    # Values are converted from float seconds to int milliseconds later.\n    ssl_time = -.001\n    connect_time = -.001\n    if flow.server_conn not in context.seen_server:\n        # Calculate the connect_time for this server_conn. Afterwards add it to\n        # seen list, in order to avoid the connect_time being present in entries\n        # that use an existing connection.\n        connect_time = (flow.server_conn.timestamp_tcp_setup -\n                        flow.server_conn.timestamp_start)\n        context.seen_server.add(flow.server_conn)\n\n        if flow.server_conn.timestamp_ssl_setup is not None:\n            # Get the ssl_time for this server_conn as the difference between\n            # the start of the successful tcp setup and the successful ssl\n            # setup. If no ssl setup has been made it is left as -1 since it\n            # doesn't apply to this connection.\n            ssl_time = (flow.server_conn.timestamp_ssl_setup -\n                        flow.server_conn.timestamp_tcp_setup)\n\n    # Calculate the raw timings from the different timestamps present in the\n    # request and response object. For lack of a way to measure it dns timings\n    # can not be calculated. The same goes for HAR blocked: MITMProxy will open\n    # a server connection as soon as it receives the host and port from the\n    # client connection. So the time spent waiting is actually spent waiting\n    # between request.timestamp_end and response.timestamp_start thus it\n    # correlates to HAR wait instead.\n    timings_raw = {\n        'send': flow.request.timestamp_end - flow.request.timestamp_start,\n        'wait': flow.response.timestamp_start - flow.request.timestamp_end,\n        'receive': flow.response.timestamp_end - flow.response.timestamp_start,\n        'connect': connect_time,\n        'ssl': ssl_time\n    }\n\n    # HAR timings are integers in ms, so we have to re-encode the raw timings to\n    # that format.\n    timings = dict([(k, int(1000 * v)) for k, v in six.iteritems(timings_raw)])\n\n    # The full_time is the sum of all timings.\n    # Timings set to -1 will be ignored as per spec.\n    full_time = sum(v for v in timings.values() if v > -1)\n\n    started_date_time = datetime.utcfromtimestamp(\n        flow.request.timestamp_start).replace(tzinfo=pytz.timezone(\"UTC\")).isoformat()\n\n    request_query_string = [{\"name\": k, \"value\": v}\n                            for k, v in flow.request.query or {}]\n\n    response_body_size = len(flow.response.content)\n    response_body_decoded_size = len(flow.response.get_decoded_content())\n    response_body_compression = response_body_decoded_size - response_body_size\n\n    entry = HAR.entries({\n        \"startedDateTime\": started_date_time,\n        \"time\": full_time,\n        \"request\": {\n            \"method\": flow.request.method,\n            \"url\": flow.request.url,\n            \"httpVersion\": flow.request.http_version,\n            \"cookies\": format_cookies(flow.request.cookies),\n            \"headers\": format_headers(flow.request.headers),\n            \"queryString\": request_query_string,\n            \"headersSize\": len(str(flow.request.headers)),\n            \"bodySize\": len(flow.request.content),\n        },\n        \"response\": {\n            \"status\": flow.response.status_code,\n            \"statusText\": flow.response.reason,\n            \"httpVersion\": flow.response.http_version,\n            \"cookies\": format_cookies(flow.response.cookies),\n            \"headers\": format_headers(flow.response.headers),\n            \"content\": {\n                \"size\": response_body_size,\n                \"compression\": response_body_compression,\n                \"mimeType\": flow.response.headers.get('Content-Type', '')\n            },\n            \"redirectURL\": flow.response.headers.get('Location', ''),\n            \"headersSize\": len(str(flow.response.headers)),\n            \"bodySize\": response_body_size,\n        },\n        \"cache\": {},\n        \"timings\": timings,\n    })\n\n    # If the current url is in the page list of context.HARLog or\n    # does not have a referrer, we add it as a new pages object.\n    is_new_page = (\n        flow.request.url in context.HARLog.get_page_list() or\n        flow.request.headers.get('Referer') is None\n    )\n    if is_new_page:\n        page_id = context.HARLog.create_page_id()\n        context.HARLog.add(\n            HAR.pages({\n                \"startedDateTime\": entry['startedDateTime'],\n                \"id\": page_id,\n                \"title\": flow.request.url,\n                \"pageTimings\": {}\n            })\n        )\n        context.HARLog.set_page_ref(flow.request.url, page_id)\n        entry['pageref'] = page_id\n\n    # Lookup the referer in the page_ref of context.HARLog to point this entries\n    # pageref attribute to the right pages object, then set it as a new\n    # reference to build a reference tree.\n    elif context.HARLog.get_page_ref(flow.request.headers.get('Referer')) is not None:\n        entry['pageref'] = context.HARLog.get_page_ref(\n            flow.request.headers['Referer']\n        )\n        context.HARLog.set_page_ref(\n            flow.request.headers['Referer'], entry['pageref']\n        )\n\n    context.HARLog.add(entry)\n\n\ndef done(context):\n    \"\"\"\n        Called once on script shutdown, after any other events.\n    \"\"\"\n    import pprint\n    import json\n\n    json_dump = context.HARLog.json()\n    compressed_json_dump = context.HARLog.compress()\n\n    if context.dump_file == '-':\n        context.log(pprint.pformat(json.loads(json_dump)))\n    elif context.dump_file.endswith('.zhar'):\n        file(context.dump_file, \"w\").write(compressed_json_dump)\n    else:\n        file(context.dump_file, \"w\").write(json_dump)\n    context.log(\n        \"HAR log finished with %s bytes (%s bytes compressed)\" % (\n            len(json_dump), len(compressed_json_dump)\n        )\n    )\n    context.log(\n        \"Compression rate is %s%%\" % str(\n            100. * len(compressed_json_dump) / len(json_dump)\n        )\n    )\n\n\ndef format_cookies(obj):\n    if obj:\n        return [{\"name\": k.strip(), \"value\": v[0]} for k, v in obj.items()]\n    return \"\"\n\n\ndef format_headers(obj):\n    if obj:\n        return [{\"name\": k, \"value\": v} for k, v in obj.fields]\n    return \"\"\n\n\ndef print_attributes(obj, filter_string=None, hide_privates=False):\n    \"\"\"\n        Useful helper method to quickly get all attributes of an object and its\n        values.\n    \"\"\"\n    for attr in dir(obj):\n        if hide_privates and \"__\" in attr:\n            continue\n        if filter_string is not None and filter_string not in attr:\n            continue\n        value = getattr(obj, attr)\n        print(\"%s.%s\" % ('obj', attr), value, type(value))\n", "path": "examples/har_extractor.py"}]}