manifold/web/lib/api/proxy.ts
Marshall Polaris 4700ceb14c
Refactor some backend-related stuff (#639)
* web/lib/firebase/api-call -> common/api, web/lib/firebase/api

* Reuse `APIError` type in server code

* Reuse `getFunctionUrl` in server code
2022-07-10 15:03:15 -07:00

69 lines
1.8 KiB
TypeScript

import { NextApiRequest, NextApiResponse } from 'next'
import { promisify } from 'util'
import { pipeline } from 'stream'
import { getFunctionUrl } from 'common/api'
import fetch, { Headers, Response } from 'node-fetch'
function getProxiedRequestHeaders(req: NextApiRequest, whitelist: string[]) {
const result = new Headers()
for (const name of whitelist) {
const v = req.headers[name.toLowerCase()]
if (Array.isArray(v)) {
for (const vv of v) {
result.append(name, vv)
}
} else if (v != null) {
result.append(name, v)
}
}
result.append('X-Forwarded-For', req.socket.remoteAddress || '')
result.append('Via', 'Vercel public API')
return result
}
function getProxiedResponseHeaders(res: Response, whitelist: string[]) {
const result: { [k: string]: string } = {}
for (const name of whitelist) {
const v = res.headers.get(name)
if (v != null) {
result[name] = v
}
}
return result
}
export const fetchBackend = (req: NextApiRequest, name: string) => {
const url = getFunctionUrl(name)
const headers = getProxiedRequestHeaders(req, [
'Authorization',
'Content-Length',
'Content-Type',
'Origin',
])
const hasBody = req.method != 'HEAD' && req.method != 'GET'
const body = req.body ? JSON.stringify(req.body) : req
const opts = {
headers,
method: req.method,
body: hasBody ? body : undefined,
}
return fetch(url, opts)
}
export const forwardResponse = async (
res: NextApiResponse,
backendRes: Response
) => {
const headers = getProxiedResponseHeaders(backendRes, [
'Access-Control-Allow-Origin',
'Content-Type',
'Cache-Control',
'ETag',
'Vary',
])
res.writeHead(backendRes.status, headers)
if (backendRes.body != null) {
return await promisify(pipeline)(backendRes.body, res)
}
}