mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat: Add maxRedirects to options of apiRequest.newContext (#35160)
This commit is contained in:
parent
accd7c6c9e
commit
beea7c30c5
@ -37,6 +37,13 @@ for all status codes.
|
|||||||
### option: APIRequest.newContext.ignoreHTTPSErrors = %%-context-option-ignorehttpserrors-%%
|
### option: APIRequest.newContext.ignoreHTTPSErrors = %%-context-option-ignorehttpserrors-%%
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
|
|
||||||
|
### option: APIRequest.newContext.maxRedirects
|
||||||
|
* since: v1.52
|
||||||
|
- `maxRedirects` <[int]>
|
||||||
|
|
||||||
|
Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is exceeded.
|
||||||
|
Defaults to `20`. Pass `0` to not follow redirects. This can be overwritten for each request individually.
|
||||||
|
|
||||||
### option: APIRequest.newContext.timeout
|
### option: APIRequest.newContext.timeout
|
||||||
* since: v1.16
|
* since: v1.16
|
||||||
- `timeout` <[float]>
|
- `timeout` <[float]>
|
||||||
|
7
packages/playwright-client/types/types.d.ts
vendored
7
packages/playwright-client/types/types.d.ts
vendored
@ -17573,6 +17573,13 @@ export interface APIRequest {
|
|||||||
*/
|
*/
|
||||||
ignoreHTTPSErrors?: boolean;
|
ignoreHTTPSErrors?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is
|
||||||
|
* exceeded. Defaults to `20`. Pass `0` to not follow redirects. This can be overwritten for each request
|
||||||
|
* individually.
|
||||||
|
*/
|
||||||
|
maxRedirects?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Network proxy settings.
|
* Network proxy settings.
|
||||||
*/
|
*/
|
||||||
|
@ -386,6 +386,7 @@ scheme.PlaywrightNewRequestParams = tObject({
|
|||||||
passphrase: tOptional(tString),
|
passphrase: tOptional(tString),
|
||||||
pfx: tOptional(tBinary),
|
pfx: tOptional(tBinary),
|
||||||
}))),
|
}))),
|
||||||
|
maxRedirects: tOptional(tNumber),
|
||||||
httpCredentials: tOptional(tObject({
|
httpCredentials: tOptional(tObject({
|
||||||
username: tString,
|
username: tString,
|
||||||
password: tString,
|
password: tString,
|
||||||
|
@ -56,6 +56,7 @@ type FetchRequestOptions = {
|
|||||||
proxy?: ProxySettings;
|
proxy?: ProxySettings;
|
||||||
timeoutSettings: TimeoutSettings;
|
timeoutSettings: TimeoutSettings;
|
||||||
ignoreHTTPSErrors?: boolean;
|
ignoreHTTPSErrors?: boolean;
|
||||||
|
maxRedirects?: number;
|
||||||
baseURL?: string;
|
baseURL?: string;
|
||||||
clientCertificates?: types.BrowserContextOptions['clientCertificates'];
|
clientCertificates?: types.BrowserContextOptions['clientCertificates'];
|
||||||
};
|
};
|
||||||
@ -185,6 +186,8 @@ export abstract class APIRequestContext extends SdkObject {
|
|||||||
if (proxy && proxy.server !== 'per-context' && !shouldBypassProxy(requestUrl, proxy.bypass))
|
if (proxy && proxy.server !== 'per-context' && !shouldBypassProxy(requestUrl, proxy.bypass))
|
||||||
agent = createProxyAgent(proxy);
|
agent = createProxyAgent(proxy);
|
||||||
|
|
||||||
|
let maxRedirects = params.maxRedirects ?? (defaults.maxRedirects ?? 20);
|
||||||
|
maxRedirects = maxRedirects === 0 ? -1 : maxRedirects;
|
||||||
|
|
||||||
const timeout = defaults.timeoutSettings.timeout(params);
|
const timeout = defaults.timeoutSettings.timeout(params);
|
||||||
const deadline = timeout && (monotonicTime() + timeout);
|
const deadline = timeout && (monotonicTime() + timeout);
|
||||||
@ -193,7 +196,7 @@ export abstract class APIRequestContext extends SdkObject {
|
|||||||
method,
|
method,
|
||||||
headers,
|
headers,
|
||||||
agent,
|
agent,
|
||||||
maxRedirects: params.maxRedirects === 0 ? -1 : params.maxRedirects === undefined ? 20 : params.maxRedirects,
|
maxRedirects,
|
||||||
timeout,
|
timeout,
|
||||||
deadline,
|
deadline,
|
||||||
...getMatchingTLSOptionsForOrigin(this._defaultOptions().clientCertificates, requestUrl.origin),
|
...getMatchingTLSOptionsForOrigin(this._defaultOptions().clientCertificates, requestUrl.origin),
|
||||||
@ -371,7 +374,7 @@ export abstract class APIRequestContext extends SdkObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (redirectStatus.includes(response.statusCode!) && options.maxRedirects >= 0) {
|
if (redirectStatus.includes(response.statusCode!) && options.maxRedirects >= 0) {
|
||||||
if (!options.maxRedirects) {
|
if (options.maxRedirects === 0) {
|
||||||
reject(new Error('Max redirect count exceeded'));
|
reject(new Error('Max redirect count exceeded'));
|
||||||
request.destroy();
|
request.destroy();
|
||||||
return;
|
return;
|
||||||
@ -662,6 +665,7 @@ export class GlobalAPIRequestContext extends APIRequestContext {
|
|||||||
extraHTTPHeaders: options.extraHTTPHeaders,
|
extraHTTPHeaders: options.extraHTTPHeaders,
|
||||||
failOnStatusCode: !!options.failOnStatusCode,
|
failOnStatusCode: !!options.failOnStatusCode,
|
||||||
ignoreHTTPSErrors: !!options.ignoreHTTPSErrors,
|
ignoreHTTPSErrors: !!options.ignoreHTTPSErrors,
|
||||||
|
maxRedirects: options.maxRedirects,
|
||||||
httpCredentials: options.httpCredentials,
|
httpCredentials: options.httpCredentials,
|
||||||
clientCertificates: options.clientCertificates,
|
clientCertificates: options.clientCertificates,
|
||||||
proxy,
|
proxy,
|
||||||
|
7
packages/playwright-core/types/types.d.ts
vendored
7
packages/playwright-core/types/types.d.ts
vendored
@ -17573,6 +17573,13 @@ export interface APIRequest {
|
|||||||
*/
|
*/
|
||||||
ignoreHTTPSErrors?: boolean;
|
ignoreHTTPSErrors?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of request redirects that will be followed automatically. An error will be thrown if the number is
|
||||||
|
* exceeded. Defaults to `20`. Pass `0` to not follow redirects. This can be overwritten for each request
|
||||||
|
* individually.
|
||||||
|
*/
|
||||||
|
maxRedirects?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Network proxy settings.
|
* Network proxy settings.
|
||||||
*/
|
*/
|
||||||
|
2
packages/protocol/src/channels.d.ts
vendored
2
packages/protocol/src/channels.d.ts
vendored
@ -644,6 +644,7 @@ export type PlaywrightNewRequestParams = {
|
|||||||
passphrase?: string,
|
passphrase?: string,
|
||||||
pfx?: Binary,
|
pfx?: Binary,
|
||||||
}[],
|
}[],
|
||||||
|
maxRedirects?: number,
|
||||||
httpCredentials?: {
|
httpCredentials?: {
|
||||||
username: string,
|
username: string,
|
||||||
password: string,
|
password: string,
|
||||||
@ -676,6 +677,7 @@ export type PlaywrightNewRequestOptions = {
|
|||||||
passphrase?: string,
|
passphrase?: string,
|
||||||
pfx?: Binary,
|
pfx?: Binary,
|
||||||
}[],
|
}[],
|
||||||
|
maxRedirects?: number,
|
||||||
httpCredentials?: {
|
httpCredentials?: {
|
||||||
username: string,
|
username: string,
|
||||||
password: string,
|
password: string,
|
||||||
|
@ -770,6 +770,7 @@ Playwright:
|
|||||||
key: binary?
|
key: binary?
|
||||||
passphrase: string?
|
passphrase: string?
|
||||||
pfx: binary?
|
pfx: binary?
|
||||||
|
maxRedirects: number?
|
||||||
httpCredentials:
|
httpCredentials:
|
||||||
type: object?
|
type: object?
|
||||||
properties:
|
properties:
|
||||||
|
@ -437,6 +437,8 @@ it('should return body for failing requests', async ({ playwright, server }) =>
|
|||||||
await request.dispose();
|
await request.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const HTTP_METHODS = ['GET', 'PUT', 'POST', 'OPTIONS', 'HEAD', 'PATCH'] as const;
|
||||||
|
|
||||||
it('should throw an error when maxRedirects is exceeded', async ({ playwright, server }) => {
|
it('should throw an error when maxRedirects is exceeded', async ({ playwright, server }) => {
|
||||||
server.setRedirect('/a/redirect1', '/b/c/redirect2');
|
server.setRedirect('/a/redirect1', '/b/c/redirect2');
|
||||||
server.setRedirect('/b/c/redirect2', '/b/c/redirect3');
|
server.setRedirect('/b/c/redirect2', '/b/c/redirect3');
|
||||||
@ -444,7 +446,7 @@ it('should throw an error when maxRedirects is exceeded', async ({ playwright, s
|
|||||||
server.setRedirect('/b/c/redirect4', '/simple.json');
|
server.setRedirect('/b/c/redirect4', '/simple.json');
|
||||||
|
|
||||||
const request = await playwright.request.newContext();
|
const request = await playwright.request.newContext();
|
||||||
for (const method of ['GET', 'PUT', 'POST', 'OPTIONS', 'HEAD', 'PATCH']) {
|
for (const method of HTTP_METHODS) {
|
||||||
for (const maxRedirects of [1, 2, 3])
|
for (const maxRedirects of [1, 2, 3])
|
||||||
await expect(async () => request.fetch(`${server.PREFIX}/a/redirect1`, { method: method, maxRedirects: maxRedirects })).rejects.toThrow('Max redirect count exceeded');
|
await expect(async () => request.fetch(`${server.PREFIX}/a/redirect1`, { method: method, maxRedirects: maxRedirects })).rejects.toThrow('Max redirect count exceeded');
|
||||||
}
|
}
|
||||||
@ -456,7 +458,7 @@ it('should not follow redirects when maxRedirects is set to 0', async ({ playwri
|
|||||||
server.setRedirect('/b/c/redirect2', '/simple.json');
|
server.setRedirect('/b/c/redirect2', '/simple.json');
|
||||||
|
|
||||||
const request = await playwright.request.newContext();
|
const request = await playwright.request.newContext();
|
||||||
for (const method of ['GET', 'PUT', 'POST', 'OPTIONS', 'HEAD', 'PATCH']){
|
for (const method of HTTP_METHODS){
|
||||||
const response = await request.fetch(`${server.PREFIX}/a/redirect1`, { method, maxRedirects: 0 });
|
const response = await request.fetch(`${server.PREFIX}/a/redirect1`, { method, maxRedirects: 0 });
|
||||||
expect(response.headers()['location']).toBe('/b/c/redirect2');
|
expect(response.headers()['location']).toBe('/b/c/redirect2');
|
||||||
expect(response.status()).toBe(302);
|
expect(response.status()).toBe(302);
|
||||||
@ -469,11 +471,59 @@ it('should throw an error when maxRedirects is less than 0', async ({ playwright
|
|||||||
server.setRedirect('/b/c/redirect2', '/simple.json');
|
server.setRedirect('/b/c/redirect2', '/simple.json');
|
||||||
|
|
||||||
const request = await playwright.request.newContext();
|
const request = await playwright.request.newContext();
|
||||||
for (const method of ['GET', 'PUT', 'POST', 'OPTIONS', 'HEAD', 'PATCH'])
|
for (const method of HTTP_METHODS)
|
||||||
await expect(async () => request.fetch(`${server.PREFIX}/a/redirect1`, { method, maxRedirects: -1 })).rejects.toThrow(`'maxRedirects' must be greater than or equal to '0'`);
|
await expect(async () => request.fetch(`${server.PREFIX}/a/redirect1`, { method, maxRedirects: -1 })).rejects.toThrow(`'maxRedirects' must be greater than or equal to '0'`);
|
||||||
await request.dispose();
|
await request.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not follow redirects when maxRedirects is set to 0 in newContext', async ({ playwright, server }) => {
|
||||||
|
server.setRedirect('/a/redirect1', '/b/c/redirect2');
|
||||||
|
server.setRedirect('/b/c/redirect2', '/simple.json');
|
||||||
|
|
||||||
|
const request = await playwright.request.newContext({ maxRedirects: 0 });
|
||||||
|
for (const method of HTTP_METHODS) {
|
||||||
|
const response = await request.fetch(`${server.PREFIX}/a/redirect1`, { method });
|
||||||
|
expect(response.headers()['location']).toBe('/b/c/redirect2');
|
||||||
|
expect(response.status()).toBe(302);
|
||||||
|
}
|
||||||
|
await request.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should follow redirects up to maxRedirects limit set in newContext', async ({ playwright, server }) => {
|
||||||
|
server.setRedirect('/a/redirect1', '/b/c/redirect2');
|
||||||
|
server.setRedirect('/b/c/redirect2', '/b/c/redirect3');
|
||||||
|
server.setRedirect('/b/c/redirect3', '/b/c/redirect4');
|
||||||
|
server.setRedirect('/b/c/redirect4', '/simple.json');
|
||||||
|
|
||||||
|
for (const maxRedirects of [1, 2, 3, 4]) {
|
||||||
|
const request = await playwright.request.newContext({ maxRedirects });
|
||||||
|
for (const method of HTTP_METHODS) {
|
||||||
|
if (maxRedirects < 4) {
|
||||||
|
await expect(async () => request.fetch(`${server.PREFIX}/a/redirect1`, { method }))
|
||||||
|
.rejects.toThrow('Max redirect count exceeded');
|
||||||
|
} else {
|
||||||
|
const response = await request.fetch(`${server.PREFIX}/a/redirect1`, { method });
|
||||||
|
expect(response.status()).toBe(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await request.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use maxRedirects from fetch when provided, overriding newContext', async ({ playwright, server }) => {
|
||||||
|
server.setRedirect('/a/redirect1', '/b/c/redirect2');
|
||||||
|
server.setRedirect('/b/c/redirect2', '/b/c/redirect3');
|
||||||
|
server.setRedirect('/b/c/redirect3', '/b/c/redirect4');
|
||||||
|
server.setRedirect('/b/c/redirect4', '/simple.json');
|
||||||
|
|
||||||
|
const request = await playwright.request.newContext({ maxRedirects: 1 });
|
||||||
|
for (const method of HTTP_METHODS) {
|
||||||
|
const response = await request.fetch(`${server.PREFIX}/a/redirect1`, { method, maxRedirects: 4 });
|
||||||
|
expect(response.status()).toBe(200);
|
||||||
|
}
|
||||||
|
await request.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
it('should keep headers capitalization', async ({ playwright, server }) => {
|
it('should keep headers capitalization', async ({ playwright, server }) => {
|
||||||
const request = await playwright.request.newContext();
|
const request = await playwright.request.newContext();
|
||||||
const [serverRequest, response] = await Promise.all([
|
const [serverRequest, response] = await Promise.all([
|
||||||
|
Loading…
x
Reference in New Issue
Block a user