Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/Stanley/request into json…
Browse files Browse the repository at this point in the history
…body
  • Loading branch information
mikeal committed Jan 30, 2011
2 parents 4c9c984 + af66607 commit 641ec05
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 20 deletions.
43 changes: 40 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@
npm install request
</pre>

Or from source:

<pre>
git clone git://github.com/mikeal/request.git
cd request
npm link .
</pre>

Running specs:
<pre>
node specs.js
</pre>
Note: `jasmine-node` package is required to run tests.

## Super simple to use

Request is designed to be the simplest way possible to make http calls. It support HTTPS and follows redirects by default.
Expand All @@ -17,7 +31,9 @@ The first argument is an options object. The only required option is uri, all ot
* `uri` || `url` - fully qualified uri or a parsed url object from url.parse()
* `method` - http method, defaults to GET
* `headers` - http headers, defaults to {}
* `body` - entity body for POST and PUT requests
* `body` - entity body for POST and PUT requests. Must be buffer or string.
* `json` - similar to `body` but converts value to string and adds `Content-type: application/json` header.
* `multipart` - (experimental) array of objects which contains their own headers and `body` attribute. Sends `multipart/related` request. See example below.
* `client` - existing http client object (when undefined a new one will be created and assigned to this property so you can keep around a reference to it if you would like use keep-alive on later request)
* `followRedirect` - follow HTTP 3xx responses as redirects. defaults to true.
* `maxRedirects` - the maximum number of redirects to follow, defaults to 10.
Expand All @@ -28,7 +44,7 @@ The first argument is an options object. The only required option is uri, all ot

The callback argument gets 3 arguments. The first is an error when applicable (usually from the http.Client option not the http.ClientRequest object). The second in an http.ClientResponse object. The third is the response body buffer.

Example:
Examples:
<pre>
var request = require('request');
request({uri:'http://www.google.com'}, function (error, response, body) {
Expand All @@ -38,4 +54,25 @@ Example:
})
</pre>

It's also worth noting that the options argument will mutate. When following a redirect the uri values will change. After setting up a client options it will set the client property.
<pre>
var request = require('request');
var rand = Math.floor(Math.random()*100000000).toString();
request({
method: 'PUT',
uri: 'http://mikeal.couchone.com/testjs/'+ rand,
multipart: [
{ 'content-type': 'application/json',
body: JSON.stringify({foo: 'bar', _attachments: {'message.txt': {follows: true, length: 18, 'content_type': 'text/plain' }}})},
{ body: 'I am an attachment' }
]
}, function (error, response, body) {
if(response.statusCode == 201){
console.log('document saved as: http://mikeal.couchone.com/testjs/'+ rand);
} else {
console.log('error: '+ response.statusCode);
console.log(body)
}
})
</pre>

It's also worth noting that the options argument will mutate. When following a redirect the uri values will change. After setting up a client options it will set the client property.
33 changes: 30 additions & 3 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,37 @@ function request (options, callback) {

if (options.proxy) options.fullpath = (options.uri.protocol + '//' + options.uri.host + options.fullpath);

if (options.body !== undefined) {
options.body = Buffer.isBuffer(options.body) ? options.body : new Buffer(options.body);
options.headers['content-length'] = options.body.length;
if(options.json){
options.headers['content-type'] = 'application/json';
options.body = JSON.stringify(options.json);
} else if(options.multipart){
options.body = '';
options.headers['content-type'] = 'multipart/related;boundary="frontier"';

if(!options.multipart.forEach) throw new Error('Argument error');
options.multipart.forEach(function(part){
var body = part.body;
if(!body) throw Error('Body attribute missing')
delete part.body;
options.body += '--frontier\r\n';
Object.keys(part).forEach(function(key){
options.body += key + ': ' + part[key] + '\r\n'
})
options.body += '\r\n' + body + '\r\n';
})
options.body += '--frontier--'
}

if (options.body) {
if (!Buffer.isBuffer(options.body)) {
options.body = new Buffer(options.body);
}
if(options.body.length)
options.headers['content-length'] = options.body.length;
else
throw new Error('Argument error')
}

options.request = options.client.request(options.method, options.fullpath, options.headers);
options.request.addListener("response", function (response) {
if (setHost) delete options.headers.host;
Expand Down
50 changes: 50 additions & 0 deletions specs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
var jasmine = require('jasmine-node');
var sys = require('sys'),
Path= require('path');

var SPEC_FOLDER= Path.join(process.cwd(), 'specs'),
SPEC_MATCHER_REGEX= "spec\.js$",
HELPER_MATCHER_REGEX= "helper\.js$";

for (var key in jasmine)
global[key] = jasmine[key];

var isVerbose = false;
var showColors = true;
var spec = SPEC_MATCHER_REGEX;

function escapeRegex(text) {
return text.replace(escapeRegex._escapeRegex, '\\$1');
}

/** The special characters in a string that need escaping for regular expressions. */
escapeRegex.specialCharacters= ['/', '.', '*', '+', '?', '|',
'(', ')', '[', ']', '{', '}', '\\'];

/** A regular expression that will match any special characters that need to be
escaped to create a valid regular expression. */
escapeRegex._escapeRegex= new RegExp('(\\'+ escapeRegex.specialCharacters.join("|\\") + ')', 'g');

process.argv.forEach(function(arg, index){
switch(arg){
case '--color':
showColors = true;
break;
case '--noColor':
showColors = false;
break;
case '--verbose':
isVerbose = true;
break;

default:
if (index>1)
spec= "^.*/" + escapeRegex(arg) + "$";
break;
}
});

jasmine.loadHelpersInFolder(SPEC_FOLDER, HELPER_MATCHER_REGEX);
jasmine.executeSpecsInFolder(SPEC_FOLDER, function(runner, log){
process.exit(runner.results().failedCount);
}, isVerbose, showColors, spec);
122 changes: 122 additions & 0 deletions specs/body-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
var http = require('http');
request = require('../main');

describe('raw body', function(){

var fakeClient = new http.Client();
fakeRequest = new http.ClientRequest({});

beforeEach(function(){
spyOn(http, 'createClient').andReturn(fakeClient);
spyOn(fakeClient, 'request').andReturn(fakeRequest);
});

it('should accept string', function(){

request({
method: 'POST',
uri: 'http://nodejs.org',
body: 'Oh hi.'
});

//expect(http.createClient).toHaveBeenCalledWith(80, 'nodejs.org', false); // TODO: move to basic-spec
expect(fakeClient.request).toHaveBeenCalledWith('POST', '/', { host: 'nodejs.org', 'content-length': 'Oh hi.'.length });
expect(fakeRequest.output[1].toString()).toEqual('Oh hi.');
});

it('should accept buffer', function(){
request({
method: 'POST',
uri: 'http://nodejs.org',
body: new Buffer('Oh hi.')
});

expect(fakeClient.request).toHaveBeenCalledWith('POST', '/', { host: 'nodejs.org', 'content-length': 'Oh hi.'.length });
expect(fakeRequest.output[1].toString()).toEqual('Oh hi.');
});
});

describe('json', function(){

it('should be converted to json string', function(){

var fakeClient = new http.Client();
fakeRequest = new http.ClientRequest({});

spyOn(http, 'createClient').andReturn(fakeClient);
spyOn(fakeClient, 'request').andReturn(fakeRequest);

request({
method: 'POST',
uri: 'http://nodejs.org',
json: {foo: 'bar'}
});

expect(fakeClient.request).toHaveBeenCalledWith('POST', '/', {
'host': 'nodejs.org',
'content-length': JSON.stringify({foo: 'bar'}).length,
'content-type': 'application/json' });
expect(fakeRequest.output[1].toString()).toEqual('{"foo":"bar"}');
});
});

describe('multipart', function(){
it('should be joined', function(){

var fakeClient = new http.Client();
fakeRequest = new http.ClientRequest({});

spyOn(http, 'createClient').andReturn(fakeClient);
spyOn(fakeClient, 'request').andReturn(fakeRequest);

request({
method: 'POST',
uri: 'http://nodejs.org',
multipart: [{'content-type': 'text/html', 'body': '<html><body>Oh hi.</body></html>'}, {'body': 'Oh hi.'}]
});

var body = '--frontier\r\n' +
'content-type: text/html\r\n' +
'\r\n' +
'<html><body>Oh hi.</body></html>' +
'\r\n--frontier\r\n\r\n' +
'Oh hi.' +
'\r\n--frontier--'

expect(fakeClient.request).toHaveBeenCalledWith('POST', '/', {
'host': 'nodejs.org',
'content-length': new Buffer(body).length,
'content-type': 'multipart/related;boundary="frontier"' });
expect(fakeRequest.output[1].toString()).toEqual(body);
});
});

describe('exception', function(){
it('should be thrown on non PUT and POST requests with body, json or multipart')

it('should be thrown on non comaptibile body types', function(){
expect(function(){
request({
method: 'POST',
uri: 'http://nodejs.org',
body: {foo: 'bar'}
});
}).toThrow(new Error('Argument error'))

expect(function(){
request({
method: 'POST',
uri: 'http://nodejs.org',
multipart: 'foo'
});
}).toThrow(new Error('Argument error'))

expect(function(){
request({
method: 'POST',
uri: 'http://nodejs.org',
multipart: [{}]
});
}).toThrow(new Error('Body attribute missing'))
});
})
14 changes: 0 additions & 14 deletions tests/couch.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,3 @@ function testportsStream (port) {
})
}
testports();
var randomnumber=Math.floor(Math.random()*100000000).toString();
request({uri:'http://mikeal.couchone.com/testjs', method:'POST', headers: h, body:'{"_id":"'+randomnumber+'"}'},
function (error, response, body) {
if (error) {throw new Error(error)};
assert.equal(response.statusCode, 201, body);
});

var options = {uri:'http://gmail.com'};
request(options, function (error, response, body) {
if (error) throw error;
assert.equal(response.statusCode, 200);
assert.equal(options.uri.host, 'www.google.com');
assert.equal(response.socket.port, 443);
})

0 comments on commit 641ec05

Please sign in to comment.