Created
March 25, 2024 14:47
-
-
Save colelawrence/7cf32534a43c25a5f8267affc34eb12c to your computer and use it in GitHub Desktop.
Serializing Requests into a Response
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
/** | |
* As a proof of concept, | |
* JSON'infy the args, and parse them into a new Request Init, | |
* Make the fetch request with the new Request Init | |
* JSONify the response and use the jsonified response data | |
* to recreate the response object and return that | |
* | |
*/ | |
const fetchProxyProofOfConcept: typeof fetch = async (...args) => { | |
const jsonArgs = await intoRequestJson(...args); | |
const jsonArgsStr = JSON.stringify(jsonArgs); | |
const proxiedArgs = JSON.parse(jsonArgsStr); | |
const proxiedRequest = await jsonIntoRequest(proxiedArgs); | |
const proxyResponse = await fetch(proxiedRequest); | |
const proxiedResp = await intoResponseJson(proxyResponse); | |
const proxiedRespStr = JSON.stringify(proxiedResp); | |
const jsonResp = JSON.parse(proxiedRespStr); | |
const resp = await jsonIntoResponse(jsonResp); | |
console.log("fetchProxyProofOfConcept", { | |
args, | |
jsonArgs, | |
jsonArgsStr, | |
proxiedArgs, | |
proxiedRequest, | |
proxyResponse, | |
proxiedResp, | |
proxiedRespStr, | |
jsonResp, | |
resp, | |
}); | |
return resp; | |
}; | |
type RequestJSON = Omit<RequestInit, "body" | "headers" | "signal"> & { | |
url: string; | |
body: { HEX: string } | { STR: string } | undefined; | |
headers: { [key: string]: string }; | |
}; | |
type ResponseJSON = { | |
url: string; | |
body: { HEX: string }; | |
headers: { [key: string]: string }; | |
status: number; | |
statusText: string; | |
}; | |
async function intoRequestJson( | |
...args: Parameters<typeof fetch> | |
): Promise<RequestJSON> { | |
const [input, init] = args; | |
let url = ""; | |
let body: { HEX: string } | { STR: string } | undefined = | |
typeof init.body === "string" ? { STR: init.body } : undefined; | |
{ | |
HEX: ""; | |
} | |
let useInit = init; | |
if (input instanceof URL) { | |
url = input.href; | |
} else if (input instanceof Request) { | |
body = await blobToHex(await input.blob()); | |
useInit = input; | |
url = input.url; | |
} else { | |
url = input; | |
} | |
return { | |
url, | |
body, | |
headers: | |
useInit.headers && | |
(useInit.headers instanceof Headers | |
? Object.fromEntries(useInit.headers.entries()) | |
: Array.isArray(useInit.headers) | |
? Object.fromEntries(useInit.headers) | |
: useInit.headers), | |
cache: useInit.cache, | |
credentials: useInit.credentials, | |
integrity: useInit.integrity, | |
keepalive: useInit.keepalive, | |
method: useInit.method, | |
mode: useInit.mode, | |
redirect: useInit.redirect, | |
referrer: useInit.referrer, | |
referrerPolicy: useInit.referrerPolicy, | |
}; | |
} | |
async function jsonIntoRequest(request: RequestJSON): Promise<Request> { | |
const method = request.method || "GET"; | |
const req = new Request(request.url, { | |
body: | |
method === "GET" || method === "HEAD" | |
? undefined | |
: await hexToBlob(request.body), | |
headers: new Headers(request.headers), | |
cache: request.cache, | |
credentials: request.credentials, | |
integrity: request.integrity, | |
keepalive: request.keepalive, | |
method: request.method, | |
mode: request.mode, | |
redirect: request.redirect, | |
referrer: request.referrer, | |
referrerPolicy: request.referrerPolicy, | |
}); | |
return req; | |
} | |
async function intoResponseJson(response: Response): Promise<ResponseJSON> { | |
return { | |
url: response.url, | |
body: await blobToHex(await response.blob()), | |
headers: Object.fromEntries(response.headers.entries()), | |
status: response.status, | |
statusText: response.statusText, | |
}; | |
} | |
async function jsonIntoResponse(response: ResponseJSON): Promise<Response> { | |
const resp = new Response(await hexToBlob(response.body), { | |
headers: new Headers(response.headers), | |
status: response.status, | |
statusText: response.statusText, | |
}); | |
Object.defineProperty(resp, "url", { value: response.url }); | |
return resp; | |
} | |
async function blobToHex(blob: Blob): Promise<{ HEX: string }> { | |
const buffer = await blob.arrayBuffer(); | |
const hex = Array.from(new Uint8Array(buffer)) | |
.map((b) => b.toString(16).padStart(2, "0")) | |
.join(""); | |
return { HEX: hex }; | |
} | |
async function hexToBlob( | |
body: { HEX: string } | { STR: string } | undefined, | |
): Promise<Blob> { | |
if (!body) return new Blob(); | |
if ("STR" in body) { | |
return new Blob([body.STR]); | |
} | |
const HEX = body.HEX; | |
const u8s: number[] = []; | |
for (let i = 0; i < HEX.length; i += 2) { | |
u8s.push(parseInt(HEX.slice(i, i + 2), 16)); | |
} | |
return new Blob([new Uint8Array(u8s)]); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment