Comparing version 0.5.11 to 0.5.12
0.5.12 / 2014-03-29 | ||
================== | ||
* support Digest access authentication. fix #27 | ||
* add co-urllib desc | ||
0.5.11 / 2014-03-13 | ||
@@ -3,0 +9,0 @@ ================== |
@@ -18,2 +18,4 @@ /**! | ||
var debug = require('debug')('urllib'); | ||
var utility = require('utility'); | ||
var crypto = require('crypto'); | ||
var http = require('http'); | ||
@@ -74,2 +76,3 @@ var https = require('https'); | ||
* - {String} [auth]: Basic authentication i.e. 'user:password' to compute an Authorization header. | ||
* - {String} [digestAuth]: Digest authentication i.e. 'user:password' to compute an Authorization header. | ||
* - {String|Buffer|Array} [ca]: An array of strings or Buffers of trusted certificates. | ||
@@ -230,2 +233,19 @@ * If this is omitted several well known "root" CAs will be used, like VeriSign. | ||
} | ||
// handle digest auth | ||
if (res && res.statusCode === 401 && res.headers['www-authenticate'] | ||
&& (!args.headers || !args.headers.Authorization) && args.digestAuth) { | ||
var authenticate = res.headers['www-authenticate']; | ||
if (authenticate.indexOf('Digest ') >= 0) { | ||
debug('Request#%d %s: got degist auth header WWW-Authenticate: %s', reqId, url, authenticate); | ||
args.headers = args.headers || {}; | ||
args.headers.Authorization = digestAuthHeader(options.method, options.path, authenticate, args.digestAuth); | ||
debug('Request#%d %s: auth with digest header: %s', reqId, url, args.headers.Authorization); | ||
if (res.headers['set-cookie']) { | ||
args.headers.Cookie = res.headers['set-cookie'].join(';'); | ||
} | ||
return exports.request(url, args, cb); | ||
} | ||
} | ||
cb(err, data, res); | ||
@@ -293,6 +313,7 @@ }; | ||
var reqId = ++REQUEST_ID; | ||
debug('Request#%d %s %s with headers %j', reqId, method, url, options.headers); | ||
var req = httplib.request(options, function (res) { | ||
connnected = true; | ||
debug('Request#%d %s `req response` event emit: status %d, headers: %j', | ||
reqId, options.path, res.statusCode, res.headers); | ||
reqId, url, res.statusCode, res.headers); | ||
@@ -346,3 +367,3 @@ if (writeStream) { | ||
res.on('data', function (chunk) { | ||
debug('Request#%d %s: `res data` event emit, size %d', reqId, options.path, chunk.length); | ||
debug('Request#%d %s: `res data` event emit, size %d', reqId, url, chunk.length); | ||
size += chunk.length; | ||
@@ -353,7 +374,7 @@ chunks.push(chunk); | ||
res.on('close', function () { | ||
debug('Request#%d %s: `res close` event emit, total size %d', reqId, options.path, size); | ||
debug('Request#%d %s: `res close` event emit, total size %d', reqId, url, size); | ||
}); | ||
res.on('error', function () { | ||
debug('Request#%d %s: `res error` event emit, total size %d', reqId, options.path, size); | ||
debug('Request#%d %s: `res error` event emit, total size %d', reqId, url, size); | ||
}); | ||
@@ -363,3 +384,3 @@ | ||
res.aborted = true; | ||
debug('Request#%d %s: `res aborted` event emit, total size %d', reqId, options.path, size); | ||
debug('Request#%d %s: `res aborted` event emit, total size %d', reqId, url, size); | ||
}); | ||
@@ -369,3 +390,3 @@ | ||
var body = Buffer.concat(chunks, size); | ||
debug('Request#%d %s: `res end` event emit, total size %d, _dumped: %s', reqId, options.path, size, res._dumped); | ||
debug('Request#%d %s: `res end` event emit, total size %d, _dumped: %s', reqId, url, size, res._dumped); | ||
@@ -466,1 +487,77 @@ if (__err && connnected) { | ||
}; | ||
var AUTH_KEY_VALUE_RE = /(\w+)=["']?([^'"\s]+)["']?/; | ||
var NC = 0; | ||
var NC_PAD = '00000000'; | ||
function digestAuthHeader(method, pathname, auth, userpass, options) { | ||
var parts = auth.split(','); | ||
var opts = {}; | ||
for (var i = 0; i < parts.length; i++) { | ||
var m = parts[i].match(AUTH_KEY_VALUE_RE); | ||
if (m) { | ||
opts[m[1]] = m[2].replace(/["']/g, ''); | ||
} | ||
} | ||
if (!opts.realm || !opts.nonce) { | ||
return ''; | ||
} | ||
var qop = opts.qop || ''; | ||
// WWW-Authenticate: Digest realm="testrealm@host.com", | ||
// qop="auth,auth-int", | ||
// nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", | ||
// opaque="5ccc069c403ebaf9f0171e9517f40e41" | ||
// Authorization: Digest username="Mufasa", | ||
// realm="testrealm@host.com", | ||
// nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", | ||
// uri="/dir/index.html", | ||
// qop=auth, | ||
// nc=00000001, | ||
// cnonce="0a4f113b", | ||
// response="6629fae49393a05397450978507c4ef1", | ||
// opaque="5ccc069c403ebaf9f0171e9517f40e41" | ||
// HA1 = MD5( "Mufasa:testrealm@host.com:Circle Of Life" ) | ||
// = 939e7578ed9e3c518a452acee763bce9 | ||
// | ||
// HA2 = MD5( "GET:/dir/index.html" ) | ||
// = 39aff3a2bab6126f332b942af96d3366 | ||
// | ||
// Response = MD5( "939e7578ed9e3c518a452acee763bce9:\ | ||
// dcd98b7102dd2f0e8b11d0f600bfb0c093:\ | ||
// 00000001:0a4f113b:auth:\ | ||
// 39aff3a2bab6126f332b942af96d3366" ) | ||
// = 6629fae49393a05397450978507c4ef1 | ||
userpass = userpass.split(':'); | ||
var nc = String(++NC); | ||
nc = NC_PAD.substring(nc.length) + nc; | ||
var cnonce = crypto.randomBytes(8).toString('hex'); | ||
if (options) { | ||
nc = options.nc || nc; | ||
cnonce = options.cnonce; | ||
} | ||
var ha1 = utility.md5(userpass[0] + ':' + opts.realm + ':' + userpass[1]); | ||
var ha2 = utility.md5(method.toUpperCase() + ':' + pathname); | ||
var s = ha1 + ':' + opts.nonce; | ||
if (qop) { | ||
qop = qop.split(',')[0]; | ||
s += ':' + nc + ':' + cnonce + ':' + qop; | ||
} | ||
s += ':' + ha2; | ||
var response = utility.md5(s); | ||
var authstring = 'Digest username="' + userpass[0] + '", realm="' + opts.realm | ||
+ '", nonce="' + opts.nonce + '", uri="' + pathname | ||
+ '", response="' + response + '"'; | ||
if (opts.opaque) { | ||
authstring += ', opaque="' + opts.opaque + '"'; | ||
} | ||
if (qop) { | ||
authstring +=', qop=' + qop + ', nc=' + nc + ', cnonce="' + cnonce + '"'; | ||
} | ||
return authstring; | ||
} | ||
exports.digestAuthHeader = digestAuthHeader; |
{ | ||
"name": "urllib", | ||
"version": "0.5.11", | ||
"version": "0.5.12", | ||
"description": "Help in opening URLs (mostly HTTP) in a complex world — basic and digest authentication, redirections, cookies and more.", | ||
@@ -21,3 +21,4 @@ "keywords": [ "urllib", "http", "urlopen", "curl", "wget", "request", "https" ], | ||
"debug": "0.7.4", | ||
"default-user-agent": "0.0.1" | ||
"default-user-agent": "0.0.1", | ||
"utility": "0.1.11" | ||
}, | ||
@@ -32,3 +33,3 @@ "devDependencies": { | ||
"pedding": "0.0.3", | ||
"should": "3.1.3" | ||
"should": "3.2.0-beta1" | ||
}, | ||
@@ -35,0 +36,0 @@ "engines": { "node": ">= 0.8.0" }, |
@@ -10,2 +10,5 @@ # urllib [![Build Status](https://secure.travis-ci.org/fengmk2/urllib.png?branch=master)](http://travis-ci.org/fengmk2/urllib) | ||
If you are using [co](https://github.com/visionmedia/co) or [koa](https://github.com/koajs/koa), | ||
please take a look at [co-urllib](https://github.com/dead-horse/co-urllib). | ||
## Install | ||
@@ -50,2 +53,3 @@ | ||
- ***auth*** String - `username:password` used in HTTP Basic Authorization. | ||
- ***digestAuth*** String - `username:password` used in HTTP [Digest Authorization](http://en.wikipedia.org/wiki/Digest_access_authentication). | ||
- ***agent*** [http.Agent](http://nodejs.org/api/http.html#http_class_http_agent) - HTTP Agent object. | ||
@@ -213,2 +217,3 @@ Set `false` if you does not use agent. | ||
* [√] Support `Accept-Encoding=gzip` by `options.gzip = true` | ||
* [√] Support [Digest access authentication](http://en.wikipedia.org/wiki/Digest_access_authentication) | ||
@@ -215,0 +220,0 @@ ## Authors |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
34979
491
263
3
+ Addedutility@0.1.11
+ Addedaddress@2.0.3(transitive)
+ Addedutility@0.1.11(transitive)