| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * TODO(eroman): This needs better presentation, and cleaner code. This | |
| 7 * implementation is more of a transitionary step as | |
| 8 * the old net-internals is replaced. | |
| 9 */ | |
| 10 | |
| 11 // TODO(eroman): these functions should use lower-case names. | |
| 12 var PaintLogView; | |
| 13 var PrintSourceEntriesAsText; | |
| 14 var proxySettingsToString; | |
| 15 var stripCookiesAndLoginInfo; | |
| 16 | |
| 17 // Start of anonymous namespace. | |
| 18 (function() { | |
| 19 | |
| 20 PaintLogView = function(sourceEntries, node) { | |
| 21 for (var i = 0; i < sourceEntries.length; ++i) { | |
| 22 if (i != 0) | |
| 23 addNode(node, 'hr'); | |
| 24 addSourceEntry_(node, sourceEntries[i]); | |
| 25 } | |
| 26 } | |
| 27 | |
| 28 function addSourceEntry_(node, sourceEntry) { | |
| 29 var div = addNode(node, 'div'); | |
| 30 div.className = 'logSourceEntry'; | |
| 31 | |
| 32 var p = addNode(div, 'p'); | |
| 33 var nobr = addNode(p, 'nobr'); | |
| 34 | |
| 35 addTextNode(nobr, sourceEntry.getDescription()); | |
| 36 | |
| 37 var p2 = addNode(div, 'p'); | |
| 38 var nobr2 = addNode(p2, 'nobr'); | |
| 39 | |
| 40 var logEntries = sourceEntry.getLogEntries(); | |
| 41 var startDate = convertTimeTicksToDate(logEntries[0].time); | |
| 42 addTextNode(nobr2, 'Start Time: ' + startDate.toLocaleString()); | |
| 43 | |
| 44 var pre = addNode(div, 'pre'); | |
| 45 addTextNode(pre, PrintSourceEntriesAsText(logEntries)); | |
| 46 } | |
| 47 | |
| 48 function canCollapseBeginWithEnd(beginEntry) { | |
| 49 return beginEntry && | |
| 50 beginEntry.isBegin() && | |
| 51 beginEntry.end && | |
| 52 beginEntry.end.index == beginEntry.index + 1 && | |
| 53 (!beginEntry.orig.params || !beginEntry.end.orig.params) && | |
| 54 beginEntry.orig.wasPassivelyCaptured == | |
| 55 beginEntry.end.orig.wasPassivelyCaptured; | |
| 56 } | |
| 57 | |
| 58 PrintSourceEntriesAsText = function(sourceEntries) { | |
| 59 var entries = LogGroupEntry.createArrayFrom(sourceEntries); | |
| 60 if (entries.length == 0) | |
| 61 return ''; | |
| 62 | |
| 63 var startDate = convertTimeTicksToDate(entries[0].orig.time); | |
| 64 var startTime = startDate.getTime(); | |
| 65 | |
| 66 var tablePrinter = new TablePrinter(); | |
| 67 | |
| 68 for (var i = 0; i < entries.length; ++i) { | |
| 69 var entry = entries[i]; | |
| 70 | |
| 71 // Avoid printing the END for a BEGIN that was immediately before, unless | |
| 72 // both have extra parameters. | |
| 73 if (!entry.isEnd() || !canCollapseBeginWithEnd(entry.begin)) { | |
| 74 tablePrinter.addRow(); | |
| 75 | |
| 76 // Annotate this entry with "(P)" if it was passively captured. | |
| 77 tablePrinter.addCell(entry.orig.wasPassivelyCaptured ? '(P) ' : ''); | |
| 78 | |
| 79 tablePrinter.addCell('t='); | |
| 80 var date = convertTimeTicksToDate(entry.orig.time) ; | |
| 81 var tCell = tablePrinter.addCell(date.getTime()); | |
| 82 tCell.alignRight = true; | |
| 83 tablePrinter.addCell(' [st='); | |
| 84 var stCell = tablePrinter.addCell(date.getTime() - startTime); | |
| 85 stCell.alignRight = true; | |
| 86 tablePrinter.addCell('] '); | |
| 87 | |
| 88 var indentationStr = makeRepeatedString(' ', entry.getDepth() * 3); | |
| 89 var mainCell = | |
| 90 tablePrinter.addCell(indentationStr + getTextForEvent(entry)); | |
| 91 tablePrinter.addCell(' '); | |
| 92 | |
| 93 // Get the elapsed time. | |
| 94 if (entry.isBegin()) { | |
| 95 tablePrinter.addCell('[dt='); | |
| 96 var dt = '?'; | |
| 97 // Definite time. | |
| 98 if (entry.end) { | |
| 99 dt = entry.end.orig.time - entry.orig.time; | |
| 100 } | |
| 101 var dtCell = tablePrinter.addCell(dt); | |
| 102 dtCell.alignRight = true; | |
| 103 | |
| 104 tablePrinter.addCell(']'); | |
| 105 } else { | |
| 106 mainCell.allowOverflow = true; | |
| 107 } | |
| 108 } | |
| 109 | |
| 110 // Output the extra parameters. | |
| 111 if (entry.orig.params != undefined) { | |
| 112 // Add a continuation row for each line of text from the extra parameters. | |
| 113 var extraParamsText = getTextForExtraParams( | |
| 114 entry.orig, | |
| 115 g_browser.sourceTracker.getSecurityStripping()); | |
| 116 var extraParamsTextLines = extraParamsText.split('\n'); | |
| 117 | |
| 118 for (var j = 0; j < extraParamsTextLines.length; ++j) { | |
| 119 tablePrinter.addRow(); | |
| 120 tablePrinter.addCell(''); // Empty passive annotation. | |
| 121 tablePrinter.addCell(''); // No t=. | |
| 122 tablePrinter.addCell(''); | |
| 123 tablePrinter.addCell(''); // No st=. | |
| 124 tablePrinter.addCell(''); | |
| 125 tablePrinter.addCell(' '); | |
| 126 | |
| 127 var mainExtraCell = | |
| 128 tablePrinter.addCell(indentationStr + extraParamsTextLines[j]); | |
| 129 mainExtraCell.allowOverflow = true; | |
| 130 } | |
| 131 } | |
| 132 } | |
| 133 | |
| 134 // Format the table for fixed-width text. | |
| 135 return tablePrinter.toText(0); | |
| 136 } | |
| 137 | |
| 138 /** | |
| 139 * |hexString| must be a string of hexadecimal characters with no whitespace, | |
| 140 * whose length is a multiple of two. Returns a string spanning multiple lines, | |
| 141 * with the hexadecimal characters from |hexString| on the left, in groups of | |
| 142 * two, and their corresponding ASCII characters on the right. | |
| 143 * | |
| 144 * |asciiCharsPerLine| specifies how many ASCII characters will be put on each | |
| 145 * line of the output string. | |
| 146 */ | |
| 147 function formatHexString(hexString, asciiCharsPerLine) { | |
| 148 // Number of transferred bytes in a line of output. Length of a | |
| 149 // line is roughly 4 times larger. | |
| 150 var hexCharsPerLine = 2 * asciiCharsPerLine; | |
| 151 var out = []; | |
| 152 for (var i = 0; i < hexString.length; i += hexCharsPerLine) { | |
| 153 var hexLine = ''; | |
| 154 var asciiLine = ''; | |
| 155 for (var j = i; j < i + hexCharsPerLine && j < hexString.length; j += 2) { | |
| 156 var hex = hexString.substr(j, 2); | |
| 157 hexLine += hex + ' '; | |
| 158 var charCode = parseInt(hex, 16); | |
| 159 // For ASCII codes 32 though 126, display the corresponding | |
| 160 // characters. Use a space for nulls, and a period for | |
| 161 // everything else. | |
| 162 if (charCode >= 0x20 && charCode <= 0x7E) { | |
| 163 asciiLine += String.fromCharCode(charCode); | |
| 164 } else if (charCode == 0x00) { | |
| 165 asciiLine += ' '; | |
| 166 } else { | |
| 167 asciiLine += '.'; | |
| 168 } | |
| 169 } | |
| 170 | |
| 171 // Max sure the ASCII text on last line of output lines up with previous | |
| 172 // lines. | |
| 173 hexLine += makeRepeatedString(' ', 3 * asciiCharsPerLine - hexLine.length); | |
| 174 out.push(' ' + hexLine + ' ' + asciiLine); | |
| 175 } | |
| 176 return out.join('\n'); | |
| 177 } | |
| 178 | |
| 179 function getTextForExtraParams(entry, enableSecurityStripping) { | |
| 180 // Format the extra parameters (use a custom formatter for certain types, | |
| 181 // but default to displaying as JSON). | |
| 182 | |
| 183 // If security stripping is enabled, remove data as needed. | |
| 184 if (enableSecurityStripping) | |
| 185 entry = stripCookiesAndLoginInfo(entry); | |
| 186 | |
| 187 switch (entry.type) { | |
| 188 case LogEventType.HTTP_TRANSACTION_SEND_REQUEST_HEADERS: | |
| 189 case LogEventType.HTTP_TRANSACTION_SEND_TUNNEL_HEADERS: | |
| 190 return getTextForRequestHeadersExtraParam(entry); | |
| 191 | |
| 192 case LogEventType.HTTP_TRANSACTION_READ_RESPONSE_HEADERS: | |
| 193 case LogEventType.HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS: | |
| 194 return getTextForResponseHeadersExtraParam(entry); | |
| 195 | |
| 196 case LogEventType.PROXY_CONFIG_CHANGED: | |
| 197 return getTextForProxyConfigChangedExtraParam(entry); | |
| 198 | |
| 199 default: | |
| 200 var out = []; | |
| 201 for (var k in entry.params) { | |
| 202 if (k == 'headers' && entry.params[k] instanceof Array) { | |
| 203 out.push(getTextForResponseHeadersExtraParam(entry)); | |
| 204 continue; | |
| 205 } | |
| 206 var value = entry.params[k]; | |
| 207 // For transferred bytes, display the bytes in hex and ASCII. | |
| 208 if (k == 'hex_encoded_bytes') { | |
| 209 out.push(' --> ' + k + ' ='); | |
| 210 out.push(formatHexString(value, 20)); | |
| 211 continue; | |
| 212 } | |
| 213 | |
| 214 var paramStr = ' --> ' + k + ' = ' + JSON.stringify(value); | |
| 215 | |
| 216 // Append the symbolic name for certain constants. (This relies | |
| 217 // on particular naming of event parameters to infer the type). | |
| 218 if (typeof value == 'number') { | |
| 219 if (k == 'net_error') { | |
| 220 paramStr += ' (' + getNetErrorSymbolicString(value) + ')'; | |
| 221 } else if (k == 'load_flags') { | |
| 222 paramStr += ' (' + getLoadFlagSymbolicString(value) + ')'; | |
| 223 } | |
| 224 } | |
| 225 | |
| 226 out.push(paramStr); | |
| 227 } | |
| 228 return out.join('\n'); | |
| 229 } | |
| 230 } | |
| 231 | |
| 232 /** | |
| 233 * Returns the name for netError. | |
| 234 * | |
| 235 * Example: getNetErrorSymbolicString(-105) would return | |
| 236 * "NAME_NOT_RESOLVED". | |
| 237 */ | |
| 238 function getNetErrorSymbolicString(netError) { | |
| 239 return getKeyWithValue(NetError, netError); | |
| 240 } | |
| 241 | |
| 242 /** | |
| 243 * Returns the set of LoadFlags that make up the integer |loadFlag|. | |
| 244 * For example: getLoadFlagSymbolicString( | |
| 245 */ | |
| 246 function getLoadFlagSymbolicString(loadFlag) { | |
| 247 // Load flag of 0 means "NORMAL". Special case this, since and-ing with | |
| 248 // 0 is always going to be false. | |
| 249 if (loadFlag == 0) | |
| 250 return getKeyWithValue(LoadFlag, loadFlag); | |
| 251 | |
| 252 var matchingLoadFlagNames = []; | |
| 253 | |
| 254 for (var k in LoadFlag) { | |
| 255 if (loadFlag & LoadFlag[k]) | |
| 256 matchingLoadFlagNames.push(k); | |
| 257 } | |
| 258 | |
| 259 return matchingLoadFlagNames.join(' | '); | |
| 260 } | |
| 261 | |
| 262 /** | |
| 263 * Indent |lines| by |start|. | |
| 264 * | |
| 265 * For example, if |start| = ' -> ' and |lines| = ['line1', 'line2', 'line3'] | |
| 266 * the output will be: | |
| 267 * | |
| 268 * " -> line1\n" + | |
| 269 * " line2\n" + | |
| 270 * " line3" | |
| 271 */ | |
| 272 function indentLines(start, lines) { | |
| 273 return start + lines.join('\n' + makeRepeatedString(' ', start.length)); | |
| 274 } | |
| 275 | |
| 276 /** | |
| 277 * Removes a cookie or unencrypted login information from a single HTTP header | |
| 278 * line, if present, and returns the modified line. Otherwise, just returns | |
| 279 * the original line. | |
| 280 */ | |
| 281 function stripCookieOrLoginInfo(line) { | |
| 282 var patterns = [ | |
| 283 // Cookie patterns | |
| 284 /^set-cookie:/i, | |
| 285 /^set-cookie2:/i, | |
| 286 /^cookie:/i, | |
| 287 | |
| 288 // Unencrypted authentication patterns | |
| 289 /^authorization: \S*/i, | |
| 290 /^proxy-authorization: \S*/i]; | |
| 291 for (var i = 0; i < patterns.length; i++) { | |
| 292 var match = patterns[i].exec(line); | |
| 293 if (match != null) | |
| 294 return match[0] + ' [value was stripped]'; | |
| 295 } | |
| 296 | |
| 297 // Remove authentication information from data received from the server in | |
| 298 // multi-round Negotiate authentication. | |
| 299 var challengePatterns = [ | |
| 300 /^www-authenticate: (\S*)\s*/i, | |
| 301 /^proxy-authenticate: (\S*)\s*/i]; | |
| 302 for (var i = 0; i < challengePatterns.length; i++) { | |
| 303 var match = challengePatterns[i].exec(line); | |
| 304 if (!match) | |
| 305 continue; | |
| 306 | |
| 307 // If there's no data after the scheme name, do nothing. | |
| 308 if (match[0].length == line.length) | |
| 309 break; | |
| 310 | |
| 311 // Ignore lines with commas in them, as they may contain lists of schemes, | |
| 312 // and the information we want to hide is Base64 encoded, so has no commas. | |
| 313 if (line.indexOf(',') >= 0) | |
| 314 break; | |
| 315 | |
| 316 // Ignore Basic and Digest authentication challenges, as they contain | |
| 317 // public information. | |
| 318 if (/^basic$/i.test(match[1]) || /^digest$/i.test(match[1])) | |
| 319 break; | |
| 320 | |
| 321 return match[0] + '[value was stripped]'; | |
| 322 } | |
| 323 | |
| 324 return line; | |
| 325 } | |
| 326 | |
| 327 /** | |
| 328 * If |entry| has headers, returns a copy of |entry| with all cookie and | |
| 329 * unencrypted login text removed. Otherwise, returns original |entry| object. | |
| 330 * This is needed so that JSON log dumps can be made without affecting the | |
| 331 * source data. | |
| 332 */ | |
| 333 stripCookiesAndLoginInfo = function(entry) { | |
| 334 if (!entry.params || !entry.params.headers || | |
| 335 !(entry.params.headers instanceof Array)) { | |
| 336 return entry; | |
| 337 } | |
| 338 | |
| 339 // Duplicate the top level object, and |entry.params|. All other fields are | |
| 340 // just pointers to the original values, as they won't be modified, other than | |
| 341 // |entry.params.headers|. | |
| 342 entry = shallowCloneObject(entry); | |
| 343 entry.params = shallowCloneObject(entry.params); | |
| 344 | |
| 345 entry.params.headers = entry.params.headers.map(stripCookieOrLoginInfo); | |
| 346 return entry; | |
| 347 } | |
| 348 | |
| 349 function getTextForRequestHeadersExtraParam(entry) { | |
| 350 var params = entry.params; | |
| 351 | |
| 352 // Strip the trailing CRLF that params.line contains. | |
| 353 var lineWithoutCRLF = params.line.replace(/\r\n$/g, ''); | |
| 354 return indentLines(' --> ', [lineWithoutCRLF].concat(params.headers)); | |
| 355 } | |
| 356 | |
| 357 function getTextForResponseHeadersExtraParam(entry) { | |
| 358 return indentLines(' --> ', entry.params.headers); | |
| 359 } | |
| 360 | |
| 361 function getTextForProxyConfigChangedExtraParam(entry) { | |
| 362 var params = entry.params; | |
| 363 var out = ''; | |
| 364 var indentation = ' '; | |
| 365 | |
| 366 if (params.old_config) { | |
| 367 var oldConfigString = proxySettingsToString(params.old_config); | |
| 368 // The previous configuration may not be present in the case of | |
| 369 // the initial proxy settings fetch. | |
| 370 out += ' --> old_config =\n' + | |
| 371 indentLines(indentation, oldConfigString.split('\n')); | |
| 372 out += '\n'; | |
| 373 } | |
| 374 | |
| 375 var newConfigString = proxySettingsToString(params.new_config); | |
| 376 out += ' --> new_config =\n' + | |
| 377 indentLines(indentation, newConfigString.split('\n')); | |
| 378 | |
| 379 return out; | |
| 380 } | |
| 381 | |
| 382 function getTextForEvent(entry) { | |
| 383 var text = ''; | |
| 384 | |
| 385 if (entry.isBegin() && canCollapseBeginWithEnd(entry)) { | |
| 386 // Don't prefix with '+' if we are going to collapse the END event. | |
| 387 text = ' '; | |
| 388 } else if (entry.isBegin()) { | |
| 389 text = '+' + text; | |
| 390 } else if (entry.isEnd()) { | |
| 391 text = '-' + text; | |
| 392 } else { | |
| 393 text = ' '; | |
| 394 } | |
| 395 | |
| 396 text += getKeyWithValue(LogEventType, entry.orig.type); | |
| 397 return text; | |
| 398 } | |
| 399 | |
| 400 proxySettingsToString = function(config) { | |
| 401 if (!config) | |
| 402 return ''; | |
| 403 | |
| 404 // The proxy settings specify up to three major fallback choices | |
| 405 // (auto-detect, custom pac url, or manual settings). | |
| 406 // We enumerate these to a list so we can later number them. | |
| 407 var modes = []; | |
| 408 | |
| 409 // Output any automatic settings. | |
| 410 if (config.auto_detect) | |
| 411 modes.push(['Auto-detect']); | |
| 412 if (config.pac_url) | |
| 413 modes.push(['PAC script: ' + config.pac_url]); | |
| 414 | |
| 415 // Output any manual settings. | |
| 416 if (config.single_proxy || config.proxy_per_scheme) { | |
| 417 var lines = []; | |
| 418 | |
| 419 if (config.single_proxy) { | |
| 420 lines.push('Proxy server: ' + config.single_proxy); | |
| 421 } else if (config.proxy_per_scheme) { | |
| 422 for (var urlScheme in config.proxy_per_scheme) { | |
| 423 if (urlScheme != 'fallback') { | |
| 424 lines.push('Proxy server for ' + urlScheme.toUpperCase() + ': ' + | |
| 425 config.proxy_per_scheme[urlScheme]); | |
| 426 } | |
| 427 } | |
| 428 if (config.proxy_per_scheme.fallback) { | |
| 429 lines.push('Proxy server for everything else: ' + | |
| 430 config.proxy_per_scheme.fallback); | |
| 431 } | |
| 432 } | |
| 433 | |
| 434 // Output any proxy bypass rules. | |
| 435 if (config.bypass_list) { | |
| 436 if (config.reverse_bypass) { | |
| 437 lines.push('Reversed bypass list: '); | |
| 438 } else { | |
| 439 lines.push('Bypass list: '); | |
| 440 } | |
| 441 | |
| 442 for (var i = 0; i < config.bypass_list.length; ++i) | |
| 443 lines.push(' ' + config.bypass_list[i]); | |
| 444 } | |
| 445 | |
| 446 modes.push(lines); | |
| 447 } | |
| 448 | |
| 449 // If we didn't find any proxy settings modes, we are using DIRECT. | |
| 450 if (modes.length < 1) | |
| 451 return 'Use DIRECT connections.'; | |
| 452 | |
| 453 // If there was just one mode, don't bother numbering it. | |
| 454 if (modes.length == 1) | |
| 455 return modes[0].join('\n'); | |
| 456 | |
| 457 // Otherwise concatenate all of the modes into a numbered list | |
| 458 // (which correspond with the fallback order). | |
| 459 var result = []; | |
| 460 for (var i = 0; i < modes.length; ++i) | |
| 461 result.push(indentLines('(' + (i + 1) + ') ', modes[i])); | |
| 462 | |
| 463 return result.join('\n'); | |
| 464 }; | |
| 465 | |
| 466 // End of anonymous namespace. | |
| 467 })(); | |
| 468 | |
| OLD | NEW |