Skip to content

Commit

Permalink
Closed issue 82 : handling cookies - added tests too
Browse files Browse the repository at this point in the history
  • Loading branch information
alessioalex committed Nov 12, 2011
1 parent 8869b2e commit 8323eed
Show file tree
Hide file tree
Showing 10 changed files with 309 additions and 200 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ The first argument can be either a url or an options object. The only required o
* `timeout` - Integer containing the number of milliseconds to wait for a request to respond before aborting the request
* `proxy` - An HTTP proxy to be used. Support proxy Auth with Basic Auth the same way it's supported with the `url` parameter by embedding the auth info in the uri.
* `strictSSL` - Set to `true` to require that SSL certificates be valid. Note: to use your own certificate authority, you need to specify an agent that was created with that ca as an option.
* `jar` - Set to `request.jar()` if you don't want cookies to be remembered for future use


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.
Expand Down Expand Up @@ -191,3 +192,21 @@ request.get(url)
}
)
```
The jar holds the state of the cookies being set, each request sends the proper cookies for that domain. The boiler is easy to get rid of with `defaults` or per each request if you set `options.jar`.

```javascript
var j = request.jar()
var request = request.defaults({jar:j})
request('http://www.google.com', function () {
request('http://images.google.com')
})
```
OR
```javascript
var j = request.jar()
// you can also set a cookie like so:
j.add('sid=LsoJ7tV9wfwz2VkE9lxC4EqU.%2BXxV0F9i1hfCMb4uNKZTpAUJLTwlf57FewvHne%2BAGVI; path=/; expires=Sun, 23 Nov 2011 16:50:21 GMT; httpOnly')
request(url: 'http://www.google.com', jar: j, function () {
request('http://images.google.com')
})
```
82 changes: 34 additions & 48 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ var http = require('http')
, stream = require('stream')
, qs = require('querystring')
, mimetypes = require('./mimetypes')
, oauth = require('./oauth')
, uuid = require('./uuid')
, Cookie = require('./vendor/cookie')
, CookieJar = require('./vendor/cookie/jar')
, cookieJar = new CookieJar
;

try {
Expand Down Expand Up @@ -137,6 +138,18 @@ Request.prototype.request = function () {
setHost = true
}

// if user tries to define a bad cookie jar
if (self.jar && !(self.jar instanceof CookieJar)) {
throw new Error("Argument error, options.jar.")
} else if (self.jar) {
// fetch cookie from the user defined cookie jar
var cookies = self.jar.get({ url: self.uri.href })
} else {
// fetch cookie from the global cookie jar
var cookies = cookieJar.get({ url: self.uri.href })
}
if (cookies) {self.headers.Cookie = cookies}

if (!self.uri.pathname) {self.uri.pathname = '/'}
if (!self.uri.port) {
if (self.uri.protocol == 'http:') {self.uri.port = 80}
Expand Down Expand Up @@ -164,52 +177,6 @@ Request.prototype.request = function () {
if (self.onResponse) self.on('error', function (e) {self.onResponse(e)})
if (self.callback) self.on('error', function (e) {self.callback(e)})

if (self.form) {
self.headers['content-type'] = 'application/x-www-form-urlencoded; charset=utf-8'
self.body = qs.stringify(self.form).toString('utf8')
}

if (self.oauth) {
var form
if (self.headers['content-type'] &&
self.headers['content-type'].slice(0, 'application/x-www-form-urlencoded'.length) ===
'application/x-www-form-urlencoded'
) {
form = qs.parse(self.body)
}
if (self.uri.query) {
form = qs.parse(self.uri.query)
}
if (!form) form = {}
var oa = {}
for (i in form) oa[i] = form[i]
for (i in self.oauth) oa['oauth_'+i] = self.oauth[i]
if (!oa.oauth_version) oa.oauth_version = '1.0'
if (!oa.oauth_timestamp) oa.oauth_timestamp = Math.floor( (new Date()).getTime() / 1000 ).toString()
if (!oa.oauth_nonce) oa.oauth_nonce = uuid().replace(/-/g, '')

oa.oauth_signature_method = 'HMAC-SHA1'

var consumer_secret = oa.oauth_consumer_secret
delete oa.oauth_consumer_secret
var token_secret = oa.oauth_token_secret
delete oa.oauth_token_secret

var baseurl = self.uri.protocol + '//' + self.uri.host + self.uri.pathname
var signature = oauth.hmacsign(self.method, baseurl, oa, consumer_secret, token_secret)

// oa.oauth_signature = signature
for (i in form) {
if ( i.slice(0, 'oauth_') in self.oauth) {
// skip
} else {
delete oa['oauth_'+i]
}
}
self.headers.authorization =
'OAuth '+Object.keys(oa).sort().map(function (i) {return i+'="'+encodeURIComponent(oa[i])+'"'}).join(',')
self.headers.authorization += ',oauth_signature="'+encodeURIComponent(signature)+'"'
}

if (self.uri.auth && !self.headers.authorization) {
self.headers.authorization = "Basic " + toBase64(self.uri.auth.split(':').map(function(item){ return qs.unescape(item)}).join(':'))
Expand Down Expand Up @@ -406,6 +373,15 @@ Request.prototype.request = function () {
response.body = JSON.parse(response.body)
} catch (e) {}
}
if (response.statusCode == 200) {
// only add cookies if no custom jar is defined (by the user)
if (response.headers['set-cookie'] && !self.jar) {
response.headers['set-cookie'].forEach(function(cookie) {
cookieJar.add(new Cookie(cookie));
});
}
}

self.callback(null, response, response.body)
})
}
Expand Down Expand Up @@ -525,6 +501,8 @@ request.defaults = function (options) {
de.put = def(request.put)
de.head = def(request.head)
de.del = def(request.del)
de.cookie = def(request.cookie)
de.jar = def(request.jar)
return de
}

Expand Down Expand Up @@ -552,3 +530,11 @@ request.del = function (options, callback) {
options.method = 'DELETE'
return request(options, callback)
}
request.cookie = function (str) {
if (typeof str !== 'string') throw new Error("The cookie function only accepts STRING as param")
return new Cookie(str)
}
request.jar = function () {
return new CookieJar
}

23 changes: 0 additions & 23 deletions oauth.js

This file was deleted.

29 changes: 29 additions & 0 deletions tests/test-cookie.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
var Cookie = require('../vendor/cookie')
, assert = require('assert');

var str = 'sid=s543qactge.wKE61E01Bs%2BKhzmxrwrnug; path=/; httpOnly; expires=Sat, 04 Dec 2010 23:27:28 GMT';
var cookie = new Cookie(str);

// test .toString()
assert.equal(cookie.toString(), str);

// test .path
assert.equal(cookie.path, '/');

// test .httpOnly
assert.equal(cookie.httpOnly, true);

// test .name
assert.equal(cookie.name, 'sid');

// test .value
assert.equal(cookie.value, 's543qactge.wKE61E01Bs%2BKhzmxrwrnug');

// test .expires
assert.equal(cookie.expires instanceof Date, true);

// test .path default
var cookie = new Cookie('foo=bar', { url: 'http://foo.com/bar' });
assert.equal(cookie.path, '/bar');

console.log('All tests passed');
90 changes: 90 additions & 0 deletions tests/test-cookiejar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
var Cookie = require('../vendor/cookie')
, Jar = require('../vendor/cookie/jar')
, assert = require('assert');

function expires(ms) {
return new Date(Date.now() + ms).toUTCString();
}

// test .get() expiration
(function() {
var jar = new Jar;
var cookie = new Cookie('sid=1234; path=/; expires=' + expires(1000));
jar.add(cookie);
setTimeout(function(){
var cookies = jar.get({ url: 'http://foo.com/foo' });
assert.equal(cookies.length, 1);
assert.equal(cookies[0], cookie);
setTimeout(function(){
var cookies = jar.get({ url: 'http://foo.com/foo' });
assert.equal(cookies.length, 0);
}, 1000);
}, 5);
})();

// test .get() path support
(function() {
var jar = new Jar;
var a = new Cookie('sid=1234; path=/');
var b = new Cookie('sid=1111; path=/foo/bar');
var c = new Cookie('sid=2222; path=/');
jar.add(a);
jar.add(b);
jar.add(c);

// should remove the duplicates
assert.equal(jar.cookies.length, 2);

// same name, same path, latter prevails
var cookies = jar.get({ url: 'http://foo.com/' });
assert.equal(cookies.length, 1);
assert.equal(cookies[0], c);

// same name, diff path, path specifity prevails, latter prevails
var cookies = jar.get({ url: 'http://foo.com/foo/bar' });
assert.equal(cookies.length, 1);
assert.equal(cookies[0], b);

var jar = new Jar;
var a = new Cookie('sid=1111; path=/foo/bar');
var b = new Cookie('sid=1234; path=/');
jar.add(a);
jar.add(b);

var cookies = jar.get({ url: 'http://foo.com/foo/bar' });
assert.equal(cookies.length, 1);
assert.equal(cookies[0], a);

var cookies = jar.get({ url: 'http://foo.com/' });
assert.equal(cookies.length, 1);
assert.equal(cookies[0], b);

var jar = new Jar;
var a = new Cookie('sid=1111; path=/foo/bar');
var b = new Cookie('sid=3333; path=/foo/bar');
var c = new Cookie('pid=3333; path=/foo/bar');
var d = new Cookie('sid=2222; path=/foo/');
var e = new Cookie('sid=1234; path=/');
jar.add(a);
jar.add(b);
jar.add(c);
jar.add(d);
jar.add(e);

var cookies = jar.get({ url: 'http://foo.com/foo/bar' });
assert.equal(cookies.length, 2);
assert.equal(cookies[0], b);
assert.equal(cookies[1], c);

var cookies = jar.get({ url: 'http://foo.com/foo/' });
assert.equal(cookies.length, 1);
assert.equal(cookies[0], d);

var cookies = jar.get({ url: 'http://foo.com/' });
assert.equal(cookies.length, 1);
assert.equal(cookies[0], e);
})();

setTimeout(function() {
console.log('All tests passed');
}, 1200);
9 changes: 8 additions & 1 deletion tests/test-errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,18 @@ try {
assert.equal(e.message, 'Argument error, options.multipart.')
}

try {
request({uri:local, jar: 'foo'})
assert.fail("Should have throw")
} catch(e) {
assert.equal(e.message, 'Argument error, options.jar.')
}

try {
request({uri:local, multipart: [{}]})
assert.fail("Should have throw")
} catch(e) {
assert.equal(e.message, 'Body attribute missing in multipart.')
}

console.log("All tests passed.")
console.log("All tests passed.")
Loading

0 comments on commit 8323eed

Please sign in to comment.