Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6)

Side by Side Diff: tools/android/loading/request_track.py

Issue 1633813005: tools/android/loading: Add support for multiple redirects. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: . Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
(...skipping 28 matching lines...) Expand all
39 """ 39 """
40 return json.loads(json.dumps(timing)) 40 return json.loads(json.dumps(timing))
41 41
42 class Request(object): 42 class Request(object):
43 """Represents a single request. 43 """Represents a single request.
44 44
45 Generally speaking, fields here closely mirror those documented in 45 Generally speaking, fields here closely mirror those documented in
46 third_party/WebKit/Source/devtools/protocol.json. 46 third_party/WebKit/Source/devtools/protocol.json.
47 47
48 Fields: 48 Fields:
49 request_id: (str) unique request ID. Postfixed with REDIRECT_SUFFIX for 49 request_id: (str) unique request ID. Postfixed with _REDIRECT_SUFFIX for
50 redirects. 50 redirects.
51 frame_id: (str) unique frame identifier. 51 frame_id: (str) unique frame identifier.
52 loader_id: (str) unique frame identifier. 52 loader_id: (str) unique frame identifier.
53 document_url: (str) URL of the document this request is loaded for. 53 document_url: (str) URL of the document this request is loaded for.
54 url: (str) Request URL. 54 url: (str) Request URL.
55 protocol: (str) protocol used for the request. 55 protocol: (str) protocol used for the request.
56 method: (str) HTTP method, such as POST or GET. 56 method: (str) HTTP method, such as POST or GET.
57 request_headers: (dict) {'header': 'value'} Request headers. 57 request_headers: (dict) {'header': 'value'} Request headers.
58 response_headers: (dict) {'header': 'value'} Response headers. 58 response_headers: (dict) {'header': 'value'} Response headers.
59 initial_priority: (str) Initial request priority, in REQUEST_PRIORITIES. 59 initial_priority: (str) Initial request priority, in REQUEST_PRIORITIES.
(...skipping 10 matching lines...) Expand all
70 encoded_data_length: (int) Total encoded data length. 70 encoded_data_length: (int) Total encoded data length.
71 data_chunks: (list) [(offset, encoded_data_length), ...] List of data 71 data_chunks: (list) [(offset, encoded_data_length), ...] List of data
72 chunks received, with their offset in ms relative to 72 chunks received, with their offset in ms relative to
73 Timing.requestTime. 73 Timing.requestTime.
74 failed: (bool) Whether the request failed. 74 failed: (bool) Whether the request failed.
75 """ 75 """
76 REQUEST_PRIORITIES = ('VeryLow', 'Low', 'Medium', 'High', 'VeryHigh') 76 REQUEST_PRIORITIES = ('VeryLow', 'Low', 'Medium', 'High', 'VeryHigh')
77 RESOURCE_TYPES = ('Document', 'Stylesheet', 'Image', 'Media', 'Font', 77 RESOURCE_TYPES = ('Document', 'Stylesheet', 'Image', 'Media', 'Font',
78 'Script', 'TextTrack', 'XHR', 'Fetch', 'EventSource', 78 'Script', 'TextTrack', 'XHR', 'Fetch', 'EventSource',
79 'WebSocket', 'Manifest', 'Other') 79 'WebSocket', 'Manifest', 'Other')
80 INITIATORS = ('parser', 'script', 'other') 80 INITIATORS = ('parser', 'script', 'other', 'redirect')
81 INITIATING_REQUEST = 'initiating_request'
82 ORIGINAL_INITIATOR = 'original_initiator'
81 def __init__(self): 83 def __init__(self):
82 self.request_id = None 84 self.request_id = None
83 self.frame_id = None 85 self.frame_id = None
84 self.loader_id = None 86 self.loader_id = None
85 self.document_url = None 87 self.document_url = None
86 self.url = None 88 self.url = None
87 self.protocol = None 89 self.protocol = None
88 self.method = None 90 self.method = None
89 self.request_headers = None 91 self.request_headers = None
90 self.response_headers = None 92 self.response_headers = None
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
165 167
166 def __hash__(self): 168 def __hash__(self):
167 return hash(self.request_id) 169 return hash(self.request_id)
168 170
169 def __str__(self): 171 def __str__(self):
170 return json.dumps(self.ToJsonDict(), sort_keys=True, indent=2) 172 return json.dumps(self.ToJsonDict(), sort_keys=True, indent=2)
171 173
172 174
173 class RequestTrack(devtools_monitor.Track): 175 class RequestTrack(devtools_monitor.Track):
174 """Aggregates request data.""" 176 """Aggregates request data."""
175 REDIRECT_SUFFIX = '.redirect' 177 _REDIRECT_SUFFIX = '.redirect'
176 # Request status 178 # Request status
177 _STATUS_SENT = 0 179 _STATUS_SENT = 0
178 _STATUS_RESPONSE = 1 180 _STATUS_RESPONSE = 1
179 _STATUS_DATA = 2 181 _STATUS_DATA = 2
180 _STATUS_FINISHED = 3 182 _STATUS_FINISHED = 3
181 _STATUS_FAILED = 4 183 _STATUS_FAILED = 4
182 # Serialization KEYS 184 # Serialization KEYS
183 _EVENTS_KEY = 'events' 185 _EVENTS_KEY = 'events'
184 _METADATA_KEY = 'metadata' 186 _METADATA_KEY = 'metadata'
185 _DUPLICATES_KEY = 'duplicates_count' 187 _DUPLICATES_KEY = 'duplicates_count'
188 _INCONSISTENT_INITIATORS_KEY = 'inconsistent_initiators'
186 def __init__(self, connection): 189 def __init__(self, connection):
187 super(RequestTrack, self).__init__(connection) 190 super(RequestTrack, self).__init__(connection)
188 self._connection = connection 191 self._connection = connection
189 self._requests = [] 192 self._requests = []
190 self._requests_in_flight = {} # requestId -> (request, status) 193 self._requests_in_flight = {} # requestId -> (request, status)
191 self._completed_requests_by_id = {} 194 self._completed_requests_by_id = {}
195 self._redirects_count_by_id = collections.defaultdict(int)
192 if connection: # Optional for testing. 196 if connection: # Optional for testing.
193 for method in RequestTrack._METHOD_TO_HANDLER: 197 for method in RequestTrack._METHOD_TO_HANDLER:
194 self._connection.RegisterListener(method, self) 198 self._connection.RegisterListener(method, self)
195 # responseReceived message are sometimes duplicated. Records the message to 199 # responseReceived message are sometimes duplicated. Records the message to
196 # detect this. 200 # detect this.
197 self._request_id_to_response_received = {} 201 self._request_id_to_response_received = {}
198 self.duplicates_count = 0 202 self.duplicates_count = 0
203 self.inconsistent_initiators_count = 0
199 204
200 def Handle(self, method, msg): 205 def Handle(self, method, msg):
201 assert method in RequestTrack._METHOD_TO_HANDLER 206 assert method in RequestTrack._METHOD_TO_HANDLER
202 params = msg['params'] 207 params = msg['params']
203 request_id = params['requestId'] 208 request_id = params['requestId']
204 RequestTrack._METHOD_TO_HANDLER[method](self, request_id, params) 209 RequestTrack._METHOD_TO_HANDLER[method](self, request_id, params)
205 210
206 def GetEvents(self): 211 def GetEvents(self):
207 if self._requests_in_flight: 212 if self._requests_in_flight:
208 logging.warning('Number of requests still in flight: %d.' 213 logging.warning('Number of requests still in flight: %d.'
209 % len(self._requests_in_flight)) 214 % len(self._requests_in_flight))
210 return self._requests 215 return self._requests
211 216
212 def ToJsonDict(self): 217 def ToJsonDict(self):
213 if self._requests_in_flight: 218 if self._requests_in_flight:
214 logging.warning('Requests in flight, will be ignored in the dump') 219 logging.warning('Requests in flight, will be ignored in the dump')
215 return {self._EVENTS_KEY: [ 220 return {self._EVENTS_KEY: [
216 request.ToJsonDict() for request in self._requests], 221 request.ToJsonDict() for request in self._requests],
217 self._METADATA_KEY: {self._DUPLICATES_KEY: self.duplicates_count}} 222 self._METADATA_KEY: {
223 self._DUPLICATES_KEY: self.duplicates_count,
224 self._INCONSISTENT_INITIATORS_KEY:
225 self.inconsistent_initiators_count}}
218 226
219 @classmethod 227 @classmethod
220 def FromJsonDict(cls, json_dict): 228 def FromJsonDict(cls, json_dict):
221 assert cls._EVENTS_KEY in json_dict 229 assert cls._EVENTS_KEY in json_dict
222 assert cls._METADATA_KEY in json_dict 230 assert cls._METADATA_KEY in json_dict
223 result = RequestTrack(None) 231 result = RequestTrack(None)
224 requests = [Request.FromJsonDict(request) 232 requests = [Request.FromJsonDict(request)
225 for request in json_dict[cls._EVENTS_KEY]] 233 for request in json_dict[cls._EVENTS_KEY]]
226 result._requests = requests 234 result._requests = requests
227 result.duplicates_count = json_dict[cls._METADATA_KEY][cls._DUPLICATES_KEY] 235 metadata = json_dict[cls._METADATA_KEY]
236 result.duplicates_count = metadata[cls._DUPLICATES_KEY]
237 result.inconsistent_initiators_count = metadata[
238 cls._INCONSISTENT_INITIATORS_KEY]
mattcary 2016/01/26 13:20:26 Call get() instead so we can load previous version
Benoit L 2016/01/26 13:40:47 Done.
228 return result 239 return result
229 240
230 def _RequestWillBeSent(self, request_id, params): 241 def _RequestWillBeSent(self, request_id, params):
231 # Several "requestWillBeSent" events can be dispatched in a row in the case 242 # Several "requestWillBeSent" events can be dispatched in a row in the case
232 # of redirects. 243 # of redirects.
244 redirect_initiator = None
233 if request_id in self._requests_in_flight: 245 if request_id in self._requests_in_flight:
234 self._HandleRedirect(request_id, params) 246 redirect_initiator = self._HandleRedirect(request_id, params)
235 assert (request_id not in self._requests_in_flight 247 assert (request_id not in self._requests_in_flight
236 and request_id not in self._completed_requests_by_id) 248 and request_id not in self._completed_requests_by_id)
237 r = Request() 249 r = Request()
238 r.request_id = request_id 250 r.request_id = request_id
239 _CopyFromDictToObject( 251 _CopyFromDictToObject(
240 params, r, (('frameId', 'frame_id'), ('loaderId', 'loader_id'), 252 params, r, (('frameId', 'frame_id'), ('loaderId', 'loader_id'),
241 ('documentURL', 'document_url'), 253 ('documentURL', 'document_url'),
242 ('timestamp', 'timestamp'), ('wallTime', 'wall_time'), 254 ('timestamp', 'timestamp'), ('wallTime', 'wall_time'),
243 ('initiator', 'initiator'))) 255 ('initiator', 'initiator')))
244 request = params['request'] 256 request = params['request']
245 _CopyFromDictToObject( 257 _CopyFromDictToObject(
246 request, r, (('url', 'url'), ('method', 'method'), 258 request, r, (('url', 'url'), ('method', 'method'),
247 ('headers', 'headers'), 259 ('headers', 'headers'),
248 ('initialPriority', 'initial_priority'))) 260 ('initialPriority', 'initial_priority')))
249 r.resource_type = params.get('type', 'Other') 261 r.resource_type = params.get('type', 'Other')
262 if redirect_initiator:
263 original_initiator = r.initiator
264 r.initiator = redirect_initiator
265 r.initiator[Request.ORIGINAL_INITIATOR] = original_initiator
266 initiating_request = self._completed_requests_by_id[
267 redirect_initiator[Request.INITIATING_REQUEST]]
268 initiating_initiator = initiating_request.initiator.get(
269 Request.ORIGINAL_INITIATOR, initiating_request.initiator)
270 if initiating_initiator != original_initiator:
271 self.inconsistent_initiators_count += 1
250 self._requests_in_flight[request_id] = (r, RequestTrack._STATUS_SENT) 272 self._requests_in_flight[request_id] = (r, RequestTrack._STATUS_SENT)
251 273
252 def _HandleRedirect(self, request_id, params): 274 def _HandleRedirect(self, request_id, params):
253 (r, status) = self._requests_in_flight[request_id] 275 (r, status) = self._requests_in_flight[request_id]
254 assert status == RequestTrack._STATUS_SENT 276 assert status == RequestTrack._STATUS_SENT
255 # The second request contains timing information pertaining to the first 277 # The second request contains timing information pertaining to the first
256 # one. Finalize the first request. 278 # one. Finalize the first request.
257 assert 'redirectResponse' in params 279 assert 'redirectResponse' in params
258 redirect_response = params['redirectResponse'] 280 redirect_response = params['redirectResponse']
281
259 _CopyFromDictToObject(redirect_response, r, 282 _CopyFromDictToObject(redirect_response, r,
260 (('headers', 'response_headers'), 283 (('headers', 'response_headers'),
261 ('encodedDataLength', 'encoded_data_length'), 284 ('encodedDataLength', 'encoded_data_length'),
262 ('fromDiskCache', 'from_disk_cache'))) 285 ('fromDiskCache', 'from_disk_cache')))
263 r.timing = TimingFromDict(redirect_response['timing']) 286 r.timing = TimingFromDict(redirect_response['timing'])
264 r.request_id = request_id + self.REDIRECT_SUFFIX 287
288 redirect_index = self._redirects_count_by_id[request_id]
289 self._redirects_count_by_id[request_id] += 1
290 r.request_id = '%s%s.%d' % (request_id, self._REDIRECT_SUFFIX,
291 redirect_index + 1)
292 initiator = {
293 'type': 'redirect', Request.INITIATING_REQUEST: r.request_id}
265 self._requests_in_flight[r.request_id] = (r, RequestTrack._STATUS_FINISHED) 294 self._requests_in_flight[r.request_id] = (r, RequestTrack._STATUS_FINISHED)
266 del self._requests_in_flight[request_id] 295 del self._requests_in_flight[request_id]
267 self._FinalizeRequest(r.request_id) 296 self._FinalizeRequest(r.request_id)
297 return initiator
268 298
269 def _RequestServedFromCache(self, request_id, _): 299 def _RequestServedFromCache(self, request_id, _):
270 assert request_id in self._requests_in_flight 300 assert request_id in self._requests_in_flight
271 (request, status) = self._requests_in_flight[request_id] 301 (request, status) = self._requests_in_flight[request_id]
272 assert status == RequestTrack._STATUS_SENT 302 assert status == RequestTrack._STATUS_SENT
273 request.served_from_cache = True 303 request.served_from_cache = True
274 304
275 def _ResponseReceived(self, request_id, params): 305 def _ResponseReceived(self, request_id, params):
276 assert request_id in self._requests_in_flight 306 assert request_id in self._requests_in_flight
277 (r, status) = self._requests_in_flight[request_id] 307 (r, status) = self._requests_in_flight[request_id]
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
374 404
375 405
376 if __name__ == '__main__': 406 if __name__ == '__main__':
377 import json 407 import json
378 import sys 408 import sys
379 events = json.load(open(sys.argv[1], 'r')) 409 events = json.load(open(sys.argv[1], 'r'))
380 request_track = RequestTrack(None) 410 request_track = RequestTrack(None)
381 for event in events: 411 for event in events:
382 event_method = event['method'] 412 event_method = event['method']
383 request_track.Handle(event_method, event) 413 request_track.Handle(event_method, event)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698