| 'use strict'; |
|
|
| const { |
| buildInternalGoURL, |
| buildInternalGoHeaders, |
| isAbortError, |
| } = require('./http_internal'); |
|
|
| async function proxyToGo(req, res, rawBody) { |
| const url = buildInternalGoURL(req); |
| const controller = new AbortController(); |
| let clientClosed = false; |
| const markClientClosed = () => { |
| if (clientClosed) { |
| return; |
| } |
| clientClosed = true; |
| controller.abort(); |
| }; |
| const onReqAborted = () => markClientClosed(); |
| const onResClose = () => { |
| if (!res.writableEnded) { |
| markClientClosed(); |
| } |
| }; |
| req.on('aborted', onReqAborted); |
| res.on('close', onResClose); |
|
|
| try { |
| let upstream; |
| try { |
| upstream = await fetch(url.toString(), { |
| method: 'POST', |
| headers: buildInternalGoHeaders(req, { withContentType: true }), |
| body: rawBody, |
| signal: controller.signal, |
| }); |
| } catch (err) { |
| if (clientClosed || isAbortError(err)) { |
| if (!res.writableEnded) { |
| res.end(); |
| } |
| return; |
| } |
| throw err; |
| } |
| if (clientClosed) { |
| if (!res.writableEnded) { |
| res.end(); |
| } |
| return; |
| } |
|
|
| res.statusCode = upstream.status; |
| upstream.headers.forEach((value, key) => { |
| const lower = key.toLowerCase(); |
| if (lower === 'content-length' || lower === 'content-encoding') { |
| return; |
| } |
| res.setHeader(key, value); |
| }); |
|
|
| if (!upstream.body || typeof upstream.body.getReader !== 'function') { |
| const bytes = Buffer.from(await upstream.arrayBuffer()); |
| res.end(bytes); |
| return; |
| } |
|
|
| const reader = upstream.body.getReader(); |
| try { |
| |
| while (true) { |
| if (clientClosed) { |
| break; |
| } |
| const { value, done } = await reader.read(); |
| if (done) { |
| break; |
| } |
| if (value && value.length > 0) { |
| res.write(Buffer.from(value)); |
| if (typeof res.flush === 'function') { |
| res.flush(); |
| } |
| } |
| } |
| if (!res.writableEnded) { |
| res.end(); |
| } |
| } catch (err) { |
| if (!isAbortError(err) && !res.writableEnded) { |
| res.end(); |
| } |
| } |
| } finally { |
| req.removeListener('aborted', onReqAborted); |
| res.removeListener('close', onResClose); |
| if (!res.writableEnded) { |
| res.end(); |
| } |
| } |
| } |
|
|
| module.exports = { |
| proxyToGo, |
| }; |
|
|