Last active
June 19, 2022 10:38
-
-
Save yingziwu/1ed1ad71f876638cde8790adc1cc2295 to your computer and use it in GitHub Desktop.
Revisions
-
yingziwu revised this gist
Dec 22, 2021 . 1 changed file with 1 addition and 2 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -59,9 +59,8 @@ async function proxy(request: Request): Promise<Response> { function isIgnore(name: string) { const ignoreList = [ "content-length", /^cf\-/, /^x\-forwarded\-/, "x-real-ip", ]; -
yingziwu revised this gist
Dec 22, 2021 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -24,8 +24,8 @@ function options(): Promise<Response> { } async function proxy(request: Request): Promise<Response> { const { href, origin } = new URL(request.url); const url = href.replace(origin, "").replace(/^\//, ""); if (!urlTest(url)) { return error(400); } -
yingziwu revised this gist
Dec 22, 2021 . 1 changed file with 42 additions and 54 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -31,75 +31,65 @@ async function proxy(request: Request): Promise<Response> { } const { host } = new URL(url); async function getProxyRequest(req: Request): Promise<Request> { const init = { body: req.body, cache: req.cache, headers: getRequestHeaders(req), keepalive: req.keepalive, method: req.method, redirect: "follow" as RequestRedirect, }; const proxyReq = new Request(url, init); return proxyReq; function getRequestHeaders(_req: Request) { const headers: Record<string, string> = {}; [..._req.headers.entries()].forEach((kv) => { if (kv[0].toLowerCase() === "host") { return (headers["host"] = host); } else if (isIgnore(kv[0])) { return; } else { return (headers[kv[0]] = kv[1]); } }); return headers; function isIgnore(name: string) { const ignoreList = [ "content-length", /^access\-control\-/, /^cf\-/, "x-forwarded-proto", "x-real-ip", ]; 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; } } } async function getProxyResponse(req: Request): Promise<Response> { const resp = await fetch(req); const headers = getResponseHeaders(resp); const status = resp.status; const body = resp.body ? bodyHandler(resp.body) : resp.body; const response = new Response(body, { headers, status, }); return response; @@ -113,15 +103,13 @@ async function proxy(request: Request): Promise<Response> { "Access-Control-Max-Age": "2073600", }; const headers: Record<string, string> = {}; [..._headers.entries()].forEach( (kv) => (headers[kv[0].toLocaleLowerCase()] = kv[1]) ); Object.entries(corsHeaders).forEach( (kv) => (headers[kv[0].toLocaleLowerCase()] = kv[1]) ); return headers; } @@ -166,9 +154,9 @@ async function proxy(request: Request): Promise<Response> { } } const proxyRequest = await getProxyRequest(request); const proxyResponse = await getProxyResponse(proxyRequest); return proxyResponse; function urlTest(url: string) { try { -
yingziwu revised this gist
Dec 22, 2021 . 1 changed file with 143 additions and 72 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -1,8 +1,8 @@ import { serve } from "https://deno.land/std/http/server.ts"; function error(code: number): Promise<Response> { const response = new Response(null, { status: code, }); return Promise.resolve(response); } @@ -26,78 +26,84 @@ function options(): Promise<Response> { async function proxy(request: Request): Promise<Response> { const { pathname } = new URL(request.url); const url = pathname.replace(/^\//, ""); if (!urlTest(url)) { return error(400); } const { host } = new URL(url); async function getProxyRequest(): Promise<Request> { const init = { body: getRequestBody(request), cache: request.cache, headers: getRequestHeaders(), keepalive: request.keepalive, method: request.method, redirect: "follow" as RequestRedirect, }; const req = new Request(url, init); return req; function getRequestBody(_req: Request) { const body = _req.body; return body ?? null; } function getRequestHeaders() { const headers: Record<string, string> = {}; [...request.headers.entries()].forEach((kv) => { if (kv[0].toLowerCase() === "host") { return (headers["host"] = host); } else if (isIgnore(kv[0])) { return; } else { return (headers[kv[0]] = kv[1]); } function isIgnore(name: string) { const ignoreList = [ "content-length", /^access\-control\-/, /^cf\-/, "x-forwarded-proto", "x-real-ip", ]; 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; } }); return headers; } } async function getProxyResponse(req: Request): Promise<Response> { const resp = await fetch(req); const headers = getResponseHeaders(resp); const status = resp.status; const _body = resp.body; let body: ReadableStream<Uint8Array> | null; if (_body !== null) { body = bodyHandler(_body); } else { body = _body; } const response = new Response(body, { headers: headers, status: status, }); return response; function getResponseHeaders(_resp: Response): HeadersInit { const _headers = _resp.headers; const corsHeaders = { "Access-Control-Allow-Origin": "*", @@ -117,25 +123,85 @@ async function proxy(request: Request): Promise<Response> { const headers: Record<string, string> = {}; [...headersMap.entries()].forEach((kv) => (headers[kv[0]] = kv[1])); return headers; } function bodyHandler( body: ReadableStream<Uint8Array> ): ReadableStream<Uint8Array> { // See also: https://zhuanlan.zhihu.com/p/98848420 let aborter: (reason: any) => void; let aborted = false; const reader = body.getReader(); const stream = new ReadableStream({ start(controller) { function push() { reader .read() .then(({ value, done }) => { if (done) { if (!aborted) { controller.close(); } return; } controller.enqueue(value); push(); }) .catch((error) => console.error(error)); } aborter = (reason) => { controller.error(new Error(reason)); aborted = true; }; push(); }, cancel(reason) { reader.cancel(reason); aborter(reason); }, }); return stream; } } const proxyReq = await getProxyRequest(); const proxyResp = await getProxyResponse(proxyReq); return proxyResp; function urlTest(url: string) { try { new URL(url); return true; } catch (error) { return false; } } } function logError(request: Request, error: Error) { const logObj = { time: new Date().toISOString(), type: "error", request: { url: request.url, method: request.method, headers: [...request.headers.entries()], }, error: { name: error.name, message: error.message, stack: error.stack, }, }; console.log(JSON.stringify(logObj)); } function log(request: Request, response: Response) { const logObj = { time: new Date().toISOString(), type: "info", request: { url: request.url, method: request.method, @@ -154,7 +220,12 @@ async function handler(request: Request): Promise<Response> { if (request.method === "OPTIONS") { response = await options(); } else { try { response = await proxy(request); } catch (err) { logError(request, err); response = await error(500); } } log(request, response); return response; -
yingziwu revised this gist
Dec 22, 2021 . 1 changed file with 24 additions and 2 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -52,10 +52,32 @@ async function proxy(request: Request): Promise<Response> { 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]; -
yingziwu created this gist
Dec 22, 2021 .There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,141 @@ 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"]; if (kv[0].toLowerCase() === "host") { headers["host"] = host; } else if (ignoreList.includes(kv[0].toLowerCase())) { // 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);