Skip to content
This repository has been archived by the owner on Dec 16, 2023. It is now read-only.

Commit

Permalink
CHANGED resources.request -> resources.fetch
Browse files Browse the repository at this point in the history
  • Loading branch information
assaf committed Mar 31, 2015
1 parent aab869e commit 1cc47de
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 147 deletions.
94 changes: 21 additions & 73 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -908,8 +908,6 @@ Each resource provides four properties:
- `request` - The request object
- `response` - The resource object (if received)
- `error` - The error received instead of response
- `target` - The target element or document (when loading HTML page, script,
etc)

The request object is based on the [Fetch API Request
object](https://fetch.spec.whatwg.org/#request-class).
Expand Down Expand Up @@ -975,89 +973,39 @@ If you need to retrieve or operate on resources directly, you can do that as
well, using all the same features available to Zombie, including cookies,
authentication, etc.

#### resources.addHandler(handler)

Adds a handler to the pipeline of this browser instance. To add a handler to the
pipeline of every browser instance, use `Browser.Resources.addHandler`.
#### resources.fetch(input, init)

#### resources.dump(output?)

Dumps the resources list to the output stream (defaults to standard output
stream).

#### resources.pipeline

Returns the current pipeline (array of handlers) for this browser instance.

#### resources.get(url, callback)

Retrieves a resource with the given URL and passes response to the callback.
Retrieves a resource and returns a promise. Based on the [Fetch
API](https://fetch.spec.whatwg.org/#fetch-method).

For example:

```js
browser.resources.get('http://some.service', function(error, response) {
console.log(response.statusText);
console.log(response.body);
});
```

#### resources.post(url, options, callback)

Posts a document to the resource with the given URL and passes the response to
the callback.

Supported options are:

- `body`- Request document body
- `headers` - Headers to include in the request
- `params` - Parameters to pass in the document body
- `timeout` - Request timeout in milliseconds (0 or `null` for no timeout)

For example:

```js
const params = {
'count': 5
};
browser.resources.post(
'http://some.service',
{ params: params },
function(error, response) {
. . .
});
browser.resources
.fetch('http://some.service')
.then(function(response) {
console.log('Status:', response.status);
return response.text();
})
.then(function(text) {
console.log('Body:', text);
});

const headers = {
'Content-Type': 'application/x-www-form-urlencoded'
};
browser.resources.post(
'http://some.service',
{ headers: headers, body: 'count=5' },
function(error, response) {
. . .
});
browser.resources.fetch('http://some.service', { method: 'DELETE' });
```

#### resources.dump(output?)

#### resources.request(method, url, options, callback)
Dumps the resources list to the output stream (defaults to standard output
stream).

Makes an HTTP request to the resource and passes the response to the callback.
#### resources.addHandler(handler)

Supported options are:
Adds a handler to the pipeline of this browser instance. To add a handler to the
pipeline of every browser instance, use `Browser.Resources.addHandler`.

- `body`- Request document body
- `headers` - Headers to include in the request
- `params` - Parameters to pass in the query string (`GET`, `DELETE`) or
document body (`POST`, `PUT`)
- `timeout` - Request timeout in milliseconds (0 or `null` for no timeout)
#### resources.pipeline

For example:
Returns the current pipeline (array of handlers) for this browser instance.

```js
browser.resources.request('DELETE',
'http://some.service',
function(error) {
. . .
});
```

8 changes: 4 additions & 4 deletions src/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -609,18 +609,18 @@ module.exports = function loadDocument(args) {

const referrer = args.referrer || browser.referrer || browser.referer || history.url;
const request = buildRequest({ method: args.method, url, referrer, headers: args.headers, params: args.params, encoding: args.encoding });
window._request = request;

window._eventQueue.http(request, document, async (response)=> {
window._eventQueue.http(request, async (error, response)=> {

window._response = response;
if (response.type === 'error') {
if (error) {
document.write(`<html><body>Network Error</body></html>`);
document.close();
browser.emit('error', new DOM.DOMException(DOM.NETWORK_ERR));
return;
}

window._request = response.request;
window._response = response;
const done = window._eventQueue.waitForCompletion();
try {
history.updateLocation(window, response._url);
Expand Down
4 changes: 2 additions & 2 deletions src/dom/jsdom_patches.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,10 @@ DOM.resourceLoader.load = function(element, href, callback) {
// JSDOM queue before we add to the Zombie event queue.
const enqueued = this.enqueue(element, callback && callback.bind(element), url);
const request = new Fetch.Request(url);
window._eventQueue.http(request, element, (response)=> {
window._eventQueue.http(request, (error, response)=> {
// Since this is used by resourceLoader that doesn't check the response,
// we're responsible to turn anything other than 2xx/3xx into an error
if (response.type === 'error')
if (error)
enqueued(new Error('Network error'));
else if (response.status >= 400)
enqueued(new Error(`Server returned status code ${response.status} from ${url}`));
Expand Down
19 changes: 12 additions & 7 deletions src/eventloop.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,25 +224,30 @@ class EventQueue {
// Makes an HTTP request.
//
// request - Request object
// target - Target element / document
// callback - Called with Response object
http(request, target, callback) {
// callback - Called with Response object to process the response
//
// Because the callback is added to the queue, we can't use promises
http(request, callback) {
assert(this.queue, 'This browser has been destroyed');

const done = this.waitForCompletion();
this.browser.resources
.request(request, target)
.fetch(request)
.then((response)=> {
// We can't cancel pending requests, but we can ignore the response if
// window already closed
if (this.queue)
// This will get completion function to execute, e.g. to check a page
// before meta tag refresh
this.enqueue(()=> {
callback(response);
callback(null, response);
});
done();
});
})
.catch((error)=> {
if (this.queue)
callback(error);
})
.then(done);
}

// Fire an error event. Used by JSDOM patches.
Expand Down
2 changes: 1 addition & 1 deletion src/fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ class Request extends Body {
constructor(input, init) {
let bodyInit = null;

if (input instanceof Request && input.body) {
if (input instanceof Request && input._stream) {
if (input._bodyUsed)
throw new TypeError('Request body already used');
bodyInit = input;
Expand Down
54 changes: 12 additions & 42 deletions src/resources.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,15 @@ const Fetch = require('./fetch');
const { Headers } = require('./fetch');
const { isArray } = require('util');
const Path = require('path');
const QS = require('querystring');
const Request = require('request');
const URL = require('url');
const Utils = require('jsdom/lib/jsdom/utils');


class Resource {

constructor({ request, target }) {
constructor({ request }) {
this.request = request;
this.target = target;
this.error = null;
this.response = null;
}
Expand All @@ -29,22 +27,14 @@ class Resource {
}

dump(output) {
const { request, response, error, target } = this;
const { request, response, error } = this;
// Write summary request/response header
if (response) {
const elapsed = response.time - request.time;
output.write(`${request.method} ${this.url} - ${response.status} ${response.statusText} - ${elapsed}ms\n`);
} else
output.write(`${request.method} ${this.url}\n`);

// Tell us which element/document is loading this.
if (target instanceof DOM.Document)
output.write(' Loaded as HTML document\n');
else if (target && target.id)
output.write(` Loading by element #${target.id}\n`);
else if (target)
output.write(` Loading as ${target.tagName} element\n`);

// If response, write out response headers and sample of document entity
// If error, write out the error message
// Otherwise, indicate this is a pending request
Expand Down Expand Up @@ -84,50 +74,30 @@ class Resources extends Array {
}


// Make an HTTP request (also supports file: protocol).
//
// method - Request method (GET, POST, etc)
// url - Request URL
// options - See below
//
// Options:
// headers - Name/value pairs of headers to send in request
// params - Parameters to pass in query string or document body
// body - Request document body
// timeout - Request timeout in milliseconds (0 or null for no timeout)
async request(request, target) {
const resource = new Resource({ request, target });
async fetch(input, init) {
const request = new Fetch.Request(input, init);
const resource = new Resource({ request });
this.push(resource);
this.browser.emit('request', request);

try {
const response = await this._runPipeline(request);
if (response) {
response.time = Date.now();
response.request = request;
resource.response = response;
} else
this.browser.emit('response', request, response);
return response;
} else {
resource.response = Fetch.Resource.error();
throw new TypeError('No response');
}
} catch (error) {
console.error(error.stack)
this.browser._debug('Resource error', error.stack);
resource.error = error;
resource.response = Fetch.Response.error();
throw new TypeError(error.message);
}
this.browser.emit('response', request, resource.response);
return resource.response;
}


// GET request.
async get(request) {
request = new Fetch.Request(request, { method: 'GET' });
return await this.request(request);
}

// POST request.
async post(request) {
request = new Fetch.Request(request, { method: 'POST' });
return await this.request(request);
}


Expand Down
32 changes: 18 additions & 14 deletions src/xhr.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,27 @@ class XMLHttpRequest extends DOM.EventTarget {
this._pending = request;
this._fire('loadstart');

const timeout = setTimeout(function() {
const timeout = setTimeout(()=> {
if (this._pending === request)
this._pending = null;
request.timedOut = true;

this._stateChanged(XMLHttpRequest.DONE);
this._fire('progress');
this._error = new DOM.DOMException(DOM.TIMEOUT_ERR, 'The request timed out');
this._fire('timeout', this._error);
this._fire('loadend');
this._browser.errors.push(this._error);
}, this.timeout || ms('2m'));

this._window._eventQueue.http(request, this, (response)=> {
this._window._eventQueue.http(request, (error, response)=> {
// Request already timed-out, nothing to do
if (request.timedOut)
return;
clearTimeout(timeout);

if (this._pending === request)
this._pending = null;
clearTimeout(timeout);

// Request aborted
if (request.aborted) {
Expand All @@ -133,17 +146,8 @@ class XMLHttpRequest extends DOM.EventTarget {
return;
}

if (request.timedOut) {
this._stateChanged(XMLHttpRequest.DONE);
this._fire('progress');
this._error = new DOM.DOMException(DOM.TIMEOUT_ERR, 'The request timed out');
this._fire('timeout', this._error);
this._fire('loadend');
this._browser.errors.push(this._error);
return;
}

if (response.type === 'error') {
// If not aborted, then we look at networking error
if (error) {
this._stateChanged(XMLHttpRequest.DONE);
this._fire('progress');
this._error = new DOM.DOMException(DOM.NETWORK_ERR);
Expand Down
Loading

0 comments on commit 1cc47de

Please sign in to comment.