Skip to content

Instantly share code, notes, and snippets.

@colelawrence
Created March 25, 2024 14:47
Show Gist options
  • Save colelawrence/7cf32534a43c25a5f8267affc34eb12c to your computer and use it in GitHub Desktop.
Save colelawrence/7cf32534a43c25a5f8267affc34eb12c to your computer and use it in GitHub Desktop.
Serializing Requests into a Response
/**
* 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