123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607 |
- "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.kMaxCookieExpiresDateInSeconds = void 0;
- 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");
- var _browserContext = require("./browserContext");
- /**
- * 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 = exports.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();
- this._request._context.emit(_browserContext.BrowserContext.Events.RequestAborted, this._request);
- 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);
- this._request._context.emit(_browserContext.BrowserContext.Events.RequestFulfilled, this._request);
- await this._delegate.fulfill({
- status: overrides.status || 200,
- headers,
- body: 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);
- if (!overrides.isFallback) this._request._context.emit(_browserContext.BrowserContext.Events.RequestContinued, this._request);
- 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 = exports.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'
- };
- 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;
- }
|