????
Your IP : 18.217.226.1
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.WebSocket = exports.STATUS_TEXTS = exports.Route = exports.Response = exports.Request = void 0;
exports.filterCookies = filterCookies;
exports.mergeHeaders = mergeHeaders;
exports.parsedURL = parsedURL;
exports.rewriteCookies = rewriteCookies;
exports.singleHeader = singleHeader;
exports.stripFragmentFromUrl = stripFragmentFromUrl;
var _utils = require("../utils");
var _manualPromise = require("../utils/manualPromise");
var _instrumentation = require("./instrumentation");
var _fetch = require("./fetch");
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function filterCookies(cookies, urls) {
const parsedURLs = urls.map(s => new URL(s));
// Chromiums's cookies are missing sameSite when it is 'None'
return cookies.filter(c => {
if (!parsedURLs.length) return true;
for (const parsedURL of parsedURLs) {
let domain = c.domain;
if (!domain.startsWith('.')) domain = '.' + domain;
if (!('.' + parsedURL.hostname).endsWith(domain)) continue;
if (!parsedURL.pathname.startsWith(c.path)) continue;
if (parsedURL.protocol !== 'https:' && parsedURL.hostname !== 'localhost' && c.secure) continue;
return true;
}
return false;
});
}
// Rollover to 5-digit year:
// 253402300799 == Fri, 31 Dec 9999 23:59:59 +0000 (UTC)
// 253402300800 == Sat, 1 Jan 1000 00:00:00 +0000 (UTC)
const kMaxCookieExpiresDateInSeconds = 253402300799;
function rewriteCookies(cookies) {
return cookies.map(c => {
(0, _utils.assert)(c.url || c.domain && c.path, 'Cookie should have a url or a domain/path pair');
(0, _utils.assert)(!(c.url && c.domain), 'Cookie should have either url or domain');
(0, _utils.assert)(!(c.url && c.path), 'Cookie should have either url or path');
(0, _utils.assert)(!(c.expires && c.expires < 0 && c.expires !== -1), 'Cookie should have a valid expires, only -1 or a positive number for the unix timestamp in seconds is allowed');
(0, _utils.assert)(!(c.expires && c.expires > 0 && c.expires > kMaxCookieExpiresDateInSeconds), 'Cookie should have a valid expires, only -1 or a positive number for the unix timestamp in seconds is allowed');
const copy = {
...c
};
if (copy.url) {
(0, _utils.assert)(copy.url !== 'about:blank', `Blank page can not have cookie "${c.name}"`);
(0, _utils.assert)(!copy.url.startsWith('data:'), `Data URL page can not have cookie "${c.name}"`);
const url = new URL(copy.url);
copy.domain = url.hostname;
copy.path = url.pathname.substring(0, url.pathname.lastIndexOf('/') + 1);
copy.secure = url.protocol === 'https:';
}
return copy;
});
}
function parsedURL(url) {
try {
return new URL(url);
} catch (e) {
return null;
}
}
function stripFragmentFromUrl(url) {
if (!url.includes('#')) return url;
return url.substring(0, url.indexOf('#'));
}
class Request extends _instrumentation.SdkObject {
constructor(context, frame, serviceWorker, redirectedFrom, documentId, url, resourceType, method, postData, headers) {
super(frame || context, 'request');
this._response = null;
this._redirectedFrom = void 0;
this._redirectedTo = null;
this._documentId = void 0;
this._isFavicon = void 0;
this._failureText = null;
this._url = void 0;
this._resourceType = void 0;
this._method = void 0;
this._postData = void 0;
this._headers = void 0;
this._headersMap = new Map();
this._frame = null;
this._serviceWorker = null;
this._context = void 0;
this._rawRequestHeadersPromise = new _manualPromise.ManualPromise();
this._waitForResponsePromise = new _manualPromise.ManualPromise();
this._responseEndTiming = -1;
this._overrides = void 0;
(0, _utils.assert)(!url.startsWith('data:'), 'Data urls should not fire requests');
this._context = context;
this._frame = frame;
this._serviceWorker = serviceWorker;
this._redirectedFrom = redirectedFrom;
if (redirectedFrom) redirectedFrom._redirectedTo = this;
this._documentId = documentId;
this._url = stripFragmentFromUrl(url);
this._resourceType = resourceType;
this._method = method;
this._postData = postData;
this._headers = headers;
this._updateHeadersMap();
this._isFavicon = url.endsWith('/favicon.ico') || !!(redirectedFrom !== null && redirectedFrom !== void 0 && redirectedFrom._isFavicon);
}
_setFailureText(failureText) {
this._failureText = failureText;
this._waitForResponsePromise.resolve(null);
}
_setOverrides(overrides) {
this._overrides = overrides;
this._updateHeadersMap();
}
_updateHeadersMap() {
for (const {
name,
value
} of this.headers()) this._headersMap.set(name.toLowerCase(), value);
}
_hasOverrides() {
return !!this._overrides;
}
url() {
var _this$_overrides;
return ((_this$_overrides = this._overrides) === null || _this$_overrides === void 0 ? void 0 : _this$_overrides.url) || this._url;
}
resourceType() {
return this._resourceType;
}
method() {
var _this$_overrides2;
return ((_this$_overrides2 = this._overrides) === null || _this$_overrides2 === void 0 ? void 0 : _this$_overrides2.method) || this._method;
}
postDataBuffer() {
var _this$_overrides3;
return ((_this$_overrides3 = this._overrides) === null || _this$_overrides3 === void 0 ? void 0 : _this$_overrides3.postData) || this._postData;
}
headers() {
var _this$_overrides4;
return ((_this$_overrides4 = this._overrides) === null || _this$_overrides4 === void 0 ? void 0 : _this$_overrides4.headers) || this._headers;
}
headerValue(name) {
return this._headersMap.get(name);
}
// "null" means no raw headers available - we'll use provisional headers as raw headers.
setRawRequestHeaders(headers) {
if (!this._rawRequestHeadersPromise.isDone()) this._rawRequestHeadersPromise.resolve(headers || this._headers);
}
async rawRequestHeaders() {
var _this$_overrides5;
return ((_this$_overrides5 = this._overrides) === null || _this$_overrides5 === void 0 ? void 0 : _this$_overrides5.headers) || this._rawRequestHeadersPromise;
}
response() {
return this._waitForResponsePromise;
}
_existingResponse() {
return this._response;
}
_setResponse(response) {
this._response = response;
this._waitForResponsePromise.resolve(response);
}
_finalRequest() {
return this._redirectedTo ? this._redirectedTo._finalRequest() : this;
}
frame() {
return this._frame;
}
serviceWorker() {
return this._serviceWorker;
}
isNavigationRequest() {
return !!this._documentId;
}
redirectedFrom() {
return this._redirectedFrom;
}
failure() {
if (this._failureText === null) return null;
return {
errorText: this._failureText
};
}
bodySize() {
var _this$postDataBuffer;
return ((_this$postDataBuffer = this.postDataBuffer()) === null || _this$postDataBuffer === void 0 ? void 0 : _this$postDataBuffer.length) || 0;
}
async requestHeadersSize() {
let headersSize = 4; // 4 = 2 spaces + 2 line breaks (GET /path \r\n)
headersSize += this.method().length;
headersSize += new URL(this.url()).pathname.length;
headersSize += 8; // httpVersion
const headers = await this.rawRequestHeaders();
for (const header of headers) headersSize += header.name.length + header.value.length + 4; // 4 = ': ' + '\r\n'
return headersSize;
}
}
exports.Request = Request;
class Route extends _instrumentation.SdkObject {
constructor(request, delegate) {
super(request._frame || request._context, 'route');
this._request = void 0;
this._delegate = void 0;
this._handled = false;
this._request = request;
this._delegate = delegate;
this._request._context.addRouteInFlight(this);
}
request() {
return this._request;
}
async abort(errorCode = 'failed') {
this._startHandling();
await this._delegate.abort(errorCode);
this._endHandling();
}
async redirectNavigationRequest(url) {
this._startHandling();
(0, _utils.assert)(this._request.isNavigationRequest());
this._request.frame().redirectNavigation(url, this._request._documentId, this._request.headerValue('referer'));
}
async fulfill(overrides) {
this._startHandling();
let body = overrides.body;
let isBase64 = overrides.isBase64 || false;
if (body === undefined) {
if (overrides.fetchResponseUid) {
const buffer = this._request._context.fetchRequest.fetchResponses.get(overrides.fetchResponseUid) || _fetch.APIRequestContext.findResponseBody(overrides.fetchResponseUid);
(0, _utils.assert)(buffer, 'Fetch response has been disposed');
body = buffer.toString('base64');
isBase64 = true;
} else {
body = '';
isBase64 = false;
}
}
const headers = [...(overrides.headers || [])];
this._maybeAddCorsHeaders(headers);
await this._delegate.fulfill({
status: overrides.status || 200,
headers,
body,
isBase64
});
this._endHandling();
}
// See https://github.com/microsoft/playwright/issues/12929
_maybeAddCorsHeaders(headers) {
const origin = this._request.headerValue('origin');
if (!origin) return;
const requestUrl = new URL(this._request.url());
if (!requestUrl.protocol.startsWith('http')) return;
if (requestUrl.origin === origin.trim()) return;
const corsHeader = headers.find(({
name
}) => name === 'access-control-allow-origin');
if (corsHeader) return;
headers.push({
name: 'access-control-allow-origin',
value: origin
});
headers.push({
name: 'access-control-allow-credentials',
value: 'true'
});
headers.push({
name: 'vary',
value: 'Origin'
});
}
async continue(overrides = {}) {
this._startHandling();
if (overrides.url) {
const newUrl = new URL(overrides.url);
const oldUrl = new URL(this._request.url());
if (oldUrl.protocol !== newUrl.protocol) throw new Error('New URL must have same protocol as overridden URL');
}
this._request._setOverrides(overrides);
await this._delegate.continue(this._request, overrides);
this._endHandling();
}
_startHandling() {
(0, _utils.assert)(!this._handled, 'Route is already handled!');
this._handled = true;
}
_endHandling() {
this._request._context.removeRouteInFlight(this);
}
}
exports.Route = Route;
class Response extends _instrumentation.SdkObject {
constructor(request, status, statusText, headers, timing, getResponseBodyCallback, fromServiceWorker, httpVersion) {
super(request.frame() || request._context, 'response');
this._request = void 0;
this._contentPromise = null;
this._finishedPromise = new _manualPromise.ManualPromise();
this._status = void 0;
this._statusText = void 0;
this._url = void 0;
this._headers = void 0;
this._headersMap = new Map();
this._getResponseBodyCallback = void 0;
this._timing = void 0;
this._serverAddrPromise = new _manualPromise.ManualPromise();
this._securityDetailsPromise = new _manualPromise.ManualPromise();
this._rawResponseHeadersPromise = new _manualPromise.ManualPromise();
this._httpVersion = void 0;
this._fromServiceWorker = void 0;
this._encodedBodySizePromise = new _manualPromise.ManualPromise();
this._transferSizePromise = new _manualPromise.ManualPromise();
this._responseHeadersSizePromise = new _manualPromise.ManualPromise();
this._request = request;
this._timing = timing;
this._status = status;
this._statusText = statusText;
this._url = request.url();
this._headers = headers;
for (const {
name,
value
} of this._headers) this._headersMap.set(name.toLowerCase(), value);
this._getResponseBodyCallback = getResponseBodyCallback;
this._request._setResponse(this);
this._httpVersion = httpVersion;
this._fromServiceWorker = fromServiceWorker;
}
_serverAddrFinished(addr) {
this._serverAddrPromise.resolve(addr);
}
_securityDetailsFinished(securityDetails) {
this._securityDetailsPromise.resolve(securityDetails);
}
_requestFinished(responseEndTiming) {
this._request._responseEndTiming = Math.max(responseEndTiming, this._timing.responseStart);
// Set start time equal to end when request is served from memory cache.
if (this._timing.requestStart === -1) this._timing.requestStart = this._request._responseEndTiming;
this._finishedPromise.resolve();
}
_setHttpVersion(httpVersion) {
this._httpVersion = httpVersion;
}
url() {
return this._url;
}
status() {
return this._status;
}
statusText() {
return this._statusText;
}
headers() {
return this._headers;
}
headerValue(name) {
return this._headersMap.get(name);
}
async rawResponseHeaders() {
return this._rawResponseHeadersPromise;
}
// "null" means no raw headers available - we'll use provisional headers as raw headers.
setRawResponseHeaders(headers) {
if (!this._rawResponseHeadersPromise.isDone()) this._rawResponseHeadersPromise.resolve(headers || this._headers);
}
setTransferSize(size) {
this._transferSizePromise.resolve(size);
}
setEncodedBodySize(size) {
this._encodedBodySizePromise.resolve(size);
}
setResponseHeadersSize(size) {
this._responseHeadersSizePromise.resolve(size);
}
timing() {
return this._timing;
}
async serverAddr() {
return (await this._serverAddrPromise) || null;
}
async securityDetails() {
return (await this._securityDetailsPromise) || null;
}
body() {
if (!this._contentPromise) {
this._contentPromise = this._finishedPromise.then(async () => {
if (this._status >= 300 && this._status <= 399) throw new Error('Response body is unavailable for redirect responses');
return this._getResponseBodyCallback();
});
}
return this._contentPromise;
}
request() {
return this._request;
}
frame() {
return this._request.frame();
}
httpVersion() {
if (!this._httpVersion) return 'HTTP/1.1';
if (this._httpVersion === 'http/1.1') return 'HTTP/1.1';
if (this._httpVersion === 'h2') return 'HTTP/2.0';
return this._httpVersion;
}
fromServiceWorker() {
return this._fromServiceWorker;
}
async responseHeadersSize() {
const availableSize = await this._responseHeadersSizePromise;
if (availableSize !== null) return availableSize;
// Fallback to calculating it manually.
let headersSize = 4; // 4 = 2 spaces + 2 line breaks (HTTP/1.1 200 Ok\r\n)
headersSize += 8; // httpVersion;
headersSize += 3; // statusCode;
headersSize += this.statusText().length;
const headers = await this._rawResponseHeadersPromise;
for (const header of headers) headersSize += header.name.length + header.value.length + 4; // 4 = ': ' + '\r\n'
headersSize += 2; // '\r\n'
return headersSize;
}
async sizes() {
const requestHeadersSize = await this._request.requestHeadersSize();
const responseHeadersSize = await this.responseHeadersSize();
let encodedBodySize = await this._encodedBodySizePromise;
if (encodedBodySize === null) {
var _headers$find;
// Fallback to calculating it manually.
const headers = await this._rawResponseHeadersPromise;
const contentLength = (_headers$find = headers.find(h => h.name.toLowerCase() === 'content-length')) === null || _headers$find === void 0 ? void 0 : _headers$find.value;
encodedBodySize = contentLength ? +contentLength : 0;
}
let transferSize = await this._transferSizePromise;
if (transferSize === null) {
// Fallback to calculating it manually.
transferSize = responseHeadersSize + encodedBodySize;
}
return {
requestBodySize: this._request.bodySize(),
requestHeadersSize,
responseBodySize: encodedBodySize,
responseHeadersSize,
transferSize
};
}
}
exports.Response = Response;
class WebSocket extends _instrumentation.SdkObject {
constructor(parent, url) {
super(parent, 'ws');
this._url = void 0;
this._notified = false;
this._url = url;
}
markAsNotified() {
// Sometimes we get "onWebSocketRequest" twice, at least in Chromium.
// Perhaps websocket is restarted because of chrome.webRequest extensions api?
// Or maybe the handshake response was a redirect?
if (this._notified) return false;
this._notified = true;
return true;
}
url() {
return this._url;
}
frameSent(opcode, data) {
this.emit(WebSocket.Events.FrameSent, {
opcode,
data
});
}
frameReceived(opcode, data) {
this.emit(WebSocket.Events.FrameReceived, {
opcode,
data
});
}
error(errorMessage) {
this.emit(WebSocket.Events.SocketError, errorMessage);
}
closed() {
this.emit(WebSocket.Events.Close);
}
}
exports.WebSocket = WebSocket;
WebSocket.Events = {
Close: 'close',
SocketError: 'socketerror',
FrameReceived: 'framereceived',
FrameSent: 'framesent'
};
// List taken from https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml with extra 306 and 418 codes.
const STATUS_TEXTS = {
'100': 'Continue',
'101': 'Switching Protocols',
'102': 'Processing',
'103': 'Early Hints',
'200': 'OK',
'201': 'Created',
'202': 'Accepted',
'203': 'Non-Authoritative Information',
'204': 'No Content',
'205': 'Reset Content',
'206': 'Partial Content',
'207': 'Multi-Status',
'208': 'Already Reported',
'226': 'IM Used',
'300': 'Multiple Choices',
'301': 'Moved Permanently',
'302': 'Found',
'303': 'See Other',
'304': 'Not Modified',
'305': 'Use Proxy',
'306': 'Switch Proxy',
'307': 'Temporary Redirect',
'308': 'Permanent Redirect',
'400': 'Bad Request',
'401': 'Unauthorized',
'402': 'Payment Required',
'403': 'Forbidden',
'404': 'Not Found',
'405': 'Method Not Allowed',
'406': 'Not Acceptable',
'407': 'Proxy Authentication Required',
'408': 'Request Timeout',
'409': 'Conflict',
'410': 'Gone',
'411': 'Length Required',
'412': 'Precondition Failed',
'413': 'Payload Too Large',
'414': 'URI Too Long',
'415': 'Unsupported Media Type',
'416': 'Range Not Satisfiable',
'417': 'Expectation Failed',
'418': 'I\'m a teapot',
'421': 'Misdirected Request',
'422': 'Unprocessable Entity',
'423': 'Locked',
'424': 'Failed Dependency',
'425': 'Too Early',
'426': 'Upgrade Required',
'428': 'Precondition Required',
'429': 'Too Many Requests',
'431': 'Request Header Fields Too Large',
'451': 'Unavailable For Legal Reasons',
'500': 'Internal Server Error',
'501': 'Not Implemented',
'502': 'Bad Gateway',
'503': 'Service Unavailable',
'504': 'Gateway Timeout',
'505': 'HTTP Version Not Supported',
'506': 'Variant Also Negotiates',
'507': 'Insufficient Storage',
'508': 'Loop Detected',
'510': 'Not Extended',
'511': 'Network Authentication Required'
};
exports.STATUS_TEXTS = STATUS_TEXTS;
function singleHeader(name, value) {
return [{
name,
value
}];
}
function mergeHeaders(headers) {
const lowerCaseToValue = new Map();
const lowerCaseToOriginalCase = new Map();
for (const h of headers) {
if (!h) continue;
for (const {
name,
value
} of h) {
const lower = name.toLowerCase();
lowerCaseToOriginalCase.set(lower, name);
lowerCaseToValue.set(lower, value);
}
}
const result = [];
for (const [lower, value] of lowerCaseToValue) result.push({
name: lowerCaseToOriginalCase.get(lower),
value
});
return result;
}