| OLD | NEW |
| 1 # Copyright (c) 2016 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2016 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """The request data track. | 5 """The request data track. |
| 6 | 6 |
| 7 When executed, parses a JSON dump of DevTools messages. | 7 When executed, parses a JSON dump of DevTools messages. |
| 8 """ | 8 """ |
| 9 | 9 |
| 10 import collections | 10 import collections |
| 11 import copy | 11 import copy |
| 12 import json |
| 12 import logging | 13 import logging |
| 14 import re |
| 13 | 15 |
| 14 import devtools_monitor | 16 import devtools_monitor |
| 15 | 17 |
| 16 | 18 |
| 17 _TIMING_NAMES_MAPPING = { | 19 _TIMING_NAMES_MAPPING = { |
| 18 'connectEnd': 'connect_end', 'connectStart': 'connect_start', | 20 'connectEnd': 'connect_end', 'connectStart': 'connect_start', |
| 19 'dnsEnd': 'dns_end', 'dnsStart': 'dns_start', 'proxyEnd': 'proxy_end', | 21 'dnsEnd': 'dns_end', 'dnsStart': 'dns_start', 'proxyEnd': 'proxy_end', |
| 20 'proxyStart': 'proxy_start', 'receiveHeadersEnd': 'receive_headers_end', | 22 'proxyStart': 'proxy_start', 'receiveHeadersEnd': 'receive_headers_end', |
| 21 'requestTime': 'request_time', 'sendEnd': 'send_end', | 23 'requestTime': 'request_time', 'sendEnd': 'send_end', |
| 22 'sendStart': 'send_start', 'sslEnd': 'ssl_end', 'sslStart': 'ssl_start', | 24 'sendStart': 'send_start', 'sslEnd': 'ssl_end', 'sslStart': 'ssl_start', |
| 23 'workerReady': 'worker_ready', 'workerStart': 'worker_start', | 25 'workerReady': 'worker_ready', 'workerStart': 'worker_start', |
| 24 'loadingFinished': 'loading_finished'} | 26 'loadingFinished': 'loading_finished'} |
| 25 | 27 |
| 26 Timing = collections.namedtuple('Timing', _TIMING_NAMES_MAPPING.values()) | 28 Timing = collections.namedtuple('Timing', _TIMING_NAMES_MAPPING.values()) |
| 27 | 29 |
| 30 def TimingAsList(timing): |
| 31 """Transform Timing to a list, eg as is used in JSON output. |
| 32 |
| 33 Args: |
| 34 timing: a Timing. |
| 35 |
| 36 Returns: |
| 37 A list identical to what the eventual JSON output will be (eg, |
| 38 Request.ToJsonDict). |
| 39 """ |
| 40 return json.loads(json.dumps(timing)) |
| 28 | 41 |
| 29 class Request(object): | 42 class Request(object): |
| 30 """Represents a single request. | 43 """Represents a single request. |
| 31 | 44 |
| 32 Generally speaking, fields here closely mirror those documented in | 45 Generally speaking, fields here closely mirror those documented in |
| 33 third_party/WebKit/Source/devtools/protocol.json. | 46 third_party/WebKit/Source/devtools/protocol.json. |
| 34 | 47 |
| 35 Fields: | 48 Fields: |
| 36 request_id: (str) unique request ID. Postfixed with REDIRECT_SUFFIX for | 49 request_id: (str) unique request ID. Postfixed with REDIRECT_SUFFIX for |
| 37 redirects. | 50 redirects. |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 95 return (timestamp - request_time) * 1000 | 108 return (timestamp - request_time) * 1000 |
| 96 | 109 |
| 97 def ToJsonDict(self): | 110 def ToJsonDict(self): |
| 98 return copy.deepcopy(self.__dict__) | 111 return copy.deepcopy(self.__dict__) |
| 99 | 112 |
| 100 @classmethod | 113 @classmethod |
| 101 def FromJsonDict(cls, data_dict): | 114 def FromJsonDict(cls, data_dict): |
| 102 result = Request() | 115 result = Request() |
| 103 for (k, v) in data_dict.items(): | 116 for (k, v) in data_dict.items(): |
| 104 setattr(result, k, v) | 117 setattr(result, k, v) |
| 118 if not result.response_headers: |
| 119 result.response_headers = {} |
| 105 if result.timing: | 120 if result.timing: |
| 106 result.timing = Timing(*result.timing) | 121 result.timing = Timing(*result.timing) |
| 122 else: |
| 123 result.timing = TimingFromDict({'requestTime': result.timestamp}) |
| 107 return result | 124 return result |
| 108 | 125 |
| 109 def GetContentType(self): | 126 def GetContentType(self): |
| 110 """Returns the content type, or None.""" | 127 """Returns the content type, or None.""" |
| 111 content_type = self.response_headers.get('Content-Type', None) | 128 content_type = self.response_headers.get('Content-Type', None) |
| 112 if not content_type or ';' not in content_type: | 129 if not content_type or ';' not in content_type: |
| 113 return content_type | 130 return content_type |
| 114 else: | 131 else: |
| 115 return content_type[:content_type.index(';')] | 132 return content_type[:content_type.index(';')] |
| 116 | 133 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 130 parts = [s.strip() for s in directive.split('=')] | 147 parts = [s.strip() for s in directive.split('=')] |
| 131 if len(parts) == 1: | 148 if len(parts) == 1: |
| 132 cache_control[parts[0]] = True | 149 cache_control[parts[0]] = True |
| 133 else: | 150 else: |
| 134 cache_control[parts[0]] = parts[1] | 151 cache_control[parts[0]] = parts[1] |
| 135 if (u'no-store' in cache_control | 152 if (u'no-store' in cache_control |
| 136 or u'no-cache' in cache_control | 153 or u'no-cache' in cache_control |
| 137 or len(cache_control) == 0): | 154 or len(cache_control) == 0): |
| 138 return -1 | 155 return -1 |
| 139 if 'max-age' in cache_control: | 156 if 'max-age' in cache_control: |
| 140 return int(cache_control['max-age']) | 157 age_match = re.match(r'\s*(\d+)+', cache_control['max-age']) |
| 158 if not age_match: |
| 159 return -1 |
| 160 return int(age_match.group(1)) |
| 141 return -1 | 161 return -1 |
| 142 | 162 |
| 143 def __eq__(self, o): | 163 def __eq__(self, o): |
| 144 return self.__dict__ == o.__dict__ | 164 return self.__dict__ == o.__dict__ |
| 145 | 165 |
| 146 def __hash__(self): | 166 def __hash__(self): |
| 147 return hash(self.request_id) | 167 return hash(self.request_id) |
| 148 | 168 |
| 149 | 169 |
| 150 class RequestTrack(devtools_monitor.Track): | 170 class RequestTrack(devtools_monitor.Track): |
| (...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 330 | 350 |
| 331 | 351 |
| 332 if __name__ == '__main__': | 352 if __name__ == '__main__': |
| 333 import json | 353 import json |
| 334 import sys | 354 import sys |
| 335 events = json.load(open(sys.argv[1], 'r')) | 355 events = json.load(open(sys.argv[1], 'r')) |
| 336 request_track = RequestTrack(None) | 356 request_track = RequestTrack(None) |
| 337 for event in events: | 357 for event in events: |
| 338 event_method = event['method'] | 358 event_method = event['method'] |
| 339 request_track.Handle(event_method, event) | 359 request_track.Handle(event_method, event) |
| OLD | NEW |