UNPKG

hono

Version:

Web framework built on Web Standards

310 lines (309 loc) 9.91 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var handler_exports = {}; __export(handler_exports, { getProcessor: () => getProcessor, handle: () => handle, isContentEncodingBinary: () => isContentEncodingBinary, isContentTypeBinary: () => isContentTypeBinary, streamHandle: () => streamHandle }); module.exports = __toCommonJS(handler_exports); var import_node_crypto = __toESM(require("node:crypto"), 1); var import_encode = require("../../utils/encode"); globalThis.crypto ??= import_node_crypto.default; const getRequestContext = (event) => { return event.requestContext; }; const streamToNodeStream = async (reader, writer) => { let readResult = await reader.read(); while (!readResult.done) { writer.write(readResult.value); readResult = await reader.read(); } writer.end(); }; const streamHandle = (app) => { return awslambda.streamifyResponse( async (event, responseStream, context) => { const processor = getProcessor(event); try { const req = processor.createRequest(event); const requestContext = getRequestContext(event); const res = await app.fetch(req, { event, requestContext, context }); const headers = {}; const cookies = []; res.headers.forEach((value, name) => { if (name === "set-cookie") { cookies.push(value); } else { headers[name] = value; } }); const httpResponseMetadata = { statusCode: res.status, headers, cookies }; responseStream = awslambda.HttpResponseStream.from(responseStream, httpResponseMetadata); if (res.body) { await streamToNodeStream(res.body.getReader(), responseStream); } else { responseStream.write(""); } } catch (error) { console.error("Error processing request:", error); responseStream.write("Internal Server Error"); } finally { responseStream.end(); } } ); }; const handle = (app) => { return async (event, lambdaContext) => { const processor = getProcessor(event); const req = processor.createRequest(event); const requestContext = getRequestContext(event); const res = await app.fetch(req, { event, requestContext, lambdaContext }); return processor.createResult(event, res); }; }; class EventProcessor { createRequest(event) { const queryString = this.getQueryString(event); const domainName = event.requestContext && "domainName" in event.requestContext ? event.requestContext.domainName : event.headers?.["host"] ?? event.multiValueHeaders?.["host"]?.[0]; const path = this.getPath(event); const urlPath = `https://${domainName}${path}`; const url = queryString ? `${urlPath}?${queryString}` : urlPath; const headers = this.getHeaders(event); const method = this.getMethod(event); const requestInit = { headers, method }; if (event.body) { requestInit.body = event.isBase64Encoded ? (0, import_encode.decodeBase64)(event.body) : event.body; } return new Request(url, requestInit); } async createResult(event, res) { const contentType = res.headers.get("content-type"); let isBase64Encoded = contentType && isContentTypeBinary(contentType) ? true : false; if (!isBase64Encoded) { const contentEncoding = res.headers.get("content-encoding"); isBase64Encoded = isContentEncodingBinary(contentEncoding); } const body = isBase64Encoded ? (0, import_encode.encodeBase64)(await res.arrayBuffer()) : await res.text(); const result = { body, headers: {}, multiValueHeaders: event.multiValueHeaders ? {} : void 0, statusCode: res.status, isBase64Encoded }; this.setCookies(event, res, result); res.headers.forEach((value, key) => { result.headers[key] = value; if (event.multiValueHeaders && result.multiValueHeaders) { result.multiValueHeaders[key] = [value]; } }); return result; } setCookies(event, res, result) { if (res.headers.has("set-cookie")) { const cookies = res.headers.getSetCookie ? res.headers.getSetCookie() : Array.from(res.headers.entries()).filter(([k]) => k === "set-cookie").map(([, v]) => v); if (Array.isArray(cookies)) { this.setCookiesToResult(event, result, cookies); res.headers.delete("set-cookie"); } } } } class EventV2Processor extends EventProcessor { getPath(event) { return event.rawPath; } getMethod(event) { return event.requestContext.http.method; } getQueryString(event) { return event.rawQueryString; } getCookies(event, headers) { if (Array.isArray(event.cookies)) { headers.set("Cookie", event.cookies.join("; ")); } } setCookiesToResult(_, result, cookies) { result.cookies = cookies; } getHeaders(event) { const headers = new Headers(); this.getCookies(event, headers); if (event.headers) { for (const [k, v] of Object.entries(event.headers)) { if (v) { headers.set(k, v); } } } return headers; } } const v2Processor = new EventV2Processor(); class EventV1Processor extends EventProcessor { getPath(event) { return event.path; } getMethod(event) { return event.httpMethod; } getQueryString(event) { return Object.entries(event.queryStringParameters || {}).filter(([, value]) => value).map(([key, value]) => `${key}=${value}`).join("&"); } getCookies(event, headers) { } getHeaders(event) { const headers = new Headers(); this.getCookies(event, headers); if (event.headers) { for (const [k, v] of Object.entries(event.headers)) { if (v) { headers.set(k, v); } } } if (event.multiValueHeaders) { for (const [k, values] of Object.entries(event.multiValueHeaders)) { if (values) { const foundK = headers.get(k); values.forEach((v) => (!foundK || !foundK.includes(v)) && headers.append(k, v)); } } } return headers; } setCookiesToResult(_, result, cookies) { result.multiValueHeaders = { "set-cookie": cookies }; } } const v1Processor = new EventV1Processor(); class ALBProcessor extends EventProcessor { getHeaders(event) { const headers = new Headers(); if (event.multiValueHeaders) { for (const [key, values] of Object.entries(event.multiValueHeaders)) { if (values && Array.isArray(values)) { headers.set(key, values.join("; ")); } } } else { for (const [key, value] of Object.entries(event.headers ?? {})) { if (value) { headers.set(key, value); } } } return headers; } getPath(event) { return event.path; } getMethod(event) { return event.httpMethod; } getQueryString(event) { if (event.multiValueQueryStringParameters) { return Object.entries(event.multiValueQueryStringParameters || {}).filter(([, value]) => value).map(([key, value]) => `${key}=${value.join(`&${key}=`)}`).join("&"); } else { return Object.entries(event.queryStringParameters || {}).filter(([, value]) => value).map(([key, value]) => `${key}=${value}`).join("&"); } } getCookies(event, headers) { let cookie; if (event.multiValueHeaders) { cookie = event.multiValueHeaders["cookie"]?.join("; "); } else { cookie = event.headers ? event.headers["cookie"] : void 0; } if (cookie) { headers.append("Cookie", cookie); } } setCookiesToResult(event, result, cookies) { if (event.multiValueHeaders && result.multiValueHeaders) { result.multiValueHeaders["set-cookie"] = cookies; } else { result.headers["set-cookie"] = cookies.join(", "); } } } const albProcessor = new ALBProcessor(); const getProcessor = (event) => { if (isProxyEventALB(event)) { return albProcessor; } if (isProxyEventV2(event)) { return v2Processor; } return v1Processor; }; const isProxyEventALB = (event) => { return Object.hasOwn(event.requestContext, "elb"); }; const isProxyEventV2 = (event) => { return Object.hasOwn(event, "rawPath"); }; const isContentTypeBinary = (contentType) => { return !/^(text\/(plain|html|css|javascript|csv).*|application\/(.*json|.*xml).*|image\/svg\+xml.*)$/.test( contentType ); }; const isContentEncodingBinary = (contentEncoding) => { if (contentEncoding === null) { return false; } return /^(gzip|deflate|compress|br)/.test(contentEncoding); }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { getProcessor, handle, isContentEncodingBinary, isContentTypeBinary, streamHandle });