gombot/lib/http_forward.js

116 строки
3.3 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const url = require('url');
const http = require('http');
const https = require('https');
const querystring = require('querystring');
const public_url = url.parse(require('../etc/config').public_url);
var global_forward_timeout;
exports.setTimeout = function(to) {
if (typeof to !== 'number') throw 'setTimeout expects a numeric argument';
global_forward_timeout = to;
};
exports.forward = function(dest, req, res, cb) {
var _cb = cb;
var requestTimeout;
cb = function() {
if (requestTimeout) clearTimeout(requestTimeout);
if (_cb) _cb.apply(null, arguments);
};
function cleanupReq() {
if (preq) {
preq.removeAllListeners();
preq.destroy();
preq = undefined;
}
}
var u = url.parse(dest.toString());
var m = u.protocol === 'http:' ? http : https;
var preq = m.request({
host: u.hostname,
port: u.port,
path: u.path,
method: req.method,
agent: false
}, function(pres) {
res.statusCode = pres.statusCode;
// forward necessary headers
['Content-Type', 'Content-Length', 'Set-Cookie', 'Vary', 'Cache-Control', 'ETag', 'X-Frame-Options', 'Location', 'Access-Control-Allow-Origin']
.forEach(function (header) {
if (pres.headers.hasOwnProperty(header.toLowerCase())) {
res.setHeader(header, pres.headers[header.toLowerCase()]);
}
});
pres.on('data', function (chunk) {
res.write(chunk);
}).on('end', function() {
res.end();
pres.removeAllListeners();
pres.destroy();
pres = undefined;
cleanupReq();
cb();
});
}).on('error', function(e) {
cleanupReq();
cb(e);
});
if (global_forward_timeout) {
requestTimeout = setTimeout(function() { preq.destroy(); }, global_forward_timeout);
}
if (req.headers['content-type']) {
preq.setHeader('Content-Type', req.headers['content-type']);
}
// forward cookies
if (req.headers.cookie) {
preq.setHeader('Cookie', req.headers.cookie);
}
// forward header
if (req.headers['accept-language']) {
preq.setHeader('Accept-Language', req.headers['accept-language']);
}
if (req.headers['if-none-match']) {
preq.setHeader('If-None-Match', req.headers['if-none-match']);
}
if (req.headers['authorization']) {
preq.setHeader('Authorization', req.headers['authorization']);
}
// Hawk checks a single host header when generating the MAC,
// so ensure that the correct port is included when using ssl
var host = req.headers.host;
if (host.indexOf(':') < 0 && public_url.protocol === 'https:') host += ':443';
preq.setHeader('X-Forwarded-Host', host);
// if the body has already been parsed, we'll write it
if (req.body) {
var data;
if (req.headers['content-type'].indexOf('application/json') === 0) data = JSON.stringify(req.body);
else data = querystring.stringify(req.body);
preq.setHeader('content-length', Buffer.byteLength(data));
preq.write(data);
preq.end();
} else {
req.on('data', function(chunk) { preq.write(chunk); })
.on('end', function() { preq.end(); });
}
console.log('forwarding request: ' + req.url + ' -> ' + dest);
};