@trifrost/core
Version:
Blazingly fast, runtime-agnostic server framework for modern edge and node environments
179 lines (178 loc) • 5.82 kB
JavaScript
"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;