UNPKG

@trifrost/core

Version:

Blazingly fast, runtime-agnostic server framework for modern edge and node environments

179 lines (178 loc) 5.82 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WorkerdContext = void 0; const Context_1 = require("../../Context"); const constants_1 = require("../../types/constants"); const Request_1 = require("../../utils/BodyParser/Request"); const Http_1 = require("../../utils/Http"); const types_1 = require("../../utils/BodyParser/types"); const Stream_1 = require("../../utils/Stream"); const encoder = new TextEncoder(); class WorkerdContext extends Context_1.Context { /* Workerd Request instance */ workerd_req; /* Workerd execution context */ workerd_ctx; /* Internal Response instance */ res = null; constructor(cfg, logger, req, ctx) { /* Extract path and query */ const { path, query } = (0, Http_1.extractPartsFromUrl)(req.url); /* Hydrate headers */ const headers = {}; /* eslint-disable-next-line */ /* @ts-ignore */ req.headers.forEach((value, key) => { headers[key.toLowerCase()] = value; }); super(logger, cfg, { path, method: constants_1.HttpMethodToNormal[req.method], headers, query, }); this.workerd_req = req; this.workerd_ctx = ctx; } /** * Getter for the final response */ get response() { return this.res; } /** * Initializes the context, this happens when a route is matched and tied to this context. */ async init(val) { await super.init(val, async () => (0, Request_1.parseBody)(this, this.workerd_req, val.route.bodyParser || types_1.DEFAULT_BODY_PARSER_OPTIONS)); } /** * Get a stream for a particular path * * @param {string} path - Path to the file */ async getStream(path) { try { if (!('ASSETS' in this.env) || !this.env.ASSETS.fetch) throw new Error('WorkerdContext@getStream: ASSETS is not configured on env'); const response = await this.env.ASSETS.fetch(new Request(`http://${this.host || '0.0.0.0'}${path[0] !== '/' ? '/' : ''}${path}`)); if (!response.ok) { this.logger.warn('WorkerdContext@getStream: File not found', { path }); return null; } /* Get content length of asset */ const contentLength = response.headers.get('content-length'); /* Determine size if possible */ const size = contentLength ? parseInt(contentLength, 10) : null; /* Get stream */ const stream = response.body; if (!stream) throw new Error('WorkerdContext@getStream: Can not create stream from response body'); return { stream, size }; } catch (err) { this.logger.error(err, { path }); return null; } } /** * Stream a file-like response in Workerd * * @param {unknown} stream - Stream to respond with * @param {number|null} size - Size of the stream */ stream(stream, size = null) { /* If already locked do nothing */ if (this.isLocked) return; /* Verify stream */ (0, Stream_1.verifyFileStream)(stream); super.stream(stream, size); /* Set response with stream */ this.res = new Response(stream, { status: this.res_code, headers: this.res_headers, }); /* Write cookies */ this.writeCookies(); } /** * Abort the request * * @param {HttpStatusCode?} status - Status to abort with (defaults to 503) */ abort(status) { if (this.isLocked) return; super.abort(status); /* Set response */ this.res = new Response(null, { status: this.res_code, headers: this.res_headers, }); /* Write cookies */ this.writeCookies(); } /** * End the request and respond to callee */ end() { if (this.isLocked) return; super.end(); switch (this.method) { case constants_1.HttpMethods.HEAD: { this.res_headers['content-length'] = typeof this.res_body === 'string' ? '' + encoder.encode(this.res_body).length : '0'; /* Set response */ this.res = new Response(null, { status: this.res_code, headers: this.res_headers, }); /* Write cookies */ this.writeCookies(); break; } default: /* Set response */ this.res = new Response(this.res_body, { status: this.res_code, headers: this.res_headers, }); /* Write cookies */ this.writeCookies(); break; } } /** * Run jobs after the response has gone out */ runAfter() { const hooks = this.afterHooks; for (let i = 0; i < hooks.length; i++) { try { this.workerd_ctx.waitUntil(hooks[i](this)); } catch { /* No-Op */ } } } /** * MARK: Protected */ getIP() { /* Workerd itself does not expose raw remote IP, hence we should trust headers, see main context */ return null; } /** * MARK: Private */ writeCookies() { if (!this.$cookies) return; const outgoing = this.$cookies.outgoing; for (let i = 0; i < outgoing.length; i++) this.response.headers.append('set-cookie', outgoing[i]); } } exports.WorkerdContext = WorkerdContext;