Created
August 22, 2011 16:37
-
-
Save xla/1162848 to your computer and use it in GitHub Desktop.
Proxy module used and written for the SoundCloud mobile project
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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