Skip to content

Commit

Permalink
Implement Body interface for both Request and Response
Browse files Browse the repository at this point in the history
This patch partially implements Fetch API's [Body](https://developer.mozilla.org/en-US/docs/Web/API/Body)
interface for both Request and Response.

Fixes #26.
  • Loading branch information
aslushnikov committed Jul 6, 2017
1 parent 64fed38 commit 19a8d74
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 6 deletions.
73 changes: 68 additions & 5 deletions lib/NetworkManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class NetworkManager extends EventEmitter {
_onRequestWillBeSent(event) {
if (event.redirectResponse) {
let request = this._idToRequest.get(event.requestId);
let response = new Response(request, event.redirectResponse);
let response = new Response(request, event.redirectResponse, this._getResponseBody.bind(this, event.requestId));
request._response = response;
this.emit(NetworkManager.Events.Response, response);
this.emit(NetworkManager.Events.RequestFinished, request);
Expand All @@ -109,7 +109,7 @@ class NetworkManager extends EventEmitter {
*/
_onResponseReceived(event) {
let request = this._idToRequest.get(event.requestId) || null;
let response = new Response(request, event.response);
let response = new Response(request, event.response, this._getResponseBody.bind(this, event.requestId));
request._response = response;
this.emit(NetworkManager.Events.Response, response);
}
Expand All @@ -131,6 +131,15 @@ class NetworkManager extends EventEmitter {
this._idToRequest.delete(event.requestId);
this.emit(NetworkManager.Events.RequestFailed, request);
}

/**
* @param {string} requestId
* @return {!Promise<!Buffer>}
*/
async _getResponseBody(requestId) {
let response = await this._client.send('Network.getResponseBody', {requestId});
return Buffer.from(response.body, response.base64Encoded ? 'base64' : 'utf8');
}
}

class Headers {
Expand Down Expand Up @@ -218,11 +227,63 @@ class Headers {
}
}

class Request {
class Body {
/**
* @param {function():!Promise<!Buffer>} contentCallback
*/
constructor(contentCallback) {
this._contentCallback = contentCallback;
/** @type {?Promise<!Buffer>} */
this._contentPromise = null;
}

/**
* @return {!Promise<!Buffer>}
*/
buffer() {
if (!this._contentPromise)
this._contentPromise = this._contentCallback();
return this._contentPromise;
}

/**
* @return {boolean}
*/
get bodyUsed() {
return !!this._contentPromise;
}

/**
* @return {!Promise<string>}
*/
async text() {
let content = await this.buffer();
return content.toString('utf8');
}

/**
* @return {!Promise<!Object>}
*/
async json() {
let content = await this.text();
return JSON.parse(content);
}

/**
* @return {!Promise<!ArrayBuffer>}
*/
async arrayBuffer() {
let content = await this.buffer();
return content.buffer;
}
}

class Request extends Body {
/**
* @param {!Object} payload
*/
constructor(payload) {
super(() => Promise.resolve(payload.postData || ''));
this._response = null;
this.url = payload.url;
this.method = payload.method;
Expand All @@ -238,12 +299,14 @@ class Request {
}
}

class Response {
class Response extends Body {
/**
* @param {?Request} request
* @param {!Object} payload
* @param {function():!Promise<!Buffer>} contentCallback
*/
constructor(request, payload) {
constructor(request, payload, contentCallback) {
super(contentCallback);
this._request = request;
this.headers = Headers.fromPayload(payload.headers);
this.ok = payload.status >= 200 && payload.status <= 299;
Expand Down
1 change: 1 addition & 0 deletions test/assets/simple.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"foo": "bar"}
28 changes: 27 additions & 1 deletion test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ describe('Puppeteer', function() {
});

afterAll(function() {
browser.close();
staticServer.stop();
browser.close();
});

beforeEach(SX(async function() {
Expand Down Expand Up @@ -526,6 +526,32 @@ describe('Puppeteer', function() {
expect(result).toBe('<div>hello</div>');
}));
});
describe('Request implements Body', function() {
it('should work', SX(async function() {
await page.navigate(EMPTY_PAGE);
staticServer.setRoute('/post', (req, res) => res.end());
let request = null;
page.on('request', r => request = r);
await page.evaluate(() => fetch('./post', { method: 'POST', body: JSON.stringify({foo: 'bar'})}));
expect(request).toBeTruthy();
expect(request.bodyUsed).toBe(false);
expect(await request.text()).toBe('{"foo":"bar"}');
expect(await request.json()).toEqual({foo: 'bar'});
expect(request.bodyUsed).toBe(true);
}));
});
describe('Response implements Body', function() {
it('should work', SX(async function() {
let response = null;
page.on('response', r => response = r);
await page.navigate(STATIC_PREFIX + '/simple.json');
expect(response).toBeTruthy();
expect(response.bodyUsed).toBe(false);
expect(await response.text()).toBe('{"foo": "bar"}\n');
expect(await response.json()).toEqual({foo: 'bar'});
expect(response.bodyUsed).toBe(true);
}));
});
describe('Network Events', function() {
it('Page.Events.Request', SX(async function() {
let requests = [];
Expand Down

0 comments on commit 19a8d74

Please sign in to comment.