Skip to content

Instantly share code, notes, and snippets.

@xla
Created August 22, 2011 16:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xla/1162848 to your computer and use it in GitHub Desktop.
Save xla/1162848 to your computer and use it in GitHub Desktop.
Proxy module used and written for the SoundCloud mobile project
/**
* module imports
*/
var _ = require('underscore')._;
var url = require('url');
var http = require('http');
var https = require('https');
/**
* module exports
*/
exports.Proxy = Proxy;
exports.createProxy = createProxy;
/**
* Proxy
*
* @type {Constructor}
* @param {Object} config
* @return {Proxy} New instance of Proxy
*
* Examples:
*
* var proxy = proxy.createProxy({
* host: 'api.soundcloud.com',
* root: '/_api'
* });
*/
function Proxy(config) {
this.config = _.extend({}, this.defaults, config);
this.replaceRegexp = new RegExp('https?:\/\/' + this.config.host);
}
/**
* Defaults
*
* @type {Object}
* @param {Number} port Port to connect to on the foreign host
* @param {String} host Domain or ip address to connect to
* @param {String} root Location where the proxy is available for local requests
*/
Proxy.prototype.defaults = {
port: 80,
host: 'lolcathost.org',
root: '/_'
};
/**
* Constructs options object for proxy requests
*
* @api private
* @type {Function}
* @param {ServerRequest} request nodejs ServerRequest compliant request object
* @param {Object} opts
* @return {Object}
*/
Proxy.prototype.proxyOptions = function(request, opts) {
var options = _.extend({}, this.config, opts);
options.method = request.method;
options.path = request.url.replace(this.config.root, '');
options.headers = _.extend(request.headers, {
host: options.host
});
return options;
};
/**
* Constructs the location header for redirects
*
* @api private
* @type {Function}
* @param {ServerRequest} proxyRequest nodejs ServerRequest compliant request object
* @param {ServerResponse} proxyResponse nodejs ServerResponse compliant response object
* @return {String}
*/
Proxy.prototype.getLocation = function(proxyRequest, proxyResponse) {
return proxyResponse.headers['location'].replace(this.replaceRegexp, that.config.root);
};
/**
* Logic for proxied requests against a foreign host
*
* @api private
* @type {Function}
* @param {ServerRequest} request nodejs ServerRequest compliant request object
* @param {ServerResponse} response nodejs ServerResponse compliant response object
* @param {ClientRequest} proxyRequest ClientRequest compliant request object
*/
Proxy.prototype.request = function(request, response, proxyRequest) {
var that = this;
proxyRequest.on('error', function(error) {
var responseBody = JSON.stringify({
error: error.message
});
response.writeHead(500, {
'content-length': responseBody.length,
'content-type': 'application/json'
});
response.end(responseBody);
});
proxyRequest.on('response', function(proxyResponse) {
if (proxyResponse.headers && proxyResponse.headers.location) {
_.extend(proxyResponse.headers, {
location: that.getLocation(proxyRequest, proxyResponse)
});
}
// XXX hotfix for TLS respones sometimes not fire `end` event
// issue: https://github.com/joyent/node/issues/728
// Follow the issue to see a high-level description of the problem
function endResponse() {
response.end();
}
proxyResponse.on('close', endResponse);
proxyResponse.on('end', endResponse);
response.writeHead(proxyResponse.statusCode, proxyResponse.headers);
proxyResponse.pipe(response, {
end: false
});
});
request.pipe(proxyRequest);
};
/**
* Standard interface for proxied requests over http
*
* @type {Function}
* @param {ServerRequest} request nodejs ServerRequest compliant request object
* @param {ServerResponse} response nodejs ServerResponse compliant response object
* @param {Object} opts Object with specialized configuration for the current request
*/
Proxy.prototype.http = function(request, response, opts) {
var options = this.proxyOptions(request, opts);
var proxyRequest = http.request(options);
this.request(request, response, proxyRequest);
};
/**
* Standard interface for proxied requests over https
*
* @type {Function}
* @param {ServerRequest} request nodejs ServerRequest compliant request object
* @param {ServerResponse} response nodejs ServerResponse compliant response object
* @param {Object} opts Object with specialized configuration for the current request
*/
Proxy.prototype.https = function(request, response, opts) {
var opts = _.extend({}, {
port: 443
}, opts);
var options = this.proxyOptions(request, opts);
var proxyRequest = https.request(options);
this.request(request, response, proxyRequest);
};
/**
* Factory for Proxy instances
*
* @type {Function}
* @param {Object} config Configuration for returned Proxy instance
* @return {Proxy}
*/
function createProxy(config) {
return new Proxy(config);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment