UNPKG

debug-server-next

Version:

Dev server for hippy-core.

1,255 lines 55 kB
/* * 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. */ import * as TextUtils from '../../models/text_utils/text_utils.js'; import * as Common from '../common/common.js'; import * as i18n from '../i18n/i18n.js'; import * as Platform from '../platform/platform.js'; import { Attributes } from './Cookie.js'; import { CookieParser } from './CookieParser.js'; import { NetworkManager } from './NetworkManager.js'; import { Type } from './Target.js'; import { ServerTiming } from './ServerTiming.js'; // clang-format off const UIStrings = { /** *@description Text in Network Request */ binary: '(binary)', /** *@description Tooltip to explain why a cookie was blocked */ secureOnly: 'This cookie was blocked because it had the "`Secure`" attribute and the connection was not secure.', /** *@description Tooltip to explain why a cookie was blocked */ notOnPath: 'This cookie was blocked because its path was not an exact match for or a superdirectory of the request url\'s path.', /** *@description Tooltip to explain why a cookie was blocked */ domainMismatch: 'This cookie was blocked because neither did the request URL\'s domain exactly match the cookie\'s domain, nor was the request URL\'s domain a subdomain of the cookie\'s Domain attribute value.', /** *@description Tooltip to explain why a cookie was blocked */ sameSiteStrict: 'This cookie was blocked because it had the "`SameSite=Strict`" attribute and the request was made from a different site. This includes top-level navigation requests initiated by other sites.', /** *@description Tooltip to explain why a cookie was blocked */ sameSiteLax: 'This cookie was blocked because it had the "`SameSite=Lax`" attribute and the request was made from a different site and was not initiated by a top-level navigation.', /** *@description Tooltip to explain why a cookie was blocked */ sameSiteUnspecifiedTreatedAsLax: 'This cookie didn\'t specify a "`SameSite`" attribute when it was stored and was defaulted to "SameSite=Lax," and was blocked because the request was made from a different site and was not initiated by a top-level navigation. The cookie had to have been set with "`SameSite=None`" to enable cross-site usage.', /** *@description Tooltip to explain why a cookie was blocked */ sameSiteNoneInsecure: 'This cookie was blocked because it had the "`SameSite=None`" attribute but was not marked "Secure". Cookies without SameSite restrictions must be marked "Secure" and sent over a secure connection.', /** *@description Tooltip to explain why a cookie was blocked */ userPreferences: 'This cookie was blocked due to user preferences.', /** *@description Tooltip to explain why a cookie was blocked */ unknownError: 'An unknown error was encountered when trying to send this cookie.', /** *@description Tooltip to explain why a cookie was blocked due to Schemeful Same-Site */ schemefulSameSiteStrict: 'This cookie was blocked because it had the "`SameSite=Strict`" attribute but the request was cross-site. This includes top-level navigation requests initiated by other sites. This request is considered cross-site because the URL has a different scheme than the current site.', /** *@description Tooltip to explain why a cookie was blocked due to Schemeful Same-Site */ schemefulSameSiteLax: 'This cookie was blocked because it had the "`SameSite=Lax`" attribute but the request was cross-site and was not initiated by a top-level navigation. This request is considered cross-site because the URL has a different scheme than the current site.', /** *@description Tooltip to explain why a cookie was blocked due to Schemeful Same-Site */ schemefulSameSiteUnspecifiedTreatedAsLax: 'This cookie didn\'t specify a "`SameSite`" attribute when it was stored, was defaulted to "`SameSite=Lax"`, and was blocked because the request was cross-site and was not initiated by a top-level navigation. This request is considered cross-site because the URL has a different scheme than the current site.', /** *@description Tooltip to explain why a cookie was blocked due to SameParty */ samePartyFromCrossPartyContext: 'This cookie was blocked because it had the "`SameParty`" attribute but the request was cross-party. The request was considered cross-party because the domain of the resource\'s URL and the domains of the resource\'s enclosing frames/documents are neither owners nor members in the same First-Party Set.', /** *@description Tooltip to explain why an attempt to set a cookie via `Set-Cookie` HTTP header on a request's response was blocked. */ thisSetcookieWasBlockedDueToUser: 'This attempt to set a cookie via a `Set-Cookie` header was blocked due to user preferences.', /** *@description Tooltip to explain why an attempt to set a cookie via `Set-Cookie` HTTP header on a request's response was blocked. */ thisSetcookieHadInvalidSyntax: 'This `Set-Cookie` header had invalid syntax.', /** *@description Tooltip to explain why a cookie was blocked */ theSchemeOfThisConnectionIsNot: 'The scheme of this connection is not allowed to store cookies.', /** *@description Tooltip to explain why a cookie was blocked */ anUnknownErrorWasEncounteredWhenTrying: 'An unknown error was encountered when trying to store this cookie.', /** *@description Tooltip to explain why a cookie was blocked due to Schemeful Same-Site *@example {SameSite=Strict} PH1 */ thisSetcookieWasBlockedBecauseItHadTheSamesiteStrictLax: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because it had the "{PH1}" attribute but came from a cross-site response which was not the response to a top-level navigation. This response is considered cross-site because the URL has a different scheme than the current site.', /** *@description Tooltip to explain why a cookie was blocked due to Schemeful Same-Site */ thisSetcookieDidntSpecifyASamesite: 'This `Set-Cookie` header didn\'t specify a "`SameSite`" attribute, was defaulted to "`SameSite=Lax"`, and was blocked because it came from a cross-site response which was not the response to a top-level navigation. This response is considered cross-site because the URL has a different scheme than the current site.', /** *@description Tooltip to explain why a cookie was blocked due to SameParty */ thisSetcookieWasBlockedBecauseItHadTheSameparty: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because it had the "`SameParty`" attribute but the request was cross-party. The request was considered cross-party because the domain of the resource\'s URL and the domains of the resource\'s enclosing frames/documents are neither owners nor members in the same First-Party Set.', /** *@description Tooltip to explain why a cookie was blocked due to SameParty */ thisSetcookieWasBlockedBecauseItHadTheSamepartyAttribute: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because it had the "`SameParty`" attribute but also had other conflicting attributes. Chrome requires cookies that use the "`SameParty`" attribute to also have the "Secure" attribute, and to not be restricted to "`SameSite=Strict`".', /** *@description Tooltip to explain why an attempt to set a cookie via a `Set-Cookie` HTTP header on a request's response was blocked. */ blockedReasonSecureOnly: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because it had the "Secure" attribute but was not received over a secure connection.', /** *@description Tooltip to explain why an attempt to set a cookie via a `Set-Cookie` HTTP header on a request's response was blocked. *@example {SameSite=Strict} PH1 */ blockedReasonSameSiteStrictLax: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because it had the "{PH1}" attribute but came from a cross-site response which was not the response to a top-level navigation.', /** *@description Tooltip to explain why an attempt to set a cookie via a `Set-Cookie` HTTP header on a request's response was blocked. */ blockedReasonSameSiteUnspecifiedTreatedAsLax: 'This `Set-Cookie` header didn\'t specify a "`SameSite`" attribute and was defaulted to "`SameSite=Lax,`" and was blocked because it came from a cross-site response which was not the response to a top-level navigation. The `Set-Cookie` had to have been set with "`SameSite=None`" to enable cross-site usage.', /** *@description Tooltip to explain why an attempt to set a cookie via a `Set-Cookie` HTTP header on a request's response was blocked. */ blockedReasonSameSiteNoneInsecure: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because it had the "`SameSite=None`" attribute but did not have the "Secure" attribute, which is required in order to use "`SameSite=None`".', /** *@description Tooltip to explain why an attempt to set a cookie via a `Set-Cookie` HTTP header on a request's response was blocked. */ blockedReasonOverwriteSecure: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because it was not sent over a secure connection and would have overwritten a cookie with the Secure attribute.', /** *@description Tooltip to explain why an attempt to set a cookie via a `Set-Cookie` HTTP header on a request's response was blocked. */ blockedReasonInvalidDomain: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because its Domain attribute was invalid with regards to the current host url.', /** *@description Tooltip to explain why an attempt to set a cookie via a `Set-Cookie` HTTP header on a request's response was blocked. */ blockedReasonInvalidPrefix: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because it used the "`__Secure-`" or "`__Host-`" prefix in its name and broke the additional rules applied to cookies with these prefixes as defined in `https://tools.ietf.org/html/draft-west-cookie-prefixes-05`.', }; // clang-format on const str_ = i18n.i18n.registerUIStrings('core/sdk/NetworkRequest.ts', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); // TODO(crbug.com/1167717): Make this a const enum again // eslint-disable-next-line rulesdir/const_enum, @typescript-eslint/naming-convention export var MIME_TYPE; (function (MIME_TYPE) { MIME_TYPE["HTML"] = "text/html"; MIME_TYPE["XML"] = "text/xml"; MIME_TYPE["PLAIN"] = "text/plain"; MIME_TYPE["XHTML"] = "application/xhtml+xml"; MIME_TYPE["SVG"] = "image/svg+xml"; MIME_TYPE["CSS"] = "text/css"; MIME_TYPE["XSL"] = "text/xsl"; MIME_TYPE["VTT"] = "text/vtt"; MIME_TYPE["PDF"] = "application/pdf"; MIME_TYPE["EVENTSTREAM"] = "text/event-stream"; })(MIME_TYPE || (MIME_TYPE = {})); export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper { _requestId; _backendRequestId; _documentURL; _frameId; _loaderId; _initiator; _redirectSource; _preflightRequest; _preflightInitiatorRequest; _isRedirect; _redirectDestination; _issueTime; _startTime; _endTime; _blockedReason; _corsErrorStatus; statusCode; statusText; requestMethod; requestTime; protocol; mixedContentType; _initialPriority; _currentPriority; _signedExchangeInfo; _webBundleInfo; _webBundleInnerRequestInfo; _resourceType; _contentData; _frames; _eventSourceMessages; _responseHeaderValues; _responseHeadersText; _requestHeaders; _requestHeaderValues; _remoteAddress; _remoteAddressSpace; _referrerPolicy; _securityState; _securityDetails; connectionId; connectionReused; hasNetworkData; _formParametersPromise; _requestFormDataPromise; _hasExtraRequestInfo; _hasExtraResponseInfo; _blockedRequestCookies; _includedRequestCookies; _blockedResponseCookies; localizedFailDescription; _url; _responseReceivedTime; _transferSize; _finished; _failed; _canceled; _preserved; _mimeType; _parsedURL; _name; _path; _clientSecurityState; _trustTokenParams; _trustTokenOperationDoneEvent; _responseCacheStorageCacheName; _serviceWorkerResponseSource; _wallIssueTime; _responseRetrievalTime; _resourceSize; _fromMemoryCache; _fromDiskCache; _fromPrefetchCache; _fetchedViaServiceWorker; _timing; _requestHeadersText; _responseHeaders; _sortedResponseHeaders; _responseCookies; _serverTimings; _queryString; _parsedQueryParameters; _contentDataProvider; _isSameSite; constructor(requestId, backendRequestId, url, documentURL, frameId, loaderId, initiator) { super(); this._requestId = requestId; this._backendRequestId = backendRequestId; this.setUrl(url); this._documentURL = documentURL; this._frameId = frameId; this._loaderId = loaderId; this._initiator = initiator; this._redirectSource = null; this._preflightRequest = null; this._preflightInitiatorRequest = null; this._isRedirect = false; this._redirectDestination = null; this._issueTime = -1; this._startTime = -1; this._endTime = -1; this._blockedReason = undefined; this._corsErrorStatus = undefined; this.statusCode = 0; this.statusText = ''; this.requestMethod = ''; this.requestTime = 0; this.protocol = ''; this.mixedContentType = "none" /* None */; this._initialPriority = null; this._currentPriority = null; this._signedExchangeInfo = null; this._webBundleInfo = null; this._webBundleInnerRequestInfo = null; this._resourceType = Common.ResourceType.resourceTypes.Other; this._contentData = null; this._frames = []; this._eventSourceMessages = []; this._responseHeaderValues = {}; this._responseHeadersText = ''; this._requestHeaders = []; this._requestHeaderValues = {}; this._remoteAddress = ''; this._remoteAddressSpace = "Unknown" /* Unknown */; this._referrerPolicy = null; this._securityState = "unknown" /* Unknown */; this._securityDetails = null; this.connectionId = '0'; this.connectionReused = false; this.hasNetworkData = false; this._formParametersPromise = null; this._requestFormDataPromise = Promise.resolve(null); this._hasExtraRequestInfo = false; this._hasExtraResponseInfo = false; this._blockedRequestCookies = []; this._includedRequestCookies = []; this._blockedResponseCookies = []; this.localizedFailDescription = null; this._isSameSite = null; } static create(backendRequestId, url, documentURL, frameId, loaderId, initiator) { return new NetworkRequest(backendRequestId, backendRequestId, url, documentURL, frameId, loaderId, initiator); } static createForWebSocket(backendRequestId, requestURL, initiator) { return new NetworkRequest(backendRequestId, backendRequestId, requestURL, '', '', '', initiator || null); } static createWithoutBackendRequest(requestId, url, documentURL, initiator) { return new NetworkRequest(requestId, undefined, url, documentURL, '', '', initiator); } identityCompare(other) { const thisId = this.requestId(); const thatId = other.requestId(); if (thisId > thatId) { return 1; } if (thisId < thatId) { return -1; } return 0; } requestId() { return this._requestId; } backendRequestId() { return this._backendRequestId; } url() { return this._url; } isBlobRequest() { return this._url.startsWith('blob:'); } setUrl(x) { if (this._url === x) { return; } this._url = x; this._parsedURL = new Common.ParsedURL.ParsedURL(x); delete this._queryString; delete this._parsedQueryParameters; delete this._name; delete this._path; } get documentURL() { return this._documentURL; } get parsedURL() { return this._parsedURL; } get frameId() { return this._frameId; } get loaderId() { return this._loaderId; } setRemoteAddress(ip, port) { this._remoteAddress = ip + ':' + port; this.dispatchEventToListeners(Events.RemoteAddressChanged, this); } remoteAddress() { return this._remoteAddress; } remoteAddressSpace() { return this._remoteAddressSpace; } /** * The cache name of the CacheStorage from where the response is served via * the ServiceWorker. */ getResponseCacheStorageCacheName() { return this._responseCacheStorageCacheName; } setResponseCacheStorageCacheName(x) { this._responseCacheStorageCacheName = x; } serviceWorkerResponseSource() { return this._serviceWorkerResponseSource; } setServiceWorkerResponseSource(serviceWorkerResponseSource) { this._serviceWorkerResponseSource = serviceWorkerResponseSource; } setReferrerPolicy(referrerPolicy) { this._referrerPolicy = referrerPolicy; } referrerPolicy() { return this._referrerPolicy; } securityState() { return this._securityState; } setSecurityState(securityState) { this._securityState = securityState; } securityDetails() { return this._securityDetails; } securityOrigin() { return this._parsedURL.securityOrigin(); } setSecurityDetails(securityDetails) { this._securityDetails = securityDetails; } get startTime() { return this._startTime || -1; } setIssueTime(monotonicTime, wallTime) { this._issueTime = monotonicTime; this._wallIssueTime = wallTime; this._startTime = monotonicTime; } issueTime() { return this._issueTime; } pseudoWallTime(monotonicTime) { return this._wallIssueTime ? this._wallIssueTime - this._issueTime + monotonicTime : monotonicTime; } get responseReceivedTime() { return this._responseReceivedTime || -1; } set responseReceivedTime(x) { this._responseReceivedTime = x; } /** * The time at which the returned response was generated. For cached * responses, this is the last time the cache entry was validated. */ getResponseRetrievalTime() { return this._responseRetrievalTime; } setResponseRetrievalTime(x) { this._responseRetrievalTime = x; } get endTime() { return this._endTime || -1; } 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(Events.TimingChanged, this); } get duration() { if (this._endTime === -1 || this._startTime === -1) { return -1; } return this._endTime - this._startTime; } get latency() { if (this._responseReceivedTime === -1 || this._startTime === -1) { return -1; } return this._responseReceivedTime - this._startTime; } get resourceSize() { return this._resourceSize || 0; } set resourceSize(x) { this._resourceSize = x; } get transferSize() { return this._transferSize || 0; } increaseTransferSize(x) { this._transferSize = (this._transferSize || 0) + x; } setTransferSize(x) { this._transferSize = x; } get finished() { return this._finished; } set finished(x) { if (this._finished === x) { return; } this._finished = x; if (x) { this.dispatchEventToListeners(Events.FinishedLoading, this); } } get failed() { return this._failed; } set failed(x) { this._failed = x; } get canceled() { return this._canceled; } set canceled(x) { this._canceled = x; } get preserved() { return this._preserved; } set preserved(x) { this._preserved = x; } blockedReason() { return this._blockedReason; } setBlockedReason(reason) { this._blockedReason = reason; } corsErrorStatus() { return this._corsErrorStatus; } setCorsErrorStatus(corsErrorStatus) { this._corsErrorStatus = corsErrorStatus; } wasBlocked() { return Boolean(this._blockedReason); } cached() { return (Boolean(this._fromMemoryCache) || Boolean(this._fromDiskCache)) && !this._transferSize; } cachedInMemory() { return Boolean(this._fromMemoryCache) && !this._transferSize; } fromPrefetchCache() { return Boolean(this._fromPrefetchCache); } setFromMemoryCache() { this._fromMemoryCache = true; delete this._timing; } setFromDiskCache() { this._fromDiskCache = true; } setFromPrefetchCache() { this._fromPrefetchCache = true; } /** * Returns true if the request was intercepted by a service worker and it * provided its own response. */ get fetchedViaServiceWorker() { return Boolean(this._fetchedViaServiceWorker); } set fetchedViaServiceWorker(x) { this._fetchedViaServiceWorker = x; } /** * Returns true if the request was sent by a service worker. */ initiatedByServiceWorker() { const networkManager = NetworkManager.forRequest(this); if (!networkManager) { return false; } return networkManager.target().type() === Type.ServiceWorker; } get timing() { return this._timing; } 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(Events.TimingChanged, this); } get mimeType() { return this._mimeType; } set mimeType(x) { this._mimeType = x; } get displayName() { return this._parsedURL.displayName; } name() { if (this._name) { return this._name; } this._parseNameAndPathFromURL(); return this._name; } 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.isBlobURL()) { this._name = this._parsedURL.url; 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 = NetworkManager.forRequest(this); const inspectedURL = networkManager ? Common.ParsedURL.ParsedURL.fromString(networkManager.target().inspectedURL()) : null; this._path = Platform.StringUtilities.trimURL(this._path, 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 = ''; } } } 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) : ''; } get pathname() { return this._parsedURL.path; } resourceType() { return this._resourceType; } setResourceType(resourceType) { this._resourceType = resourceType; } get domain() { return this._parsedURL.host; } get scheme() { return this._parsedURL.scheme; } redirectSource() { return this._redirectSource; } setRedirectSource(originatingRequest) { this._redirectSource = originatingRequest; } preflightRequest() { return this._preflightRequest; } setPreflightRequest(preflightRequest) { this._preflightRequest = preflightRequest; } preflightInitiatorRequest() { return this._preflightInitiatorRequest; } setPreflightInitiatorRequest(preflightInitiatorRequest) { this._preflightInitiatorRequest = preflightInitiatorRequest; } isPreflightRequest() { return this._initiator !== null && this._initiator !== undefined && this._initiator.type === "preflight" /* Preflight */; } redirectDestination() { return this._redirectDestination; } setRedirectDestination(redirectDestination) { this._redirectDestination = redirectDestination; } requestHeaders() { return this._requestHeaders; } setRequestHeaders(headers) { this._requestHeaders = headers; this.dispatchEventToListeners(Events.RequestHeadersChanged); } requestHeadersText() { return this._requestHeadersText; } setRequestHeadersText(text) { this._requestHeadersText = text; this.dispatchEventToListeners(Events.RequestHeadersChanged); } requestHeaderValue(headerName) { if (this._requestHeaderValues[headerName]) { return this._requestHeaderValues[headerName]; } this._requestHeaderValues[headerName] = this._computeHeaderValue(this.requestHeaders(), headerName); return this._requestHeaderValues[headerName]; } requestFormData() { if (!this._requestFormDataPromise) { this._requestFormDataPromise = NetworkManager.requestPostData(this); } return this._requestFormDataPromise; } setRequestFormData(hasData, data) { this._requestFormDataPromise = (hasData && data === null) ? null : Promise.resolve(data); this._formParametersPromise = null; } _filteredProtocolName() { const protocol = this.protocol.toLowerCase(); if (protocol === 'h2') { return 'http/2.0'; } return protocol.replace(/^http\/2(\.0)?\+/, 'http/2.0+'); } 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'; } get responseHeaders() { return this._responseHeaders || []; } set responseHeaders(x) { this._responseHeaders = x; delete this._sortedResponseHeaders; delete this._serverTimings; delete this._responseCookies; this._responseHeaderValues = {}; this.dispatchEventToListeners(Events.ResponseHeadersChanged); } get responseHeadersText() { return this._responseHeadersText; } set responseHeadersText(x) { this._responseHeadersText = x; this.dispatchEventToListeners(Events.ResponseHeadersChanged); } get sortedResponseHeaders() { if (this._sortedResponseHeaders !== undefined) { return this._sortedResponseHeaders; } this._sortedResponseHeaders = this.responseHeaders.slice(); this._sortedResponseHeaders.sort(function (a, b) { return Platform.StringUtilities.compare(a.name.toLowerCase(), b.name.toLowerCase()); }); return this._sortedResponseHeaders; } responseHeaderValue(headerName) { if (headerName in this._responseHeaderValues) { return this._responseHeaderValues[headerName]; } this._responseHeaderValues[headerName] = this._computeHeaderValue(this.responseHeaders, headerName); return this._responseHeaderValues[headerName]; } get responseCookies() { if (!this._responseCookies) { this._responseCookies = CookieParser.parseSetCookie(this.responseHeaderValue('Set-Cookie'), this.domain) || []; } return this._responseCookies; } responseLastModified() { return this.responseHeaderValue('last-modified'); } allCookiesIncludingBlockedOnes() { return [ ...this.includedRequestCookies(), ...this.responseCookies, ...this.blockedRequestCookies().map(blockedRequestCookie => blockedRequestCookie.cookie), ...this.blockedResponseCookies().map(blockedResponseCookie => blockedResponseCookie.cookie), ].filter(v => Boolean(v)); } get serverTimings() { if (typeof this._serverTimings === 'undefined') { this._serverTimings = ServerTiming.parseHeaders(this.responseHeaders); } return this._serverTimings; } 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; } get queryParameters() { if (this._parsedQueryParameters) { return this._parsedQueryParameters; } const queryString = this.queryString(); if (!queryString) { return null; } this._parsedQueryParameters = this._parseParameters(queryString); return this._parsedQueryParameters; } 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); } formParameters() { if (!this._formParametersPromise) { this._formParametersPromise = this._parseFormParameters(); } return this._formParametersPromise; } 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'; } _parseParameters(queryString) { function parseNameValue(pair) { const position = pair.indexOf('='); if (position === -1) { return { name: pair, value: '' }; } 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-- */ _parseMultipartFormDataParameters(data, boundary) { const sanitizedBoundary = Platform.StringUtilities.escapeForRegExp(boundary); 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, []); function parseMultipartField(result, field) { const [match, name, filename, contentType, value] = field.match(keyValuePattern) || []; if (!match) { return result; } const processedValue = (filename || contentType) ? i18nString(UIStrings.binary) : value; result.push({ name, value: processedValue }); return result; } } _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(', '); } contentData() { if (this._contentData) { return this._contentData; } if (this._contentDataProvider) { this._contentData = this._contentDataProvider(); } else { this._contentData = NetworkManager.requestContentData(this); } return this._contentData; } setContentDataProvider(dataProvider) { console.assert(!this._contentData, 'contentData can only be set once.'); this._contentDataProvider = dataProvider; } contentURL() { return this._url; } contentType() { return this._resourceType; } async contentEncoded() { return (await this.contentData()).encoded; } async requestContent() { const { content, error, encoded } = await this.contentData(); return { content, error, isEncoded: encoded, }; } async searchInContent(query, caseSensitive, isRegex) { if (!this._contentDataProvider) { return NetworkManager.searchInRequest(this, query, caseSensitive, isRegex); } const contentData = await this.contentData(); let content = contentData.content; if (!content) { return []; } if (contentData.encoded) { content = window.atob(content); } return TextUtils.TextUtils.performSearchInContent(content, query, caseSensitive, isRegex); } isHttpFamily() { return Boolean(this.url().match(/^https?:/i)); } requestContentType() { return this.requestHeaderValue('Content-Type'); } hasErrorStatusCode() { return this.statusCode >= 400; } setInitialPriority(priority) { this._initialPriority = priority; } initialPriority() { return this._initialPriority; } setPriority(priority) { this._currentPriority = priority; } priority() { return this._currentPriority || this._initialPriority || null; } setSignedExchangeInfo(info) { this._signedExchangeInfo = info; } signedExchangeInfo() { return this._signedExchangeInfo; } setWebBundleInfo(info) { this._webBundleInfo = info; } webBundleInfo() { return this._webBundleInfo; } setWebBundleInnerRequestInfo(info) { this._webBundleInnerRequestInfo = info; } webBundleInnerRequestInfo() { return this._webBundleInnerRequestInfo; } async populateImageSource(image) { const { content, encoded } = await this.contentData(); let imageSrc = TextUtils.ContentProvider.contentAsDataURL(content, this._mimeType, encoded); if (imageSrc === null && !this._failed) { const cacheControl = this.responseHeaderValue('cache-control') || ''; if (!cacheControl.includes('no-cache')) { imageSrc = this._url; } } if (imageSrc !== null) { image.src = imageSrc; } } initiator() { return this._initiator || null; } frames() { return this._frames; } addProtocolFrameError(errorMessage, time) { this.addFrame({ type: WebSocketFrameType.Error, text: errorMessage, time: this.pseudoWallTime(time), opCode: -1, mask: false }); } addProtocolFrame(response, time, sent) { const type = sent ? WebSocketFrameType.Send : WebSocketFrameType.Receive; this.addFrame({ type: type, text: response.payloadData, time: this.pseudoWallTime(time), opCode: response.opcode, mask: response.mask, }); } addFrame(frame) { this._frames.push(frame); this.dispatchEventToListeners(Events.WebsocketFrameAdded, frame); } eventSourceMessages() { return this._eventSourceMessages; } addEventSourceMessage(time, eventName, eventId, data) { const message = { time: this.pseudoWallTime(time), eventName: eventName, eventId: eventId, data: data }; this._eventSourceMessages.push(message); this.dispatchEventToListeners(Events.EventSourceMessageAdded, message); } markAsRedirect(redirectCount) { this._isRedirect = true; this._requestId = `${this._backendRequestId}:redirected.${redirectCount}`; } isRedirect() { return this._isRedirect; } setRequestIdForTest(requestId) { this._backendRequestId = requestId; this._requestId = requestId; } charset() { const contentTypeHeader = this.responseHeaderValue('content-type'); if (!contentTypeHeader) { return null; } const responseCharsets = contentTypeHeader.replace(/ /g, '') .split(';') .filter(parameter => parameter.toLowerCase().startsWith('charset=')) .map(parameter => parameter.slice('charset='.length)); if (responseCharsets.length) { return responseCharsets[0]; } return null; } addExtraRequestInfo(extraRequestInfo) { this._blockedRequestCookies = extraRequestInfo.blockedRequestCookies; this._includedRequestCookies = extraRequestInfo.includedRequestCookies; this.setRequestHeaders(extraRequestInfo.requestHeaders); this._hasExtraRequestInfo = true; this.setRequestHeadersText(''); // Mark request headers as non-provisional this._clientSecurityState = extraRequestInfo.clientSecurityState; } hasExtraRequestInfo() { return this._hasExtraRequestInfo; } blockedRequestCookies() { return this._blockedRequestCookies; } includedRequestCookies() { return this._includedRequestCookies; } hasRequestCookies() { return this._includedRequestCookies.length > 0 || this._blockedRequestCookies.length > 0; } addExtraResponseInfo(extraResponseInfo) { this._blockedResponseCookies = extraResponseInfo.blockedResponseCookies; this.responseHeaders = extraResponseInfo.responseHeaders; if (extraResponseInfo.responseHeadersText) { this.responseHeadersText = extraResponseInfo.responseHeadersText; if (!this.requestHeadersText()) { // Generate request headers text from raw headers in extra request info because // Network.requestWillBeSentExtraInfo doesn't include headers text. let requestHeadersText = `${this.requestMethod} ${this.parsedURL.path}`; if (this.parsedURL.queryParams) { requestHeadersText += `?${this.parsedURL.queryParams}`; } requestHeadersText += ' HTTP/1.1\r\n'; for (const { name, value } of this.requestHeaders()) { requestHeadersText += `${name}: ${value}\r\n`; } this.setRequestHeadersText(requestHeadersText); } } this._remoteAddressSpace = extraResponseInfo.resourceIPAddressSpace; this._hasExtraResponseInfo = true; } hasExtraResponseInfo() { return this._hasExtraResponseInfo; } blockedResponseCookies() { return this._blockedResponseCookies; } redirectSourceSignedExchangeInfoHasNoErrors() { return this._redirectSource !== null && this._redirectSource._signedExchangeInfo !== null && !this._redirectSource._signedExchangeInfo.errors; } clientSecurityState() { return this._clientSecurityState; } setTrustTokenParams(trustTokenParams) { this._trustTokenParams = trustTokenParams; } trustTokenParams() { return this._trustTokenParams; } setTrustTokenOperationDoneEvent(doneEvent) { this._trustTokenOperationDoneEvent = doneEvent; this.dispatchEventToListeners(Events.TrustTokenResultAdded); } trustTokenOperationDoneEvent() { return this._trustTokenOperationDoneEvent; } setIsSameSite(isSameSite) { this._isSameSite = isSameSite; } isSameSite() { return this._isSameSite; } } // TODO(crbug.com/1167717): Make this a const enum again // eslint-disable-next-line rulesdir/const_enum export var Events; (function (Events) { Events["FinishedLoading"] = "FinishedLoading"; Events["TimingChanged"] = "TimingChanged"; Events["RemoteAddressChanged"] = "RemoteAddressChanged"; Events["RequestHeadersChanged"] = "RequestHeadersChanged"; Events["ResponseHeadersChanged"] = "ResponseHeadersChanged"; Events["WebsocketFrameAdded"] = "WebsocketFrameAdded"; Events["EventSourceMessageAdded"] = "EventSourceMessageAdded"; Events["TrustTokenResultAdded"] = "TrustTokenResultAdded"; })(Events || (Events = {})); // TODO(crbug.com/1167717): Make this a const enum again // eslint-disable-next-line rulesdir/const_enum export var InitiatorType; (function (InitiatorType) { InitiatorType["Other"] = "other"; InitiatorType["Parser"] = "parser"; InitiatorType["Redirect"] = "redirect"; InitiatorType["Script"] = "script"; InitiatorType["Preload"] = "preload"; InitiatorType["SignedExchange"] = "signedExchange"; InitiatorType["Preflight"] = "preflight"; })(InitiatorType || (InitiatorType = {})); // TODO(crbug.com/1167717): Make this a const enum again // eslint-disable-next-line rulesdir/const_enum export var WebSocketFrameType; (function (WebSocketFrameType) { WebSocketFrameType["Send"] = "send"; WebSocketFrameType["Receive"] = "receive"; WebSocketFrameType["Error"] = "error"; })(WebSocketFrameType || (WebSocketFrameType = {})); export const cookieBlockedReasonToUiString = function (blockedReason) { switch (blockedReason) { case "SecureOnly" /* SecureOnly */: return i18nString(UIStrings.secureOnly); case "NotOnPath" /* NotOnPath */: return i18nString(UIStrings.notOnPath); case "DomainMismatch" /* DomainMismatch */: return i18nString(UIStrings.domainMismatch); case "SameSiteStrict" /* SameSiteStrict */: return i18nString(UIStrings.sameSiteStrict); case "SameSiteLax" /* SameSiteLax */: return i18nString(UIStrings.sameSiteLax); case "SameSiteUnspecifiedTreatedAsLax" /* SameSiteUnspecifiedTreatedAsLax */: return i18nString(UIStrings.sameSiteUnspecifiedTreatedAsLax); case "SameSiteNoneInsecure" /* SameSiteNoneInsecure */: return i18nString(UIStrings.sameSiteNoneInsecure); case "UserPreferences" /* UserPreferences */: return i18nString(UIStrings.userPreferences); case "UnknownError" /* UnknownError */: return i18nString(UIStrings.unknownError); case "SchemefulSameSiteStrict" /* SchemefulSameSiteStrict */: return i18nString(UIStrings.schemefulSameSiteStrict); case "SchemefulSameSiteLax" /* SchemefulSameSiteLax */: return i18nString(UIStrings.schemefulSameSiteLax); case "SchemefulSameSiteUnspecifiedTreatedAsLax" /* SchemefulSameSiteUnspecifiedTreatedAsLax */: return i18nString(UIStrings.schemefulSameSiteUnspecifiedTreatedAsLax); case "SamePartyFromCrossPartyContext" /* SamePartyFromCrossPartyContext */: return i18nString(UIStrings.samePartyFromCrossPartyContext); } return ''; }; export const setCookieBlockedReasonToUiString = function (blockedReason) { switch (blockedReason) { case "SecureOnly" /* SecureOnly */: return i18nString(UIStrings.blockedReasonSecureOnly); case "SameSiteStrict" /* SameSiteSt