debug-server-next
Version:
Dev server for hippy-core.
1,081 lines • 61.2 kB
JavaScript
/*
* Copyright (C) 2011 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 Common from '../common/common.js';
import * as Host from '../host/host.js';
import * as i18n from '../i18n/i18n.js';
import * as Platform from '../platform/platform.js';
import { Cookie } from './Cookie.js';
import { Events as NetworkRequestEvents, NetworkRequest } from './NetworkRequest.js';
import { Capability } from './Target.js';
import { SDKModel } from './SDKModel.js';
import { TargetManager } from './TargetManager.js';
const UIStrings = {
/**
*@description Text to indicate that network throttling is disabled
*/
noThrottling: 'No throttling',
/**
*@description Text to indicate the network connectivity is offline
*/
offline: 'Offline',
/**
*@description Text in Network Manager
*/
slowG: 'Slow 3G',
/**
*@description Text in Network Manager
*/
fastG: 'Fast 3G',
/**
*@description Text in Network Manager
*@example {https://example.com} PH1
*/
setcookieHeaderIsIgnoredIn: 'Set-Cookie header is ignored in response from url: {PH1}. Cookie length should be less than or equal to 4096 characters.',
/**
*@description Text in Network Manager
*@example {https://example.com} PH1
*/
requestWasBlockedByDevtoolsS: 'Request was blocked by DevTools: "{PH1}"',
/**
*@description Text in Network Manager
*@example {https://example.com} PH1
*@example {application} PH2
*/
crossoriginReadBlockingCorb: 'Cross-Origin Read Blocking (CORB) blocked cross-origin response {PH1} with MIME type {PH2}. See https://www.chromestatus.com/feature/5629709824032768 for more details.',
/**
*@description Message in Network Manager
*@example {XHR} PH1
*@example {GET} PH2
*@example {https://example.com} PH3
*/
sFailedLoadingSS: '{PH1} failed loading: {PH2} "{PH3}".',
/**
*@description Message in Network Manager
*@example {XHR} PH1
*@example {GET} PH2
*@example {https://example.com} PH3
*/
sFinishedLoadingSS: '{PH1} finished loading: {PH2} "{PH3}".',
};
const str_ = i18n.i18n.registerUIStrings('core/sdk/NetworkManager.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
const requestToManagerMap = new WeakMap();
const CONNECTION_TYPES = new Map([
['2g', "cellular2g" /* Cellular2g */],
['3g', "cellular3g" /* Cellular3g */],
['4g', "cellular4g" /* Cellular4g */],
['bluetooth', "bluetooth" /* Bluetooth */],
['wifi', "wifi" /* Wifi */],
['wimax', "wimax" /* Wimax */],
]);
export class NetworkManager extends SDKModel {
_dispatcher;
_networkAgent;
_bypassServiceWorkerSetting;
constructor(target) {
super(target);
this._dispatcher = new NetworkDispatcher(this);
this._networkAgent = target.networkAgent();
target.registerNetworkDispatcher(this._dispatcher);
if (Common.Settings.Settings.instance().moduleSetting('cacheDisabled').get()) {
this._networkAgent.invoke_setCacheDisabled({ cacheDisabled: true });
}
this._networkAgent.invoke_enable({ maxPostDataSize: MAX_EAGER_POST_REQUEST_BODY_LENGTH });
this._networkAgent.invoke_setAttachDebugStack({ enabled: true });
this._bypassServiceWorkerSetting = Common.Settings.Settings.instance().createSetting('bypassServiceWorker', false);
if (this._bypassServiceWorkerSetting.get()) {
this._bypassServiceWorkerChanged();
}
this._bypassServiceWorkerSetting.addChangeListener(this._bypassServiceWorkerChanged, this);
Common.Settings.Settings.instance()
.moduleSetting('cacheDisabled')
.addChangeListener(this._cacheDisabledSettingChanged, this);
}
static forRequest(request) {
return requestToManagerMap.get(request) || null;
}
static canReplayRequest(request) {
return Boolean(requestToManagerMap.get(request)) && Boolean(request.backendRequestId()) && !request.isRedirect() &&
request.resourceType() === Common.ResourceType.resourceTypes.XHR;
}
static replayRequest(request) {
const manager = requestToManagerMap.get(request);
const requestId = request.backendRequestId();
if (!manager || !requestId || request.isRedirect()) {
return;
}
manager._networkAgent.invoke_replayXHR({ requestId });
}
static async searchInRequest(request, query, caseSensitive, isRegex) {
const manager = NetworkManager.forRequest(request);
const requestId = request.backendRequestId();
if (!manager || !requestId || request.isRedirect()) {
return [];
}
const response = await manager._networkAgent.invoke_searchInResponseBody({ requestId, query: query, caseSensitive: caseSensitive, isRegex: isRegex });
return response.result || [];
}
static async requestContentData(request) {
if (request.resourceType() === Common.ResourceType.resourceTypes.WebSocket) {
return { error: 'Content for WebSockets is currently not supported', content: null, encoded: false };
}
if (!request.finished) {
await request.once(NetworkRequestEvents.FinishedLoading);
}
if (request.isRedirect()) {
return { error: 'No content available because this request was redirected', content: null, encoded: false };
}
const manager = NetworkManager.forRequest(request);
if (!manager) {
return { error: 'No network manager for request', content: null, encoded: false };
}
const requestId = request.backendRequestId();
if (!requestId) {
return { error: 'No backend request id for request', content: null, encoded: false };
}
const response = await manager._networkAgent.invoke_getResponseBody({ requestId });
const error = response.getError() || null;
return { error: error, content: error ? null : response.body, encoded: response.base64Encoded };
}
static async requestPostData(request) {
const manager = NetworkManager.forRequest(request);
if (!manager) {
console.error('No network manager for request');
return null;
}
const requestId = request.backendRequestId();
if (!requestId) {
console.error('No backend request id for request');
return null;
}
try {
const { postData } = await manager._networkAgent.invoke_getRequestPostData({ requestId });
return postData;
}
catch (e) {
return e.message;
}
}
static _connectionType(conditions) {
if (!conditions.download && !conditions.upload) {
return "none" /* None */;
}
const title = typeof conditions.title === 'function' ? conditions.title().toLowerCase() : conditions.title.toLowerCase();
for (const [name, protocolType] of CONNECTION_TYPES) {
if (title.includes(name)) {
return protocolType;
}
}
return "other" /* Other */;
}
static lowercaseHeaders(headers) {
const newHeaders = {};
for (const headerName in headers) {
newHeaders[headerName.toLowerCase()] = headers[headerName];
}
return newHeaders;
}
requestForURL(url) {
return this._dispatcher.requestForURL(url);
}
_cacheDisabledSettingChanged(event) {
const enabled = event.data;
this._networkAgent.invoke_setCacheDisabled({ cacheDisabled: enabled });
}
dispose() {
Common.Settings.Settings.instance()
.moduleSetting('cacheDisabled')
.removeChangeListener(this._cacheDisabledSettingChanged, this);
}
_bypassServiceWorkerChanged() {
this._networkAgent.invoke_setBypassServiceWorker({ bypass: this._bypassServiceWorkerSetting.get() });
}
async getSecurityIsolationStatus(frameId) {
const result = await this._networkAgent.invoke_getSecurityIsolationStatus({ frameId });
if (result.getError()) {
return null;
}
return result.status;
}
async loadNetworkResource(frameId, url, options) {
const result = await this._networkAgent.invoke_loadNetworkResource({ frameId, url, options });
if (result.getError()) {
throw new Error(result.getError());
}
return result.resource;
}
clearRequests() {
this._dispatcher.clearRequests();
}
}
// TODO(crbug.com/1167717): Make this a const enum again
// eslint-disable-next-line rulesdir/const_enum
export var Events;
(function (Events) {
Events["RequestStarted"] = "RequestStarted";
Events["RequestUpdated"] = "RequestUpdated";
Events["RequestFinished"] = "RequestFinished";
Events["RequestUpdateDropped"] = "RequestUpdateDropped";
Events["ResponseReceived"] = "ResponseReceived";
Events["MessageGenerated"] = "MessageGenerated";
Events["RequestRedirected"] = "RequestRedirected";
Events["LoadingFinished"] = "LoadingFinished";
})(Events || (Events = {}));
export const NoThrottlingConditions = {
title: i18nLazyString(UIStrings.noThrottling),
i18nTitleKey: UIStrings.noThrottling,
download: -1,
upload: -1,
latency: 0,
};
export const OfflineConditions = {
title: i18nLazyString(UIStrings.offline),
i18nTitleKey: UIStrings.offline,
download: 0,
upload: 0,
latency: 0,
};
export const Slow3GConditions = {
title: i18nLazyString(UIStrings.slowG),
i18nTitleKey: UIStrings.slowG,
download: 500 * 1000 / 8 * .8,
upload: 500 * 1000 / 8 * .8,
latency: 400 * 5,
};
export const Fast3GConditions = {
title: i18nLazyString(UIStrings.fastG),
i18nTitleKey: UIStrings.fastG,
download: 1.6 * 1000 * 1000 / 8 * .9,
upload: 750 * 1000 / 8 * .9,
latency: 150 * 3.75,
};
const MAX_EAGER_POST_REQUEST_BODY_LENGTH = 64 * 1024; // bytes
export class NetworkDispatcher {
_manager;
requestsById;
requestsByURL;
_requestIdToExtraInfoBuilder;
_requestIdToTrustTokenEvent;
constructor(manager) {
this._manager = manager;
this.requestsById = new Map();
this.requestsByURL = new Map();
this._requestIdToExtraInfoBuilder = new Map();
/**
* In case of an early abort or a cache hit, the Trust Token done event is
* reported before the request itself is created in `requestWillBeSent`.
* This causes the event to be lost as no `NetworkRequest` instance has been
* created yet.
* This map caches the events temporarliy and populates the NetworKRequest
* once it is created in `requestWillBeSent`.
*/
this._requestIdToTrustTokenEvent = new Map();
}
_headersMapToHeadersArray(headersMap) {
const result = [];
for (const name in headersMap) {
const values = headersMap[name].split('\n');
for (let i = 0; i < values.length; ++i) {
result.push({ name: name, value: values[i] });
}
}
return result;
}
_updateNetworkRequestWithRequest(networkRequest, request) {
networkRequest.requestMethod = request.method;
networkRequest.setRequestHeaders(this._headersMapToHeadersArray(request.headers));
networkRequest.setRequestFormData(Boolean(request.hasPostData), request.postData || null);
networkRequest.setInitialPriority(request.initialPriority);
networkRequest.mixedContentType = request.mixedContentType || "none" /* None */;
networkRequest.setReferrerPolicy(request.referrerPolicy);
networkRequest.setIsSameSite(request.isSameSite || false);
}
_updateNetworkRequestWithResponse(networkRequest, response) {
if (response.url && networkRequest.url() !== response.url) {
networkRequest.setUrl(response.url);
}
networkRequest.mimeType = response.mimeType;
networkRequest.statusCode = response.status;
networkRequest.statusText = response.statusText;
if (!networkRequest.hasExtraResponseInfo()) {
networkRequest.responseHeaders = this._headersMapToHeadersArray(response.headers);
}
if (response.encodedDataLength >= 0) {
networkRequest.setTransferSize(response.encodedDataLength);
}
if (response.requestHeaders && !networkRequest.hasExtraRequestInfo()) {
// TODO(http://crbug.com/1004979): Stop using response.requestHeaders and
// response.requestHeadersText once shared workers
// emit Network.*ExtraInfo events for their network requests.
networkRequest.setRequestHeaders(this._headersMapToHeadersArray(response.requestHeaders));
networkRequest.setRequestHeadersText(response.requestHeadersText || '');
}
networkRequest.connectionReused = response.connectionReused;
networkRequest.connectionId = String(response.connectionId);
if (response.remoteIPAddress) {
networkRequest.setRemoteAddress(response.remoteIPAddress, response.remotePort || -1);
}
if (response.fromServiceWorker) {
networkRequest.fetchedViaServiceWorker = true;
}
if (response.fromDiskCache) {
networkRequest.setFromDiskCache();
}
if (response.fromPrefetchCache) {
networkRequest.setFromPrefetchCache();
}
if (response.cacheStorageCacheName) {
networkRequest.setResponseCacheStorageCacheName(response.cacheStorageCacheName);
}
if (response.responseTime) {
networkRequest.setResponseRetrievalTime(new Date(response.responseTime));
}
networkRequest.timing = response.timing;
networkRequest.protocol = response.protocol || '';
if (response.serviceWorkerResponseSource) {
networkRequest.setServiceWorkerResponseSource(response.serviceWorkerResponseSource);
}
networkRequest.setSecurityState(response.securityState);
if (response.securityDetails) {
networkRequest.setSecurityDetails(response.securityDetails);
}
const newResourceType = Common.ResourceType.ResourceType.fromMimeTypeOverride(networkRequest.mimeType);
if (newResourceType) {
networkRequest.setResourceType(newResourceType);
}
}
requestForId(url) {
return this.requestsById.get(url) || null;
}
requestForURL(url) {
return this.requestsByURL.get(url) || null;
}
resourceChangedPriority({ requestId, newPriority }) {
const networkRequest = this.requestsById.get(requestId);
if (networkRequest) {
networkRequest.setPriority(newPriority);
}
}
signedExchangeReceived({ requestId, info }) {
// While loading a signed exchange, a signedExchangeReceived event is sent
// between two requestWillBeSent events.
// 1. The first requestWillBeSent is sent while starting the navigation (or
// prefetching).
// 2. This signedExchangeReceived event is sent when the browser detects the
// signed exchange.
// 3. The second requestWillBeSent is sent with the generated redirect
// response and a new redirected request which URL is the inner request
// URL of the signed exchange.
let networkRequest = this.requestsById.get(requestId);
// |requestId| is available only for navigation requests. If the request was
// sent from a renderer process for prefetching, it is not available. In the
// case, need to fallback to look for the URL.
// TODO(crbug/841076): Sends the request ID of prefetching to the browser
// process and DevTools to find the matching request.
if (!networkRequest) {
networkRequest = this.requestsByURL.get(info.outerResponse.url);
if (!networkRequest) {
return;
}
}
networkRequest.setSignedExchangeInfo(info);
networkRequest.setResourceType(Common.ResourceType.resourceTypes.SignedExchange);
this._updateNetworkRequestWithResponse(networkRequest, info.outerResponse);
this._updateNetworkRequest(networkRequest);
this._manager.dispatchEventToListeners(Events.ResponseReceived, { request: networkRequest, response: info.outerResponse });
}
requestWillBeSent({ requestId, loaderId, documentURL, request, timestamp, wallTime, initiator, redirectResponse, type, frameId }) {
let networkRequest = this.requestsById.get(requestId);
if (networkRequest) {
// FIXME: move this check to the backend.
if (!redirectResponse) {
return;
}
// If signedExchangeReceived event has already been sent for the request,
// ignores the internally generated |redirectResponse|. The
// |outerResponse| of SignedExchangeInfo was set to |networkRequest| in
// signedExchangeReceived().
if (!networkRequest.signedExchangeInfo()) {
this.responseReceived({
requestId,
loaderId,
timestamp,
type: type || "Other" /* Other */,
response: redirectResponse,
frameId,
});
}
networkRequest = this._appendRedirect(requestId, timestamp, request.url);
this._manager.dispatchEventToListeners(Events.RequestRedirected, networkRequest);
}
else {
networkRequest = NetworkRequest.create(requestId, request.url, documentURL, frameId || '', loaderId, initiator);
requestToManagerMap.set(networkRequest, this._manager);
}
networkRequest.hasNetworkData = true;
this._updateNetworkRequestWithRequest(networkRequest, request);
networkRequest.setIssueTime(timestamp, wallTime);
networkRequest.setResourceType(type ? Common.ResourceType.resourceTypes[type] : Common.ResourceType.resourceTypes.Other);
if (request.trustTokenParams) {
networkRequest.setTrustTokenParams(request.trustTokenParams);
}
const maybeTrustTokenEvent = this._requestIdToTrustTokenEvent.get(requestId);
if (maybeTrustTokenEvent) {
networkRequest.setTrustTokenOperationDoneEvent(maybeTrustTokenEvent);
this._requestIdToTrustTokenEvent.delete(requestId);
}
this._getExtraInfoBuilder(requestId).addRequest(networkRequest);
this._startNetworkRequest(networkRequest, request);
}
requestServedFromCache({ requestId }) {
const networkRequest = this.requestsById.get(requestId);
if (!networkRequest) {
return;
}
networkRequest.setFromMemoryCache();
}
responseReceived({ requestId, loaderId, timestamp, type, response, frameId }) {
const networkRequest = this.requestsById.get(requestId);
const lowercaseHeaders = NetworkManager.lowercaseHeaders(response.headers);
if (!networkRequest) {
const lastModifiedHeader = lowercaseHeaders['last-modified'];
// We missed the requestWillBeSent.
const eventData = {
url: response.url,
frameId: frameId || '',
loaderId: loaderId,
resourceType: type,
mimeType: response.mimeType,
lastModified: lastModifiedHeader ? new Date(lastModifiedHeader) : null,
};
this._manager.dispatchEventToListeners(Events.RequestUpdateDropped, eventData);
return;
}
networkRequest.responseReceivedTime = timestamp;
networkRequest.setResourceType(Common.ResourceType.resourceTypes[type]);
// net::ParsedCookie::kMaxCookieSize = 4096 (net/cookies/parsed_cookie.h)
if ('set-cookie' in lowercaseHeaders && lowercaseHeaders['set-cookie'].length > 4096) {
const values = lowercaseHeaders['set-cookie'].split('\n');
for (let i = 0; i < values.length; ++i) {
if (values[i].length <= 4096) {
continue;
}
const message = i18nString(UIStrings.setcookieHeaderIsIgnoredIn, { PH1: response.url });
this._manager.dispatchEventToListeners(Events.MessageGenerated, { message: message, requestId: requestId, warning: true });
}
}
this._updateNetworkRequestWithResponse(networkRequest, response);
this._updateNetworkRequest(networkRequest);
this._manager.dispatchEventToListeners(Events.ResponseReceived, { request: networkRequest, response });
}
dataReceived({ requestId, timestamp, dataLength, encodedDataLength }) {
let networkRequest = this.requestsById.get(requestId);
if (!networkRequest) {
networkRequest = this._maybeAdoptMainResourceRequest(requestId);
}
if (!networkRequest) {
return;
}
networkRequest.resourceSize += dataLength;
if (encodedDataLength !== -1) {
networkRequest.increaseTransferSize(encodedDataLength);
}
networkRequest.endTime = timestamp;
this._updateNetworkRequest(networkRequest);
}
loadingFinished({ requestId, timestamp: finishTime, encodedDataLength, shouldReportCorbBlocking }) {
let networkRequest = this.requestsById.get(requestId);
if (!networkRequest) {
networkRequest = this._maybeAdoptMainResourceRequest(requestId);
}
if (!networkRequest) {
return;
}
this._getExtraInfoBuilder(requestId).finished();
this._finishNetworkRequest(networkRequest, finishTime, encodedDataLength, shouldReportCorbBlocking);
this._manager.dispatchEventToListeners(Events.LoadingFinished, networkRequest);
}
loadingFailed({ requestId, timestamp: time, type: resourceType, errorText: localizedDescription, canceled, blockedReason, corsErrorStatus, }) {
const networkRequest = this.requestsById.get(requestId);
if (!networkRequest) {
return;
}
networkRequest.failed = true;
networkRequest.setResourceType(Common.ResourceType.resourceTypes[resourceType]);
networkRequest.canceled = Boolean(canceled);
if (blockedReason) {
networkRequest.setBlockedReason(blockedReason);
if (blockedReason === "inspector" /* Inspector */) {
const message = i18nString(UIStrings.requestWasBlockedByDevtoolsS, { PH1: networkRequest.url() });
this._manager.dispatchEventToListeners(Events.MessageGenerated, { message: message, requestId: requestId, warning: true });
}
}
if (corsErrorStatus) {
networkRequest.setCorsErrorStatus(corsErrorStatus);
}
networkRequest.localizedFailDescription = localizedDescription;
this._getExtraInfoBuilder(requestId).finished();
this._finishNetworkRequest(networkRequest, time, -1);
}
webSocketCreated({ requestId, url: requestURL, initiator }) {
const networkRequest = NetworkRequest.createForWebSocket(requestId, requestURL, initiator);
requestToManagerMap.set(networkRequest, this._manager);
networkRequest.setResourceType(Common.ResourceType.resourceTypes.WebSocket);
this._startNetworkRequest(networkRequest, null);
}
webSocketWillSendHandshakeRequest({ requestId, timestamp: time, wallTime, request }) {
const networkRequest = this.requestsById.get(requestId);
if (!networkRequest) {
return;
}
networkRequest.requestMethod = 'GET';
networkRequest.setRequestHeaders(this._headersMapToHeadersArray(request.headers));
networkRequest.setIssueTime(time, wallTime);
this._updateNetworkRequest(networkRequest);
}
webSocketHandshakeResponseReceived({ requestId, timestamp: time, response }) {
const networkRequest = this.requestsById.get(requestId);
if (!networkRequest) {
return;
}
networkRequest.statusCode = response.status;
networkRequest.statusText = response.statusText;
networkRequest.responseHeaders = this._headersMapToHeadersArray(response.headers);
networkRequest.responseHeadersText = response.headersText || '';
if (response.requestHeaders) {
networkRequest.setRequestHeaders(this._headersMapToHeadersArray(response.requestHeaders));
}
if (response.requestHeadersText) {
networkRequest.setRequestHeadersText(response.requestHeadersText);
}
networkRequest.responseReceivedTime = time;
networkRequest.protocol = 'websocket';
this._updateNetworkRequest(networkRequest);
}
webSocketFrameReceived({ requestId, timestamp: time, response }) {
const networkRequest = this.requestsById.get(requestId);
if (!networkRequest) {
return;
}
networkRequest.addProtocolFrame(response, time, false);
networkRequest.responseReceivedTime = time;
this._updateNetworkRequest(networkRequest);
}
webSocketFrameSent({ requestId, timestamp: time, response }) {
const networkRequest = this.requestsById.get(requestId);
if (!networkRequest) {
return;
}
networkRequest.addProtocolFrame(response, time, true);
networkRequest.responseReceivedTime = time;
this._updateNetworkRequest(networkRequest);
}
webSocketFrameError({ requestId, timestamp: time, errorMessage }) {
const networkRequest = this.requestsById.get(requestId);
if (!networkRequest) {
return;
}
networkRequest.addProtocolFrameError(errorMessage, time);
networkRequest.responseReceivedTime = time;
this._updateNetworkRequest(networkRequest);
}
webSocketClosed({ requestId, timestamp: time }) {
const networkRequest = this.requestsById.get(requestId);
if (!networkRequest) {
return;
}
this._finishNetworkRequest(networkRequest, time, -1);
}
eventSourceMessageReceived({ requestId, timestamp: time, eventName, eventId, data }) {
const networkRequest = this.requestsById.get(requestId);
if (!networkRequest) {
return;
}
networkRequest.addEventSourceMessage(time, eventName, eventId, data);
}
requestIntercepted({ interceptionId, request, frameId, resourceType, isNavigationRequest, isDownload, redirectUrl, authChallenge, responseErrorReason, responseStatusCode, responseHeaders, requestId, }) {
MultitargetNetworkManager.instance()._requestIntercepted(new InterceptedRequest(this._manager.target().networkAgent(), interceptionId, request, frameId, resourceType, isNavigationRequest, isDownload, redirectUrl, authChallenge, responseErrorReason, responseStatusCode, responseHeaders, requestId));
}
requestWillBeSentExtraInfo({ requestId, associatedCookies, headers, clientSecurityState }) {
const blockedRequestCookies = [];
const includedRequestCookies = [];
for (const { blockedReasons, cookie } of associatedCookies) {
if (blockedReasons.length === 0) {
includedRequestCookies.push(Cookie.fromProtocolCookie(cookie));
}
else {
blockedRequestCookies.push({ blockedReasons, cookie: Cookie.fromProtocolCookie(cookie) });
}
}
const extraRequestInfo = {
blockedRequestCookies,
includedRequestCookies,
requestHeaders: this._headersMapToHeadersArray(headers),
clientSecurityState: clientSecurityState,
};
this._getExtraInfoBuilder(requestId).addRequestExtraInfo(extraRequestInfo);
}
responseReceivedExtraInfo({ requestId, blockedCookies, headers, headersText, resourceIPAddressSpace }) {
const extraResponseInfo = {
blockedResponseCookies: blockedCookies.map(blockedCookie => {
return {
blockedReasons: blockedCookie.blockedReasons,
cookieLine: blockedCookie.cookieLine,
cookie: blockedCookie.cookie ? Cookie.fromProtocolCookie(blockedCookie.cookie) : null,
};
}),
responseHeaders: this._headersMapToHeadersArray(headers),
responseHeadersText: headersText,
resourceIPAddressSpace,
};
this._getExtraInfoBuilder(requestId).addResponseExtraInfo(extraResponseInfo);
}
_getExtraInfoBuilder(requestId) {
let builder;
if (!this._requestIdToExtraInfoBuilder.has(requestId)) {
builder = new ExtraInfoBuilder();
this._requestIdToExtraInfoBuilder.set(requestId, builder);
}
else {
builder = this._requestIdToExtraInfoBuilder.get(requestId);
}
return builder;
}
_appendRedirect(requestId, time, redirectURL) {
const originalNetworkRequest = this.requestsById.get(requestId);
if (!originalNetworkRequest) {
throw new Error(`Could not find original network request for ${requestId}`);
}
let redirectCount = 0;
for (let redirect = originalNetworkRequest.redirectSource(); redirect; redirect = redirect.redirectSource()) {
redirectCount++;
}
originalNetworkRequest.markAsRedirect(redirectCount);
this._finishNetworkRequest(originalNetworkRequest, time, -1);
const newNetworkRequest = NetworkRequest.create(requestId, redirectURL, originalNetworkRequest.documentURL, originalNetworkRequest.frameId, originalNetworkRequest.loaderId, originalNetworkRequest.initiator());
requestToManagerMap.set(newNetworkRequest, this._manager);
newNetworkRequest.setRedirectSource(originalNetworkRequest);
originalNetworkRequest.setRedirectDestination(newNetworkRequest);
return newNetworkRequest;
}
_maybeAdoptMainResourceRequest(requestId) {
const request = MultitargetNetworkManager.instance()._inflightMainResourceRequests.get(requestId);
if (!request) {
return null;
}
const oldDispatcher = NetworkManager.forRequest(request)._dispatcher;
oldDispatcher.requestsById.delete(requestId);
oldDispatcher.requestsByURL.delete(request.url());
this.requestsById.set(requestId, request);
this.requestsByURL.set(request.url(), request);
requestToManagerMap.set(request, this._manager);
return request;
}
_startNetworkRequest(networkRequest, originalRequest) {
this.requestsById.set(networkRequest.requestId(), networkRequest);
this.requestsByURL.set(networkRequest.url(), networkRequest);
// The following relies on the fact that loaderIds and requestIds are
// globally unique and that the main request has them equal.
if (networkRequest.loaderId === networkRequest.requestId()) {
MultitargetNetworkManager.instance()._inflightMainResourceRequests.set(networkRequest.requestId(), networkRequest);
}
this._manager.dispatchEventToListeners(Events.RequestStarted, { request: networkRequest, originalRequest });
}
_updateNetworkRequest(networkRequest) {
this._manager.dispatchEventToListeners(Events.RequestUpdated, networkRequest);
}
_finishNetworkRequest(networkRequest, finishTime, encodedDataLength, shouldReportCorbBlocking) {
networkRequest.endTime = finishTime;
networkRequest.finished = true;
if (encodedDataLength >= 0) {
const redirectSource = networkRequest.redirectSource();
if (redirectSource && redirectSource.signedExchangeInfo()) {
networkRequest.setTransferSize(0);
redirectSource.setTransferSize(encodedDataLength);
this._updateNetworkRequest(redirectSource);
}
else {
networkRequest.setTransferSize(encodedDataLength);
}
}
this._manager.dispatchEventToListeners(Events.RequestFinished, networkRequest);
MultitargetNetworkManager.instance()._inflightMainResourceRequests.delete(networkRequest.requestId());
if (shouldReportCorbBlocking) {
const message = i18nString(UIStrings.crossoriginReadBlockingCorb, { PH1: networkRequest.url(), PH2: networkRequest.mimeType });
this._manager.dispatchEventToListeners(Events.MessageGenerated, { message: message, requestId: networkRequest.requestId(), warning: true });
}
if (Common.Settings.Settings.instance().moduleSetting('monitoringXHREnabled').get() &&
networkRequest.resourceType().category() === Common.ResourceType.resourceCategories.XHR) {
let message;
const failedToLoad = networkRequest.failed || networkRequest.hasErrorStatusCode();
if (failedToLoad) {
message = i18nString(UIStrings.sFailedLoadingSS, { PH1: networkRequest.resourceType().title(), PH2: networkRequest.requestMethod, PH3: networkRequest.url() });
}
else {
message = i18nString(UIStrings.sFinishedLoadingSS, { PH1: networkRequest.resourceType().title(), PH2: networkRequest.requestMethod, PH3: networkRequest.url() });
}
this._manager.dispatchEventToListeners(Events.MessageGenerated, { message: message, requestId: networkRequest.requestId(), warning: false });
}
}
clearRequests() {
this.requestsById.clear();
this.requestsByURL.clear();
this._requestIdToExtraInfoBuilder.clear();
}
webTransportCreated({ transportId, url: requestURL, timestamp: time, initiator }) {
const networkRequest = NetworkRequest.createForWebSocket(transportId, requestURL, initiator);
networkRequest.hasNetworkData = true;
requestToManagerMap.set(networkRequest, this._manager);
networkRequest.setResourceType(Common.ResourceType.resourceTypes.WebTransport);
networkRequest.setIssueTime(time, 0);
// TODO(yoichio): Add appropreate events to address abort cases.
this._startNetworkRequest(networkRequest, null);
}
webTransportConnectionEstablished({ transportId, timestamp: time }) {
const networkRequest = this.requestsById.get(transportId);
if (!networkRequest) {
return;
}
// This dummy deltas are needed to show this request as being
// downloaded(blue) given typical WebTransport is kept for a while.
// TODO(yoichio): Add appropreate events to fix these dummy datas.
// DNS lookup?
networkRequest.responseReceivedTime = time;
networkRequest.endTime = time + 0.001;
this._updateNetworkRequest(networkRequest);
}
webTransportClosed({ transportId, timestamp: time }) {
const networkRequest = this.requestsById.get(transportId);
if (!networkRequest) {
return;
}
networkRequest.endTime = time;
this._finishNetworkRequest(networkRequest, time, 0);
}
trustTokenOperationDone(event) {
const request = this.requestsById.get(event.requestId);
if (!request) {
this._requestIdToTrustTokenEvent.set(event.requestId, event);
return;
}
request.setTrustTokenOperationDoneEvent(event);
}
subresourceWebBundleMetadataReceived({ requestId, urls }) {
const extraInfoBuilder = this._getExtraInfoBuilder(requestId);
extraInfoBuilder.setWebBundleInfo({ resourceUrls: urls });
const finalRequest = extraInfoBuilder.finalRequest();
if (finalRequest) {
this._updateNetworkRequest(finalRequest);
}
}
subresourceWebBundleMetadataError({ requestId, errorMessage }) {
const extraInfoBuilder = this._getExtraInfoBuilder(requestId);
extraInfoBuilder.setWebBundleInfo({ errorMessage });
const finalRequest = extraInfoBuilder.finalRequest();
if (finalRequest) {
this._updateNetworkRequest(finalRequest);
}
}
subresourceWebBundleInnerResponseParsed({ innerRequestId, bundleRequestId }) {
const extraInfoBuilder = this._getExtraInfoBuilder(innerRequestId);
extraInfoBuilder.setWebBundleInnerRequestInfo({ bundleRequestId });
const finalRequest = extraInfoBuilder.finalRequest();
if (finalRequest) {
this._updateNetworkRequest(finalRequest);
}
}
subresourceWebBundleInnerResponseError({ innerRequestId, errorMessage }) {
const extraInfoBuilder = this._getExtraInfoBuilder(innerRequestId);
extraInfoBuilder.setWebBundleInnerRequestInfo({ errorMessage });
const finalRequest = extraInfoBuilder.finalRequest();
if (finalRequest) {
this._updateNetworkRequest(finalRequest);
}
}
/**
* @deprecated
* This method is only kept for usage in a web test.
*/
_createNetworkRequest(requestId, frameId, loaderId, url, documentURL, initiator) {
const request = NetworkRequest.create(requestId, url, documentURL, frameId, loaderId, initiator);
requestToManagerMap.set(request, this._manager);
return request;
}
}
let multiTargetNetworkManagerInstance;
export class MultitargetNetworkManager extends Common.ObjectWrapper.ObjectWrapper {
_userAgentOverride;
_userAgentMetadataOverride;
_customAcceptedEncodings;
_agents;
_inflightMainResourceRequests;
_networkConditions;
_updatingInterceptionPatternsPromise;
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
// eslint-disable-next-line @typescript-eslint/no-explicit-any
_blockingEnabledSetting;
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
// eslint-disable-next-line @typescript-eslint/no-explicit-any
_blockedPatternsSetting;
_effectiveBlockedURLs;
_urlsForRequestInterceptor;
_extraHeaders;
_customUserAgent;
constructor() {
super();
this._userAgentOverride = '';
this._userAgentMetadataOverride = null;
this._customAcceptedEncodings = null;
this._agents = new Set();
this._inflightMainResourceRequests = new Map();
this._networkConditions = NoThrottlingConditions;
this._updatingInterceptionPatternsPromise = null;
// TODO(allada) Remove these and merge it with request interception.
this._blockingEnabledSetting = Common.Settings.Settings.instance().moduleSetting('requestBlockingEnabled');
this._blockedPatternsSetting = Common.Settings.Settings.instance().createSetting('networkBlockedPatterns', []);
this._effectiveBlockedURLs = [];
this._updateBlockedPatterns();
this._urlsForRequestInterceptor = new Platform.MapUtilities.Multimap();
TargetManager.instance().observeModels(NetworkManager, this);
}
static instance(opts = { forceNew: null }) {
const { forceNew } = opts;
if (!multiTargetNetworkManagerInstance || forceNew) {
multiTargetNetworkManagerInstance = new MultitargetNetworkManager();
}
return multiTargetNetworkManagerInstance;
}
static getChromeVersion() {
const chromeRegex = /(?:^|\W)(?:Chrome|HeadlessChrome)\/(\S+)/;
const chromeMatch = navigator.userAgent.match(chromeRegex);
if (chromeMatch && chromeMatch.length > 1) {
return chromeMatch[1];
}
return '';
}
static patchUserAgentWithChromeVersion(uaString) {
// Patches Chrome/ChrOS version from user agent ("1.2.3.4" when user agent is: "Chrome/1.2.3.4").
// Otherwise, ignore it. This assumes additional appVersions appear after the Chrome version.
const chromeVersion = MultitargetNetworkManager.getChromeVersion();
if (chromeVersion.length > 0) {
// "1.2.3.4" becomes "1.0.100.0"
const additionalAppVersion = chromeVersion.split('.', 1)[0] + '.0.100.0';
return Platform.StringUtilities.sprintf(uaString, chromeVersion, additionalAppVersion);
}
return uaString;
}
static patchUserAgentMetadataWithChromeVersion(userAgentMetadata) {
// Patches Chrome/ChrOS version from user agent metadata ("1.2.3.4" when user agent is: "Chrome/1.2.3.4").
// Otherwise, ignore it. This assumes additional appVersions appear after the Chrome version.
if (!userAgentMetadata.brands) {
return;
}
const chromeVersion = MultitargetNetworkManager.getChromeVersion();
if (chromeVersion.length === 0) {
return;
}
const majorVersion = chromeVersion.split('.', 1)[0];
for (const brand of userAgentMetadata.brands) {
if (brand.version.includes('%s')) {
brand.version = Platform.StringUtilities.sprintf(brand.version, majorVersion);
}
}
if (userAgentMetadata.fullVersion) {
if (userAgentMetadata.fullVersion.includes('%s')) {
userAgentMetadata.fullVersion = Platform.StringUtilities.sprintf(userAgentMetadata.fullVersion, chromeVersion);
}
}
}
modelAdded(networkManager) {
const networkAgent = networkManager.target().networkAgent();
if (this._extraHeaders) {
networkAgent.invoke_setExtraHTTPHeaders({ headers: this._extraHeaders });
}
if (this.currentUserAgent()) {
networkAgent.invoke_setUserAgentOverride({ userAgent: this.currentUserAgent(), userAgentMetadata: this._userAgentMetadataOverride || undefined });
}
if (this._effectiveBlockedURLs.length) {
networkAgent.invoke_setBlockedURLs({ urls: this._effectiveBlockedURLs });
}
if (this.isIntercepting()) {
networkAgent.invoke_setRequestInterception({ patterns: this._urlsForRequestInterceptor.valuesArray() });
}
if (this._customAcceptedEncodings === null) {
networkAgent.invoke_clearAcceptedEncodingsOverride();
}
else {
networkAgent.invoke_setAcceptedEncodings({ encodings: this._customAcceptedEncodings });
}
this._agents.add(networkAgent);
if (this.isThrottling()) {
this._updateNetworkConditions(networkAgent);
}
}
modelRemoved(networkManager) {
for (const entry of this._inflightMainResourceRequests) {
const manager = NetworkManager.forRequest(entry[1]);
if (manager !== networkManager) {
continue;
}
this._inflightMainResourceRequests.delete(entry[0]);
}
this._agents.delete(networkManager.target().networkAgent());
}
isThrottling() {
return this._networkConditions.download >= 0 || this._networkConditions.upload >= 0 ||
this._networkConditions.latency > 0;
}
isOffline() {
return !this._networkConditions.download && !this._networkConditions.upload;
}
setNetworkConditions(conditions) {
this._networkConditions = conditions;
for (const agent of this._agents) {
this._updateNetworkConditions(agent);
}
this.dispatchEventToListeners(MultitargetNetworkManager.Events.ConditionsChanged);
}
networkConditions() {
return this._networkConditions;
}
_updateNetworkConditions(networkAgent) {
const conditions = this._networkConditions;
if (!this.isThrottling()) {
networkAgent.invoke_emulateNetworkConditions({ offline: false, latency: 0, downloadThroughput: 0, uploadThroughput: 0 });
}
else {
networkAgent.invoke_emulateNetworkConditions({
offline: this.isOffline(),
latency: conditions.latency,
downloadThroughput: conditions.download < 0 ? 0 : conditions.download,
uploadThroughput: conditions.upload < 0 ? 0 : conditions.upload,
connectionType: NetworkManager._connectionType(conditions),
});
}
}
setExtraHTTPHeaders(headers) {
this._extraHeaders = headers;
for (const agent of this._agents) {
agent.invoke_setExtraHTTPHeaders({ headers: this._extraHeaders });
}
}
currentUserAgent() {
return this._customUserAgent ? this._customUserAgent : this._userAgentOverride;
}
_updateUserAgentOverride() {
const userAgent = this.currentUserAgent();
for (const agent of this._agents) {
agent.invoke_setUserAgentOverride({ userAgent: userAgent, userAgentMetadata: this._userAgentMetadataOverride || undefined });
}
}
setUserAgentOverride(userAgent, userAgentMetadataOverride) {
const uaChanged = (this._userAgentOverride !== userAgent);
this._userAgentOverride = userAgent;
if (!this._customUserAgent) {
this._userAgentMetadataOverride = userAgentMetadataOverride;
this._updateUserAgentOverride();
}
else {
this._userAgentMetadataOverride = null;
}
if (uaChanged) {
this.dispatchEventToListeners(MultitargetNetworkManager.Events.UserAgentChanged);
}
}
userAgentOverride() {
return this._userAgentOverride;
}
setCustomUserAgentOverride(userAgent, userAgentMetadataOverride = null) {
this._customUserAgent = userAgent;
this._userAgentMetadataOverride = userAgentMetadataOverride;
this._updateUserAgentOverride();
}
setCustomAcceptedEncodingsOverride(acceptedEncodings) {
this._customAcceptedEncodings = acceptedEncodings;
this._updateAcceptedEncodingsOverride();
this.dispatchEventToListeners(MultitargetNetworkManager.Events.AcceptedEncodingsChanged);
}
clearCustomAcceptedEncodingsOverride() {
this._customAcceptedEncodings = null;
this._updateAcceptedEncodingsOverride();
this.dispatchEventToListeners(MultitargetNetworkManager.Events.AcceptedEncodingsChanged);
}
isAcceptedEncodingOverrideSet() {
return this._customAcceptedEncodings !== null;
}
_updateAcceptedEncodingsOverride() {
const customAcceptedEncodings = this._customAcceptedEncodings;
for (const agent of this._agents) {
if (customAcceptedEncodings === null) {
agent.invoke_clearAcceptedEncodingsOverride();
}
else {
agent.invoke_setAcceptedEncodings({ encodings: customAcceptedEncodings });
}
}
}
// TODO(allada) Move all request blocking into interception and let view manage blocking.
blockedPatterns() {
return this._blockedPatternsSetting.get().slice();
}
blockingEnabled() {
return this._blockingEnabledSetting.get();
}
isBlocking() {
return Boolean(this._effectiveBlockedURLs.length);
}
setBlockedPatterns(patterns) {
this._blockedPatternsSetting.set(patterns);
this._updateBlockedPatterns();
this.dispatchEventToListeners(MultitargetNetworkManager.Events.BlockedPatternsChanged);
}
setBlockingEnabled(enabled) {
if (this._blockingEnabledSetting.get() === enabled) {
return;
}
this._blockingEnabledSetting.set(enabled);
this._updateBlockedPatterns();
this.dispatchEventToListeners(MultitargetNetworkManager.Events.BlockedPatternsChanged);
}
_updateBlockedPatterns() {
const urls = [];
if (this._blockingEnabledSetting.get()) {
for (const pattern of this._blockedPatternsSetting.get()) {
if (pattern.enabled) {
urls.push(pattern.url);
}
}
}
if (!urls.length && !this._effectiveBlockedURLs.length) {
return;
}
this._effectiveBlockedURLs = urls;
f