| /* |
| * Copyright (C) 2012 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| /** |
| * @implements {Common.ContentProvider} |
| * @unrestricted |
| */ |
| SDK.NetworkRequest = class extends Common.Object { |
| /** |
| * @param {!Protocol.Network.RequestId} requestId |
| * @param {string} url |
| * @param {string} documentURL |
| * @param {!Protocol.Page.FrameId} frameId |
| * @param {!Protocol.Network.LoaderId} loaderId |
| * @param {?Protocol.Network.Initiator} initiator |
| */ |
| constructor(requestId, url, documentURL, frameId, loaderId, initiator) { |
| super(); |
| |
| this._requestId = requestId; |
| this._backendRequestId = requestId; |
| this.setUrl(url); |
| this._documentURL = documentURL; |
| this._frameId = frameId; |
| this._loaderId = loaderId; |
| /** @type {?Protocol.Network.Initiator} */ |
| this._initiator = initiator; |
| /** @type {?SDK.NetworkRequest} */ |
| this._redirectSource = null; |
| /** @type {?SDK.NetworkRequest} */ |
| this._redirectDestination = null; |
| this._issueTime = -1; |
| this._startTime = -1; |
| this._endTime = -1; |
| /** @type {!Protocol.Network.BlockedReason|undefined} */ |
| this._blockedReason = undefined; |
| |
| this.statusCode = 0; |
| this.statusText = ''; |
| this.requestMethod = ''; |
| this.requestTime = 0; |
| this.protocol = ''; |
| /** @type {!Protocol.Security.MixedContentType} */ |
| this.mixedContentType = Protocol.Security.MixedContentType.None; |
| |
| /** @type {?Protocol.Network.ResourcePriority} */ |
| this._initialPriority = null; |
| /** @type {?Protocol.Network.ResourcePriority} */ |
| this._currentPriority = null; |
| |
| /** @type {?Protocol.Network.SignedExchangeInfo} */ |
| this._signedExchangeInfo = null; |
| |
| /** @type {!Common.ResourceType} */ |
| this._resourceType = Common.resourceTypes.Other; |
| /** @type {?Promise<!SDK.NetworkRequest.ContentData>} */ |
| this._contentData = null; |
| /** @type {!Array.<!SDK.NetworkRequest.WebSocketFrame>} */ |
| this._frames = []; |
| /** @type {!Array.<!SDK.NetworkRequest.EventSourceMessage>} */ |
| this._eventSourceMessages = []; |
| |
| /** @type {!Object<string, (string|undefined)>} */ |
| this._responseHeaderValues = {}; |
| this._responseHeadersText = ''; |
| |
| /** @type {!Array<!SDK.NetworkRequest.NameValue>} */ |
| this._requestHeaders = []; |
| /** @type {!Object<string, (string|undefined)>} */ |
| this._requestHeaderValues = {}; |
| |
| this._remoteAddress = ''; |
| |
| /** @type {?Protocol.Network.RequestReferrerPolicy} */ |
| this._referrerPolicy = null; |
| |
| /** @type {!Protocol.Security.SecurityState} */ |
| this._securityState = Protocol.Security.SecurityState.Unknown; |
| /** @type {?Protocol.Network.SecurityDetails} */ |
| this._securityDetails = null; |
| |
| /** @type {string} */ |
| this.connectionId = '0'; |
| /** @type {?Promise<?Array.<!SDK.NetworkRequest.NameValue>>} */ |
| this._formParametersPromise = null; |
| // Assume no body initially |
| /** @type {?Promise<?string>} */ |
| this._requestFormDataPromise = /** @type {?Promise<?string>} */ (Promise.resolve(null)); |
| } |
| |
| /** |
| * @param {!SDK.NetworkRequest} other |
| * @return {number} |
| */ |
| indentityCompare(other) { |
| const thisId = this.requestId(); |
| const thatId = other.requestId(); |
| if (thisId > thatId) |
| return 1; |
| if (thisId < thatId) |
| return -1; |
| return 0; |
| } |
| |
| /** |
| * @return {!Protocol.Network.RequestId} |
| */ |
| requestId() { |
| return this._requestId; |
| } |
| |
| /** |
| * @return {!Protocol.Network.RequestId} |
| */ |
| backendRequestId() { |
| return this._backendRequestId; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| url() { |
| return this._url; |
| } |
| |
| /** |
| * @param {string} x |
| */ |
| setUrl(x) { |
| if (this._url === x) |
| return; |
| |
| this._url = x; |
| this._parsedURL = new Common.ParsedURL(x); |
| delete this._queryString; |
| delete this._parsedQueryParameters; |
| delete this._name; |
| delete this._path; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| get documentURL() { |
| return this._documentURL; |
| } |
| |
| get parsedURL() { |
| return this._parsedURL; |
| } |
| |
| /** |
| * @return {!Protocol.Page.FrameId} |
| */ |
| get frameId() { |
| return this._frameId; |
| } |
| |
| /** |
| * @return {!Protocol.Network.LoaderId} |
| */ |
| get loaderId() { |
| return this._loaderId; |
| } |
| |
| /** |
| * @param {string} ip |
| * @param {number} port |
| */ |
| setRemoteAddress(ip, port) { |
| this._remoteAddress = ip + ':' + port; |
| this.dispatchEventToListeners(SDK.NetworkRequest.Events.RemoteAddressChanged, this); |
| } |
| |
| /** |
| * @return {string} |
| */ |
| remoteAddress() { |
| return this._remoteAddress; |
| } |
| |
| /** |
| * @param {!Protocol.Network.RequestReferrerPolicy} referrerPolicy |
| */ |
| setReferrerPolicy(referrerPolicy) { |
| this._referrerPolicy = referrerPolicy; |
| } |
| |
| /** |
| * @return {?Protocol.Network.RequestReferrerPolicy} |
| */ |
| referrerPolicy() { |
| return this._referrerPolicy; |
| } |
| |
| /** |
| * @return {!Protocol.Security.SecurityState} |
| */ |
| securityState() { |
| return this._securityState; |
| } |
| |
| /** |
| * @param {!Protocol.Security.SecurityState} securityState |
| */ |
| setSecurityState(securityState) { |
| this._securityState = securityState; |
| } |
| |
| /** |
| * @return {?Protocol.Network.SecurityDetails} |
| */ |
| securityDetails() { |
| return this._securityDetails; |
| } |
| |
| /** |
| * @param {!Protocol.Network.SecurityDetails} securityDetails |
| */ |
| setSecurityDetails(securityDetails) { |
| this._securityDetails = securityDetails; |
| } |
| |
| /** |
| * @return {number} |
| */ |
| get startTime() { |
| return this._startTime || -1; |
| } |
| |
| /** |
| * @param {number} monotonicTime |
| * @param {number} wallTime |
| */ |
| setIssueTime(monotonicTime, wallTime) { |
| this._issueTime = monotonicTime; |
| this._wallIssueTime = wallTime; |
| this._startTime = monotonicTime; |
| } |
| |
| /** |
| * @return {number} |
| */ |
| issueTime() { |
| return this._issueTime; |
| } |
| |
| /** |
| * @param {number} monotonicTime |
| * @return {number} |
| */ |
| pseudoWallTime(monotonicTime) { |
| return this._wallIssueTime ? this._wallIssueTime - this._issueTime + monotonicTime : monotonicTime; |
| } |
| |
| /** |
| * @return {number} |
| */ |
| get responseReceivedTime() { |
| return this._responseReceivedTime || -1; |
| } |
| |
| /** |
| * @param {number} x |
| */ |
| set responseReceivedTime(x) { |
| this._responseReceivedTime = x; |
| } |
| |
| /** |
| * @return {number} |
| */ |
| get endTime() { |
| return this._endTime || -1; |
| } |
| |
| /** |
| * @param {number} x |
| */ |
| set endTime(x) { |
| if (this.timing && this.timing.requestTime) { |
| // Check against accurate responseReceivedTime. |
| this._endTime = Math.max(x, this.responseReceivedTime); |
| } else { |
| // Prefer endTime since it might be from the network stack. |
| this._endTime = x; |
| if (this._responseReceivedTime > x) |
| this._responseReceivedTime = x; |
| } |
| this.dispatchEventToListeners(SDK.NetworkRequest.Events.TimingChanged, this); |
| } |
| |
| /** |
| * @return {number} |
| */ |
| get duration() { |
| if (this._endTime === -1 || this._startTime === -1) |
| return -1; |
| return this._endTime - this._startTime; |
| } |
| |
| /** |
| * @return {number} |
| */ |
| get latency() { |
| if (this._responseReceivedTime === -1 || this._startTime === -1) |
| return -1; |
| return this._responseReceivedTime - this._startTime; |
| } |
| |
| /** |
| * @return {number} |
| */ |
| get resourceSize() { |
| return this._resourceSize || 0; |
| } |
| |
| /** |
| * @param {number} x |
| */ |
| set resourceSize(x) { |
| this._resourceSize = x; |
| } |
| |
| /** |
| * @return {number} |
| */ |
| get transferSize() { |
| return this._transferSize || 0; |
| } |
| |
| /** |
| * @param {number} x |
| */ |
| increaseTransferSize(x) { |
| this._transferSize = (this._transferSize || 0) + x; |
| } |
| |
| /** |
| * @param {number} x |
| */ |
| setTransferSize(x) { |
| this._transferSize = x; |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| get finished() { |
| return this._finished; |
| } |
| |
| /** |
| * @param {boolean} x |
| */ |
| set finished(x) { |
| if (this._finished === x) |
| return; |
| |
| this._finished = x; |
| |
| if (x) |
| this.dispatchEventToListeners(SDK.NetworkRequest.Events.FinishedLoading, this); |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| get failed() { |
| return this._failed; |
| } |
| |
| /** |
| * @param {boolean} x |
| */ |
| set failed(x) { |
| this._failed = x; |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| get canceled() { |
| return this._canceled; |
| } |
| |
| /** |
| * @param {boolean} x |
| */ |
| set canceled(x) { |
| this._canceled = x; |
| } |
| |
| /** |
| * @return {!Protocol.Network.BlockedReason|undefined} |
| */ |
| blockedReason() { |
| return this._blockedReason; |
| } |
| |
| /** |
| * @param {!Protocol.Network.BlockedReason} reason |
| */ |
| setBlockedReason(reason) { |
| this._blockedReason = reason; |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| wasBlocked() { |
| return !!this._blockedReason; |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| cached() { |
| return (!!this._fromMemoryCache || !!this._fromDiskCache) && !this._transferSize; |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| cachedInMemory() { |
| return !!this._fromMemoryCache && !this._transferSize; |
| } |
| |
| setFromMemoryCache() { |
| this._fromMemoryCache = true; |
| delete this._timing; |
| } |
| |
| setFromDiskCache() { |
| this._fromDiskCache = true; |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| get fetchedViaServiceWorker() { |
| return this._fetchedViaServiceWorker; |
| } |
| |
| /** |
| * @param {boolean} x |
| */ |
| set fetchedViaServiceWorker(x) { |
| this._fetchedViaServiceWorker = x; |
| } |
| |
| /** |
| * @return {!Protocol.Network.ResourceTiming|undefined} |
| */ |
| get timing() { |
| return this._timing; |
| } |
| |
| /** |
| * @param {!Protocol.Network.ResourceTiming|undefined} timingInfo |
| */ |
| set timing(timingInfo) { |
| if (!timingInfo || this._fromMemoryCache) |
| return; |
| // Take startTime and responseReceivedTime from timing data for better accuracy. |
| // Timing's requestTime is a baseline in seconds, rest of the numbers there are ticks in millis. |
| this._startTime = timingInfo.requestTime; |
| const headersReceivedTime = timingInfo.requestTime + timingInfo.receiveHeadersEnd / 1000.0; |
| if ((this._responseReceivedTime || -1) < 0 || this._responseReceivedTime > headersReceivedTime) |
| this._responseReceivedTime = headersReceivedTime; |
| if (this._startTime > this._responseReceivedTime) |
| this._responseReceivedTime = this._startTime; |
| |
| this._timing = timingInfo; |
| this.dispatchEventToListeners(SDK.NetworkRequest.Events.TimingChanged, this); |
| } |
| |
| /** |
| * @return {string} |
| */ |
| get mimeType() { |
| return this._mimeType; |
| } |
| |
| /** |
| * @param {string} x |
| */ |
| set mimeType(x) { |
| this._mimeType = x; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| get displayName() { |
| return this._parsedURL.displayName; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| name() { |
| if (this._name) |
| return this._name; |
| this._parseNameAndPathFromURL(); |
| return this._name; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| path() { |
| if (this._path) |
| return this._path; |
| this._parseNameAndPathFromURL(); |
| return this._path; |
| } |
| |
| _parseNameAndPathFromURL() { |
| if (this._parsedURL.isDataURL()) { |
| this._name = this._parsedURL.dataURLDisplayName(); |
| this._path = ''; |
| } else if (this._parsedURL.isAboutBlank()) { |
| this._name = this._parsedURL.url; |
| this._path = ''; |
| } else { |
| this._path = this._parsedURL.host + this._parsedURL.folderPathComponents; |
| |
| const networkManager = SDK.NetworkManager.forRequest(this); |
| const inspectedURL = networkManager ? networkManager.target().inspectedURL().asParsedURL() : null; |
| this._path = this._path.trimURL(inspectedURL ? inspectedURL.host : ''); |
| if (this._parsedURL.lastPathComponent || this._parsedURL.queryParams) { |
| this._name = |
| this._parsedURL.lastPathComponent + (this._parsedURL.queryParams ? '?' + this._parsedURL.queryParams : ''); |
| } else if (this._parsedURL.folderPathComponents) { |
| this._name = |
| this._parsedURL.folderPathComponents.substring(this._parsedURL.folderPathComponents.lastIndexOf('/') + 1) + |
| '/'; |
| this._path = this._path.substring(0, this._path.lastIndexOf('/')); |
| } else { |
| this._name = this._parsedURL.host; |
| this._path = ''; |
| } |
| } |
| } |
| |
| /** |
| * @return {string} |
| */ |
| get folder() { |
| let path = this._parsedURL.path; |
| const indexOfQuery = path.indexOf('?'); |
| if (indexOfQuery !== -1) |
| path = path.substring(0, indexOfQuery); |
| const lastSlashIndex = path.lastIndexOf('/'); |
| return lastSlashIndex !== -1 ? path.substring(0, lastSlashIndex) : ''; |
| } |
| |
| /** |
| * @return {!Common.ResourceType} |
| */ |
| resourceType() { |
| return this._resourceType; |
| } |
| |
| /** |
| * @param {!Common.ResourceType} resourceType |
| */ |
| setResourceType(resourceType) { |
| this._resourceType = resourceType; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| get domain() { |
| return this._parsedURL.host; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| get scheme() { |
| return this._parsedURL.scheme; |
| } |
| |
| /** |
| * @return {?SDK.NetworkRequest} |
| */ |
| redirectSource() { |
| return this._redirectSource; |
| } |
| |
| /** |
| * @param {?SDK.NetworkRequest} originatingRequest |
| */ |
| setRedirectSource(originatingRequest) { |
| this._redirectSource = originatingRequest; |
| } |
| |
| /** |
| * @return {?SDK.NetworkRequest} |
| */ |
| redirectDestination() { |
| return this._redirectDestination; |
| } |
| |
| /** |
| * @param {?SDK.NetworkRequest} redirectDestination |
| */ |
| setRedirectDestination(redirectDestination) { |
| this._redirectDestination = redirectDestination; |
| } |
| |
| /** |
| * @return {!Array.<!SDK.NetworkRequest.NameValue>} |
| */ |
| requestHeaders() { |
| return this._requestHeaders; |
| } |
| |
| /** |
| * @param {!Array.<!SDK.NetworkRequest.NameValue>} headers |
| */ |
| setRequestHeaders(headers) { |
| this._requestHeaders = headers; |
| delete this._requestCookies; |
| |
| this.dispatchEventToListeners(SDK.NetworkRequest.Events.RequestHeadersChanged); |
| } |
| |
| /** |
| * @return {string|undefined} |
| */ |
| requestHeadersText() { |
| return this._requestHeadersText; |
| } |
| |
| /** |
| * @param {string} text |
| */ |
| setRequestHeadersText(text) { |
| this._requestHeadersText = text; |
| |
| this.dispatchEventToListeners(SDK.NetworkRequest.Events.RequestHeadersChanged); |
| } |
| |
| /** |
| * @param {string} headerName |
| * @return {string|undefined} |
| */ |
| requestHeaderValue(headerName) { |
| if (headerName in this._requestHeaderValues) |
| return this._requestHeaderValues[headerName]; |
| this._requestHeaderValues[headerName] = this._computeHeaderValue(this.requestHeaders(), headerName); |
| return this._requestHeaderValues[headerName]; |
| } |
| |
| /** |
| * @return {!Array.<!SDK.Cookie>} |
| */ |
| get requestCookies() { |
| if (!this._requestCookies) |
| this._requestCookies = SDK.CookieParser.parseCookie(this.requestHeaderValue('Cookie')); |
| return this._requestCookies; |
| } |
| |
| /** |
| * @return {!Promise<?string>} |
| */ |
| requestFormData() { |
| if (!this._requestFormDataPromise) |
| this._requestFormDataPromise = SDK.NetworkManager.requestPostData(this); |
| return this._requestFormDataPromise; |
| } |
| |
| /** |
| * @param {boolean} hasData |
| * @param {?string} data |
| */ |
| setRequestFormData(hasData, data) { |
| this._requestFormDataPromise = (hasData && data === null) ? null : Promise.resolve(data); |
| this._formParametersPromise = null; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| _filteredProtocolName() { |
| const protocol = this.protocol.toLowerCase(); |
| if (protocol === 'h2') |
| return 'http/2.0'; |
| return protocol.replace(/^http\/2(\.0)?\+/, 'http/2.0+'); |
| } |
| |
| /** |
| * @return {string} |
| */ |
| requestHttpVersion() { |
| const headersText = this.requestHeadersText(); |
| if (!headersText) { |
| const version = this.requestHeaderValue('version') || this.requestHeaderValue(':version'); |
| if (version) |
| return version; |
| return this._filteredProtocolName(); |
| } |
| const firstLine = headersText.split(/\r\n/)[0]; |
| const match = firstLine.match(/(HTTP\/\d+\.\d+)$/); |
| return match ? match[1] : 'HTTP/0.9'; |
| } |
| |
| /** |
| * @return {!Array.<!SDK.NetworkRequest.NameValue>} |
| */ |
| get responseHeaders() { |
| return this._responseHeaders || []; |
| } |
| |
| /** |
| * @param {!Array.<!SDK.NetworkRequest.NameValue>} x |
| */ |
| set responseHeaders(x) { |
| this._responseHeaders = x; |
| delete this._sortedResponseHeaders; |
| delete this._serverTimings; |
| delete this._responseCookies; |
| this._responseHeaderValues = {}; |
| |
| this.dispatchEventToListeners(SDK.NetworkRequest.Events.ResponseHeadersChanged); |
| } |
| |
| /** |
| * @return {string} |
| */ |
| get responseHeadersText() { |
| return this._responseHeadersText; |
| } |
| |
| /** |
| * @param {string} x |
| */ |
| set responseHeadersText(x) { |
| this._responseHeadersText = x; |
| |
| this.dispatchEventToListeners(SDK.NetworkRequest.Events.ResponseHeadersChanged); |
| } |
| |
| /** |
| * @return {!Array.<!SDK.NetworkRequest.NameValue>} |
| */ |
| get sortedResponseHeaders() { |
| if (this._sortedResponseHeaders !== undefined) |
| return this._sortedResponseHeaders; |
| |
| this._sortedResponseHeaders = this.responseHeaders.slice(); |
| this._sortedResponseHeaders.sort(function(a, b) { |
| return a.name.toLowerCase().compareTo(b.name.toLowerCase()); |
| }); |
| return this._sortedResponseHeaders; |
| } |
| |
| /** |
| * @param {string} headerName |
| * @return {string|undefined} |
| */ |
| responseHeaderValue(headerName) { |
| if (headerName in this._responseHeaderValues) |
| return this._responseHeaderValues[headerName]; |
| this._responseHeaderValues[headerName] = this._computeHeaderValue(this.responseHeaders, headerName); |
| return this._responseHeaderValues[headerName]; |
| } |
| |
| /** |
| * @return {!Array.<!SDK.Cookie>} |
| */ |
| get responseCookies() { |
| if (!this._responseCookies) |
| this._responseCookies = SDK.CookieParser.parseSetCookie(this.responseHeaderValue('Set-Cookie')); |
| return this._responseCookies; |
| } |
| |
| /** |
| * @return {string|undefined} |
| */ |
| responseLastModified() { |
| return this.responseHeaderValue('last-modified'); |
| } |
| |
| /** |
| * @return {?Array.<!SDK.ServerTiming>} |
| */ |
| get serverTimings() { |
| if (typeof this._serverTimings === 'undefined') |
| this._serverTimings = SDK.ServerTiming.parseHeaders(this.responseHeaders); |
| return this._serverTimings; |
| } |
| |
| /** |
| * @return {?string} |
| */ |
| queryString() { |
| if (this._queryString !== undefined) |
| return this._queryString; |
| |
| let queryString = null; |
| const url = this.url(); |
| const questionMarkPosition = url.indexOf('?'); |
| if (questionMarkPosition !== -1) { |
| queryString = url.substring(questionMarkPosition + 1); |
| const hashSignPosition = queryString.indexOf('#'); |
| if (hashSignPosition !== -1) |
| queryString = queryString.substring(0, hashSignPosition); |
| } |
| this._queryString = queryString; |
| return this._queryString; |
| } |
| |
| /** |
| * @return {?Array.<!SDK.NetworkRequest.NameValue>} |
| */ |
| get queryParameters() { |
| if (this._parsedQueryParameters) |
| return this._parsedQueryParameters; |
| const queryString = this.queryString(); |
| if (!queryString) |
| return null; |
| this._parsedQueryParameters = this._parseParameters(queryString); |
| return this._parsedQueryParameters; |
| } |
| |
| /** |
| * @return {!Promise<?Array<!SDK.NetworkRequest.NameValue>>} |
| */ |
| async _parseFormParameters() { |
| const requestContentType = this.requestContentType(); |
| |
| if (!requestContentType) |
| return null; |
| |
| // Handling application/x-www-form-urlencoded request bodies. |
| if (requestContentType.match(/^application\/x-www-form-urlencoded\s*(;.*)?$/i)) { |
| const formData = await this.requestFormData(); |
| if (!formData) |
| return null; |
| |
| return this._parseParameters(formData); |
| } |
| |
| // Handling multipart/form-data request bodies. |
| const multipartDetails = requestContentType.match(/^multipart\/form-data\s*;\s*boundary\s*=\s*(\S+)\s*$/); |
| |
| if (!multipartDetails) |
| return null; |
| |
| const boundary = multipartDetails[1]; |
| if (!boundary) |
| return null; |
| |
| const formData = await this.requestFormData(); |
| if (!formData) |
| return null; |
| |
| return this._parseMultipartFormDataParameters(formData, boundary); |
| } |
| |
| /** |
| * @return {!Promise<?Array<!SDK.NetworkRequest.NameValue>>} |
| */ |
| formParameters() { |
| if (!this._formParametersPromise) |
| this._formParametersPromise = this._parseFormParameters(); |
| return this._formParametersPromise; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| responseHttpVersion() { |
| const headersText = this._responseHeadersText; |
| if (!headersText) { |
| const version = this.responseHeaderValue('version') || this.responseHeaderValue(':version'); |
| if (version) |
| return version; |
| return this._filteredProtocolName(); |
| } |
| const firstLine = headersText.split(/\r\n/)[0]; |
| const match = firstLine.match(/^(HTTP\/\d+\.\d+)/); |
| return match ? match[1] : 'HTTP/0.9'; |
| } |
| |
| /** |
| * @param {string} queryString |
| * @return {!Array.<!SDK.NetworkRequest.NameValue>} |
| */ |
| _parseParameters(queryString) { |
| function parseNameValue(pair) { |
| const position = pair.indexOf('='); |
| if (position === -1) |
| return {name: pair, value: ''}; |
| else |
| return {name: pair.substring(0, position), value: pair.substring(position + 1)}; |
| } |
| return queryString.split('&').map(parseNameValue); |
| } |
| |
| /** |
| * Parses multipart/form-data; boundary=boundaryString request bodies - |
| * --boundaryString |
| * Content-Disposition: form-data; name="field-name"; filename="r.gif" |
| * Content-Type: application/octet-stream |
| * |
| * optionalValue |
| * --boundaryString |
| * Content-Disposition: form-data; name="field-name-2" |
| * |
| * optionalValue2 |
| * --boundaryString-- |
| * |
| * @param {string} data |
| * @param {string} boundary |
| * @return {!Array.<!SDK.NetworkRequest.NameValue>} |
| */ |
| _parseMultipartFormDataParameters(data, boundary) { |
| const sanitizedBoundary = boundary.escapeForRegExp(); |
| const keyValuePattern = new RegExp( |
| // Header with an optional file name. |
| '^\\r\\ncontent-disposition\\s*:\\s*form-data\\s*;\\s*name="([^"]*)"(?:\\s*;\\s*filename="([^"]*)")?' + |
| // Optional secondary header with the content type. |
| '(?:\\r\\ncontent-type\\s*:\\s*([^\\r\\n]*))?' + |
| // Padding. |
| '\\r\\n\\r\\n' + |
| // Value |
| '(.*)' + |
| // Padding. |
| '\\r\\n$', |
| 'is'); |
| const fields = data.split(new RegExp(`--${sanitizedBoundary}(?:--\s*$)?`, 'g')); |
| return fields.reduce(parseMultipartField, []); |
| |
| /** |
| * @param {!Array.<!SDK.NetworkRequest.NameValue>} result |
| * @param {string} field |
| * @return {!Array.<!SDK.NetworkRequest.NameValue>} |
| */ |
| function parseMultipartField(result, field) { |
| const [match, name, filename, contentType, value] = field.match(keyValuePattern) || []; |
| |
| if (!match) |
| return result; |
| |
| const processedValue = (filename || contentType) ? ls`(binary)` : value; |
| result.push({name, value: processedValue}); |
| |
| return result; |
| } |
| } |
| |
| /** |
| * @param {!Array.<!SDK.NetworkRequest.NameValue>} headers |
| * @param {string} headerName |
| * @return {string|undefined} |
| */ |
| _computeHeaderValue(headers, headerName) { |
| headerName = headerName.toLowerCase(); |
| |
| const values = []; |
| for (let i = 0; i < headers.length; ++i) { |
| if (headers[i].name.toLowerCase() === headerName) |
| values.push(headers[i].value); |
| } |
| if (!values.length) |
| return undefined; |
| // Set-Cookie values should be separated by '\n', not comma, otherwise cookies could not be parsed. |
| if (headerName === 'set-cookie') |
| return values.join('\n'); |
| return values.join(', '); |
| } |
| |
| /** |
| * @return {!Promise<!SDK.NetworkRequest.ContentData>} |
| */ |
| contentData() { |
| if (this._contentData) |
| return this._contentData; |
| if (this._contentDataProvider) |
| this._contentData = this._contentDataProvider(); |
| else |
| this._contentData = SDK.NetworkManager.requestContentData(this); |
| return this._contentData; |
| } |
| |
| /** |
| * @param {function():!Promise<!SDK.NetworkRequest.ContentData>} dataProvider |
| */ |
| setContentDataProvider(dataProvider) { |
| console.assert(!this._contentData, 'contentData can only be set once.'); |
| this._contentDataProvider = dataProvider; |
| } |
| |
| /** |
| * @override |
| * @return {string} |
| */ |
| contentURL() { |
| return this._url; |
| } |
| |
| /** |
| * @override |
| * @return {!Common.ResourceType} |
| */ |
| contentType() { |
| return this._resourceType; |
| } |
| |
| /** |
| * @override |
| * @return {!Promise<boolean>} |
| */ |
| async contentEncoded() { |
| return (await this.contentData()).encoded; |
| } |
| |
| /** |
| * @override |
| * @return {!Promise<?string>} |
| */ |
| async requestContent() { |
| return (await this.contentData()).content; |
| } |
| |
| /** |
| * @override |
| * @param {string} query |
| * @param {boolean} caseSensitive |
| * @param {boolean} isRegex |
| * @return {!Promise<!Array<!Common.ContentProvider.SearchMatch>>} |
| */ |
| async searchInContent(query, caseSensitive, isRegex) { |
| if (!this._contentDataProvider) |
| return SDK.NetworkManager.searchInRequest(this, query, caseSensitive, isRegex); |
| |
| const content = await this.requestContent(); |
| if (!content) |
| return []; |
| return Common.ContentProvider.performSearchInContent(content, query, caseSensitive, isRegex); |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| isHttpFamily() { |
| return !!this.url().match(/^https?:/i); |
| } |
| |
| /** |
| * @return {string|undefined} |
| */ |
| requestContentType() { |
| return this.requestHeaderValue('Content-Type'); |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| hasErrorStatusCode() { |
| return this.statusCode >= 400; |
| } |
| |
| /** |
| * @param {!Protocol.Network.ResourcePriority} priority |
| */ |
| setInitialPriority(priority) { |
| this._initialPriority = priority; |
| } |
| |
| /** |
| * @return {?Protocol.Network.ResourcePriority} |
| */ |
| initialPriority() { |
| return this._initialPriority; |
| } |
| |
| /** |
| * @param {!Protocol.Network.ResourcePriority} priority |
| */ |
| setPriority(priority) { |
| this._currentPriority = priority; |
| } |
| |
| /** |
| * @return {?Protocol.Network.ResourcePriority} |
| */ |
| priority() { |
| return this._currentPriority || this._initialPriority || null; |
| } |
| |
| /** |
| * @param {!Protocol.Network.SignedExchangeInfo} info |
| */ |
| setSignedExchangeInfo(info) { |
| this._signedExchangeInfo = info; |
| } |
| |
| /** |
| * @return {?Protocol.Network.SignedExchangeInfo} |
| */ |
| signedExchangeInfo() { |
| return this._signedExchangeInfo; |
| } |
| |
| /** |
| * @param {!Element} image |
| */ |
| populateImageSource(image) { |
| /** |
| * @param {?string} content |
| * @this {SDK.NetworkRequest} |
| */ |
| function onResourceContent(content) { |
| let imageSrc = Common.ContentProvider.contentAsDataURL(content, this._mimeType, true); |
| const cacheControl = this.responseHeaderValue('cache-control'); |
| if (imageSrc === null && (!cacheControl || !cacheControl.includes('no-cache'))) |
| imageSrc = this._url; |
| image.src = imageSrc; |
| } |
| this.requestContent().then(onResourceContent.bind(this)); |
| } |
| |
| /** |
| * @return {?Protocol.Network.Initiator} |
| */ |
| initiator() { |
| return this._initiator; |
| } |
| |
| /** |
| * @return {!Array.<!SDK.NetworkRequest.WebSocketFrame>} |
| */ |
| frames() { |
| return this._frames; |
| } |
| |
| /** |
| * @param {string} errorMessage |
| * @param {number} time |
| */ |
| addFrameError(errorMessage, time) { |
| this._addFrame({ |
| type: SDK.NetworkRequest.WebSocketFrameType.Error, |
| text: errorMessage, |
| time: this.pseudoWallTime(time), |
| opCode: -1, |
| mask: false |
| }); |
| } |
| |
| /** |
| * @param {!Protocol.Network.WebSocketFrame} response |
| * @param {number} time |
| * @param {boolean} sent |
| */ |
| addFrame(response, time, sent) { |
| const type = sent ? SDK.NetworkRequest.WebSocketFrameType.Send : SDK.NetworkRequest.WebSocketFrameType.Receive; |
| this._addFrame({ |
| type: type, |
| text: response.payloadData, |
| time: this.pseudoWallTime(time), |
| opCode: response.opcode, |
| mask: response.mask |
| }); |
| } |
| |
| /** |
| * @param {!SDK.NetworkRequest.WebSocketFrame} frame |
| */ |
| _addFrame(frame) { |
| this._frames.push(frame); |
| this.dispatchEventToListeners(SDK.NetworkRequest.Events.WebsocketFrameAdded, frame); |
| } |
| |
| /** |
| * @return {!Array.<!SDK.NetworkRequest.EventSourceMessage>} |
| */ |
| eventSourceMessages() { |
| return this._eventSourceMessages; |
| } |
| |
| /** |
| * @param {number} time |
| * @param {string} eventName |
| * @param {string} eventId |
| * @param {string} data |
| */ |
| addEventSourceMessage(time, eventName, eventId, data) { |
| const message = {time: this.pseudoWallTime(time), eventName: eventName, eventId: eventId, data: data}; |
| this._eventSourceMessages.push(message); |
| this.dispatchEventToListeners(SDK.NetworkRequest.Events.EventSourceMessageAdded, message); |
| } |
| |
| /** |
| * @param {number} redirectCount |
| */ |
| markAsRedirect(redirectCount) { |
| this._requestId = `${this._backendRequestId}:redirected.${redirectCount}`; |
| } |
| |
| /** |
| * @param {string} requestId |
| */ |
| setRequestIdForTest(requestId) { |
| this._backendRequestId = requestId; |
| this._requestId = requestId; |
| } |
| }; |
| |
| /** @enum {symbol} */ |
| SDK.NetworkRequest.Events = { |
| FinishedLoading: Symbol('FinishedLoading'), |
| TimingChanged: Symbol('TimingChanged'), |
| RemoteAddressChanged: Symbol('RemoteAddressChanged'), |
| RequestHeadersChanged: Symbol('RequestHeadersChanged'), |
| ResponseHeadersChanged: Symbol('ResponseHeadersChanged'), |
| WebsocketFrameAdded: Symbol('WebsocketFrameAdded'), |
| EventSourceMessageAdded: Symbol('EventSourceMessageAdded') |
| }; |
| |
| /** @enum {string} */ |
| SDK.NetworkRequest.InitiatorType = { |
| Other: 'other', |
| Parser: 'parser', |
| Redirect: 'redirect', |
| Script: 'script', |
| Preload: 'preload', |
| SignedExchange: 'signedExchange' |
| }; |
| |
| /** @typedef {!{name: string, value: string}} */ |
| SDK.NetworkRequest.NameValue; |
| |
| /** @enum {string} */ |
| SDK.NetworkRequest.WebSocketFrameType = { |
| Send: 'send', |
| Receive: 'receive', |
| Error: 'error' |
| }; |
| |
| /** @typedef {!{type: SDK.NetworkRequest.WebSocketFrameType, time: number, text: string, opCode: number, mask: boolean}} */ |
| SDK.NetworkRequest.WebSocketFrame; |
| |
| /** @typedef {!{time: number, eventName: string, eventId: string, data: string}} */ |
| SDK.NetworkRequest.EventSourceMessage; |
| |
| /** @typedef {!{error: ?string, content: ?string, encoded: boolean}} */ |
| SDK.NetworkRequest.ContentData; |