feat: support fetching request POST data (#11598)

This commit is contained in:
Alex Rudenko 2024-01-02 11:02:08 +01:00 committed by GitHub
parent c660d4001d
commit 80143def96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 122 additions and 0 deletions

View File

@ -0,0 +1,19 @@
---
sidebar_label: HTTPRequest.fetchPostData
---
# HTTPRequest.fetchPostData() method
Fetches the POST data for the request from the browser.
#### Signature:
```typescript
class HTTPRequest {
abstract fetchPostData(): Promise<string | undefined>;
}
```
**Returns:**
Promise&lt;string \| undefined&gt;

View File

@ -0,0 +1,19 @@
---
sidebar_label: HTTPRequest.hasPostData
---
# HTTPRequest.hasPostData() method
True when the request has POST data. Note that [HTTPRequest.postData()](./puppeteer.httprequest.postdata.md) might still be undefined when this flag is true when the data is too long or not readily available in the decoded form. In that case, use [HTTPRequest.fetchPostData()](./puppeteer.httprequest.fetchpostdata.md).
#### Signature:
```typescript
class HTTPRequest {
abstract hasPostData(): boolean;
}
```
**Returns:**
boolean

View File

@ -48,8 +48,10 @@ The constructor for this class is marked as internal. Third-party code should no
| [continueRequestOverrides()](./puppeteer.httprequest.continuerequestoverrides.md) | | The <code>ContinueRequestOverrides</code> that will be used if the interception is allowed to continue (ie, <code>abort()</code> and <code>respond()</code> aren't called). |
| [enqueueInterceptAction(pendingHandler)](./puppeteer.httprequest.enqueueinterceptaction.md) | | Adds an async request handler to the processing queue. Deferred handlers are not guaranteed to execute in any particular order, but they are guaranteed to resolve before the request interception is finalized. |
| [failure()](./puppeteer.httprequest.failure.md) | | Access information about the request's failure. |
| [fetchPostData()](./puppeteer.httprequest.fetchpostdata.md) | | Fetches the POST data for the request from the browser. |
| [finalizeInterceptions()](./puppeteer.httprequest.finalizeinterceptions.md) | | Awaits pending interception handlers and then decides how to fulfill the request interception. |
| [frame()](./puppeteer.httprequest.frame.md) | | The frame that initiated the request, or null if navigating to error pages. |
| [hasPostData()](./puppeteer.httprequest.haspostdata.md) | | True when the request has POST data. Note that [HTTPRequest.postData()](./puppeteer.httprequest.postdata.md) might still be undefined when this flag is true when the data is too long or not readily available in the decoded form. In that case, use [HTTPRequest.fetchPostData()](./puppeteer.httprequest.fetchpostdata.md). |
| [headers()](./puppeteer.httprequest.headers.md) | | An object with HTTP headers associated with the request. All header names are lower-case. |
| [initiator()](./puppeteer.httprequest.initiator.md) | | The initiator of the request. |
| [interceptResolutionState()](./puppeteer.httprequest.interceptresolutionstate.md) | | <p>An InterceptResolutionState object describing the current resolution action and priority.</p><p>InterceptResolutionState contains: action: InterceptResolutionAction priority?: number</p><p>InterceptResolutionAction is one of: <code>abort</code>, <code>respond</code>, <code>continue</code>, <code>disabled</code>, <code>none</code>, or <code>already-handled</code>.</p> |

View File

@ -212,6 +212,19 @@ export abstract class HTTPRequest {
*/
abstract postData(): string | undefined;
/**
* True when the request has POST data. Note that {@link HTTPRequest.postData}
* might still be undefined when this flag is true when the data is too long
* or not readily available in the decoded form. In that case, use
* {@link HTTPRequest.fetchPostData}.
*/
abstract hasPostData(): boolean;
/**
* Fetches the POST data for the request from the browser.
*/
abstract fetchPostData(): Promise<string | undefined>;
/**
* An object with HTTP headers associated with the request. All
* header names are lower-case.

View File

@ -89,6 +89,14 @@ export class BidiHTTPRequest extends HTTPRequest {
return this.#postData;
}
override hasPostData(): boolean {
return this.#postData !== undefined;
}
override async fetchPostData(): Promise<string | undefined> {
return this.#postData;
}
override headers(): Record<string, string> {
return this.#headers;
}

View File

@ -49,6 +49,7 @@ export class CdpHTTPRequest extends HTTPRequest {
#resourceType: ResourceType;
#method: string;
#hasPostData = false;
#postData?: string;
#headers: Record<string, string> = {};
#frame: Frame | null;
@ -109,6 +110,7 @@ export class CdpHTTPRequest extends HTTPRequest {
this.#resourceType = (data.type || 'other').toLowerCase() as ResourceType;
this.#method = data.request.method;
this.#postData = data.request.postData;
this.#hasPostData = data.request.hasPostData ?? false;
this.#frame = frame;
this._redirectChain = redirectChain;
this.#continueRequestOverrides = {};
@ -189,6 +191,22 @@ export class CdpHTTPRequest extends HTTPRequest {
return this.#postData;
}
override hasPostData(): boolean {
return this.#hasPostData;
}
override async fetchPostData(): Promise<string | undefined> {
try {
const result = await this.#client.send('Network.getRequestPostData', {
requestId: this._requestId,
});
return result.postData;
} catch (err) {
debugError(err);
return;
}
}
override headers(): Record<string, string> {
return this.#headers;
}

View File

@ -851,6 +851,13 @@
"parameters": ["webDriverBiDi"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[network.spec] network Request.postData should work with blobs",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["SKIP"],
"comment": "Not implemented for BiDi yet."
},
{
"testIdPattern": "[network.spec] network Response.fromServiceWorker Response.fromServiceWorker",
"platforms": ["darwin", "linux", "win32"],
@ -2711,6 +2718,13 @@
"parameters": ["cdp", "firefox"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[network.spec] network Request.postData should work with blobs",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox"],
"expectations": ["SKIP"],
"comment": "Blobs have no POST data in Firefox's CDP implementation."
},
{
"testIdPattern": "[network.spec] network Response.buffer should throw if the response does not have a body",
"platforms": ["darwin", "linux", "win32"],

View File

@ -267,12 +267,41 @@ describe('network', function () {
expect(request).toBeTruthy();
expect(request.postData()).toBe('{"foo":"bar"}');
});
it('should be |undefined| when there is no post data', async () => {
const {page, server} = await getTestState();
const response = (await page.goto(server.EMPTY_PAGE))!;
expect(response.request().postData()).toBe(undefined);
});
it('should work with blobs', async () => {
const {page, server} = await getTestState();
await page.goto(server.EMPTY_PAGE);
server.setRoute('/post', (_req, res) => {
return res.end();
});
const [request] = await Promise.all([
waitEvent<HTTPRequest>(page, 'request', r => {
return !isFavicon(r);
}),
page.evaluate(() => {
return fetch('./post', {
method: 'POST',
body: new Blob([JSON.stringify({foo: 'bar'})], {
type: 'application/json',
}),
});
}),
]);
expect(request).toBeTruthy();
expect(request.postData()).toBe(undefined);
expect(request.hasPostData()).toBe(true);
expect(await request.fetchPostData()).toBe('{"foo":"bar"}');
});
});
describe('Response.text', function () {