PageRenderTime 56ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/front_end/sdk/HAREntry.js

https://gitlab.com/robinduckett/chrome-devtools-secure
JavaScript | 379 lines | 226 code | 37 blank | 116 comment | 31 complexity | 615d2e1fc03296ed9d9a7c5cdef3a456 MD5 | raw file
  1. /*
  2. * Copyright (C) 2012 Google Inc. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are
  6. * met:
  7. *
  8. * * Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * * Redistributions in binary form must reproduce the above
  11. * copyright notice, this list of conditions and the following disclaimer
  12. * in the documentation and/or other materials provided with the
  13. * distribution.
  14. * * Neither the name of Google Inc. nor the names of its
  15. * contributors may be used to endorse or promote products derived from
  16. * this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. // See http://www.softwareishard.com/blog/har-12-spec/
  31. // for HAR specification.
  32. // FIXME: Some fields are not yet supported due to back-end limitations.
  33. // See https://bugs.webkit.org/show_bug.cgi?id=58127 for details.
  34. /**
  35. * @constructor
  36. * @param {!WebInspector.NetworkRequest} request
  37. */
  38. WebInspector.HAREntry = function(request)
  39. {
  40. this._request = request;
  41. }
  42. WebInspector.HAREntry.prototype = {
  43. /**
  44. * @return {!Object}
  45. */
  46. build: function()
  47. {
  48. var ipAddress = this._request.remoteAddress();
  49. var portPositionInString = ipAddress.lastIndexOf(":");
  50. if (portPositionInString !== -1)
  51. ipAddress = ipAddress.substr(0, portPositionInString);
  52. var entry = {
  53. startedDateTime: WebInspector.HARLog.pseudoWallTime(this._request, this._request.startTime),
  54. time: this._request.timing ? WebInspector.HAREntry._toMilliseconds(this._request.duration) : 0,
  55. request: this._buildRequest(),
  56. response: this._buildResponse(),
  57. cache: { }, // Not supported yet.
  58. timings: this._buildTimings(),
  59. serverIPAddress: ipAddress
  60. };
  61. if (this._request.connectionId !== "0")
  62. entry.connection = this._request.connectionId;
  63. var page = this._request.networkLog().pageLoadForRequest(this._request);
  64. if (page)
  65. entry.pageref = "page_" + page.id;
  66. return entry;
  67. },
  68. /**
  69. * @return {!Object}
  70. */
  71. _buildRequest: function()
  72. {
  73. var headersText = this._request.requestHeadersText();
  74. var res = {
  75. method: this._request.requestMethod,
  76. url: this._buildRequestURL(this._request.url),
  77. httpVersion: this._request.requestHttpVersion(),
  78. headers: this._request.requestHeaders(),
  79. queryString: this._buildParameters(this._request.queryParameters || []),
  80. cookies: this._buildCookies(this._request.requestCookies || []),
  81. headersSize: headersText ? headersText.length : -1,
  82. bodySize: this.requestBodySize
  83. };
  84. if (this._request.requestFormData)
  85. res.postData = this._buildPostData();
  86. return res;
  87. },
  88. /**
  89. * @return {!Object}
  90. */
  91. _buildResponse: function()
  92. {
  93. var headersText = this._request.responseHeadersText;
  94. return {
  95. status: this._request.statusCode,
  96. statusText: this._request.statusText,
  97. httpVersion: this._request.responseHttpVersion(),
  98. headers: this._request.responseHeaders,
  99. cookies: this._buildCookies(this._request.responseCookies || []),
  100. content: this._buildContent(),
  101. redirectURL: this._request.responseHeaderValue("Location") || "",
  102. headersSize: headersText ? headersText.length : -1,
  103. bodySize: this.responseBodySize,
  104. _transferSize: this._request.transferSize,
  105. _error: this._request.localizedFailDescription
  106. };
  107. },
  108. /**
  109. * @return {!Object}
  110. */
  111. _buildContent: function()
  112. {
  113. var content = {
  114. size: this._request.resourceSize,
  115. mimeType: this._request.mimeType || "x-unknown",
  116. // text: this._request.content // TODO: pull out into a boolean flag, as content can be huge (and needs to be requested with an async call)
  117. };
  118. var compression = this.responseCompression;
  119. if (typeof compression === "number")
  120. content.compression = compression;
  121. return content;
  122. },
  123. /**
  124. * @return {!Object}
  125. */
  126. _buildTimings: function()
  127. {
  128. // Order of events: request_start = 0, [proxy], [dns], [connect [ssl]], [send], receive_headers_end
  129. // HAR 'blocked' time is time before first network activity.
  130. var timing = this._request.timing;
  131. if (!timing)
  132. return {blocked: -1, dns: -1, connect: -1, send: 0, wait: 0, receive: 0, ssl: -1};
  133. function firstNonNegative(values)
  134. {
  135. for (var i = 0; i < values.length; ++i) {
  136. if (values[i] >= 0)
  137. return values[i];
  138. }
  139. console.assert(false, "Incomplete request timing information.");
  140. }
  141. var blocked = firstNonNegative([timing.dnsStart, timing.connectStart, timing.sendStart]);
  142. var dns = -1;
  143. if (timing.dnsStart >= 0)
  144. dns = firstNonNegative([timing.connectStart, timing.sendStart]) - timing.dnsStart;
  145. var connect = -1;
  146. if (timing.connectStart >= 0)
  147. connect = timing.sendStart - timing.connectStart;
  148. var send = timing.sendEnd - timing.sendStart;
  149. var wait = timing.receiveHeadersEnd - timing.sendEnd;
  150. var receive = WebInspector.HAREntry._toMilliseconds(this._request.duration) - timing.receiveHeadersEnd;
  151. var ssl = -1;
  152. if (timing.sslStart >= 0 && timing.sslEnd >= 0)
  153. ssl = timing.sslEnd - timing.sslStart;
  154. return {blocked: blocked, dns: dns, connect: connect, send: send, wait: wait, receive: receive, ssl: ssl};
  155. },
  156. /**
  157. * @return {!Object}
  158. */
  159. _buildPostData: function()
  160. {
  161. var res = {
  162. mimeType: this._request.requestContentType(),
  163. text: this._request.requestFormData
  164. };
  165. if (this._request.formParameters)
  166. res.params = this._buildParameters(this._request.formParameters);
  167. return res;
  168. },
  169. /**
  170. * @param {!Array.<!Object>} parameters
  171. * @return {!Array.<!Object>}
  172. */
  173. _buildParameters: function(parameters)
  174. {
  175. return parameters.slice();
  176. },
  177. /**
  178. * @param {string} url
  179. * @return {string}
  180. */
  181. _buildRequestURL: function(url)
  182. {
  183. return url.split("#", 2)[0];
  184. },
  185. /**
  186. * @param {!Array.<!WebInspector.Cookie>} cookies
  187. * @return {!Array.<!Object>}
  188. */
  189. _buildCookies: function(cookies)
  190. {
  191. return cookies.map(this._buildCookie.bind(this));
  192. },
  193. /**
  194. * @param {!WebInspector.Cookie} cookie
  195. * @return {!Object}
  196. */
  197. _buildCookie: function(cookie)
  198. {
  199. var c = {
  200. name: cookie.name(),
  201. value: cookie.value(),
  202. path: cookie.path(),
  203. domain: cookie.domain(),
  204. expires: cookie.expiresDate(WebInspector.HARLog.pseudoWallTime(this._request, this._request.startTime)),
  205. httpOnly: cookie.httpOnly(),
  206. secure: cookie.secure()
  207. };
  208. if (cookie.sameSite())
  209. c.sameSite = cookie.sameSite();
  210. return c;
  211. },
  212. /**
  213. * @return {number}
  214. */
  215. get requestBodySize()
  216. {
  217. return !this._request.requestFormData ? 0 : this._request.requestFormData.length;
  218. },
  219. /**
  220. * @return {number}
  221. */
  222. get responseBodySize()
  223. {
  224. if (this._request.cached() || this._request.statusCode === 304)
  225. return 0;
  226. if (!this._request.responseHeadersText)
  227. return -1;
  228. return this._request.transferSize - this._request.responseHeadersText.length;
  229. },
  230. /**
  231. * @return {number|undefined}
  232. */
  233. get responseCompression()
  234. {
  235. if (this._request.cached() || this._request.statusCode === 304 || this._request.statusCode === 206)
  236. return;
  237. if (!this._request.responseHeadersText)
  238. return;
  239. return this._request.resourceSize - this.responseBodySize;
  240. }
  241. }
  242. /**
  243. * @param {number} time
  244. * @return {number}
  245. */
  246. WebInspector.HAREntry._toMilliseconds = function(time)
  247. {
  248. return time === -1 ? -1 : time * 1000;
  249. }
  250. /**
  251. * @constructor
  252. * @param {!Array.<!WebInspector.NetworkRequest>} requests
  253. */
  254. WebInspector.HARLog = function(requests)
  255. {
  256. this._requests = requests;
  257. }
  258. /**
  259. * @param {!WebInspector.NetworkRequest} request
  260. * @param {number} monotonicTime
  261. * @return {!Date}
  262. */
  263. WebInspector.HARLog.pseudoWallTime = function(request, monotonicTime)
  264. {
  265. return new Date(request.pseudoWallTime(monotonicTime) * 1000);
  266. }
  267. WebInspector.HARLog.prototype = {
  268. /**
  269. * @return {!Object}
  270. */
  271. build: function()
  272. {
  273. return {
  274. version: "1.2",
  275. creator: this._creator(),
  276. pages: this._buildPages(),
  277. entries: this._requests.map(this._convertResource.bind(this))
  278. }
  279. },
  280. _creator: function()
  281. {
  282. var webKitVersion = /AppleWebKit\/([^ ]+)/.exec(window.navigator.userAgent);
  283. return {
  284. name: "WebInspector",
  285. version: webKitVersion ? webKitVersion[1] : "n/a"
  286. };
  287. },
  288. /**
  289. * @return {!Array.<!Object>}
  290. */
  291. _buildPages: function()
  292. {
  293. var seenIdentifiers = {};
  294. var pages = [];
  295. for (var i = 0; i < this._requests.length; ++i) {
  296. var request = this._requests[i];
  297. var page = request.networkLog().pageLoadForRequest(request);
  298. if (!page || seenIdentifiers[page.id])
  299. continue;
  300. seenIdentifiers[page.id] = true;
  301. pages.push(this._convertPage(page, request));
  302. }
  303. return pages;
  304. },
  305. /**
  306. * @param {!WebInspector.PageLoad} page
  307. * @param {!WebInspector.NetworkRequest} request
  308. * @return {!Object}
  309. */
  310. _convertPage: function(page, request)
  311. {
  312. return {
  313. startedDateTime: WebInspector.HARLog.pseudoWallTime(request, page.startTime),
  314. id: "page_" + page.id,
  315. title: page.url, // We don't have actual page title here. URL is probably better than nothing.
  316. pageTimings: {
  317. onContentLoad: this._pageEventTime(page, page.contentLoadTime),
  318. onLoad: this._pageEventTime(page, page.loadTime)
  319. }
  320. }
  321. },
  322. /**
  323. * @param {!WebInspector.NetworkRequest} request
  324. * @return {!Object}
  325. */
  326. _convertResource: function(request)
  327. {
  328. return (new WebInspector.HAREntry(request)).build();
  329. },
  330. /**
  331. * @param {!WebInspector.PageLoad} page
  332. * @param {number} time
  333. * @return {number}
  334. */
  335. _pageEventTime: function(page, time)
  336. {
  337. var startTime = page.startTime;
  338. if (time === -1 || startTime === -1)
  339. return -1;
  340. return WebInspector.HAREntry._toMilliseconds(time - startTime);
  341. }
  342. }