"use strict";
/**
* HTTP Proxy.
*
* @class
* @name HttpProxy
* @arg {object} opts - Proxy options.
* @arg {string} opts.url - URL which should be proxied.
* them from cache.
*/
var http = require("http");
var https = require("https");
var url = require("url");
var util = require("util");
require("colors");
var expect = require("chai").expect;
var express = require("express");
var httpProxy = require("http-proxy");
var U = require("glace-utils");
var BaseProxy = require("./baseProxy");
var cache = require("./middleware/cache");
var middleware = require("./middleware");
var LOG = U.logger;
var HttpProxy = function (opts) {
BaseProxy.call(this, opts);
this.url = null;
this._initUrl = null;
this._server = null;
this._proxyOptions = {};
this._proxy = httpProxy.createProxyServer();
this._proxy.on("proxyReq", (proxyReq, req) => {
if (req.body) proxyReq.end(req.body);
});
this._proxy.on("error", this.__onError.bind(this));
this.setUrl(opts.url);
var app = express();
app.use(async (req, res) => {
this.req = req;
if (this.req._reconnect === undefined) {
this.req._reconnect = this._reconnect;
};
this.res = res;
for (var mw of middleware) if (await mw.call(this)) return;
delete this.req;
delete this.res;
this._proxy.web(req, res, this._proxyOptions);
});
this._server = http.createServer(app);
};
util.inherits(HttpProxy, BaseProxy);
module.exports = HttpProxy;
/**
* Sets proxied URL.
*
* @method
* @arg {string} targetUrl - URL which should be proxied.
*/
HttpProxy.prototype.setUrl = function (targetUrl) {
this._initUrl = targetUrl;
var parsedUrl = url.parse(targetUrl);
expect(["http:", "https:"],
"Unsupported protocol").include(parsedUrl.protocol);
// FIXME work around bug in `node-http-proxy`: https://github.com/nodejitsu/node-http-proxy/pull/1074
this._proxy.options = { proxyTimeout: this._timeout };
if (parsedUrl.protocol === "https:") {
this._proxyOptions = {
target: "https://" + parsedUrl.host,
agent: https.globalAgent,
headers: { host: parsedUrl.hostname },
};
} else {
this._proxyOptions = {
target: {
host: parsedUrl.hostname,
port: parsedUrl.port,
},
};
};
};
/**
* Starts proxy server if it’s not started yet.
*
* @async
* @method
* @return {Promise<string>} - Proxy URL.
*/
HttpProxy.prototype.start = function () {
return new Promise((resolve, reject) => {
if (this.isRunning) return resolve();
this._server.listen(this._port, err => {
if (err) return reject(err);
this._port = this._server.address().port;
this.url = `http://${U.hostname}:${this._port}`;
this.isRunning = true;
resolve();
});
})
.then(() => cache.init())
.then(() => this.url);
};
/**
* Stops proxy server if it’s not stopped yet.
*
* @method
*/
HttpProxy.prototype.stop = function () {
if (!this.isRunning) return;
this._server.close();
this._proxy.close();
this.proxyUrl = null;
this.isRunning = false;
};
/**
* Helper to be called on proxy error to retry request.
*
* @ignore
* @method
* @protected
* @arg {Error} err
* @arg {Request} req
* @arg {Response} res
*/
HttpProxy.prototype.__onError = function (err, req, res) {
if (req._reconnect > 0 && !req.socket.destroyed) {
LOG.warn(util.format("Request reconnected", U.getReqKey(req)));
req._reconnect--;
this._proxy.web(req, res, this._proxyOptions);
} else {
LOG.error(util.format(U.getReqKey(req), err));
};
};