koa-even-better-http-proxy
Version:
http proxy middleware for Koa
93 lines (78 loc) • 2.87 kB
JavaScript
import chunkLength from '../../lib/chunkLength';
let timeoutDuration = null;
let abortRequest = null;
// "secureConnect" includes time taken for "lookup" (dns), "connect" and
// "ready" events, as well as tls handshake.
const getEventListener = (isSecure) => (isSecure ? 'secureConnect' : 'connect');
const onError = (ctx, container, err, resolve, reject) => {
if (err.code === 'ECONNRESET') {
ctx.set(
'X-Timout-Reason',
`The proxy timed out your request after ${timeoutDuration}ms.`
);
ctx.set('Content-Type', 'text/plain');
ctx.status = 504;
resolve(container);
} else {
reject(err);
}
};
const onSocketOpen = (container, socket) => {
const { options } = container;
const { isSecure } = container.proxy;
const { timeout, connectTimeout } = options;
if (connectTimeout) {
timeoutDuration = connectTimeout;
socket.setTimeout(connectTimeout, abortRequest);
socket.on(getEventListener(isSecure), () => {
if (timeout) {
timeoutDuration = timeout;
socket.setTimeout(timeout, abortRequest);
} else {
// 0 to reset to the default of no timeout for the rest of the request
socket.setTimeout(0);
}
});
} else if (timeout) {
timeoutDuration = timeout;
socket.setTimeout(timeout, abortRequest);
}
};
const getProxyReq = (container, resolve, reject) => {
const { proxy } = container;
const reqOpt = proxy.reqBuilder;
return proxy.requestModule.request(reqOpt, (rsp) => {
const chunks = [];
rsp.on('data', (chunk) => chunks.push(chunk));
rsp.on('end', () => {
proxy.res = rsp;
proxy.resData = Buffer.concat(chunks, chunkLength(chunks));
resolve(container);
});
rsp.on('error', reject);
});
};
export default (Container) => {
const { ctx } = Container.user;
const { bodyContent } = Container.proxy;
const { options } = Container;
return new Promise((resolve, reject) => {
const proxyReq = getProxyReq(Container, resolve, reject);
abortRequest = () => proxyReq.abort();
proxyReq.on('socket', (socket) => onSocketOpen(Container, socket));
proxyReq.on('error', (err) =>
onError(ctx, Container, err, resolve, reject)
);
// this guy should go elsewhere, down the chain
if (options.parseReqBody) {
if (bodyContent.length) {
proxyReq.write(bodyContent);
}
proxyReq.end();
} else {
// Pipe will call end when it has completely read from the request.
ctx.req.pipe(proxyReq);
}
ctx.req.on('aborted', () => proxyReq.abort()); // reject?
});
};