UNPKG

siegel

Version:

Web application development ecosystem

112 lines (111 loc) 4.5 kB
import http from 'http'; import https from 'https'; import isEmptyObject from '../../../common/is/empty_obj/index.js'; import populateURLParams from '../../../common/populate_url_params/index.js'; const createHttpHeader = (line, headers) => (Object.keys(headers) .reduce((head, key) => { const value = headers[key]; if (Array.isArray(value)) { for (let i = 0; i < value.length; i++) { head.push(`${key}: ${value[i]}`); } } else head.push(`${key}: ${value}`); return head; }, [line]) .join('\r\n') + '\r\n\r\n'); function updateSocket(socket, head) { socket.setTimeout(0); socket.setNoDelay(true); socket.setKeepAlive(true, 0); head?.length && socket.unshift(head); } function getProxyRequestOptions(proxyParams, clientReq, isWS) { const { host, port, changeOrigin, postProcessReq, secure } = proxyParams; const { url, method, headers } = clientReq; let finalPath; if (isWS) finalPath = url; else { const { query, params, path } = clientReq; const proxyQuery = proxyParams.query || query; const proxyPath = proxyParams.path || path; finalPath = isEmptyObject(proxyQuery) ? proxyPath : `${proxyPath}?${new URLSearchParams(proxyQuery)}`; params && (finalPath = populateURLParams(finalPath, params)); } const proxyReqOptions = { host, method, headers, port: !port && secure ? 443 : port, path: finalPath }; if (changeOrigin) { proxyReqOptions.headers.host = host; port && (proxyReqOptions.headers.host += `:${port}`); } postProcessReq?.(clientReq, proxyReqOptions); return proxyReqOptions; } const PROXY_WS_SUBSCRIPTIONS = {}; const proxy = proxyParams => { const { secure, ws, wsEndpoints, host, port } = proxyParams; const client = secure ? https : http; const proxyId = `${host}:${port}`; const wsEndpointsSet = wsEndpoints && new Set(wsEndpoints); const proxyRequest = (clientReq, clientRes) => { const { body, socket } = clientReq; if (ws && !PROXY_WS_SUBSCRIPTIONS[proxyId]) { socket.server .addListener('upgrade', (req, socket, head) => { const { url, method, headers } = req; let isAllowed = true; if (wsEndpointsSet) { const endpointIndexOfQuery = url.indexOf('?'); const endpoint = endpointIndexOfQuery > -1 ? url.substring(0, endpointIndexOfQuery) : url; isAllowed = wsEndpointsSet?.has(endpoint); } if (isAllowed) { if (method !== 'GET' || headers.upgrade?.toLowerCase() != 'websocket') { socket.destroy(); return; } updateSocket(socket, head); client.request(getProxyRequestOptions(proxyParams, req, true)) .on('upgrade', (pRes, pSocket, pHead) => { updateSocket(pSocket, pHead); socket.write(createHttpHeader('HTTP/1.1 101 Switching Protocols', pRes.headers)); pSocket.pipe(socket).pipe(pSocket); }) .on('response', res => { const { headers, httpVersion, statusCode, statusMessage } = res; socket.write(createHttpHeader(`HTTP/${httpVersion} ${statusCode} ${statusMessage}`, headers)); res.pipe(socket); }) .on('error', console.error) .end(); } }); PROXY_WS_SUBSCRIPTIONS[proxyId] = true; } const proxyReq = client.request(getProxyRequestOptions(proxyParams, clientReq), proxyRes => { const { statusCode, headers } = proxyRes; if (statusCode !== 200) { console.error(clientReq.path, statusCode); proxyRes.resume(); } clientRes.writeHead(statusCode, headers); proxyRes.pipe(clientRes); }); body && proxyReq.write(body.constructor == Object ? JSON.stringify(body) : body); proxyReq.on('error', console.error); clientReq.pipe(proxyReq); }; return proxyRequest; }; export default proxy;