Last active
June 19, 2022 10:38
-
-
Save yingziwu/1ed1ad71f876638cde8790adc1cc2295 to your computer and use it in GitHub Desktop.
A CORS proxy write by deno
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { serve } from "https://deno.land/std/http/server.ts"; | |
| function error(): Promise<Response> { | |
| const response = new Response(null, { | |
| status: 400, | |
| }); | |
| return Promise.resolve(response); | |
| } | |
| function options(): Promise<Response> { | |
| const headers = { | |
| "Access-Control-Allow-Origin": "*", | |
| "Access-Control-Allow-Methods": "*", | |
| "Access-Control-Allow-Headers": "*", | |
| "Access-Control-Expose-Headers": | |
| "Date, Etag, Content-Length, Accept-Ranges, Content-Range, Server, Location", | |
| "Access-Control-Max-Age": "2073600", | |
| }; | |
| const response = new Response(null, { | |
| headers, | |
| status: 204, | |
| }); | |
| return Promise.resolve(response); | |
| } | |
| async function proxy(request: Request): Promise<Response> { | |
| const { pathname } = new URL(request.url); | |
| const url = pathname.replace(/^\//, ""); | |
| const urlTest = (url: string) => { | |
| try { | |
| new URL(url); | |
| return true; | |
| } catch (error) { | |
| return false; | |
| } | |
| }; | |
| if (!urlTest(url)) { | |
| return error(); | |
| } | |
| const { host } = new URL(url); | |
| async function getNewRequest(): Promise<Request> { | |
| const getRequestBody = async (_req: Request): Promise<BodyInit | null> => { | |
| if (!_req.body) { | |
| return null; | |
| } | |
| const reader = _req.body.getReader(); | |
| const stream = await reader.read(); | |
| const body = stream.value; | |
| return body ?? null; | |
| }; | |
| const _headers = request.headers; | |
| const headers: Record<string, string> = {}; | |
| [..._headers.entries()].forEach((kv) => { | |
| const ignoreList = [ | |
| "content-length", | |
| /^access\-control\-/, | |
| /^cf\-/, | |
| "x-forwarded-proto", | |
| "x-real-ip", | |
| ]; | |
| const isIgnore = (name: string) => { | |
| for (const i of ignoreList) { | |
| if (typeof i === "string") { | |
| if (i === name.toLocaleLowerCase()) { | |
| return true; | |
| } | |
| } | |
| if (i instanceof RegExp) { | |
| if (i.test(name)) { | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| }; | |
| if (kv[0].toLowerCase() === "host") { | |
| headers["host"] = host; | |
| } else if (isIgnore(kv[0])) { | |
| // pass | |
| } else { | |
| headers[kv[0]] = kv[1]; | |
| } | |
| }); | |
| const init = { | |
| body: await getRequestBody(request), | |
| cache: request.cache, | |
| headers, | |
| keepalive: request.keepalive, | |
| method: request.method, | |
| redirect: "follow" as RequestRedirect, | |
| }; | |
| const req = new Request(url, init); | |
| return req; | |
| } | |
| async function getNewResponse(req: Request): Promise<Response> { | |
| const resp = await fetch(req); | |
| const getResponseHeaders = (_resp: Response): HeadersInit => { | |
| const _headers = _resp.headers; | |
| const corsHeaders = { | |
| "Access-Control-Allow-Origin": "*", | |
| "Access-Control-Allow-Methods": "*", | |
| "Access-Control-Allow-Headers": "*", | |
| "Access-Control-Expose-Headers": [..._headers.keys()].join(", "), | |
| "Access-Control-Max-Age": "2073600", | |
| }; | |
| const headersMap = new Map( | |
| [..._headers.entries()].map((kv) => [kv[0].toLowerCase(), kv[1]]) | |
| ); | |
| Object.entries(corsHeaders).forEach((kv) => | |
| headersMap.set(kv[0].toLowerCase(), kv[1]) | |
| ); | |
| const headers: Record<string, string> = {}; | |
| [...headersMap.entries()].forEach((kv) => (headers[kv[0]] = kv[1])); | |
| return headers; | |
| }; | |
| const respHeaders = getResponseHeaders(resp); | |
| const respStatus = resp.status; | |
| const respBody = await resp.blob(); | |
| const response = new Response(respBody, { | |
| headers: respHeaders, | |
| status: respStatus, | |
| }); | |
| return response; | |
| } | |
| const newReq = await getNewRequest(); | |
| const newResp = await getNewResponse(newReq); | |
| return newResp; | |
| } | |
| function log(request: Request, response: Response) { | |
| const logObj = { | |
| time: new Date().toISOString(), | |
| request: { | |
| url: request.url, | |
| method: request.method, | |
| headers: [...request.headers.entries()], | |
| }, | |
| response: { | |
| status: response.status, | |
| headers: [...response.headers.entries()], | |
| }, | |
| }; | |
| console.log(JSON.stringify(logObj)); | |
| } | |
| async function handler(request: Request): Promise<Response> { | |
| let response; | |
| if (request.method === "OPTIONS") { | |
| response = await options(); | |
| } else { | |
| response = await proxy(request); | |
| } | |
| log(request, response); | |
| return response; | |
| } | |
| serve(handler); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment