Skip to content

Instantly share code, notes, and snippets.

@ForbesLindesay
Created April 24, 2013 03:17
Show Gist options
  • Select an option

  • Save ForbesLindesay/5449331 to your computer and use it in GitHub Desktop.

Select an option

Save ForbesLindesay/5449331 to your computer and use it in GitHub Desktop.
First stab at a plugable request library with promises and v0.10 streams
var url = require('url');
var http = require('http');
var https = require('https');
var Promise = require('promise');
var sprom = require('sprom');
var Duplex = require('./lib/duplex');
var wrap = require('./lib/wrap');
var through = require('./lib/through');
//https://github.com/substack/hyperquest/archive/master.zip
function Request(method, uri) {
if (typeof uri === 'object') {
uri = url.format(uri);
}
this.uri = uri;
this.middleware = [];
this._headers = {};
this._ws = through();
this._rs = through();
var self = this;
Duplex.call(this, this._ws, this._rs);
Promise.call(this, function (resolve, reject) {
process.nextTick(function () {
resolve(self._hook('preRequest').then(function () {
return self._doRequest()
}).then(function (res) {
return self._hook('postResponse', res);
}));
});
});
this.on('pipe', function () {
self._piped = true;
});
}
Request.prototype = Object.create(Promise.prototype);
Request.prototype.constructor = Request;
for (var key in Duplex.prototype) {
if (key != 'constructor') {
Request.prototype[key] = Duplex.prototype[key];
}
}
Request.prototype._doRequest = function () {
var self = this;
var u = url.parse(self.uri);
var interface = (u.protocol === 'https:') ? https : http;
var req = interface.request({
method: self.method,
host: u.hostname,
port: Number(u.port),
path: u.path,
agent: false,
headers: self._headers
});
var res = self._getResponse(req);
return self._hook('postRequest', req)
.then(function () {
if (!self._piped) {
req.end();
} else {
self._ws.pipe(req);
}
return res;
});
};
Request.prototype._getResponse = function (req) {
var self = this;
return promise(function (resolve, reject) {
req.on('response', function (res) {
res.body = wrap(res);
var buf = null;
res.buffer = function () {
if (buf) return buf;
return buf = sprom(res.body);
}
resolve(res);
})
})
.then(function (res) {
return self._hook('preResponse', res)
});
};
/**
* Registers middlware and returns this
*
* @param {Object} middleware
* @api public
*/
Request.prototype.use = function (middleware) {
for (var i = 0; i < arguments.length; i++) {
this.middleware.push(arguments[i]);
}
return this;
};
/**
* Calls all the hooks of a given name
*
* @param {String} name
* @param {Any} arg
* @api private
*/
Request.prototype._hook = function (name, arg) {
var self = this;
var middleware = self.middleware;
next(0, arg);
function next(i, arg) {
if (i >= middleware.length) return new Promise(function (resolve) { resolve(arg); });
if (typeof middleware[i][name] !== 'function') return next(i + 1, arg);
return new Promise(function (resolve) { resolve(middleware[i][name].apply(self, arg)); })
.then(function (arg) {
return next(i + 1, arg);
});
}
};
function bufferResponse() {
return {
preResponse: function (res) {
return res.buffer()
.then(function (buf) {
res.body = buf;
return res;
});
}
}
}
function handleRedirects() {
return {
preResponse: function (res) {
if (res.statusCode === 303 || res.statusCode === 302 || res.statusCode === 301) {
if (this.method !== 'HEAD') this.method = 'GET';
this.uri = res.headers.location;
//redo the request with the new location
this._piped = false;//don't redo the body
return this._doRequest();
} else if (res.statusCode === 307 || res.statusCode === 308) {
this.uri = res.headers.location;
this._piped = false;//don't redo the body
return this._doRequest();
} else {
return res;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment