123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601 |
- "use strict";
- Object.defineProperty(exports, "__esModule", {
- value: true
- });
- exports.WebSocket = exports.RouteHandler = exports.Route = exports.Response = exports.Request = exports.RawHeaders = void 0;
- exports.validateHeaders = validateHeaders;
- var _url = require("url");
- var _channelOwner = require("./channelOwner");
- var _frame = require("./frame");
- var _worker = require("./worker");
- var _fs = _interopRequireDefault(require("fs"));
- var _utilsBundle = require("../utilsBundle");
- var _utils = require("../utils");
- var _manualPromise = require("../utils/manualPromise");
- var _events = require("./events");
- var _waiter = require("./waiter");
- var _network = require("../utils/network");
- var _multimap = require("../utils/multimap");
- var _fetch = require("./fetch");
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
- /**
- * 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.
- */
- class Request extends _channelOwner.ChannelOwner {
- static from(request) {
- return request._object;
- }
- static fromNullable(request) {
- return request ? Request.from(request) : null;
- }
- constructor(parent, type, guid, initializer) {
- super(parent, type, guid, initializer);
- this._redirectedFrom = null;
- this._redirectedTo = null;
- this._failureText = null;
- this._provisionalHeaders = void 0;
- this._actualHeadersPromise = void 0;
- this._timing = void 0;
- this._fallbackOverrides = {};
- this._redirectedFrom = Request.fromNullable(initializer.redirectedFrom);
- if (this._redirectedFrom) this._redirectedFrom._redirectedTo = this;
- this._provisionalHeaders = new RawHeaders(initializer.headers);
- this._fallbackOverrides.postDataBuffer = initializer.postData;
- this._timing = {
- startTime: 0,
- domainLookupStart: -1,
- domainLookupEnd: -1,
- connectStart: -1,
- secureConnectionStart: -1,
- connectEnd: -1,
- requestStart: -1,
- responseStart: -1,
- responseEnd: -1
- };
- }
- url() {
- return this._fallbackOverrides.url || this._initializer.url;
- }
- resourceType() {
- return this._initializer.resourceType;
- }
- method() {
- return this._fallbackOverrides.method || this._initializer.method;
- }
- postData() {
- var _this$_fallbackOverri;
- return ((_this$_fallbackOverri = this._fallbackOverrides.postDataBuffer) === null || _this$_fallbackOverri === void 0 ? void 0 : _this$_fallbackOverri.toString('utf-8')) || null;
- }
- postDataBuffer() {
- return this._fallbackOverrides.postDataBuffer || null;
- }
- postDataJSON() {
- const postData = this.postData();
- if (!postData) return null;
- const contentType = this.headers()['content-type'];
- if (contentType === 'application/x-www-form-urlencoded') {
- const entries = {};
- const parsed = new _url.URLSearchParams(postData);
- for (const [k, v] of parsed.entries()) entries[k] = v;
- return entries;
- }
- try {
- return JSON.parse(postData);
- } catch (e) {
- throw new Error('POST data is not a valid JSON object: ' + postData);
- }
- }
- /**
- * @deprecated
- */
- headers() {
- if (this._fallbackOverrides.headers) return RawHeaders._fromHeadersObjectLossy(this._fallbackOverrides.headers).headers();
- return this._provisionalHeaders.headers();
- }
- async _actualHeaders() {
- if (this._fallbackOverrides.headers) return RawHeaders._fromHeadersObjectLossy(this._fallbackOverrides.headers);
- if (!this._actualHeadersPromise) {
- this._actualHeadersPromise = this._wrapApiCall(async () => {
- return new RawHeaders((await this._channel.rawRequestHeaders()).headers);
- });
- }
- return await this._actualHeadersPromise;
- }
- async allHeaders() {
- return (await this._actualHeaders()).headers();
- }
- async headersArray() {
- return (await this._actualHeaders()).headersArray();
- }
- async headerValue(name) {
- return (await this._actualHeaders()).get(name);
- }
- async response() {
- return Response.fromNullable((await this._channel.response()).response);
- }
- async _internalResponse() {
- return await this._wrapApiCall(async () => {
- return Response.fromNullable((await this._channel.response()).response);
- }, true);
- }
- frame() {
- if (!this._initializer.frame) {
- (0, _utils.assert)(this.serviceWorker());
- throw new Error('Service Worker requests do not have an associated frame.');
- }
- const frame = _frame.Frame.from(this._initializer.frame);
- if (!frame._page) {
- throw new Error(['Frame for this navigation request is not available, because the request', 'was issued before the frame is created. You can check whether the request', 'is a navigation request by calling isNavigationRequest() method.'].join('\n'));
- }
- return frame;
- }
- _safePage() {
- var _Frame$fromNullable;
- return ((_Frame$fromNullable = _frame.Frame.fromNullable(this._initializer.frame)) === null || _Frame$fromNullable === void 0 ? void 0 : _Frame$fromNullable._page) || null;
- }
- serviceWorker() {
- return this._initializer.serviceWorker ? _worker.Worker.from(this._initializer.serviceWorker) : null;
- }
- isNavigationRequest() {
- return this._initializer.isNavigationRequest;
- }
- redirectedFrom() {
- return this._redirectedFrom;
- }
- redirectedTo() {
- return this._redirectedTo;
- }
- failure() {
- if (this._failureText === null) return null;
- return {
- errorText: this._failureText
- };
- }
- timing() {
- return this._timing;
- }
- async sizes() {
- const response = await this.response();
- if (!response) throw new Error('Unable to fetch sizes for failed request');
- return (await response._channel.sizes()).sizes;
- }
- _setResponseEndTiming(responseEndTiming) {
- this._timing.responseEnd = responseEndTiming;
- if (this._timing.responseStart === -1) this._timing.responseStart = responseEndTiming;
- }
- _finalRequest() {
- return this._redirectedTo ? this._redirectedTo._finalRequest() : this;
- }
- _applyFallbackOverrides(overrides) {
- if (overrides.url) this._fallbackOverrides.url = overrides.url;
- if (overrides.method) this._fallbackOverrides.method = overrides.method;
- if (overrides.headers) this._fallbackOverrides.headers = overrides.headers;
- if ((0, _utils.isString)(overrides.postData)) this._fallbackOverrides.postDataBuffer = Buffer.from(overrides.postData, 'utf-8');else if (overrides.postData instanceof Buffer) this._fallbackOverrides.postDataBuffer = overrides.postData;else if (overrides.postData) this._fallbackOverrides.postDataBuffer = Buffer.from(JSON.stringify(overrides.postData), 'utf-8');
- }
- _fallbackOverridesForContinue() {
- return this._fallbackOverrides;
- }
- _targetClosedScope() {
- var _this$serviceWorker, _this$_safePage;
- return ((_this$serviceWorker = this.serviceWorker()) === null || _this$serviceWorker === void 0 ? void 0 : _this$serviceWorker._closedScope) || ((_this$_safePage = this._safePage()) === null || _this$_safePage === void 0 ? void 0 : _this$_safePage._closedOrCrashedScope) || new _manualPromise.LongStandingScope();
- }
- }
- exports.Request = Request;
- class Route extends _channelOwner.ChannelOwner {
- static from(route) {
- return route._object;
- }
- constructor(parent, type, guid, initializer) {
- super(parent, type, guid, initializer);
- this._handlingPromise = null;
- this._context = void 0;
- this._didThrow = false;
- }
- request() {
- return Request.from(this._initializer.request);
- }
- async _raceWithTargetClose(promise) {
- // When page closes or crashes, we catch any potential rejects from this Route.
- // Note that page could be missing when routing popup's initial request that
- // does not have a Page initialized just yet.
- return await this.request()._targetClosedScope().safeRace(promise);
- }
- async _startHandling() {
- this._handlingPromise = new _manualPromise.ManualPromise();
- return await this._handlingPromise;
- }
- async fallback(options = {}) {
- this._checkNotHandled();
- this.request()._applyFallbackOverrides(options);
- this._reportHandled(false);
- }
- async abort(errorCode) {
- await this._handleRoute(async () => {
- await this._raceWithTargetClose(this._channel.abort({
- requestUrl: this.request()._initializer.url,
- errorCode
- }));
- });
- }
- async _redirectNavigationRequest(url) {
- await this._handleRoute(async () => {
- await this._raceWithTargetClose(this._channel.redirectNavigationRequest({
- url
- }));
- });
- }
- async fetch(options = {}) {
- return await this._wrapApiCall(async () => {
- return await this._context.request._innerFetch({
- request: this.request(),
- data: options.postData,
- ...options
- });
- });
- }
- async fulfill(options = {}) {
- await this._handleRoute(async () => {
- await this._wrapApiCall(async () => {
- await this._innerFulfill(options);
- });
- });
- }
- async _handleRoute(callback) {
- this._checkNotHandled();
- try {
- await callback();
- this._reportHandled(true);
- } catch (e) {
- this._didThrow = true;
- throw e;
- }
- }
- async _innerFulfill(options = {}) {
- let fetchResponseUid;
- let {
- status: statusOption,
- headers: headersOption,
- body
- } = options;
- if (options.json !== undefined) {
- (0, _utils.assert)(options.body === undefined, 'Can specify either body or json parameters');
- body = JSON.stringify(options.json);
- }
- if (options.response instanceof _fetch.APIResponse) {
- var _statusOption, _headersOption;
- (_statusOption = statusOption) !== null && _statusOption !== void 0 ? _statusOption : statusOption = options.response.status();
- (_headersOption = headersOption) !== null && _headersOption !== void 0 ? _headersOption : headersOption = options.response.headers();
- if (body === undefined && options.path === undefined) {
- if (options.response._request._connection === this._connection) fetchResponseUid = options.response._fetchUid();else body = await options.response.body();
- }
- }
- let isBase64 = false;
- let length = 0;
- if (options.path) {
- const buffer = await _fs.default.promises.readFile(options.path);
- body = buffer.toString('base64');
- isBase64 = true;
- length = buffer.length;
- } else if ((0, _utils.isString)(body)) {
- isBase64 = false;
- length = Buffer.byteLength(body);
- } else if (body) {
- length = body.length;
- body = body.toString('base64');
- isBase64 = true;
- }
- const headers = {};
- for (const header of Object.keys(headersOption || {})) headers[header.toLowerCase()] = String(headersOption[header]);
- if (options.contentType) headers['content-type'] = String(options.contentType);else if (options.json) headers['content-type'] = 'application/json';else if (options.path) headers['content-type'] = _utilsBundle.mime.getType(options.path) || 'application/octet-stream';
- if (length && !('content-length' in headers)) headers['content-length'] = String(length);
- await this._raceWithTargetClose(this._channel.fulfill({
- requestUrl: this.request()._initializer.url,
- status: statusOption || 200,
- headers: (0, _utils.headersObjectToArray)(headers),
- body,
- isBase64,
- fetchResponseUid
- }));
- }
- async continue(options = {}) {
- await this._handleRoute(async () => {
- this.request()._applyFallbackOverrides(options);
- await this._innerContinue();
- });
- }
- _checkNotHandled() {
- if (!this._handlingPromise) throw new Error('Route is already handled!');
- }
- _reportHandled(done) {
- const chain = this._handlingPromise;
- this._handlingPromise = null;
- chain.resolve(done);
- }
- async _innerContinue(internal = false) {
- const options = this.request()._fallbackOverridesForContinue();
- return await this._wrapApiCall(async () => {
- await this._raceWithTargetClose(this._channel.continue({
- requestUrl: this.request()._initializer.url,
- url: options.url,
- method: options.method,
- headers: options.headers ? (0, _utils.headersObjectToArray)(options.headers) : undefined,
- postData: options.postDataBuffer,
- isFallback: internal
- }));
- }, !!internal);
- }
- }
- exports.Route = Route;
- class Response extends _channelOwner.ChannelOwner {
- static from(response) {
- return response._object;
- }
- static fromNullable(response) {
- return response ? Response.from(response) : null;
- }
- constructor(parent, type, guid, initializer) {
- super(parent, type, guid, initializer);
- this._provisionalHeaders = void 0;
- this._actualHeadersPromise = void 0;
- this._request = void 0;
- this._finishedPromise = new _manualPromise.ManualPromise();
- this._provisionalHeaders = new RawHeaders(initializer.headers);
- this._request = Request.from(this._initializer.request);
- Object.assign(this._request._timing, this._initializer.timing);
- }
- url() {
- return this._initializer.url;
- }
- ok() {
- // Status 0 is for file:// URLs
- return this._initializer.status === 0 || this._initializer.status >= 200 && this._initializer.status <= 299;
- }
- status() {
- return this._initializer.status;
- }
- statusText() {
- return this._initializer.statusText;
- }
- fromServiceWorker() {
- return this._initializer.fromServiceWorker;
- }
- /**
- * @deprecated
- */
- headers() {
- return this._provisionalHeaders.headers();
- }
- async _actualHeaders() {
- if (!this._actualHeadersPromise) {
- this._actualHeadersPromise = (async () => {
- return new RawHeaders((await this._channel.rawResponseHeaders()).headers);
- })();
- }
- return await this._actualHeadersPromise;
- }
- async allHeaders() {
- return (await this._actualHeaders()).headers();
- }
- async headersArray() {
- return (await this._actualHeaders()).headersArray().slice();
- }
- async headerValue(name) {
- return (await this._actualHeaders()).get(name);
- }
- async headerValues(name) {
- return (await this._actualHeaders()).getAll(name);
- }
- async finished() {
- return await this.request()._targetClosedScope().race(this._finishedPromise);
- }
- async body() {
- return (await this._channel.body()).binary;
- }
- async text() {
- const content = await this.body();
- return content.toString('utf8');
- }
- async json() {
- const content = await this.text();
- return JSON.parse(content);
- }
- request() {
- return this._request;
- }
- frame() {
- return this._request.frame();
- }
- async serverAddr() {
- return (await this._channel.serverAddr()).value || null;
- }
- async securityDetails() {
- return (await this._channel.securityDetails()).value || null;
- }
- }
- exports.Response = Response;
- class WebSocket extends _channelOwner.ChannelOwner {
- static from(webSocket) {
- return webSocket._object;
- }
- constructor(parent, type, guid, initializer) {
- super(parent, type, guid, initializer);
- this._page = void 0;
- this._isClosed = void 0;
- this._isClosed = false;
- this._page = parent;
- this._channel.on('frameSent', event => {
- if (event.opcode === 1) this.emit(_events.Events.WebSocket.FrameSent, {
- payload: event.data
- });else if (event.opcode === 2) this.emit(_events.Events.WebSocket.FrameSent, {
- payload: Buffer.from(event.data, 'base64')
- });
- });
- this._channel.on('frameReceived', event => {
- if (event.opcode === 1) this.emit(_events.Events.WebSocket.FrameReceived, {
- payload: event.data
- });else if (event.opcode === 2) this.emit(_events.Events.WebSocket.FrameReceived, {
- payload: Buffer.from(event.data, 'base64')
- });
- });
- this._channel.on('socketError', ({
- error
- }) => this.emit(_events.Events.WebSocket.Error, error));
- this._channel.on('close', () => {
- this._isClosed = true;
- this.emit(_events.Events.WebSocket.Close, this);
- });
- }
- url() {
- return this._initializer.url;
- }
- isClosed() {
- return this._isClosed;
- }
- async waitForEvent(event, optionsOrPredicate = {}) {
- return await this._wrapApiCall(async () => {
- const timeout = this._page._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
- const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
- const waiter = _waiter.Waiter.createForEvent(this, event);
- waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
- if (event !== _events.Events.WebSocket.Error) waiter.rejectOnEvent(this, _events.Events.WebSocket.Error, new Error('Socket error'));
- if (event !== _events.Events.WebSocket.Close) waiter.rejectOnEvent(this, _events.Events.WebSocket.Close, new Error('Socket closed'));
- waiter.rejectOnEvent(this._page, _events.Events.Page.Close, () => this._page._closeErrorWithReason());
- const result = await waiter.waitForEvent(this, event, predicate);
- waiter.dispose();
- return result;
- });
- }
- }
- exports.WebSocket = WebSocket;
- function validateHeaders(headers) {
- for (const key of Object.keys(headers)) {
- const value = headers[key];
- if (!Object.is(value, undefined) && !(0, _utils.isString)(value)) throw new Error(`Expected value of header "${key}" to be String, but "${typeof value}" is found.`);
- }
- }
- class RouteHandler {
- constructor(baseURL, url, handler, times = Number.MAX_SAFE_INTEGER) {
- this.handledCount = 0;
- this._baseURL = void 0;
- this._times = void 0;
- this.url = void 0;
- this.handler = void 0;
- this._ignoreException = false;
- this._activeInvocations = new Set();
- this._baseURL = baseURL;
- this._times = times;
- this.url = url;
- this.handler = handler;
- }
- static prepareInterceptionPatterns(handlers) {
- const patterns = [];
- let all = false;
- for (const handler of handlers) {
- if ((0, _utils.isString)(handler.url)) patterns.push({
- glob: handler.url
- });else if ((0, _utils.isRegExp)(handler.url)) patterns.push({
- regexSource: handler.url.source,
- regexFlags: handler.url.flags
- });else all = true;
- }
- if (all) return [{
- glob: '**/*'
- }];
- return patterns;
- }
- matches(requestURL) {
- return (0, _network.urlMatches)(this._baseURL, requestURL, this.url);
- }
- async handle(route) {
- const handlerInvocation = {
- complete: new _manualPromise.ManualPromise(),
- route
- };
- this._activeInvocations.add(handlerInvocation);
- try {
- return await this._handleInternal(route);
- } catch (e) {
- // If the handler was stopped (without waiting for completion), we ignore all exceptions.
- if (this._ignoreException) return false;
- throw e;
- } finally {
- handlerInvocation.complete.resolve();
- this._activeInvocations.delete(handlerInvocation);
- }
- }
- async stop(behavior) {
- // When a handler is manually unrouted or its page/context is closed we either
- // - wait for the current handler invocations to finish
- // - or do not wait, if the user opted out of it, but swallow all exceptions
- // that happen after the unroute/close.
- if (behavior === 'ignoreErrors') {
- this._ignoreException = true;
- } else {
- const promises = [];
- for (const activation of this._activeInvocations) {
- if (!activation.route._didThrow) promises.push(activation.complete);
- }
- await Promise.all(promises);
- }
- }
- async _handleInternal(route) {
- ++this.handledCount;
- const handledPromise = route._startHandling();
- // Extract handler into a variable to avoid [RouteHandler.handler] in the stack.
- const handler = this.handler;
- const [handled] = await Promise.all([handledPromise, handler(route, route.request())]);
- return handled;
- }
- willExpire() {
- return this.handledCount + 1 >= this._times;
- }
- }
- exports.RouteHandler = RouteHandler;
- class RawHeaders {
- static _fromHeadersObjectLossy(headers) {
- const headersArray = Object.entries(headers).map(([name, value]) => ({
- name,
- value
- })).filter(header => header.value !== undefined);
- return new RawHeaders(headersArray);
- }
- constructor(headers) {
- this._headersArray = void 0;
- this._headersMap = new _multimap.MultiMap();
- this._headersArray = headers;
- for (const header of headers) this._headersMap.set(header.name.toLowerCase(), header.value);
- }
- get(name) {
- const values = this.getAll(name);
- if (!values || !values.length) return null;
- return values.join(name.toLowerCase() === 'set-cookie' ? '\n' : ', ');
- }
- getAll(name) {
- return [...this._headersMap.get(name.toLowerCase())];
- }
- headers() {
- const result = {};
- for (const name of this._headersMap.keys()) result[name] = this.get(name);
- return result;
- }
- headersArray() {
- return this._headersArray;
- }
- }
- exports.RawHeaders = RawHeaders;
|