handler.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. // src/adapter/aws-lambda/handler.ts
  2. import { decodeBase64, encodeBase64 } from "../../utils/encode.js";
  3. function sanitizeHeaderValue(value) {
  4. const hasNonAscii = /[^\x00-\x7F]/.test(value);
  5. if (!hasNonAscii) {
  6. return value;
  7. }
  8. return encodeURIComponent(value);
  9. }
  10. var getRequestContext = (event) => {
  11. return event.requestContext;
  12. };
  13. var streamToNodeStream = async (reader, writer) => {
  14. let readResult = await reader.read();
  15. while (!readResult.done) {
  16. writer.write(readResult.value);
  17. readResult = await reader.read();
  18. }
  19. writer.end();
  20. };
  21. var streamHandle = (app) => {
  22. return awslambda.streamifyResponse(
  23. async (event, responseStream, context) => {
  24. const processor = getProcessor(event);
  25. try {
  26. const req = processor.createRequest(event);
  27. const requestContext = getRequestContext(event);
  28. const res = await app.fetch(req, {
  29. event,
  30. requestContext,
  31. context
  32. });
  33. const headers = {};
  34. const cookies = [];
  35. res.headers.forEach((value, name) => {
  36. if (name === "set-cookie") {
  37. cookies.push(value);
  38. } else {
  39. headers[name] = value;
  40. }
  41. });
  42. const httpResponseMetadata = {
  43. statusCode: res.status,
  44. headers,
  45. cookies
  46. };
  47. responseStream = awslambda.HttpResponseStream.from(responseStream, httpResponseMetadata);
  48. if (res.body) {
  49. await streamToNodeStream(res.body.getReader(), responseStream);
  50. } else {
  51. responseStream.write("");
  52. }
  53. } catch (error) {
  54. console.error("Error processing request:", error);
  55. responseStream.write("Internal Server Error");
  56. } finally {
  57. responseStream.end();
  58. }
  59. }
  60. );
  61. };
  62. var handle = (app, { isContentTypeBinary } = { isContentTypeBinary: void 0 }) => {
  63. return async (event, lambdaContext) => {
  64. const processor = getProcessor(event);
  65. const req = processor.createRequest(event);
  66. const requestContext = getRequestContext(event);
  67. const res = await app.fetch(req, {
  68. event,
  69. requestContext,
  70. lambdaContext
  71. });
  72. return processor.createResult(event, res, { isContentTypeBinary });
  73. };
  74. };
  75. var EventProcessor = class {
  76. getHeaderValue(headers, key) {
  77. const value = headers ? Array.isArray(headers[key]) ? headers[key][0] : headers[key] : void 0;
  78. return value;
  79. }
  80. getDomainName(event) {
  81. if (event.requestContext && "domainName" in event.requestContext) {
  82. return event.requestContext.domainName;
  83. }
  84. const hostFromHeaders = this.getHeaderValue(event.headers, "host");
  85. if (hostFromHeaders) {
  86. return hostFromHeaders;
  87. }
  88. const multiValueHeaders = "multiValueHeaders" in event ? event.multiValueHeaders : {};
  89. const hostFromMultiValueHeaders = this.getHeaderValue(multiValueHeaders, "host");
  90. return hostFromMultiValueHeaders;
  91. }
  92. createRequest(event) {
  93. const queryString = this.getQueryString(event);
  94. const domainName = this.getDomainName(event);
  95. const path = this.getPath(event);
  96. const urlPath = `https://${domainName}${path}`;
  97. const url = queryString ? `${urlPath}?${queryString}` : urlPath;
  98. const headers = this.getHeaders(event);
  99. const method = this.getMethod(event);
  100. const requestInit = {
  101. headers,
  102. method
  103. };
  104. if (event.body) {
  105. requestInit.body = event.isBase64Encoded ? decodeBase64(event.body) : event.body;
  106. }
  107. return new Request(url, requestInit);
  108. }
  109. async createResult(event, res, options) {
  110. const contentType = res.headers.get("content-type");
  111. const isContentTypeBinary = options.isContentTypeBinary ?? defaultIsContentTypeBinary;
  112. let isBase64Encoded = contentType && isContentTypeBinary(contentType) ? true : false;
  113. if (!isBase64Encoded) {
  114. const contentEncoding = res.headers.get("content-encoding");
  115. isBase64Encoded = isContentEncodingBinary(contentEncoding);
  116. }
  117. const body = isBase64Encoded ? encodeBase64(await res.arrayBuffer()) : await res.text();
  118. const result = {
  119. body,
  120. statusCode: res.status,
  121. isBase64Encoded,
  122. ..."multiValueHeaders" in event && event.multiValueHeaders ? {
  123. multiValueHeaders: {}
  124. } : {
  125. headers: {}
  126. }
  127. };
  128. this.setCookies(event, res, result);
  129. if (result.multiValueHeaders) {
  130. res.headers.forEach((value, key) => {
  131. result.multiValueHeaders[key] = [value];
  132. });
  133. } else {
  134. res.headers.forEach((value, key) => {
  135. result.headers[key] = value;
  136. });
  137. }
  138. return result;
  139. }
  140. setCookies(_event, res, result) {
  141. if (res.headers.has("set-cookie")) {
  142. const cookies = res.headers.getSetCookie ? res.headers.getSetCookie() : Array.from(res.headers.entries()).filter(([k]) => k === "set-cookie").map(([, v]) => v);
  143. if (Array.isArray(cookies)) {
  144. this.setCookiesToResult(result, cookies);
  145. res.headers.delete("set-cookie");
  146. }
  147. }
  148. }
  149. };
  150. var EventV2Processor = class extends EventProcessor {
  151. getPath(event) {
  152. return event.rawPath;
  153. }
  154. getMethod(event) {
  155. return event.requestContext.http.method;
  156. }
  157. getQueryString(event) {
  158. return event.rawQueryString;
  159. }
  160. getCookies(event, headers) {
  161. if (Array.isArray(event.cookies)) {
  162. headers.set("Cookie", event.cookies.join("; "));
  163. }
  164. }
  165. setCookiesToResult(result, cookies) {
  166. result.cookies = cookies;
  167. }
  168. getHeaders(event) {
  169. const headers = new Headers();
  170. this.getCookies(event, headers);
  171. if (event.headers) {
  172. for (const [k, v] of Object.entries(event.headers)) {
  173. if (v) {
  174. headers.set(k, v);
  175. }
  176. }
  177. }
  178. return headers;
  179. }
  180. };
  181. var v2Processor = new EventV2Processor();
  182. var EventV1Processor = class extends EventProcessor {
  183. getPath(event) {
  184. return event.path;
  185. }
  186. getMethod(event) {
  187. return event.httpMethod;
  188. }
  189. getQueryString(event) {
  190. if (event.multiValueQueryStringParameters) {
  191. return Object.entries(event.multiValueQueryStringParameters || {}).filter(([, value]) => value).map(
  192. ([key, values]) => values.map((value) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&")
  193. ).join("&");
  194. } else {
  195. return Object.entries(event.queryStringParameters || {}).filter(([, value]) => value).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value || "")}`).join("&");
  196. }
  197. }
  198. getCookies(_event, _headers) {
  199. }
  200. getHeaders(event) {
  201. const headers = new Headers();
  202. this.getCookies(event, headers);
  203. if (event.headers) {
  204. for (const [k, v] of Object.entries(event.headers)) {
  205. if (v) {
  206. headers.set(k, sanitizeHeaderValue(v));
  207. }
  208. }
  209. }
  210. if (event.multiValueHeaders) {
  211. for (const [k, values] of Object.entries(event.multiValueHeaders)) {
  212. if (values) {
  213. const foundK = headers.get(k);
  214. values.forEach((v) => {
  215. const sanitizedValue = sanitizeHeaderValue(v);
  216. return (!foundK || !foundK.includes(sanitizedValue)) && headers.append(k, sanitizedValue);
  217. });
  218. }
  219. }
  220. }
  221. return headers;
  222. }
  223. setCookiesToResult(result, cookies) {
  224. result.multiValueHeaders = {
  225. "set-cookie": cookies
  226. };
  227. }
  228. };
  229. var v1Processor = new EventV1Processor();
  230. var ALBProcessor = class extends EventProcessor {
  231. getHeaders(event) {
  232. const headers = new Headers();
  233. if (event.multiValueHeaders) {
  234. for (const [key, values] of Object.entries(event.multiValueHeaders)) {
  235. if (values && Array.isArray(values)) {
  236. const sanitizedValue = sanitizeHeaderValue(values.join("; "));
  237. headers.set(key, sanitizedValue);
  238. }
  239. }
  240. } else {
  241. for (const [key, value] of Object.entries(event.headers ?? {})) {
  242. if (value) {
  243. headers.set(key, sanitizeHeaderValue(value));
  244. }
  245. }
  246. }
  247. return headers;
  248. }
  249. getPath(event) {
  250. return event.path;
  251. }
  252. getMethod(event) {
  253. return event.httpMethod;
  254. }
  255. getQueryString(event) {
  256. if (event.multiValueQueryStringParameters) {
  257. return Object.entries(event.multiValueQueryStringParameters || {}).filter(([, value]) => value).map(([key, value]) => `${key}=${value.join(`&${key}=`)}`).join("&");
  258. } else {
  259. return Object.entries(event.queryStringParameters || {}).filter(([, value]) => value).map(([key, value]) => `${key}=${value}`).join("&");
  260. }
  261. }
  262. getCookies(event, headers) {
  263. let cookie;
  264. if (event.multiValueHeaders) {
  265. cookie = event.multiValueHeaders["cookie"]?.join("; ");
  266. } else {
  267. cookie = event.headers ? event.headers["cookie"] : void 0;
  268. }
  269. if (cookie) {
  270. headers.append("Cookie", cookie);
  271. }
  272. }
  273. setCookiesToResult(result, cookies) {
  274. if (result.multiValueHeaders) {
  275. result.multiValueHeaders["set-cookie"] = cookies;
  276. } else {
  277. result.headers["set-cookie"] = cookies.join(", ");
  278. }
  279. }
  280. };
  281. var albProcessor = new ALBProcessor();
  282. var LatticeV2Processor = class extends EventProcessor {
  283. getPath(event) {
  284. return event.path;
  285. }
  286. getMethod(event) {
  287. return event.method;
  288. }
  289. getQueryString() {
  290. return "";
  291. }
  292. getHeaders(event) {
  293. const headers = new Headers();
  294. if (event.headers) {
  295. for (const [k, values] of Object.entries(event.headers)) {
  296. if (values) {
  297. const foundK = headers.get(k);
  298. values.forEach((v) => {
  299. const sanitizedValue = sanitizeHeaderValue(v);
  300. return (!foundK || !foundK.includes(sanitizedValue)) && headers.append(k, sanitizedValue);
  301. });
  302. }
  303. }
  304. }
  305. return headers;
  306. }
  307. getCookies() {
  308. }
  309. setCookiesToResult(result, cookies) {
  310. result.headers = {
  311. ...result.headers,
  312. "set-cookie": cookies.join(", ")
  313. };
  314. }
  315. };
  316. var latticeV2Processor = new LatticeV2Processor();
  317. var getProcessor = (event) => {
  318. if (isProxyEventALB(event)) {
  319. return albProcessor;
  320. }
  321. if (isProxyEventV2(event)) {
  322. return v2Processor;
  323. }
  324. if (isLatticeEventV2(event)) {
  325. return latticeV2Processor;
  326. }
  327. return v1Processor;
  328. };
  329. var isProxyEventALB = (event) => {
  330. if (event.requestContext) {
  331. return Object.hasOwn(event.requestContext, "elb");
  332. }
  333. return false;
  334. };
  335. var isProxyEventV2 = (event) => {
  336. return Object.hasOwn(event, "rawPath");
  337. };
  338. var isLatticeEventV2 = (event) => {
  339. if (event.requestContext) {
  340. return Object.hasOwn(event.requestContext, "serviceArn");
  341. }
  342. return false;
  343. };
  344. var defaultIsContentTypeBinary = (contentType) => {
  345. return !/^text\/(?:plain|html|css|javascript|csv)|(?:\/|\+)(?:json|xml)\s*(?:;|$)/.test(
  346. contentType
  347. );
  348. };
  349. var isContentEncodingBinary = (contentEncoding) => {
  350. if (contentEncoding === null) {
  351. return false;
  352. }
  353. return /^(gzip|deflate|compress|br)/.test(contentEncoding);
  354. };
  355. export {
  356. ALBProcessor,
  357. EventProcessor,
  358. EventV1Processor,
  359. EventV2Processor,
  360. LatticeV2Processor,
  361. defaultIsContentTypeBinary,
  362. getProcessor,
  363. handle,
  364. isContentEncodingBinary,
  365. streamHandle
  366. };