Skip to content

Commit

Permalink
Several fixes to http client
Browse files Browse the repository at this point in the history
  - Only attempts to "render" data on first request (not redirects)
  - Allows setting of "redirects" via the data variable
  - Fixed Host header predecence issue
  - Only setting Content-Length / Content-Type when not previously set
  - Passing response object on errors
  • Loading branch information
tj committed Apr 29, 2010
1 parent 15a24e6 commit 6232c1a
Showing 1 changed file with 41 additions and 18 deletions.
59 changes: 41 additions & 18 deletions lib/express/http.js
Expand Up @@ -21,37 +21,41 @@ var http = require('http'),
* @api private
*/

function request(method, url, data, headers, callback, redirects) {
function request(method, url, data, headers, callback, redirects, first) {
var buf = '',
redirects = redirects || 3,
redirects = typeof redirects !== 'number' ? 3 : redirects,
url = parse(url),
path = url.pathname || '/',
search = url.search || '',
hash = url.hash || '',
port = url.port || 80,
headers = { host: url.hostname }.merge(headers || {}),
headers = (headers || {}).merge({ Host: url.hostname }),
client = http.createClient(port, url.hostname)
if (headers.redirect)
redirects = headers.redirect,
delete headers.redirect
if (data) {
data = queryString.stringify(data)
if (typeof data === 'object' && 'redirects' in data)
redirects = data.redirects,
delete data.redirects
if (first && data) {
if (typeof data !== 'string')
data = queryString.stringify(data)
if (method === 'GET')
search += (search ? '&' : '?') + data
else
headers['Content-Length'] = data.length,
headers['Content-Type'] = 'application/x-www-form-urlencoded'
else {
if (!headers['Content-Length'])
headers['Content-Length'] = data.length
if (!headers['Content-Type'])
headers['Content-Type'] = 'application/x-www-form-urlencoded'
}
}
var req = client.request(method, path + search + hash, headers)
if (data && method !== 'GET') req.write(data)
req.addListener('response', function(res){
if (req.statusCode < 200 || req.statusCode >= 400)
callback(new Error('request failed with status ' + res.statusCode + ' "' + http.STATUS_CODES[res.statusCode] + '"'))
if (res.statusCode < 200 || res.statusCode >= 400)
callback(new Error('request failed with status ' + res.statusCode + ' "' + http.STATUS_CODES[res.statusCode] + '"'), '', res)
else if (res.statusCode >= 300 && res.statusCode < 400)
if (--redirects)
request(method, res.headers.location, data, headers, callback, redirects)
if (redirects--)
request(method, res.headers.location, data, headers, callback, redirects, false)
else
callback(new Error('maximum number of redirects reached'))
callback(new Error('maximum number of redirects reached'), '', res)
else {
res.setBodyEncoding('utf8')
res
Expand All @@ -72,20 +76,39 @@ function request(method, url, data, headers, callback, redirects) {

function client(method) {
return function() {
var headers, data,
var redirects,
args = Array.prototype.slice.call(arguments),
url = args.shift(),
callback = args.pop(),
data = args.shift(),
headers = args.shift()
if (typeof callback !== 'function')
throw new TypeError('http client requires a callback function')
return request(method.toUpperCase(), url, data, headers, callback)
return request(method.toUpperCase(), url, data, headers, callback, redirects, true)
}
}

// --- Public API

/**
* Examples:
*
* var http = require('express/http')
*
* http.get('http://google.com/search', { q: 'foobar' }, function(err, body, response){
* if (!err) sys.puts(body)
* })
*
* http.get('http://google.com/search?lang=en', { q: 'foobar' }, function(){
* // ...
* })
*
* http.post('http://localhost:8000', '<user>tj</user>', { 'Content-Type': 'text/xml' }, function(err, body){
* // ...
* })
*
*/

exports.get = exports.view = client('get')
exports.post = exports.create = client('post')
exports.put = exports.update = client('put')
Expand Down

0 comments on commit 6232c1a

Please sign in to comment.