@checkly/playwright-core 1.41.2-beta.1 → 1.41.2-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/checkly/fetch.js +106 -0
- package/lib/client/fetch.js +12 -1
- package/lib/protocol/validator.js +23 -2
- package/lib/server/chromium/crNetworkManager.js +27 -12
- package/lib/server/chromium/videoRecorder.js +1 -1
- package/lib/server/dispatchers/frameDispatcher.js +6 -0
- package/lib/server/dispatchers/networkDispatchers.js +8 -1
- package/lib/server/fetch.js +26 -3
- package/lib/server/network.js +5 -23
- package/lib/utils/happy-eyeballs.js +17 -1
- package/lib/vite/recorder/assets/codeMirrorModule-85487eb6.js +24 -0
- package/lib/vite/recorder/assets/{index-bbf80321.js → index-b14c63fe.js} +1 -1
- package/lib/vite/recorder/index.html +1 -1
- package/lib/vite/{recorder/assets/codeMirrorModule-2a26f817.js → traceViewer/assets/codeMirrorModule-75b0ca4f.js} +13 -13
- package/lib/vite/traceViewer/assets/codeMirrorModule-c1454a2e.js +24 -0
- package/lib/vite/traceViewer/assets/codeMirrorModule-f333a775.js +24 -0
- package/lib/vite/traceViewer/assets/wsPort-762c6840.js +64 -0
- package/lib/vite/traceViewer/assets/wsPort-98e00a94.js +64 -0
- package/lib/vite/traceViewer/assets/wsPort-ee2830d7.js +64 -0
- package/lib/vite/traceViewer/index.a265fbdb.js +2 -0
- package/lib/vite/traceViewer/index.decad628.js +2 -0
- package/lib/vite/traceViewer/index.ed9a3c58.js +2 -0
- package/lib/vite/traceViewer/index.html +2 -2
- package/lib/vite/traceViewer/uiMode.08ab2d90.js +4 -0
- package/lib/vite/traceViewer/uiMode.0d0d667b.js +4 -0
- package/lib/vite/traceViewer/uiMode.3ff70f7d.js +4 -0
- package/lib/vite/traceViewer/uiMode.html +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.setSWatcherCurrentStepData = exports.parseQueryParameters = exports.initializeTimings = exports.getTimings = exports.getRequestHeaders = exports.getClientCertificates = exports.getBody = exports.addRequestListeners = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Get client custom certificates.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} options
|
|
11
|
+
* @returns Object
|
|
12
|
+
*/
|
|
13
|
+
const getClientCertificates = options => {
|
|
14
|
+
if (!options.certs) return {};
|
|
15
|
+
return {
|
|
16
|
+
cert: options.cert,
|
|
17
|
+
key: options.key,
|
|
18
|
+
passphrase: options.passphrase,
|
|
19
|
+
ca: options.ca
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
exports.getClientCertificates = getClientCertificates;
|
|
23
|
+
const setSWatcherCurrentStepData = (sWatcher, result) => {
|
|
24
|
+
if (!sWatcher) return;
|
|
25
|
+
sWatcher.appendCurrentStepData({
|
|
26
|
+
...result.response,
|
|
27
|
+
checklyStepCategory: 'api-call'
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
exports.setSWatcherCurrentStepData = setSWatcherCurrentStepData;
|
|
31
|
+
const getRequestHeaders = headers => {
|
|
32
|
+
return Object.entries(headers).map(([key, value]) => {
|
|
33
|
+
return {
|
|
34
|
+
name: key,
|
|
35
|
+
value: value
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
exports.getRequestHeaders = getRequestHeaders;
|
|
40
|
+
const parseQueryParameters = url => {
|
|
41
|
+
const searchParams = new URL(url).searchParams;
|
|
42
|
+
const parameters = [];
|
|
43
|
+
for (const [name, value] of searchParams.entries()) {
|
|
44
|
+
parameters.push({
|
|
45
|
+
name,
|
|
46
|
+
value
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return parameters;
|
|
50
|
+
};
|
|
51
|
+
exports.parseQueryParameters = parseQueryParameters;
|
|
52
|
+
const getTimings = eventTimes => {
|
|
53
|
+
const {
|
|
54
|
+
agent,
|
|
55
|
+
values
|
|
56
|
+
} = eventTimes;
|
|
57
|
+
const end = performance.now() - values.startTimeNow;
|
|
58
|
+
const socket = values.socket || 0;
|
|
59
|
+
const lookup = values.lookup || socket;
|
|
60
|
+
const connect = values.connect || lookup;
|
|
61
|
+
const response = values.response || connect;
|
|
62
|
+
return {
|
|
63
|
+
wait: agent ? socket - lookup : socket,
|
|
64
|
+
dns: agent ? lookup : lookup - socket,
|
|
65
|
+
tcp: agent ? connect - socket : connect - lookup,
|
|
66
|
+
firstByte: response - connect,
|
|
67
|
+
download: end - response,
|
|
68
|
+
total: end
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
exports.getTimings = getTimings;
|
|
72
|
+
const getBody = body => {
|
|
73
|
+
const responseBodySizeLimit = 100000;
|
|
74
|
+
//@ts-ignore
|
|
75
|
+
const bodyAsText = Buffer.byteLength(body, 'utf-8') > responseBodySizeLimit ? body.slice(0, responseBodySizeLimit).toString('utf8') + '...' : body.toString('utf8');
|
|
76
|
+
try {
|
|
77
|
+
return JSON.parse(bodyAsText);
|
|
78
|
+
} catch {
|
|
79
|
+
return bodyAsText;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
exports.getBody = getBody;
|
|
83
|
+
const initializeTimings = () => {
|
|
84
|
+
return {
|
|
85
|
+
values: {
|
|
86
|
+
startTimeNow: performance.now()
|
|
87
|
+
},
|
|
88
|
+
agent: false
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
exports.initializeTimings = initializeTimings;
|
|
92
|
+
const addRequestListeners = (request, timings) => {
|
|
93
|
+
request.on('socket', socket => {
|
|
94
|
+
timings.values.socket = performance.now() - timings.values.startTimeNow;
|
|
95
|
+
socket.on('connect', () => {
|
|
96
|
+
timings.values.connect = performance.now() - timings.values.startTimeNow;
|
|
97
|
+
});
|
|
98
|
+
socket.on('lookup', () => {
|
|
99
|
+
timings.values.lookup = performance.now() - timings.values.startTimeNow;
|
|
100
|
+
});
|
|
101
|
+
socket.on('secureConnect', () => {
|
|
102
|
+
timings.values.tlsHandshake = performance.now() - timings.values.startTimeNow;
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
exports.addRequestListeners = addRequestListeners;
|
package/lib/client/fetch.js
CHANGED
|
@@ -13,6 +13,7 @@ var _channelOwner = require("./channelOwner");
|
|
|
13
13
|
var _network = require("./network");
|
|
14
14
|
var _tracing = require("./tracing");
|
|
15
15
|
var _errors = require("./errors");
|
|
16
|
+
var _fetch = require("playwright-core/lib/checkly/fetch");
|
|
16
17
|
let _Symbol$asyncDispose, _Symbol$asyncDispose2, _util$inspect$custom;
|
|
17
18
|
/**
|
|
18
19
|
* Copyright (c) Microsoft Corporation.
|
|
@@ -55,6 +56,9 @@ class APIRequest {
|
|
|
55
56
|
storageState,
|
|
56
57
|
tracesDir
|
|
57
58
|
})).request);
|
|
59
|
+
// Checkly related code.
|
|
60
|
+
//@ts-ignore
|
|
61
|
+
context.__sWatcher__ = options.sWatcher;
|
|
58
62
|
this._contexts.add(context);
|
|
59
63
|
context._request = this;
|
|
60
64
|
context._tracing._tracesDir = tracesDir;
|
|
@@ -198,8 +202,12 @@ class APIRequestContext extends _channelOwner.ChannelOwner {
|
|
|
198
202
|
failOnStatusCode: options.failOnStatusCode,
|
|
199
203
|
ignoreHTTPSErrors: options.ignoreHTTPSErrors,
|
|
200
204
|
maxRedirects: maxRedirects,
|
|
201
|
-
|
|
205
|
+
tokenizedURL: options.tokenizedURL,
|
|
206
|
+
...fixtures,
|
|
207
|
+
...(0, _fetch.getClientCertificates)(options)
|
|
202
208
|
});
|
|
209
|
+
//@ts-ignore
|
|
210
|
+
(0, _fetch.setSWatcherCurrentStepData)(this.__sWatcher__, result);
|
|
203
211
|
return new APIResponse(this, result.response);
|
|
204
212
|
});
|
|
205
213
|
}
|
|
@@ -251,6 +259,9 @@ class APIResponse {
|
|
|
251
259
|
headersArray() {
|
|
252
260
|
return this._headers.headersArray();
|
|
253
261
|
}
|
|
262
|
+
timings() {
|
|
263
|
+
return this._initializer.timings;
|
|
264
|
+
}
|
|
254
265
|
async body() {
|
|
255
266
|
try {
|
|
256
267
|
const result = await this._request._channel.fetchResponseBody({
|
|
@@ -207,7 +207,12 @@ _validatorPrimitives.scheme.APIRequestContextFetchParams = (0, _validatorPrimiti
|
|
|
207
207
|
timeout: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber),
|
|
208
208
|
failOnStatusCode: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tBoolean),
|
|
209
209
|
ignoreHTTPSErrors: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tBoolean),
|
|
210
|
-
maxRedirects: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber)
|
|
210
|
+
maxRedirects: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber),
|
|
211
|
+
tokenizedURL: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tString),
|
|
212
|
+
cert: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tString),
|
|
213
|
+
key: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tString),
|
|
214
|
+
ca: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tString),
|
|
215
|
+
passphrase: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tString)
|
|
211
216
|
});
|
|
212
217
|
_validatorPrimitives.scheme.APIRequestContextFetchResult = (0, _validatorPrimitives.tObject)({
|
|
213
218
|
response: (0, _validatorPrimitives.tType)('APIResponse')
|
|
@@ -240,7 +245,23 @@ _validatorPrimitives.scheme.APIResponse = (0, _validatorPrimitives.tObject)({
|
|
|
240
245
|
url: _validatorPrimitives.tString,
|
|
241
246
|
status: _validatorPrimitives.tNumber,
|
|
242
247
|
statusText: _validatorPrimitives.tString,
|
|
243
|
-
headers: (0, _validatorPrimitives.tArray)((0, _validatorPrimitives.tType)('NameValue'))
|
|
248
|
+
headers: (0, _validatorPrimitives.tArray)((0, _validatorPrimitives.tType)('NameValue')),
|
|
249
|
+
requestHeaders: (0, _validatorPrimitives.tArray)((0, _validatorPrimitives.tType)('NameValue')),
|
|
250
|
+
responseHeaders: (0, _validatorPrimitives.tArray)((0, _validatorPrimitives.tType)('NameValue')),
|
|
251
|
+
body: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tAny),
|
|
252
|
+
method: _validatorPrimitives.tString,
|
|
253
|
+
tokenizedURL: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tString),
|
|
254
|
+
timings: (0, _validatorPrimitives.tObject)({
|
|
255
|
+
wait: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber),
|
|
256
|
+
dns: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber),
|
|
257
|
+
lookup: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber),
|
|
258
|
+
connect: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber),
|
|
259
|
+
tcp: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber),
|
|
260
|
+
firstByte: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber),
|
|
261
|
+
download: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber),
|
|
262
|
+
total: (0, _validatorPrimitives.tOptional)(_validatorPrimitives.tNumber)
|
|
263
|
+
}),
|
|
264
|
+
queryParams: (0, _validatorPrimitives.tOptional)((0, _validatorPrimitives.tArray)((0, _validatorPrimitives.tType)('NameValue')))
|
|
244
265
|
});
|
|
245
266
|
_validatorPrimitives.scheme.LifecycleEvent = (0, _validatorPrimitives.tEnum)(['load', 'domcontentloaded', 'networkidle', 'commit']);
|
|
246
267
|
_validatorPrimitives.scheme.LocalUtilsInitializer = (0, _validatorPrimitives.tObject)({
|
|
@@ -8,6 +8,7 @@ var _helper = require("../helper");
|
|
|
8
8
|
var _eventsHelper = require("../../utils/eventsHelper");
|
|
9
9
|
var network = _interopRequireWildcard(require("../network"));
|
|
10
10
|
var _utils = require("../../utils");
|
|
11
|
+
var _protocolError = require("../protocolError");
|
|
11
12
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
12
13
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
13
14
|
/**
|
|
@@ -543,30 +544,44 @@ class RouteImpl {
|
|
|
543
544
|
method: overrides.method,
|
|
544
545
|
postData: overrides.postData ? overrides.postData.toString('base64') : undefined
|
|
545
546
|
};
|
|
546
|
-
await
|
|
547
|
+
await catchDisallowedErrors(async () => {
|
|
548
|
+
await this._session.send('Fetch.continueRequest', this._alreadyContinuedParams);
|
|
549
|
+
});
|
|
547
550
|
}
|
|
548
551
|
async fulfill(response) {
|
|
549
552
|
const body = response.isBase64 ? response.body : Buffer.from(response.body).toString('base64');
|
|
550
553
|
const responseHeaders = splitSetCookieHeader(response.headers);
|
|
551
|
-
await
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
554
|
+
await catchDisallowedErrors(async () => {
|
|
555
|
+
await this._session.send('Fetch.fulfillRequest', {
|
|
556
|
+
requestId: this._interceptionId,
|
|
557
|
+
responseCode: response.status,
|
|
558
|
+
responsePhrase: network.STATUS_TEXTS[String(response.status)],
|
|
559
|
+
responseHeaders,
|
|
560
|
+
body
|
|
561
|
+
});
|
|
557
562
|
});
|
|
558
563
|
}
|
|
559
564
|
async abort(errorCode = 'failed') {
|
|
560
565
|
const errorReason = errorReasons[errorCode];
|
|
561
566
|
(0, _utils.assert)(errorReason, 'Unknown error code: ' + errorCode);
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
+
await catchDisallowedErrors(async () => {
|
|
568
|
+
await this._session.send('Fetch.failRequest', {
|
|
569
|
+
requestId: this._interceptionId,
|
|
570
|
+
errorReason
|
|
571
|
+
});
|
|
567
572
|
});
|
|
568
573
|
}
|
|
569
574
|
}
|
|
575
|
+
|
|
576
|
+
// In certain cases, protocol will return error if the request was already canceled
|
|
577
|
+
// or the page was closed. We should tolerate these errors but propagate other.
|
|
578
|
+
async function catchDisallowedErrors(callback) {
|
|
579
|
+
try {
|
|
580
|
+
return await callback();
|
|
581
|
+
} catch (e) {
|
|
582
|
+
if ((0, _protocolError.isProtocolError)(e) && e.message.includes('Invalid http status code or phrase')) throw e;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
570
585
|
function splitSetCookieHeader(headers) {
|
|
571
586
|
const index = headers.findIndex(({
|
|
572
587
|
name
|
|
@@ -25,7 +25,7 @@ var _instrumentation = require("../instrumentation");
|
|
|
25
25
|
* limitations under the License.
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
const fps =
|
|
28
|
+
const fps = 12;
|
|
29
29
|
class VideoRecorder {
|
|
30
30
|
static async launch(page, ffmpegPath, options) {
|
|
31
31
|
if (!options.outputFile.endsWith('.webm')) throw new Error('File must have .webm extension');
|
|
@@ -281,6 +281,12 @@ class FrameDispatcher extends _dispatcher.Dispatcher {
|
|
|
281
281
|
expectedValue
|
|
282
282
|
});
|
|
283
283
|
if (result.received !== undefined) result.received = (0, _jsHandleDispatcher.serializeResult)(result.received);
|
|
284
|
+
if (result.matches === params.isNot) metadata.error = {
|
|
285
|
+
error: {
|
|
286
|
+
name: 'Expect',
|
|
287
|
+
message: 'Expect failed'
|
|
288
|
+
}
|
|
289
|
+
};
|
|
284
290
|
return result;
|
|
285
291
|
}
|
|
286
292
|
}
|
|
@@ -199,7 +199,14 @@ class APIRequestContextDispatcher extends _dispatcher.Dispatcher {
|
|
|
199
199
|
status: fetchResponse.status,
|
|
200
200
|
statusText: fetchResponse.statusText,
|
|
201
201
|
headers: fetchResponse.headers,
|
|
202
|
-
fetchUid: fetchResponse.fetchUid
|
|
202
|
+
fetchUid: fetchResponse.fetchUid,
|
|
203
|
+
requestHeaders: fetchResponse.requestHeaders,
|
|
204
|
+
responseHeaders: fetchResponse.responseHeaders,
|
|
205
|
+
timings: fetchResponse.timings,
|
|
206
|
+
queryParams: fetchResponse.queryParams,
|
|
207
|
+
body: fetchResponse.body,
|
|
208
|
+
method: fetchResponse.method,
|
|
209
|
+
tokenizedURL: fetchResponse.tokenizedURL
|
|
203
210
|
}
|
|
204
211
|
};
|
|
205
212
|
}
|
package/lib/server/fetch.js
CHANGED
|
@@ -9,6 +9,7 @@ var https = _interopRequireWildcard(require("https"));
|
|
|
9
9
|
var _stream = require("stream");
|
|
10
10
|
var _url = _interopRequireDefault(require("url"));
|
|
11
11
|
var _zlib = _interopRequireDefault(require("zlib"));
|
|
12
|
+
var _perf_hooks = require("perf_hooks");
|
|
12
13
|
var _timeoutSettings = require("../common/timeoutSettings");
|
|
13
14
|
var _userAgent = require("../utils/userAgent");
|
|
14
15
|
var _utils = require("../utils");
|
|
@@ -21,6 +22,7 @@ var _instrumentation = require("./instrumentation");
|
|
|
21
22
|
var _progress = require("./progress");
|
|
22
23
|
var _tracing = require("./trace/recorder/tracing");
|
|
23
24
|
var _network = require("./network");
|
|
25
|
+
var _fetch = require("playwright-core/lib/checkly/fetch");
|
|
24
26
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
25
27
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
26
28
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
@@ -124,7 +126,9 @@ class APIRequestContext extends _instrumentation.SdkObject {
|
|
|
124
126
|
maxRedirects: params.maxRedirects === 0 ? -1 : params.maxRedirects === undefined ? 20 : params.maxRedirects,
|
|
125
127
|
timeout,
|
|
126
128
|
deadline,
|
|
127
|
-
__testHookLookup: params.__testHookLookup
|
|
129
|
+
__testHookLookup: params.__testHookLookup,
|
|
130
|
+
tokenizedURL: params.tokenizedURL,
|
|
131
|
+
...(0, _fetch.getClientCertificates)(params)
|
|
128
132
|
};
|
|
129
133
|
// rejectUnauthorized = undefined is treated as true in node 12.
|
|
130
134
|
if (params.ignoreHTTPSErrors || defaults.ignoreHTTPSErrors) options.rejectUnauthorized = false;
|
|
@@ -139,7 +143,14 @@ class APIRequestContext extends _instrumentation.SdkObject {
|
|
|
139
143
|
if (params.failOnStatusCode && (fetchResponse.status < 200 || fetchResponse.status >= 400)) throw new Error(`${fetchResponse.status} ${fetchResponse.statusText}`);
|
|
140
144
|
return {
|
|
141
145
|
...fetchResponse,
|
|
142
|
-
fetchUid
|
|
146
|
+
fetchUid,
|
|
147
|
+
requestHeaders: (0, _fetch.getRequestHeaders)(headers),
|
|
148
|
+
responseHeaders: fetchResponse.headers,
|
|
149
|
+
queryParams: (0, _fetch.parseQueryParameters)(fetchResponse.url),
|
|
150
|
+
url: params.tokenizedURL || fetchResponse.url,
|
|
151
|
+
timings: (0, _fetch.getTimings)(fetchResponse.timings),
|
|
152
|
+
body: (0, _fetch.getBody)(fetchResponse.body),
|
|
153
|
+
method: options.method
|
|
143
154
|
};
|
|
144
155
|
}
|
|
145
156
|
_parseSetCookieHeader(responseUrl, setCookie) {
|
|
@@ -195,7 +206,9 @@ class APIRequestContext extends _instrumentation.SdkObject {
|
|
|
195
206
|
...options,
|
|
196
207
|
agent
|
|
197
208
|
};
|
|
209
|
+
const timings = (0, _fetch.initializeTimings)();
|
|
198
210
|
const request = requestConstructor(url, requestOptions, async response => {
|
|
211
|
+
timings.values.response = _perf_hooks.performance.now() - timings.values.startTimeNow;
|
|
199
212
|
const notifyRequestFinished = body => {
|
|
200
213
|
const requestFinishedEvent = {
|
|
201
214
|
requestEvent,
|
|
@@ -292,6 +305,7 @@ class APIRequestContext extends _instrumentation.SdkObject {
|
|
|
292
305
|
response.on('aborted', () => reject(new Error('aborted')));
|
|
293
306
|
const chunks = [];
|
|
294
307
|
const notifyBodyFinished = () => {
|
|
308
|
+
timings.values.endAt = _perf_hooks.performance.now();
|
|
295
309
|
const body = Buffer.concat(chunks);
|
|
296
310
|
notifyRequestFinished(body);
|
|
297
311
|
fulfill({
|
|
@@ -299,7 +313,15 @@ class APIRequestContext extends _instrumentation.SdkObject {
|
|
|
299
313
|
status: response.statusCode || 0,
|
|
300
314
|
statusText: response.statusMessage || '',
|
|
301
315
|
headers: toHeadersArray(response.rawHeaders),
|
|
302
|
-
body
|
|
316
|
+
body,
|
|
317
|
+
timings: {
|
|
318
|
+
//@ts-ignore
|
|
319
|
+
values: {
|
|
320
|
+
...timings.values,
|
|
321
|
+
..._happyEyeballs.httpHappyEyeballsTimings.values
|
|
322
|
+
},
|
|
323
|
+
agent: _happyEyeballs.httpHappyEyeballsTimings.agent || timings.agent
|
|
324
|
+
}
|
|
303
325
|
});
|
|
304
326
|
};
|
|
305
327
|
let body = response;
|
|
@@ -328,6 +350,7 @@ class APIRequestContext extends _instrumentation.SdkObject {
|
|
|
328
350
|
body.on('data', chunk => chunks.push(chunk));
|
|
329
351
|
body.on('end', notifyBodyFinished);
|
|
330
352
|
});
|
|
353
|
+
(0, _fetch.addRequestListeners)(request, timings);
|
|
331
354
|
request.on('error', reject);
|
|
332
355
|
const disposeListener = () => {
|
|
333
356
|
reject(new Error('Request context disposed.'));
|
package/lib/server/network.js
CHANGED
|
@@ -126,15 +126,6 @@ class Request extends _instrumentation.SdkObject {
|
|
|
126
126
|
this._failureText = failureText;
|
|
127
127
|
this._waitForResponsePromise.resolve(null);
|
|
128
128
|
}
|
|
129
|
-
async _waitForRequestFailure() {
|
|
130
|
-
const response = await this._waitForResponsePromise;
|
|
131
|
-
// If response is null it was a failure an we are done.
|
|
132
|
-
if (!response) return;
|
|
133
|
-
await response._finishedPromise;
|
|
134
|
-
if (this.failure()) return;
|
|
135
|
-
// If request finished without errors, we stall.
|
|
136
|
-
await new Promise(() => {});
|
|
137
|
-
}
|
|
138
129
|
_setOverrides(overrides) {
|
|
139
130
|
this._overrides = overrides;
|
|
140
131
|
this._updateHeadersMap();
|
|
@@ -241,10 +232,7 @@ class Route extends _instrumentation.SdkObject {
|
|
|
241
232
|
async abort(errorCode = 'failed') {
|
|
242
233
|
this._startHandling();
|
|
243
234
|
this._request._context.emit(_browserContext.BrowserContext.Events.RequestAborted, this._request);
|
|
244
|
-
await
|
|
245
|
-
// If the request is already cancelled by the page before we handle the route,
|
|
246
|
-
// we'll receive loading failed event and will ignore route handling error.
|
|
247
|
-
this._request._waitForRequestFailure()]);
|
|
235
|
+
await this._delegate.abort(errorCode);
|
|
248
236
|
this._endHandling();
|
|
249
237
|
}
|
|
250
238
|
async redirectNavigationRequest(url) {
|
|
@@ -270,15 +258,12 @@ class Route extends _instrumentation.SdkObject {
|
|
|
270
258
|
const headers = [...(overrides.headers || [])];
|
|
271
259
|
this._maybeAddCorsHeaders(headers);
|
|
272
260
|
this._request._context.emit(_browserContext.BrowserContext.Events.RequestFulfilled, this._request);
|
|
273
|
-
await
|
|
261
|
+
await this._delegate.fulfill({
|
|
274
262
|
status: overrides.status || 200,
|
|
275
263
|
headers,
|
|
276
|
-
body,
|
|
264
|
+
body: body,
|
|
277
265
|
isBase64
|
|
278
|
-
})
|
|
279
|
-
// If the request is already cancelled by the page before we handle the route,
|
|
280
|
-
// we'll receive loading failed event and will ignore route handling error.
|
|
281
|
-
this._request._waitForRequestFailure()]);
|
|
266
|
+
});
|
|
282
267
|
this._endHandling();
|
|
283
268
|
}
|
|
284
269
|
|
|
@@ -315,10 +300,7 @@ class Route extends _instrumentation.SdkObject {
|
|
|
315
300
|
}
|
|
316
301
|
this._request._setOverrides(overrides);
|
|
317
302
|
if (!overrides.isFallback) this._request._context.emit(_browserContext.BrowserContext.Events.RequestContinued, this._request);
|
|
318
|
-
await
|
|
319
|
-
// If the request is already cancelled by the page before we handle the route,
|
|
320
|
-
// we'll receive loading failed event and will ignore route handling error.
|
|
321
|
-
this._request._waitForRequestFailure()]);
|
|
303
|
+
await this._delegate.continue(this._request, overrides);
|
|
322
304
|
this._endHandling();
|
|
323
305
|
}
|
|
324
306
|
_startHandling() {
|
|
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.createSocket = createSocket;
|
|
7
|
-
exports.httpsHappyEyeballsAgent = exports.httpHappyEyeballsAgent = void 0;
|
|
7
|
+
exports.httpsHappyEyeballsAgent = exports.httpHappyEyeballsTimings = exports.httpHappyEyeballsAgent = void 0;
|
|
8
8
|
var dns = _interopRequireWildcard(require("dns"));
|
|
9
9
|
var http = _interopRequireWildcard(require("http"));
|
|
10
10
|
var https = _interopRequireWildcard(require("https"));
|
|
@@ -33,6 +33,15 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
|
|
|
33
33
|
// https://www.rfc-editor.org/rfc/rfc8305
|
|
34
34
|
// Same as in Chromium (https://source.chromium.org/chromium/chromium/src/+/5666ff4f5077a7e2f72902f3a95f5d553ea0d88d:net/socket/transport_connect_job.cc;l=102)
|
|
35
35
|
const connectionAttemptDelayMs = 300;
|
|
36
|
+
const timings = {
|
|
37
|
+
values: {
|
|
38
|
+
startTimeNow: 0,
|
|
39
|
+
socket: 0,
|
|
40
|
+
connect: 0,
|
|
41
|
+
lookup: 0
|
|
42
|
+
},
|
|
43
|
+
agent: false
|
|
44
|
+
};
|
|
36
45
|
class HttpHappyEyeballsAgent extends http.Agent {
|
|
37
46
|
createConnection(options, oncreate) {
|
|
38
47
|
// There is no ambiguity in case of IP address.
|
|
@@ -42,6 +51,8 @@ class HttpHappyEyeballsAgent extends http.Agent {
|
|
|
42
51
|
}
|
|
43
52
|
class HttpsHappyEyeballsAgent extends https.Agent {
|
|
44
53
|
createConnection(options, oncreate) {
|
|
54
|
+
timings.values.startTimeNow = performance.now();
|
|
55
|
+
timings.agent = true;
|
|
45
56
|
// There is no ambiguity in case of IP address.
|
|
46
57
|
if (net.isIP(clientRequestArgsToHostName(options))) return tls.connect(options);
|
|
47
58
|
createConnectionAsync(options, oncreate, /* useTLS */true).catch(err => oncreate === null || oncreate === void 0 ? void 0 : oncreate(err));
|
|
@@ -49,6 +60,7 @@ class HttpsHappyEyeballsAgent extends https.Agent {
|
|
|
49
60
|
}
|
|
50
61
|
const httpsHappyEyeballsAgent = exports.httpsHappyEyeballsAgent = new HttpsHappyEyeballsAgent();
|
|
51
62
|
const httpHappyEyeballsAgent = exports.httpHappyEyeballsAgent = new HttpHappyEyeballsAgent();
|
|
63
|
+
const httpHappyEyeballsTimings = exports.httpHappyEyeballsTimings = timings;
|
|
52
64
|
async function createSocket(host, port) {
|
|
53
65
|
return new Promise((resolve, reject) => {
|
|
54
66
|
if (net.isIP(host)) {
|
|
@@ -97,10 +109,13 @@ async function createConnectionAsync(options, oncreate, useTLS) {
|
|
|
97
109
|
port: options.port,
|
|
98
110
|
host: address
|
|
99
111
|
});
|
|
112
|
+
// Socket created
|
|
113
|
+
timings.values.socket = performance.now() - timings.values.startTimeNow;
|
|
100
114
|
|
|
101
115
|
// Each socket may fire only one of 'connect', 'timeout' or 'error' events.
|
|
102
116
|
// None of these events are fired after socket.destroy() is called.
|
|
103
117
|
socket.on('connect', () => {
|
|
118
|
+
timings.values.connect = performance.now() - timings.values.startTimeNow;
|
|
104
119
|
connected.resolve();
|
|
105
120
|
oncreate === null || oncreate === void 0 ? void 0 : oncreate(null, socket);
|
|
106
121
|
// TODO: Cache the result?
|
|
@@ -126,6 +141,7 @@ async function lookupAddresses(hostname) {
|
|
|
126
141
|
family: 0,
|
|
127
142
|
verbatim: true
|
|
128
143
|
});
|
|
144
|
+
timings.values.lookup = performance.now() - timings.values.startTimeNow;
|
|
129
145
|
let firstFamily = addresses.filter(({
|
|
130
146
|
family
|
|
131
147
|
}) => family === 6);
|