Permalink
Browse files

lots of stuff

  • Loading branch information...
1 parent 39b4dc9 commit aa20e82e33968bc0ee00663615da7b884c5964bc @disfated disfated committed Jan 8, 2012
Showing with 464 additions and 350 deletions.
  1. +38 −25 README.md
  2. +58 −42 lib/restler.js
  3. +5 −1 test/all.js
  4. +363 −119 test/restler.js
  5. +0 −163 test/test_helper.js
View
@@ -29,14 +29,14 @@ API
### request(url, options)
-Basic method to make a request of any type. The function returns a RestRequest object
-that emits events:
+Basic method to make a request of any type. The function returns a RestRequest object that emits events:
-* _complete_ emitted when the request has finished whether it was successful or not. Gets passed the response data and the response as arguments.
-* _success_ emitted when the request was successful. Gets passed the response data and the response as arguments.
-* _error_ emitted when the request was unsuccessful. Gets passed the response data and the response as arguments.
-* _2XX, 3XX, 4XX, 5XX etc_ emitted for all requests with response codes in the range. Eg. 2XX emitted for 200, 201, 203
-* _actual response code_ there is an event emitted for every single response code. eg. 404, 201, etc.
+* `complete: function(data, response)` - emitted when the request has finished whether it was successful or not. Gets passed the response data and the response object as arguments. If some error has occurred, `data` is always instance of `Error`.
+* `success: function(data, response)` - emitted when the request was successful. Gets passed the response data and the response object as arguments.
+* `fail`: function(data, response)` - emitted when the request was successful, but 4xx status code returned. Gets passed the response data and the response object as arguments.
+* `error`: function(err, response)` - emitted when some errors have occurred (eg. connection aborted, parse, encoding, decoding failed or some other unhandled errors). Gets passed the `Error` object and the response object (when available) as arguments.
+* `2XX`, `3XX`, `4XX`, `5XX: function(data, response)` - emitted for all requests with response codes in the range (eg. `2XX` emitted for 200, 201, 203).
+* <code><i>actual response code</i>: function(data, response)<code> - emitted for every single response code (eg. 404, 201, etc).
### get(url, options)
@@ -67,22 +67,22 @@ If the content type isn't recognised it just returns the data untouched.
All of these attempt to turn the response into a JavaScript object. In order to use the YAML and XML parsers you must have yaml and/or xml2js installed.
-### options hash
+### Options
-* _method_ Request method, can be get, post, put, del
-* _query_ Query string variables as a javascript object, will override the querystring in the URL
-* _data_ The data to be added to the body of the request. Can be a string or any object.
-Note that if you want your request body to be JSON with the Content-Type `application/json`, you need to
-JSON.stringify your object first. Otherwise, it will be sent as `application/x-www-form-urlencoded` and encoded accordingly.
-* _parser_ A function that will be called on the returned data. try parsers.auto, parsers.json etc
-* _encoding_ The encoding of the request body. defaults to utf8
-* _decoding_ The encoding of the response body. For a list of supported values see [Buffers](http://nodejs.org/docs/latest/api/buffers.html#buffers). Additionally accepts `"buffer"` - returns response as `Buffer`. Defaults to `utf8`.
-* _headers_ a hash of HTTP headers to be sent
-* _username_ Basic auth username
-* _password_ Basic auth password
-* _multipart_ If set the data passed will be formated as multipart/form-encoded. See multipart example below.
-* _client_ A http.Client instance if you want to reuse or implement some kind of connection pooling.
-* _followRedirects_ Does what it says on the tin.
+* `method` Request method, can be get, post, put, del. Defaults to `"get"`.
+* `query` Query string variables as a javascript object, will override the querystring in the URL. Defaults to empty.
+* `data` The data to be added to the body of the request. Can be a string or any object.
+Note that if you want your request body to be JSON with the `Content-Type: application/json`, you need to
+`JSON.stringify` your object first. Otherwise, it will be sent as `application/x-www-form-urlencoded` and encoded accordingly.
+* `parser` A function that will be called on the returned data. Use any of predefined `restler.parsers`. See parsers section below. Defaults to `restler.parsers.auto`.
+* `encoding` The encoding of the request body. Defaults to `"utf8"`.
+* `decoding` The encoding of the response body. For a list of supported values see [Buffers](http://nodejs.org/docs/latest/api/buffers.html#buffers). Additionally accepts `"buffer"` - returns response as `Buffer`. Defaults to `"utf8"`.
+* `headers` A hash of HTTP headers to be sent. Defaults to `{ 'Accept': '*/*', 'User-Agent': 'Restler for node.js' }`.
+* `username` Basic auth username. Defaults to empty.
+* `password` Basic auth password. Defaults to empty.
+* `multipart` If set the data passed will be formated as `multipart/form-encoded`. See multipart example below. Defaults to `false`.
+* `client` A http.Client instance if you want to reuse or implement some kind of connection pooling. Defaults to empty.
+* `followRedirects` If set will recursively follow redirects. Defaults to `true`.
Example usage
@@ -152,11 +152,24 @@ rest.post('http://example.com/action', {
Running the tests
-----------------
+install **[nodeunit](https://github.com/caolan/nodeunit)**
-```javascript
-node test/restler.js
+```bash
+npm install nodeunit
```
-
+
+then
+
+```bash
+node test/all.js
+```
+
+or
+
+```bash
+nodeunit test/restler.js
+```
+
TODO
----
* Deal with no utf-8 response bodies
View
@@ -99,11 +99,12 @@ mixin(Request.prototype, {
if (this._isRedirect(response) && this.options.followRedirects == true) {
try {
var location = url.resolve(this.url, response.headers['location']);
- this.options.originalRequest = this;
-
- request(location, this.options);
- } catch(e) {
- self._respond('error', '', 'Failed to follow redirect');
+ Request.call(this, location, this.options); // reusing request object to handle recursive redirects
+ // todo handle somehow infinite redirects
+ this.run();
+ } catch(err) {
+ err.message = 'Failed to follow redirect: ' + err.message;
+ self._fireError(err, response);
}
} else {
var body = '';
@@ -115,65 +116,65 @@ mixin(Request.prototype, {
});
response.on('end', function() {
+ response.raw = body;
self._decode(new Buffer(body, 'binary'), response, function(err, body) {
if (err) {
- self._respond('error', '', 'Failed to decode response body');
+ self._fireError(err, response);
return;
}
- self._encode(body, response, function(body) {
- self._fireEvents(body, response);
+ self._encode(body, response, function(err, body) {
+ if (err) {
+ self._fireError(err, response);
+ } else {
+ self._fireSuccess(body, response);
+ }
});
});
});
}
},
- _errorHandler: function(err) {
- this._respond('error', null, err);
- this._respond('complete', null, err);
- },
_decode: function(body, response, callback) {
- var encoder = response.headers['content-encoding'];
- if (encoder in decoders) {
- decoders[encoder].call(response, body, callback);
+ var decoder = response.headers['content-encoding'];
+ if (decoder in decoders) {
+ decoders[decoder].call(response, body, callback);
} else {
callback(null, body);
}
},
_encode: function(body, response, callback) {
var self = this;
if (self.options.decoding == 'buffer') {
- callback(body);
+ callback(null, body);
} else {
body = body.toString(self.options.decoding);
if (self.options.parser) {
self.options.parser.call(response, body, callback);
} else {
- callback(body);
+ callback(null, body);
}
}
},
- _respond: function(type, data, response) {
- if (this.options.originalRequest) {
- this.options.originalRequest.emit(type, data, response);
+ _fireError: function(err, response) {
+ this.emit('error', err, response);
+ this.emit('complete', err, response);
+ },
+ _fireSuccess: function(body, response) {
+ if (parseInt(response.statusCode) >= 400) {
+ this.emit('fail', body, response);
} else {
- this.emit(type, data, response);
+ this.emit('success', body, response);
}
- },
- _fireEvents: function(body, response) {
- if (parseInt(response.statusCode) >= 400) this._respond('error', body, response);
- else this._respond('success', body, response);
-
- this._respond(response.statusCode.toString().replace(/\d{2}$/, 'XX'), body, response);
- this._respond(response.statusCode.toString(), body, response);
- this._respond('complete', body, response);
+ this.emit(response.statusCode.toString().replace(/\d{2}$/, 'XX'), body, response);
+ this.emit(response.statusCode.toString(), body, response);
+ this.emit('complete', body, response);
},
_makeRequest: function() {
var self = this;
this.request.on('response', function(response) {
self._responseHandler(response);
}).on('error', function(err) {
- self._errorHandler(err);
+ self._fireError(err, null);
});
},
run: function() {
@@ -236,10 +237,19 @@ var parsers = {
}
}
- callback(data);
+ callback(null, data);
},
json: function(data, callback) {
- callback(data && data.length > 1 && JSON.parse(data));
+ if (data && data.length) {
+ try {
+ callback(null, JSON.parse(data));
+ } catch (err) {
+ err.message = 'Failed to parse JSON body: ' + err.message;
+ callback(err, null);
+ }
+ } else {
+ callback(null, null);
+ }
}
};
@@ -251,7 +261,16 @@ try {
var yaml = require('yaml');
parsers.yaml = function(data, callback) {
- return callback(data && yaml.eval(data));
+ if (data) {
+ try {
+ callback(null, yaml.eval(data));
+ } catch (err) {
+ err.message = 'Failed to parse YAML body: ' + err.message;
+ callback(err, null);
+ }
+ } else {
+ callback(null, null);
+ }
};
parsers.auto.matchers['application/yaml'] = parsers.yaml;
@@ -263,17 +282,14 @@ try {
parsers.xml = function(data, callback) {
if (data) {
var parser = new xml2js.Parser();
-
- parser.on('end', function(result) {
- callback(result);
+ parser.parseString(data, function(err, data) {
+ if (err) {
+ err.message = 'Failed to parse XML body: ' + err.message;
+ }
+ callback(err, data);
});
- try {
- parser.parseString(data);
- } catch (e) {
- callback({error:'Oops, something went wrong.'});
- }
} else {
- callback();
+ callback(null, null);
}
};
View
@@ -1 +1,5 @@
-require('./restler');
+
+var nodeunit = require('nodeunit');
+var reporter = nodeunit.reporters['default'];
+process.chdir(__dirname);
+reporter.run(['restler.js']);
Oops, something went wrong.

0 comments on commit aa20e82

Please sign in to comment.