From 80c0711d9808e2677094e768bcbede5b8c55bc02 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Wed, 22 Jul 2020 15:59:37 -0700 Subject: [PATCH] feat(firefox): roll firefox to r1137 (#3095) --- browsers.json | 2 +- docs/api.md | 4 ++++ src/chromium/crNetworkManager.ts | 8 +++++-- src/firefox/ffNetworkManager.ts | 6 +++-- src/network.ts | 15 ++++++++---- src/rpc/channels.ts | 2 +- src/rpc/client/network.ts | 15 ++++++++---- src/rpc/protocol.pdl | 2 +- src/rpc/server/networkDispatchers.ts | 4 ++-- src/webkit/wkInterceptableRequest.ts | 5 +++- test/network.jest.js | 34 ++++++++++++++++++++++++---- 11 files changed, 73 insertions(+), 24 deletions(-) diff --git a/browsers.json b/browsers.json index 17574bc0b6..22d8382867 100644 --- a/browsers.json +++ b/browsers.json @@ -7,7 +7,7 @@ }, { "name": "firefox", - "revision": "1134" + "revision": "1137" }, { "name": "webkit", diff --git a/docs/api.md b/docs/api.md index b4a730c45d..f21b3b7a7c 100644 --- a/docs/api.md +++ b/docs/api.md @@ -3486,6 +3486,7 @@ If request gets a 'redirect' response, the request is successfully finished with - [request.isNavigationRequest()](#requestisnavigationrequest) - [request.method()](#requestmethod) - [request.postData()](#requestpostdata) +- [request.postDataBuffer()](#requestpostdatabuffer) - [request.postDataJSON()](#requestpostdatajson) - [request.redirectedFrom()](#requestredirectedfrom) - [request.redirectedTo()](#requestredirectedto) @@ -3526,6 +3527,9 @@ Whether this request is driving frame's navigation. #### request.postData() - returns: Request's post body, if any. +#### request.postDataBuffer() +- returns: Request's post body in a binary form, if any. + #### request.postDataJSON() - returns: Parsed request's body for `form-urlencoded` and JSON as a fallback if any. diff --git a/src/chromium/crNetworkManager.ts b/src/chromium/crNetworkManager.ts index eea30def67..9f2a22ea15 100644 --- a/src/chromium/crNetworkManager.ts +++ b/src/chromium/crNetworkManager.ts @@ -342,10 +342,14 @@ class InterceptableRequest implements network.RouteDelegate { headers, method, url, - postData = null, + postDataEntries = null, } = requestPausedEvent ? requestPausedEvent.request : requestWillBeSentEvent.request; const type = (requestWillBeSentEvent.type || '').toLowerCase(); - this.request = new network.Request(allowInterception ? this : null, frame, redirectedFrom, documentId, url, type, method, postData, headersObject(headers)); + let postDataBuffer = null; + if (postDataEntries && postDataEntries.length && postDataEntries[0].bytes) + postDataBuffer = Buffer.from(postDataEntries[0].bytes, 'base64'); + + this.request = new network.Request(allowInterception ? this : null, frame, redirectedFrom, documentId, url, type, method, postDataBuffer, headersObject(headers)); } async continue(overrides: types.NormalizedContinueOverrides) { diff --git a/src/firefox/ffNetworkManager.ts b/src/firefox/ffNetworkManager.ts index c2341d6b19..bb44959795 100644 --- a/src/firefox/ffNetworkManager.ts +++ b/src/firefox/ffNetworkManager.ts @@ -153,9 +153,11 @@ class InterceptableRequest implements network.RouteDelegate { const headers: types.Headers = {}; for (const {name, value} of payload.headers) headers[name.toLowerCase()] = value; - + let postDataBuffer = null; + if (payload.postData) + postDataBuffer = Buffer.from(payload.postData, 'base64'); this.request = new network.Request(payload.isIntercepted ? this : null, frame, redirectedFrom ? redirectedFrom.request : null, payload.navigationId, - payload.url, internalCauseToResourceType[payload.internalCause] || causeToResourceType[payload.cause] || 'other', payload.method, payload.postData || null, headers); + payload.url, internalCauseToResourceType[payload.internalCause] || causeToResourceType[payload.cause] || 'other', payload.method, postDataBuffer, headers); } async continue(overrides: types.NormalizedContinueOverrides) { diff --git a/src/network.ts b/src/network.ts index 954e3874c6..580c68fc70 100644 --- a/src/network.ts +++ b/src/network.ts @@ -78,14 +78,14 @@ export class Request { private _url: string; private _resourceType: string; private _method: string; - private _postData: string | null; + private _postData: Buffer | null; private _headers: types.Headers; private _frame: frames.Frame; private _waitForResponsePromise: Promise; private _waitForResponsePromiseCallback: (value: Response | null) => void = () => {}; constructor(routeDelegate: RouteDelegate | null, frame: frames.Frame, redirectedFrom: Request | null, documentId: string | undefined, - url: string, resourceType: string, method: string, postData: string | null, headers: types.Headers) { + url: string, resourceType: string, method: string, postData: Buffer | null, headers: types.Headers) { assert(!url.startsWith('data:'), 'Data urls should not fire requests'); assert(!(routeDelegate && redirectedFrom), 'Should not be able to intercept redirects'); this._routeDelegate = routeDelegate; @@ -121,11 +121,16 @@ export class Request { } postData(): string | null { + return this._postData ? this._postData.toString('utf8') : null; + } + + postDataBuffer(): Buffer | null { return this._postData; } postDataJSON(): Object | null { - if (!this._postData) + const postData = this.postData(); + if (!postData) return null; const contentType = this.headers()['content-type']; @@ -134,13 +139,13 @@ export class Request { if (contentType === 'application/x-www-form-urlencoded') { const entries: Record = {}; - const parsed = new URLSearchParams(this._postData); + const parsed = new URLSearchParams(postData); for (const [k, v] of parsed.entries()) entries[k] = v; return entries; } - return JSON.parse(this._postData); + return JSON.parse(postData); } headers(): {[key: string]: string} { diff --git a/src/rpc/channels.ts b/src/rpc/channels.ts index 5d8c129d68..f71a9423f0 100644 --- a/src/rpc/channels.ts +++ b/src/rpc/channels.ts @@ -1400,7 +1400,7 @@ export type RequestInitializer = { url: string, resourceType: string, method: string, - postData?: string, + postData?: Binary, headers: { name: string, value: string, diff --git a/src/rpc/client/network.ts b/src/rpc/client/network.ts index d247a12b0c..c70b2a57ea 100644 --- a/src/rpc/client/network.ts +++ b/src/rpc/client/network.ts @@ -49,6 +49,7 @@ export class Request extends ChannelOwner { private _redirectedTo: Request | null = null; _failureText: string | null = null; private _headers: types.Headers; + private _postData: Buffer | null; static from(request: RequestChannel): Request { return (request as any)._object; @@ -64,6 +65,7 @@ export class Request extends ChannelOwner { if (this._redirectedFrom) this._redirectedFrom._redirectedTo = this; this._headers = headersArrayToObject(initializer.headers); + this._postData = initializer.postData ? Buffer.from(initializer.postData, 'base64') : null; } url(): string { @@ -79,11 +81,16 @@ export class Request extends ChannelOwner { } postData(): string | null { - return this._initializer.postData || null; + return this._postData ? this._postData.toString('utf8') : null; + } + + postDataBuffer(): Buffer | null { + return this._postData; } postDataJSON(): Object | null { - if (!this._initializer.postData) + const postData = this.postData(); + if (!postData) return null; const contentType = this.headers()['content-type']; @@ -92,13 +99,13 @@ export class Request extends ChannelOwner { if (contentType === 'application/x-www-form-urlencoded') { const entries: Record = {}; - const parsed = new URLSearchParams(this._initializer.postData); + const parsed = new URLSearchParams(postData); for (const [k, v] of parsed.entries()) entries[k] = v; return entries; } - return JSON.parse(this._initializer.postData); + return JSON.parse(postData); } headers(): types.Headers { diff --git a/src/rpc/protocol.pdl b/src/rpc/protocol.pdl index b71aec53a2..3659b4dcda 100644 --- a/src/rpc/protocol.pdl +++ b/src/rpc/protocol.pdl @@ -1291,7 +1291,7 @@ interface Request url: string resourceType: string method: string - postData?: string + postData?: binary headers: object[] name: string value: string diff --git a/src/rpc/server/networkDispatchers.ts b/src/rpc/server/networkDispatchers.ts index 81f753d37b..37e728d34f 100644 --- a/src/rpc/server/networkDispatchers.ts +++ b/src/rpc/server/networkDispatchers.ts @@ -33,13 +33,13 @@ export class RequestDispatcher extends Dispatcher i } private constructor(scope: DispatcherScope, request: Request) { - const postData = request.postData(); + const postData = request.postDataBuffer(); super(scope, request, 'Request', { frame: FrameDispatcher.from(scope, request.frame()), url: request.url(), resourceType: request.resourceType(), method: request.method(), - postData: postData === null ? undefined : postData, + postData: postData === null ? undefined : postData.toString('base64'), headers: headersObjectToArray(request.headers()), isNavigationRequest: request.isNavigationRequest(), redirectedFrom: RequestDispatcher.fromNullable(scope, request.redirectedFrom()), diff --git a/src/webkit/wkInterceptableRequest.ts b/src/webkit/wkInterceptableRequest.ts index 542b2a1377..f3d19ac1a1 100644 --- a/src/webkit/wkInterceptableRequest.ts +++ b/src/webkit/wkInterceptableRequest.ts @@ -53,8 +53,11 @@ export class WKInterceptableRequest implements network.RouteDelegate { this._requestId = event.requestId; this._allowInterception = allowInterception; const resourceType = event.type ? event.type.toLowerCase() : (redirectedFrom ? redirectedFrom.resourceType() : 'other'); + let postDataBuffer = null; + if (event.request.postData) + postDataBuffer = Buffer.from(event.request.postData, 'binary'); this.request = new network.Request(allowInterception ? this : null, frame, redirectedFrom, documentId, event.request.url, - resourceType, event.request.method, event.request.postData || null, headersObject(event.request.headers)); + resourceType, event.request.method, postDataBuffer, headersObject(event.request.headers)); this._interceptedPromise = new Promise(f => this._interceptedCallback = f); } diff --git a/test/network.jest.js b/test/network.jest.js index 2daf68772d..990ff8c77a 100644 --- a/test/network.jest.js +++ b/test/network.jest.js @@ -133,13 +133,39 @@ describe('Request.postData', function() { expect(request).toBeTruthy(); expect(request.postData()).toBe('{"foo":"bar"}'); }); + it('should work with binary', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + server.setRoute('/post', (req, res) => res.end()); + let request = null; + page.on('request', r => request = r); + await page.evaluate(async () => { + await fetch('./post', { method: 'POST', body: new Uint8Array(Array.from(Array(256).keys())) }) + }); + expect(request).toBeTruthy(); + const buffer = request.postDataBuffer(); + expect(buffer.length).toBe(256); + for (let i = 0; i < 256; ++i) + expect(buffer[i]).toBe(i); + }); + it('should work with binary and interception', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + server.setRoute('/post', (req, res) => res.end()); + let request = null; + await page.route('/post', route => route.continue()); + page.on('request', r => request = r); + await page.evaluate(async () => { + await fetch('./post', { method: 'POST', body: new Uint8Array(Array.from(Array(256).keys())) }) + }); + expect(request).toBeTruthy(); + const buffer = request.postDataBuffer(); + expect(buffer.length).toBe(256); + for (let i = 0; i < 256; ++i) + expect(buffer[i]).toBe(i); + }); it('should be |undefined| when there is no post data', async({page, server}) => { const response = await page.goto(server.EMPTY_PAGE); expect(response.request().postData()).toBe(null); }); -}); - -describe('Request.postDataJSON', function () { it('should parse the JSON payload', async ({ page, server }) => { await page.goto(server.EMPTY_PAGE); server.setRoute('/post', (req, res) => res.end()); @@ -149,7 +175,6 @@ describe('Request.postDataJSON', function () { expect(request).toBeTruthy(); expect(request.postDataJSON()).toEqual({ "foo": "bar" }); }); - it('should parse the data if content-type is application/x-www-form-urlencoded', async({page, server}) => { await page.goto(server.EMPTY_PAGE); server.setRoute('/post', (req, res) => res.end()); @@ -160,7 +185,6 @@ describe('Request.postDataJSON', function () { expect(request).toBeTruthy(); expect(request.postDataJSON()).toEqual({'foo':'bar','baz':'123'}); }) - it('should be |undefined| when there is no post data', async ({ page, server }) => { const response = await page.goto(server.EMPTY_PAGE); expect(response.request().postDataJSON()).toBe(null);