chore(webdriver): support options in request.continue (#12155)

This commit is contained in:
Nikolay Vitkov 2024-03-27 11:42:12 +01:00 committed by GitHub
parent b144935789
commit 6edb80b2a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 103 additions and 57 deletions

View File

@ -5,6 +5,9 @@
*/
import type {Protocol} from 'devtools-protocol';
import type {ProtocolError} from '../common/Errors.js';
import {debugError} from '../common/util.js';
import type {CDPSession} from './CDPSession.js';
import type {Frame} from './Frame.js';
import type {HTTPResponse} from './HTTPResponse.js';
@ -513,3 +516,16 @@ export const STATUS_TEXTS: Record<string, string> = {
'510': 'Not Extended',
'511': 'Network Authentication Required',
} as const;
/**
* @internal
*/
export function handleError(error: ProtocolError): void {
if (error.originalMessage.includes('Invalid header')) {
throw error;
}
// In certain cases, protocol will return error if the request was
// already canceled or the page was closed. We should tolerate these
// errors.
debugError(error);
}

View File

@ -14,10 +14,10 @@ import {
HTTPRequest,
STATUS_TEXTS,
type ResourceType,
handleError,
} from '../api/HTTPRequest.js';
import {PageEvent} from '../api/Page.js';
import {UnsupportedOperation} from '../common/Errors.js';
import {isString} from '../common/util.js';
import type {Request} from './core/Request.js';
import type {BidiFrame} from './Frame.js';
@ -148,9 +148,33 @@ export class BidiHTTPRequest extends HTTPRequest {
}
override async continue(
_overrides: ContinueRequestOverrides = {}
overrides: ContinueRequestOverrides = {}
): Promise<void> {
return await this.#request.continueRequest();
if (!this.#request.isBlocked) {
throw new Error('Request Interception is not enabled!');
}
// Request interception is not supported for data: urls.
if (this.url().startsWith('data:')) {
return;
}
const headers: Bidi.Network.Header[] = getBidiHeaders(overrides.headers);
return await this.#request
.continueRequest({
url: overrides.url,
method: overrides.method,
body: overrides.postData
? {
type: 'base64',
value: btoa(overrides.postData),
}
: undefined,
headers: headers.length > 0 ? headers : undefined,
})
.catch(error => {
return handleError(error);
});
}
override responseForRequest(): never {
@ -174,6 +198,13 @@ export class BidiHTTPRequest extends HTTPRequest {
}
override async abort(): Promise<void> {
if (!this.#request.isBlocked) {
throw new Error('Request Interception is not enabled!');
}
// Request interception is not supported for data: urls.
if (this.url().startsWith('data:')) {
return;
}
return await this.#request.failRequest();
}
@ -181,25 +212,25 @@ export class BidiHTTPRequest extends HTTPRequest {
response: Partial<ResponseForRequest>,
_priority?: number
): Promise<void> {
const responseBody: Buffer | null =
response.body && isString(response.body)
? Buffer.from(response.body)
: (response.body as Buffer) || null;
const headers: Bidi.Network.Header[] = [];
let hasContentLength = false;
for (const [name, value] of Object.entries(response.headers ?? [])) {
if (name.toLocaleLowerCase() === 'content-length') {
hasContentLength = true;
}
headers.push({
name: name.toLowerCase(),
value: {
type: 'string',
value: String(value),
},
});
if (!this.#request.isBlocked) {
throw new Error('Request Interception is not enabled!');
}
// Request interception is not supported for data: urls.
if (this.url().startsWith('data:')) {
return;
}
const responseBody: string | undefined =
response.body && response.body instanceof Uint8Array
? response.body.toString('base64')
: response.body
? btoa(response.body)
: undefined;
const headers: Bidi.Network.Header[] = getBidiHeaders(response.headers);
const hasContentLength = headers.some(header => {
return header.name === 'content-length';
});
if (response.contentType) {
headers.push({
@ -210,16 +241,17 @@ export class BidiHTTPRequest extends HTTPRequest {
},
});
}
if (responseBody && !hasContentLength) {
const encoder = new TextEncoder();
headers.push({
name: 'content-length',
value: {
type: 'string',
value: String(Buffer.byteLength(responseBody)),
value: String(encoder.encode(responseBody).byteLength),
},
});
}
const status = response.status || 200;
return await this.#request.provideResponse({
@ -229,9 +261,24 @@ export class BidiHTTPRequest extends HTTPRequest {
body: responseBody
? {
type: 'base64',
value: responseBody.toString('base64'),
value: responseBody,
}
: undefined,
});
}
}
function getBidiHeaders(rawHeaders?: Record<string, unknown>) {
const headers: Bidi.Network.Header[] = [];
for (const [name, value] of Object.entries(rawHeaders ?? [])) {
headers.push({
name: name.toLowerCase(),
value: {
type: 'string',
value: String(value),
},
});
}
return headers;
}

View File

@ -141,29 +141,29 @@ export class Request extends EventEmitter<{
get url(): string {
return this.#event.request.url;
}
get isBlocked(): boolean {
return this.#event.isBlocked;
}
// keep-sorted end
async continueRequest(): Promise<void> {
if (!this.#event.isBlocked) {
throw new Error('Request Interception is not enabled!');
}
// Request interception is not supported for data: urls.
if (this.url.startsWith('data:')) {
return;
}
async continueRequest({
url,
method,
headers,
cookies,
body,
}: Omit<Bidi.Network.ContinueRequestParameters, 'request'>): Promise<void> {
await this.#session.send('network.continueRequest', {
request: this.id,
url,
method,
headers,
body,
cookies,
});
}
async failRequest(): Promise<void> {
if (!this.#event.isBlocked) {
throw new Error('Request Interception is not enabled!');
}
// Request interception is not supported for data: urls.
if (this.url.startsWith('data:')) {
return;
}
await this.#session.send('network.failRequest', {
request: this.id,
});
@ -175,13 +175,6 @@ export class Request extends EventEmitter<{
headers,
body,
}: Omit<Bidi.Network.ProvideResponseParameters, 'request'>): Promise<void> {
if (!this.#event.isBlocked) {
throw new Error('Request Interception is not enabled!');
}
// Request interception is not supported for data: urls.
if (this.url.startsWith('data:')) {
return;
}
await this.#session.send('network.provideResponse', {
request: this.id,
statusCode,

View File

@ -17,8 +17,8 @@ import {
type ResourceType,
type ResponseForRequest,
STATUS_TEXTS,
handleError,
} from '../api/HTTPRequest.js';
import type {ProtocolError} from '../common/Errors.js';
import {debugError, isString} from '../common/util.js';
import {assert} from '../util/assert.js';
@ -438,13 +438,3 @@ const errorReasons: Record<ErrorCode, Protocol.Network.ErrorReason> = {
timedout: 'TimedOut',
failed: 'Failed',
} as const;
async function handleError(error: ProtocolError) {
if (['Invalid header'].includes(error.originalMessage)) {
throw error;
}
// In certain cases, protocol will return error if the request was
// already canceled or the page was closed. We should tolerate these
// errors.
debugError(error);
}