@mionjs/platform-uws 0.13.2 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dist/esm/src/constants.js +2 -0
- package/.dist/esm/src/constants.js.map +1 -1
- package/.dist/esm/src/types.d.ts +1 -0
- package/.dist/esm/src/uwsHttp.js +35 -36
- package/.dist/esm/src/uwsHttp.js.map +1 -1
- package/package.json +5 -7
- package/.dist/cjs/index.cjs +0 -8
- package/.dist/cjs/index.d.ts +0 -4
- package/.dist/cjs/package.json +0 -1
- package/.dist/cjs/src/constants.cjs +0 -11
- package/.dist/cjs/src/constants.cjs.map +0 -1
- package/.dist/cjs/src/constants.d.ts +0 -3
- package/.dist/cjs/src/headers.cjs +0 -16
- package/.dist/cjs/src/headers.cjs.map +0 -1
- package/.dist/cjs/src/headers.d.ts +0 -4
- package/.dist/cjs/src/types.cjs +0 -0
- package/.dist/cjs/src/types.d.ts +0 -8
- package/.dist/cjs/src/uwsHttp.cjs +0 -224
- package/.dist/cjs/src/uwsHttp.cjs.map +0 -1
- package/.dist/cjs/src/uwsHttp.d.ts +0 -12
|
@@ -2,6 +2,8 @@ import { DEFAULT_MAX_BODY_SIZE } from "@mionjs/core";
|
|
|
2
2
|
//#region src/constants.ts
|
|
3
3
|
var DEFAULT_UWS_HTTP_OPTIONS = {
|
|
4
4
|
port: 80,
|
|
5
|
+
/** uWS' own default is 4 KB, which a 4 KB `?data=` query URL alone fills: same 8 KB as the node adapter */
|
|
6
|
+
maxHeaderSize: 8192,
|
|
5
7
|
/** What a route takes when its own `maxBodySize` and its types say nothing: the shared 128 KB default */
|
|
6
8
|
maxBodySize: DEFAULT_MAX_BODY_SIZE
|
|
7
9
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","names":[],"sources":["../../../src/constants.ts"],"sourcesContent":["/* ########\n * 2022 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {DEFAULT_MAX_BODY_SIZE} from '@mionjs/core';\nimport {UwsHttpOptions} from './types.ts';\n\nexport const DEFAULT_UWS_HTTP_OPTIONS: UwsHttpOptions = {\n port: 80,\n /** What a route takes when its own `maxBodySize` and its types say nothing: the shared 128 KB default */\n maxBodySize: DEFAULT_MAX_BODY_SIZE,\n};\n"],"mappings":";;AAUA,IAAa,2BAA2C;CACtD,MAAM;;CAEN,aAAa;AACf"}
|
|
1
|
+
{"version":3,"file":"constants.js","names":[],"sources":["../../../src/constants.ts"],"sourcesContent":["/* ########\n * 2022 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {DEFAULT_MAX_BODY_SIZE} from '@mionjs/core';\nimport {UwsHttpOptions} from './types.ts';\n\nexport const DEFAULT_UWS_HTTP_OPTIONS: UwsHttpOptions = {\n port: 80,\n /** uWS' own default is 4 KB, which a 4 KB `?data=` query URL alone fills: same 8 KB as the node adapter */\n maxHeaderSize: 8192,\n /** What a route takes when its own `maxBodySize` and its types say nothing: the shared 128 KB default */\n maxBodySize: DEFAULT_MAX_BODY_SIZE,\n};\n"],"mappings":";;AAUA,IAAa,2BAA2C;CACtD,MAAM;;CAEN,eAAe;;CAEf,aAAa;AACf"}
|
package/.dist/esm/src/types.d.ts
CHANGED
package/.dist/esm/src/uwsHttp.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_UWS_HTTP_OPTIONS } from "./constants.js";
|
|
2
2
|
import { headersFromUwsRequest } from "./headers.js";
|
|
3
|
-
import { createContextFromChain,
|
|
3
|
+
import { createContextFromChain, dispatchMaybeSync, dispatchPlatformError, getDefaultHeaders, getRouterFatalErrorResponse, hostOwnsSocket, releaseHeaders, requestPayloadTooLarge, resetRouter, resolveExecutionChain, setPlatformConfig, toRpcError } from "@mionjs/router";
|
|
4
4
|
import { STATUS_CODES } from "http";
|
|
5
5
|
import { loadUws } from "@mionjs/bin-uws";
|
|
6
6
|
import { FatalError, SerializerModes, getENV } from "@mionjs/core";
|
|
@@ -32,6 +32,7 @@ async function startUwsServer(options) {
|
|
|
32
32
|
port: httpOptions.port,
|
|
33
33
|
httpOptions
|
|
34
34
|
});
|
|
35
|
+
process.env.UWS_HTTP_MAX_HEADERS_SIZE ??= String(httpOptions.maxHeaderSize);
|
|
35
36
|
const uws = loadUws();
|
|
36
37
|
const app = httpOptions.ssl ? uws.SSLApp(httpOptions.ssl) : uws.App();
|
|
37
38
|
app.any("/*", uwsRequestHandler);
|
|
@@ -64,18 +65,35 @@ async function startUwsServer(options) {
|
|
|
64
65
|
});
|
|
65
66
|
});
|
|
66
67
|
}
|
|
68
|
+
var LOWER_CASE_METHODS = Object.assign(Object.create(null), {
|
|
69
|
+
get: "GET",
|
|
70
|
+
head: "HEAD",
|
|
71
|
+
post: "POST",
|
|
72
|
+
put: "PUT",
|
|
73
|
+
delete: "DELETE",
|
|
74
|
+
connect: "CONNECT",
|
|
75
|
+
options: "OPTIONS",
|
|
76
|
+
trace: "TRACE",
|
|
77
|
+
patch: "PATCH",
|
|
78
|
+
query: "QUERY"
|
|
79
|
+
});
|
|
80
|
+
function upperCaseMethod(method) {
|
|
81
|
+
return LOWER_CASE_METHODS[method] ?? "OTHER";
|
|
82
|
+
}
|
|
67
83
|
function drainRequestBody(res) {
|
|
68
84
|
res.onData(() => {});
|
|
69
85
|
}
|
|
70
86
|
function uwsRequestHandler(res, req) {
|
|
71
|
-
const state = {
|
|
72
|
-
replied: false,
|
|
73
|
-
aborted: false
|
|
74
|
-
};
|
|
75
87
|
const path = req.getUrl();
|
|
76
88
|
const query = req.getQuery();
|
|
77
89
|
const urlQuery = query === "" ? void 0 : query;
|
|
90
|
+
const method = upperCaseMethod(req.getMethod());
|
|
78
91
|
const reqHeaders = headersFromUwsRequest(req);
|
|
92
|
+
const state = {
|
|
93
|
+
replied: false,
|
|
94
|
+
aborted: false,
|
|
95
|
+
head: method === "HEAD"
|
|
96
|
+
};
|
|
79
97
|
const respHeaders = getDefaultHeaders();
|
|
80
98
|
res.onAborted(() => {
|
|
81
99
|
state.aborted = true;
|
|
@@ -94,21 +112,9 @@ function uwsRequestHandler(res, req) {
|
|
|
94
112
|
fatalFail(res, state, respHeaders, toRpcError(err));
|
|
95
113
|
return;
|
|
96
114
|
}
|
|
97
|
-
const dispatchBody = (rawBody
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
const queryBody = readQueryBody ? decodeQueryBody(urlQuery, reqRawBody || void 0) : void 0;
|
|
102
|
-
if (queryBody) {
|
|
103
|
-
reqRawBody = queryBody.rawBody;
|
|
104
|
-
reqBodyType = queryBody.bodyType;
|
|
105
|
-
}
|
|
106
|
-
} catch (err) {
|
|
107
|
-
state.replied = true;
|
|
108
|
-
fatalFail(res, state, respHeaders, err);
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
const context = createContextFromChain(chain, path, urlQuery, reqHeaders, respHeaders, reqRawBody, reqBodyType);
|
|
115
|
+
const dispatchBody = (rawBody) => {
|
|
116
|
+
const reqBodyType = SerializerModes.stringifyJson;
|
|
117
|
+
const context = createContextFromChain(chain, path, urlQuery, method, reqHeaders, respHeaders, rawBody, reqBodyType);
|
|
112
118
|
let dispatched;
|
|
113
119
|
try {
|
|
114
120
|
dispatched = dispatchMaybeSync(context, rawRequest, res);
|
|
@@ -136,17 +142,17 @@ function uwsRequestHandler(res, req) {
|
|
|
136
142
|
};
|
|
137
143
|
if (!chain.readsBody) {
|
|
138
144
|
drainRequestBody(res);
|
|
139
|
-
dispatchBody(""
|
|
145
|
+
dispatchBody("");
|
|
140
146
|
return;
|
|
141
147
|
}
|
|
142
148
|
res.collectBody(chain.maxBodySize, (fullBody) => {
|
|
143
149
|
if (state.replied) return;
|
|
144
150
|
if (fullBody === null) {
|
|
145
|
-
answerWith(dispatchPlatformError(chain, path, urlQuery, requestPayloadTooLarge(), reqHeaders, respHeaders, rawRequest, res));
|
|
151
|
+
answerWith(dispatchPlatformError(chain, path, urlQuery, method, requestPayloadTooLarge(), reqHeaders, respHeaders, rawRequest, res));
|
|
146
152
|
return;
|
|
147
153
|
}
|
|
148
154
|
if (fullBody.byteLength <= UWS_MAX_SINGLE_READ) {
|
|
149
|
-
dispatchBody(fullBody.byteLength === 0 ? "" : Buffer.from(fullBody).toString()
|
|
155
|
+
dispatchBody(fullBody.byteLength === 0 ? "" : Buffer.from(fullBody).toString());
|
|
150
156
|
return;
|
|
151
157
|
}
|
|
152
158
|
queueMicrotask(() => {
|
|
@@ -160,7 +166,7 @@ function uwsRequestHandler(res, req) {
|
|
|
160
166
|
}));
|
|
161
167
|
return;
|
|
162
168
|
}
|
|
163
|
-
dispatchBody(releaseAfterDecode(fullBody)
|
|
169
|
+
dispatchBody(releaseAfterDecode(fullBody));
|
|
164
170
|
});
|
|
165
171
|
});
|
|
166
172
|
}
|
|
@@ -177,13 +183,6 @@ function releaseAfterDecode(fullBody) {
|
|
|
177
183
|
function fatalFail(res, state, respHeaders, error) {
|
|
178
184
|
reply(res, state, getRouterFatalErrorResponse(error, respHeaders));
|
|
179
185
|
}
|
|
180
|
-
function isHeaderSafe(text) {
|
|
181
|
-
for (let i = 0; i < text.length; i++) {
|
|
182
|
-
const code = text.charCodeAt(i);
|
|
183
|
-
if (code === 13 || code === 10 || code === 0) return false;
|
|
184
|
-
}
|
|
185
|
-
return true;
|
|
186
|
-
}
|
|
187
186
|
/** The line is a pure function of the code, so it is built once per code rather than per response. */
|
|
188
187
|
var statusLines = /* @__PURE__ */ new Map();
|
|
189
188
|
function statusLine(statusCode) {
|
|
@@ -198,19 +197,19 @@ function reply(res, state, mionResp) {
|
|
|
198
197
|
if (state.aborted) return;
|
|
199
198
|
const headers = mionResp.headers;
|
|
200
199
|
const payload = JSON.stringify(mionResp.body);
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
};
|
|
200
|
+
const close = headers.get("connection")?.toLowerCase() === "close";
|
|
201
|
+
const write = (name, value) => res.writeHeader(name, value);
|
|
204
202
|
res.cork(() => {
|
|
205
203
|
res.writeStatus(statusLine(mionResp.statusCode));
|
|
206
204
|
headers.forEach((name, value) => {
|
|
207
|
-
if (name === "content-length" || name === "content-type"
|
|
205
|
+
if (name === "content-length" || name === "content-type") return;
|
|
208
206
|
if (typeof value === "string") write(name, value);
|
|
209
207
|
else if (name === "set-cookie") for (const cookie of value) write(name, cookie);
|
|
210
208
|
else write(name, value.join(", "));
|
|
211
209
|
});
|
|
212
210
|
res.writeHeader("content-type", mionResp.contentType);
|
|
213
|
-
res.
|
|
211
|
+
if (state.head) res.endWithoutBody(Buffer.byteLength(payload, "utf8"), close);
|
|
212
|
+
else res.end(payload, close);
|
|
214
213
|
});
|
|
215
214
|
releaseHeaders(headers);
|
|
216
215
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uwsHttp.js","names":[],"sources":["../../../src/uwsHttp.ts"],"sourcesContent":["/* ########\n * 2022 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {\n dispatchMaybeSync,\n dispatchPlatformError,\n resolveExecutionChain,\n createContextFromChain,\n getRouterFatalErrorResponse,\n toRpcError,\n resetRouter,\n decodeQueryBody,\n setPlatformConfig,\n getDefaultHeaders,\n releaseHeaders,\n requestPayloadTooLarge,\n hostOwnsSocket,\n} from '@mionjs/router';\nimport {STATUS_CODES} from 'http';\nimport {loadUws} from '@mionjs/bin-uws';\nimport type {HttpRequest, HttpResponse, TemplatedApp, us_listen_socket} from '@mionjs/bin-uws';\nimport {DEFAULT_UWS_HTTP_OPTIONS} from './constants.ts';\nimport type {UwsHttpOptions} from './types.ts';\nimport type {MethodsExecutionChain, MionHeaders, MionResponse, RecordResponseHeaders} from '@mionjs/router';\nimport {getENV, SerializerModes} from '@mionjs/core';\nimport type {SerializerCode} from '@mionjs/core';\nimport {RpcError, FatalError} from '@mionjs/core';\nimport {headersFromUwsRequest} from './headers.ts';\n\n// ############# PRIVATE STATE #############\n\nlet httpOptions: Readonly<UwsHttpOptions> = {...DEFAULT_UWS_HTTP_OPTIONS};\n\n// The most bytes one uWS socket read can deliver: uSockets' LIBUS_RECV_BUFFER_LENGTH (512 KiB) at the tag pinned in\n// packages/bin-uws. A LARGER body cannot be single-read, which is what makes the zero-copy branch below safe (a\n// detachment tripwire guards it at runtime). Re-verify against uSockets on a tag bump.\nconst UWS_MAX_SINGLE_READ = 524288;\n\n/** The running server: the uWS app plus the socket handle listen() produced. */\nexport interface UwsServer {\n app: TemplatedApp;\n listenSocket: us_listen_socket;\n close(): void;\n}\n\n// ############# PUBLIC METHODS #############\n\nexport function resetUwsHttpOpts() {\n httpOptions = {...DEFAULT_UWS_HTTP_OPTIONS};\n resetRouter();\n}\n\nexport function setUwsHttpOpts(options?: Partial<UwsHttpOptions>) {\n httpOptions = {\n ...httpOptions,\n ...options,\n };\n\n return httpOptions;\n}\n\n/** The platform config the router publishes: everything but the TLS file paths. */\nfunction serializablePlatformConfig(): Record<string, unknown> {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const {ssl: _ssl, ...serializableConfig} = httpOptions;\n return serializableConfig;\n}\n\nexport async function startUwsServer(options?: Partial<UwsHttpOptions>): Promise<UwsServer> {\n const isTest = getENV('NODE_ENV') === 'test';\n\n if (options) setUwsHttpOpts(options);\n // uWS is its own C++ event loop and owns its listen socket, so it cannot mount on a host node server.\n if (hostOwnsSocket()) {\n throw new Error(\n '@mionjs/platform-uws does not support middleware mode: uWebSockets.js owns its own listen ' +\n 'socket and cannot mount on a host node server. Use @mionjs/platform-node for middleware mode.'\n );\n }\n const protocol = httpOptions.ssl ? 'https' : 'http';\n const port = httpOptions.port !== 80 ? `:${httpOptions.port}` : '';\n const url = `${protocol}://localhost${port}`;\n if (!isTest)\n console.log(`mion uws server running on ${url}`, {\n port: httpOptions.port,\n httpOptions,\n });\n\n const uws = loadUws();\n const app = httpOptions.ssl ? uws.SSLApp(httpOptions.ssl) : uws.App();\n app.any('/*', uwsRequestHandler);\n\n return new Promise<UwsServer>((resolve, reject) => {\n app.listen(httpOptions.port, (listenSocket) => {\n if (!listenSocket) {\n reject(new Error(`mion uws server failed to listen on port ${httpOptions.port} (port in use?)`));\n return;\n }\n\n setPlatformConfig(serializablePlatformConfig());\n\n let closed = false;\n const close = () => {\n if (closed) return;\n closed = true;\n uws.us_listen_socket_close(listenSocket);\n };\n const server: UwsServer = {app, listenSocket, close};\n\n const shutdownHandler = function () {\n if (!isTest) console.log(`Shutting down mion server on ${url}`);\n close();\n process.exit(0);\n };\n process.on('SIGINT', shutdownHandler);\n process.on('SIGTERM', shutdownHandler);\n\n resolve(server);\n });\n });\n}\n\n// ############# PRIVATE METHODS #############\n\n// uWS refreshes a socket's idle timeout only from inside a body-data callback, and runs it only when a reader is registered.\n// Without one, a request answered before its body finished arriving is closed mid-upload, so drop every chunk instead.\nfunction drainRequestBody(res: HttpResponse) {\n res.onData(() => {});\n}\n\n// exported for tests and for mounting on a hand-built uWS app; NOT a middleware handler (see startUwsServer).\n// uWS contract: `req` is valid only synchronously here, so the async dispatch snapshots it before the first await.\nexport function uwsRequestHandler(res: HttpResponse, req: HttpRequest): void {\n const state = {replied: false, aborted: false};\n // Everything read from `req` happens HERE, synchronously.\n const path = req.getUrl();\n const query = req.getQuery();\n const urlQuery = query === '' ? undefined : query;\n const reqHeaders = headersFromUwsRequest(req);\n\n const respHeaders = getDefaultHeaders();\n\n // must be registered before any async work: after a disconnect uWS frees the response, and touching it would crash\n res.onAborted(() => {\n state.aborted = true;\n });\n\n // route resolved BEFORE the body, synchronously: the native read stops at the chain's limit, and a late context stays GC-cheap.\n // The raw request object is built once: the one a pathTransform reads and the one the handlers see.\n const rawRequest = {path, urlQuery, headers: reqHeaders};\n let chain: MethodsExecutionChain;\n try {\n chain = resolveExecutionChain(path, urlQuery, rawRequest);\n } catch (err) {\n drainRequestBody(res);\n state.replied = true;\n fatalFail(res, state, respHeaders, toRpcError(err));\n return;\n }\n\n const dispatchBody = (rawBody: string, readQueryBody: boolean) => {\n let reqRawBody: any = rawBody;\n let reqBodyType: SerializerCode = SerializerModes.stringifyJson;\n // a throw here runs inside uWS' native callback (or a microtask): it must become a response\n try {\n const queryBody = readQueryBody ? decodeQueryBody(urlQuery, reqRawBody || undefined) : undefined;\n if (queryBody) {\n reqRawBody = queryBody.rawBody;\n reqBodyType = queryBody.bodyType;\n }\n } catch (err) {\n state.replied = true;\n fatalFail(res, state, respHeaders, err as RpcError<string>);\n return;\n }\n\n const context = createContextFromChain(chain, path, urlQuery, reqHeaders, respHeaders, reqRawBody, reqBodyType);\n let dispatched: MionResponse | Promise<MionResponse>;\n try {\n dispatched = dispatchMaybeSync(context, rawRequest, res);\n } catch (err) {\n failWith(err);\n return;\n }\n // an all-sync chain answers inside uWS' own callback, with no promise in between\n if (dispatched instanceof Promise) answerWith(dispatched);\n else answer(dispatched);\n };\n\n const answer = (mionResponse: MionResponse) => {\n if (state.replied) return;\n state.replied = true;\n try {\n reply(res, state, mionResponse);\n } catch {\n // already marked replied: a failed write has nothing left to answer with\n }\n };\n\n const failWith = (err: unknown) => {\n if (state.replied) return;\n state.replied = true;\n fatalFail(res, state, respHeaders, toRpcError(err));\n };\n\n const answerWith = (dispatched: Promise<MionResponse>) => {\n dispatched.then(answer).catch(failWith);\n };\n\n // collectBody assembles the body natively (uWS' onDataV2 preallocates) and calls back ONCE, with null past maxSize,\n // which is exactly the maxBodySize contract. A not-found chain has no route to feed: its body is dropped as it arrives.\n if (!chain.readsBody) {\n drainRequestBody(res);\n dispatchBody('', false);\n return;\n }\n\n res.collectBody(chain.maxBodySize, (fullBody) => {\n if (state.replied) return;\n if (fullBody === null) {\n // the route resolved, so the refusal still runs the chain's alwaysRun members\n answerWith(\n dispatchPlatformError(chain, path, urlQuery, requestPayloadTooLarge(), reqHeaders, respHeaders, rawRequest, res)\n );\n return;\n }\n\n // collectBody has two paths (verified in the pinned tag's HttpResponseWrapper.h and by test): a single-read body is a\n // zero-copy window into uWS' receive buffer, DETACHED when this callback returns, so it must be decoded synchronously\n // here; a multi-read body was assembled in C++ and OWNERSHIP-TRANSFERRED to JS, so it can be decoded from a microtask.\n if (fullBody.byteLength <= UWS_MAX_SINGLE_READ) {\n // a body-less request (every GET) skips the view and the decode altogether\n dispatchBody(fullBody.byteLength === 0 ? '' : Buffer.from(fullBody).toString(), true);\n return;\n }\n // bigger than one read can deliver → guaranteed the ownership-transferred path, so the buffer survives the microtask.\n // The zero-length check is a tripwire for an upstream change: fail loudly instead of parsing a neutered buffer.\n queueMicrotask(() => {\n if (state.replied) return;\n if (fullBody.byteLength === 0) {\n state.replied = true;\n const error = new FatalError({\n publicMessage: 'Internal Server Error',\n type: 'unknown-error',\n errorData: {reason: 'uws detached a multi-read body buffer (upstream behavior change) — report to mion'},\n });\n fatalFail(res, state, respHeaders, error);\n return;\n }\n dispatchBody(releaseAfterDecode(fullBody), true);\n });\n });\n}\n\n/** Frees an ownership-transferred body buffer right after decoding it; the single-read window must never come through here.\n * `transfer(0)` and not `transfer()`: measured on node 26, only `transfer(0)` releases the backing store there and then.\n * Guarded because a future uWS could hand back a buffer that cannot be transferred, which must not fail the request. */\nfunction releaseAfterDecode(fullBody: ArrayBuffer): string {\n const text = Buffer.from(fullBody).toString();\n try {\n (fullBody as ReleasableBuffer).transfer?.(0);\n } catch {\n // not transferable: the collector gets it instead, exactly as before\n }\n return text;\n}\n\n/** `transfer` is ES2024 and this package compiles against an ES2023 lib; optional because a runtime without it must work. */\ntype ReleasableBuffer = ArrayBuffer & {transfer?: (newByteLength?: number) => ArrayBuffer};\n\n// only called when there is an http error or weird unhandled route errors\nfunction fatalFail(res: HttpResponse, state: {aborted: boolean}, respHeaders: MionHeaders, error: RpcError<string>) {\n const routeResponse = getRouterFatalErrorResponse(error, respHeaders);\n reply(res, state, routeResponse);\n}\n\nfunction isHeaderSafe(text: string): boolean {\n // one pass, no allocation: a CR, LF or NUL anywhere makes the header unsafe to write\n for (let i = 0; i < text.length; i++) {\n const code = text.charCodeAt(i);\n if (code === 13 || code === 10 || code === 0) return false;\n }\n return true;\n}\n\n/** The line is a pure function of the code, so it is built once per code rather than per response. */\nconst statusLines = new Map<number, string>();\nfunction statusLine(statusCode: number): string {\n const cached = statusLines.get(statusCode);\n if (cached !== undefined) return cached;\n const statusText = STATUS_CODES[statusCode];\n const line = statusText ? `${statusCode} ${statusText}` : `${statusCode}`;\n statusLines.set(statusCode, line);\n return line;\n}\n\nfunction reply(res: HttpResponse, state: {aborted: boolean}, mionResp: MionResponse) {\n // uWS freed the response when the client left; touching it would crash\n if (state.aborted) return;\n const headers = mionResp.headers as RecordResponseHeaders;\n\n // serialized BEFORE the cork: uWS warns a cork buffer must not be held across event loop iterations\n const payload = JSON.stringify(mionResp.body);\n\n // cork makes one syscall; uWS headers are write-only and must all precede end().\n // content-length is skipped: uWS writes its own, and a duplicate corrupts the response.\n // uWS writes headers unchecked, so an echoed CR or LF would be header injection: dropped\n const write = (name: string, value: string) => {\n if (isHeaderSafe(value)) res.writeHeader(name, value);\n };\n res.cork(() => {\n res.writeStatus(statusLine(mionResp.statusCode));\n headers.forEach((name, value) => {\n if (name === 'content-length' || name === 'content-type' || !isHeaderSafe(name)) return;\n if (typeof value === 'string') write(name, value);\n // RFC 6265 forbids folding set-cookie; uWS writes a repeated name as its own line\n else if (name === 'set-cookie') for (const cookie of value) write(name, cookie);\n else write(name, value.join(', '));\n });\n // last, and the store's own skipped above: uWS appends a repeated name instead of replacing it\n res.writeHeader('content-type', mionResp.contentType);\n\n res.end(payload);\n });\n releaseHeaders(headers);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"uwsHttp.js","names":[],"sources":["../../../src/uwsHttp.ts"],"sourcesContent":["/* ########\n * 2022 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {\n dispatchMaybeSync,\n dispatchPlatformError,\n resolveExecutionChain,\n createContextFromChain,\n getRouterFatalErrorResponse,\n toRpcError,\n resetRouter,\n setPlatformConfig,\n getDefaultHeaders,\n releaseHeaders,\n requestPayloadTooLarge,\n hostOwnsSocket,\n HttpMethod,\n} from '@mionjs/router';\nimport {STATUS_CODES} from 'http';\nimport {loadUws} from '@mionjs/bin-uws';\nimport type {HttpRequest, HttpResponse, TemplatedApp, us_listen_socket} from '@mionjs/bin-uws';\nimport {DEFAULT_UWS_HTTP_OPTIONS} from './constants.ts';\nimport type {UwsHttpOptions} from './types.ts';\nimport type {MethodsExecutionChain, MionHeaders, MionResponse, RecordResponseHeaders} from '@mionjs/router';\nimport {getENV, SerializerModes} from '@mionjs/core';\nimport type {SerializerCode} from '@mionjs/core';\nimport {RpcError, FatalError} from '@mionjs/core';\nimport {headersFromUwsRequest} from './headers.ts';\n\n// ############# PRIVATE STATE #############\n\nlet httpOptions: Readonly<UwsHttpOptions> = {...DEFAULT_UWS_HTTP_OPTIONS};\n\n// The most bytes one uWS socket read can deliver: uSockets' LIBUS_RECV_BUFFER_LENGTH (512 KiB) at the tag pinned in\n// packages/bin-uws. A LARGER body cannot be single-read, which is what makes the zero-copy branch below safe (a\n// detachment tripwire guards it at runtime). Re-verify against uSockets on a tag bump.\nconst UWS_MAX_SINGLE_READ = 524288;\n\n/** The running server: the uWS app plus the socket handle listen() produced. */\nexport interface UwsServer {\n app: TemplatedApp;\n listenSocket: us_listen_socket;\n close(): void;\n}\n\n// ############# PUBLIC METHODS #############\n\nexport function resetUwsHttpOpts() {\n httpOptions = {...DEFAULT_UWS_HTTP_OPTIONS};\n resetRouter();\n}\n\nexport function setUwsHttpOpts(options?: Partial<UwsHttpOptions>) {\n httpOptions = {\n ...httpOptions,\n ...options,\n };\n\n return httpOptions;\n}\n\n/** The platform config the router publishes: everything but the TLS file paths. */\nfunction serializablePlatformConfig(): Record<string, unknown> {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const {ssl: _ssl, ...serializableConfig} = httpOptions;\n return serializableConfig;\n}\n\nexport async function startUwsServer(options?: Partial<UwsHttpOptions>): Promise<UwsServer> {\n const isTest = getENV('NODE_ENV') === 'test';\n\n if (options) setUwsHttpOpts(options);\n // uWS is its own C++ event loop and owns its listen socket, so it cannot mount on a host node server.\n if (hostOwnsSocket()) {\n throw new Error(\n '@mionjs/platform-uws does not support middleware mode: uWebSockets.js owns its own listen ' +\n 'socket and cannot mount on a host node server. Use @mionjs/platform-node for middleware mode.'\n );\n }\n const protocol = httpOptions.ssl ? 'https' : 'http';\n const port = httpOptions.port !== 80 ? `:${httpOptions.port}` : '';\n const url = `${protocol}://localhost${port}`;\n if (!isTest)\n console.log(`mion uws server running on ${url}`, {\n port: httpOptions.port,\n httpOptions,\n });\n\n // uWS reads it once, when its binary loads\n process.env.UWS_HTTP_MAX_HEADERS_SIZE ??= String(httpOptions.maxHeaderSize);\n const uws = loadUws();\n const app = httpOptions.ssl ? uws.SSLApp(httpOptions.ssl) : uws.App();\n app.any('/*', uwsRequestHandler);\n\n return new Promise<UwsServer>((resolve, reject) => {\n app.listen(httpOptions.port, (listenSocket) => {\n if (!listenSocket) {\n reject(new Error(`mion uws server failed to listen on port ${httpOptions.port} (port in use?)`));\n return;\n }\n\n setPlatformConfig(serializablePlatformConfig());\n\n let closed = false;\n const close = () => {\n if (closed) return;\n closed = true;\n uws.us_listen_socket_close(listenSocket);\n };\n const server: UwsServer = {app, listenSocket, close};\n\n const shutdownHandler = function () {\n if (!isTest) console.log(`Shutting down mion server on ${url}`);\n close();\n process.exit(0);\n };\n process.on('SIGINT', shutdownHandler);\n process.on('SIGTERM', shutdownHandler);\n\n resolve(server);\n });\n });\n}\n\n// ############# PRIVATE METHODS #############\n\n// uWS hands the method over lowercased\n// null prototype: a `constructor` method off the wire must not read Object.prototype\nconst LOWER_CASE_METHODS: Readonly<Record<string, HttpMethod>> = Object.assign(Object.create(null), {\n get: 'GET',\n head: 'HEAD',\n post: 'POST',\n put: 'PUT',\n delete: 'DELETE',\n connect: 'CONNECT',\n options: 'OPTIONS',\n trace: 'TRACE',\n patch: 'PATCH',\n query: 'QUERY',\n});\nfunction upperCaseMethod(method: string): HttpMethod {\n return LOWER_CASE_METHODS[method] ?? 'OTHER';\n}\n\n// uWS refreshes a socket's idle timeout only from inside a body-data callback, and runs it only when a reader is registered.\n// Without one, a request answered before its body finished arriving is closed mid-upload, so drop every chunk instead.\nfunction drainRequestBody(res: HttpResponse) {\n res.onData(() => {});\n}\n\n// exported for tests and for mounting on a hand-built uWS app; NOT a middleware handler (see startUwsServer).\n// uWS contract: `req` is valid only synchronously here, so the async dispatch snapshots it before the first await.\nexport function uwsRequestHandler(res: HttpResponse, req: HttpRequest): void {\n const path = req.getUrl();\n const query = req.getQuery();\n const urlQuery = query === '' ? undefined : query;\n const method = upperCaseMethod(req.getMethod());\n const reqHeaders = headersFromUwsRequest(req);\n const state: ReplyState = {replied: false, aborted: false, head: method === 'HEAD'};\n\n const respHeaders = getDefaultHeaders();\n\n // must be registered before any async work: after a disconnect uWS frees the response, and touching it would crash\n res.onAborted(() => {\n state.aborted = true;\n });\n\n // route resolved BEFORE the body: the native read stops at the chain's limit, and a late context stays GC-cheap.\n // The raw request object is built once: the one a pathTransform reads and the one the handlers see.\n const rawRequest = {path, urlQuery, headers: reqHeaders};\n let chain: MethodsExecutionChain;\n try {\n chain = resolveExecutionChain(path, urlQuery, rawRequest);\n } catch (err) {\n drainRequestBody(res);\n state.replied = true;\n fatalFail(res, state, respHeaders, toRpcError(err));\n return;\n }\n\n const dispatchBody = (rawBody: string) => {\n const reqBodyType: SerializerCode = SerializerModes.stringifyJson;\n\n const context = createContextFromChain(chain, path, urlQuery, method, reqHeaders, respHeaders, rawBody, reqBodyType);\n let dispatched: MionResponse | Promise<MionResponse>;\n try {\n dispatched = dispatchMaybeSync(context, rawRequest, res);\n } catch (err) {\n failWith(err);\n return;\n }\n // an all-sync chain answers inside uWS' own callback, with no promise in between\n if (dispatched instanceof Promise) answerWith(dispatched);\n else answer(dispatched);\n };\n\n const answer = (mionResponse: MionResponse) => {\n if (state.replied) return;\n state.replied = true;\n try {\n reply(res, state, mionResponse);\n } catch {\n // already marked replied: a failed write has nothing left to answer with\n }\n };\n\n const failWith = (err: unknown) => {\n if (state.replied) return;\n state.replied = true;\n fatalFail(res, state, respHeaders, toRpcError(err));\n };\n\n const answerWith = (dispatched: Promise<MionResponse>) => {\n dispatched.then(answer).catch(failWith);\n };\n\n // collectBody assembles the body natively (uWS' onDataV2 preallocates) and calls back ONCE, with null past maxSize,\n // which is exactly the maxBodySize contract. A not-found chain has no route to feed: its body is dropped as it arrives.\n if (!chain.readsBody) {\n drainRequestBody(res);\n dispatchBody('');\n return;\n }\n\n res.collectBody(chain.maxBodySize, (fullBody) => {\n if (state.replied) return;\n if (fullBody === null) {\n // the route resolved, so the refusal still runs the chain's alwaysRun members\n answerWith(\n dispatchPlatformError(chain, path, urlQuery, method, requestPayloadTooLarge(), reqHeaders, respHeaders, rawRequest, res)\n );\n return;\n }\n\n // A single-read body is a zero-copy window DETACHED when this callback returns (HttpResponseWrapper.h): decode now.\n // A multi-read body was assembled in C++ and OWNERSHIP-TRANSFERRED to JS, so a microtask may decode it.\n if (fullBody.byteLength <= UWS_MAX_SINGLE_READ) {\n // a body-less request (every GET) skips the view and the decode altogether\n dispatchBody(fullBody.byteLength === 0 ? '' : Buffer.from(fullBody).toString());\n return;\n }\n // past one read's size → always the ownership-transferred path, so the buffer survives the microtask.\n // The zero-length check is a tripwire for an upstream change: fail loudly instead of parsing a neutered buffer.\n queueMicrotask(() => {\n if (state.replied) return;\n if (fullBody.byteLength === 0) {\n state.replied = true;\n const error = new FatalError({\n publicMessage: 'Internal Server Error',\n type: 'unknown-error',\n errorData: {reason: 'uws detached a multi-read body buffer (upstream behavior change) — report to mion'},\n });\n fatalFail(res, state, respHeaders, error);\n return;\n }\n dispatchBody(releaseAfterDecode(fullBody));\n });\n });\n}\n\n/** Frees an ownership-transferred body buffer right after decoding it; the single-read window must never come through here.\n * `transfer(0)` and not `transfer()`: measured on node 26, only `transfer(0)` releases the backing store there and then.\n * Guarded because a future uWS could hand back a buffer that cannot be transferred, which must not fail the request. */\nfunction releaseAfterDecode(fullBody: ArrayBuffer): string {\n const text = Buffer.from(fullBody).toString();\n try {\n (fullBody as ReleasableBuffer).transfer?.(0);\n } catch {\n // not transferable: the collector gets it instead, exactly as before\n }\n return text;\n}\n\n/** `transfer` is ES2024 and this package compiles against an ES2023 lib; optional because a runtime without it must work. */\ntype ReleasableBuffer = ArrayBuffer & {transfer?: (newByteLength?: number) => ArrayBuffer};\n\ninterface ReplyState {\n replied: boolean;\n aborted: boolean;\n /** RFC 9110: a HEAD answer has no body; one sent anyway is read as the start of the next answer */\n readonly head: boolean;\n}\n\n// only for http errors and unhandled route errors\nfunction fatalFail(res: HttpResponse, state: ReplyState, respHeaders: MionHeaders, error: RpcError<string>) {\n const routeResponse = getRouterFatalErrorResponse(error, respHeaders);\n reply(res, state, routeResponse);\n}\n\n/** The line is a pure function of the code, so it is built once per code rather than per response. */\nconst statusLines = new Map<number, string>();\nfunction statusLine(statusCode: number): string {\n const cached = statusLines.get(statusCode);\n if (cached !== undefined) return cached;\n const statusText = STATUS_CODES[statusCode];\n const line = statusText ? `${statusCode} ${statusText}` : `${statusCode}`;\n statusLines.set(statusCode, line);\n return line;\n}\n\nfunction reply(res: HttpResponse, state: ReplyState, mionResp: MionResponse) {\n // uWS freed the response when the client left; touching it would crash\n if (state.aborted) return;\n const headers = mionResp.headers as RecordResponseHeaders;\n\n // serialized BEFORE the cork: uWS warns a cork buffer must not be held across event loop iterations\n const payload = JSON.stringify(mionResp.body);\n // uWS writes a `connection: close` header but keeps the socket open, so the close is asked for here\n const close = headers.get('connection')?.toLowerCase() === 'close';\n\n // cork makes one syscall; uWS headers are write-only and must all precede end().\n // content-length is skipped: uWS writes its own, and a duplicate corrupts the response.\n // the header store refused any CR, LF or NUL when it was set\n const write = (name: string, value: string) => res.writeHeader(name, value);\n res.cork(() => {\n res.writeStatus(statusLine(mionResp.statusCode));\n headers.forEach((name, value) => {\n if (name === 'content-length' || name === 'content-type') return;\n if (typeof value === 'string') write(name, value);\n // RFC 6265 forbids folding set-cookie; uWS writes a repeated name as its own line\n else if (name === 'set-cookie') for (const cookie of value) write(name, cookie);\n else write(name, value.join(', '));\n });\n // last, and the store's own skipped above: uWS appends a repeated name instead of replacing it\n res.writeHeader('content-type', mionResp.contentType);\n\n if (state.head) res.endWithoutBody(Buffer.byteLength(payload, 'utf8'), close);\n else res.end(payload, close);\n });\n releaseHeaders(headers);\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mionjs/platform-uws",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "mion HTTP Server on uWebSockets.js for maximum performance.",
|
|
6
6
|
"keywords": [
|
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
"exports": {
|
|
23
23
|
".": {
|
|
24
24
|
"types": "./.dist/esm/index.d.ts",
|
|
25
|
-
"require": "./.dist/cjs/index.cjs",
|
|
26
25
|
"default": "./.dist/esm/index.js"
|
|
27
26
|
}
|
|
28
27
|
},
|
|
@@ -46,12 +45,12 @@
|
|
|
46
45
|
},
|
|
47
46
|
"comment:engines": "Inherited constraint from @mionjs/bin-uws: uWebSockets.js ships prebuilt binaries only for these Node majors.",
|
|
48
47
|
"engines": {
|
|
49
|
-
"node": ">=
|
|
48
|
+
"node": ">=24 <25 || >=26 <27"
|
|
50
49
|
},
|
|
51
50
|
"dependencies": {
|
|
52
|
-
"@mionjs/core": "0.
|
|
53
|
-
"@mionjs/router": "0.
|
|
54
|
-
"@mionjs/bin-uws": "0.
|
|
51
|
+
"@mionjs/core": "0.14.0",
|
|
52
|
+
"@mionjs/router": "0.14.0",
|
|
53
|
+
"@mionjs/bin-uws": "0.14.0"
|
|
55
54
|
},
|
|
56
55
|
"scripts": {
|
|
57
56
|
"typecheck:test": "tsc -p tsconfig.json --noEmit",
|
|
@@ -59,7 +58,6 @@
|
|
|
59
58
|
"dev": "vite build --watch",
|
|
60
59
|
"dev:test": "vitest watch",
|
|
61
60
|
"lint": "eslint src",
|
|
62
|
-
"format": "prettier --write src/**/*.ts",
|
|
63
61
|
"build": "vite build",
|
|
64
62
|
"clean": "rimraf .dist .coverage .mion",
|
|
65
63
|
"fresh-start": "pnpm run clean && rimraf node_modules"
|
package/.dist/cjs/index.cjs
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_src_constants = require("./src/constants.cjs");
|
|
3
|
-
const require_src_uwsHttp = require("./src/uwsHttp.cjs");
|
|
4
|
-
exports.DEFAULT_UWS_HTTP_OPTIONS = require_src_constants.DEFAULT_UWS_HTTP_OPTIONS;
|
|
5
|
-
exports.resetUwsHttpOpts = require_src_uwsHttp.resetUwsHttpOpts;
|
|
6
|
-
exports.setUwsHttpOpts = require_src_uwsHttp.setUwsHttpOpts;
|
|
7
|
-
exports.startUwsServer = require_src_uwsHttp.startUwsServer;
|
|
8
|
-
exports.uwsRequestHandler = require_src_uwsHttp.uwsRequestHandler;
|
package/.dist/cjs/index.d.ts
DELETED
package/.dist/cjs/package.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"type":"commonjs"}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
//#region src/constants.ts
|
|
3
|
-
var DEFAULT_UWS_HTTP_OPTIONS = {
|
|
4
|
-
port: 80,
|
|
5
|
-
/** What a route takes when its own `maxBodySize` and its types say nothing: the shared 128 KB default */
|
|
6
|
-
maxBodySize: require("@mionjs/core").DEFAULT_MAX_BODY_SIZE
|
|
7
|
-
};
|
|
8
|
-
//#endregion
|
|
9
|
-
exports.DEFAULT_UWS_HTTP_OPTIONS = DEFAULT_UWS_HTTP_OPTIONS;
|
|
10
|
-
|
|
11
|
-
//# sourceMappingURL=constants.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"constants.cjs","names":[],"sources":["../../../src/constants.ts"],"sourcesContent":["/* ########\n * 2022 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {DEFAULT_MAX_BODY_SIZE} from '@mionjs/core';\nimport {UwsHttpOptions} from './types.ts';\n\nexport const DEFAULT_UWS_HTTP_OPTIONS: UwsHttpOptions = {\n port: 80,\n /** What a route takes when its own `maxBodySize` and its types say nothing: the shared 128 KB default */\n maxBodySize: DEFAULT_MAX_BODY_SIZE,\n};\n"],"mappings":";;AAUA,IAAa,2BAA2C;CACtD,MAAM;;CAEN,mCAAa,EAAA;AACf"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
let _mionjs_router = require("@mionjs/router");
|
|
3
|
-
//#region src/headers.ts
|
|
4
|
-
/** uWS headers are readable only synchronously inside the handler, so snapshot them; uWS already lower-cases the names. */
|
|
5
|
-
function headersFromUwsRequest(req) {
|
|
6
|
-
const record = {};
|
|
7
|
-
req.forEach((name, value) => {
|
|
8
|
-
const previous = record[name];
|
|
9
|
-
record[name] = typeof previous === "string" ? `${previous}, ${value}` : value;
|
|
10
|
-
});
|
|
11
|
-
return (0, _mionjs_router.headersFromRecord)(record, true);
|
|
12
|
-
}
|
|
13
|
-
//#endregion
|
|
14
|
-
exports.headersFromUwsRequest = headersFromUwsRequest;
|
|
15
|
-
|
|
16
|
-
//# sourceMappingURL=headers.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"headers.cjs","names":[],"sources":["../../../src/headers.ts"],"sourcesContent":["/* ########\n * 2022 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {MionHeaders, headersFromRecord} from '@mionjs/router';\nimport type {HttpRequest} from '@mionjs/bin-uws';\n\n/** uWS headers are readable only synchronously inside the handler, so snapshot them; uWS already lower-cases the names. */\nexport function headersFromUwsRequest(req: HttpRequest): MionHeaders {\n const record: Record<string, string> = {};\n req.forEach((name, value) => {\n // a string check, not undefined: a `constructor` header would otherwise find Object.prototype's function\n const previous = record[name];\n record[name] = typeof previous === 'string' ? `${previous}, ${value}` : value;\n });\n return headersFromRecord(record, true);\n}\n"],"mappings":";;;;AAWA,SAAgB,sBAAsB,KAA+B;CACnE,MAAM,SAAiC,CAAC;CACxC,IAAI,SAAS,MAAM,UAAU;EAE3B,MAAM,WAAW,OAAO;EACxB,OAAO,QAAQ,OAAO,aAAa,WAAW,GAAG,SAAS,IAAI,UAAU;CAC1E,CAAC;CACD,QAAA,GAAA,eAAA,mBAAyB,QAAQ,IAAI;AACvC"}
|
package/.dist/cjs/src/types.cjs
DELETED
|
File without changes
|
package/.dist/cjs/src/types.d.ts
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_src_constants = require("./constants.cjs");
|
|
3
|
-
const require_src_headers = require("./headers.cjs");
|
|
4
|
-
let _mionjs_router = require("@mionjs/router");
|
|
5
|
-
let http = require("http");
|
|
6
|
-
let _mionjs_bin_uws = require("@mionjs/bin-uws");
|
|
7
|
-
let _mionjs_core = require("@mionjs/core");
|
|
8
|
-
//#region src/uwsHttp.ts
|
|
9
|
-
var httpOptions = { ...require_src_constants.DEFAULT_UWS_HTTP_OPTIONS };
|
|
10
|
-
var UWS_MAX_SINGLE_READ = 524288;
|
|
11
|
-
function resetUwsHttpOpts() {
|
|
12
|
-
httpOptions = { ...require_src_constants.DEFAULT_UWS_HTTP_OPTIONS };
|
|
13
|
-
(0, _mionjs_router.resetRouter)();
|
|
14
|
-
}
|
|
15
|
-
function setUwsHttpOpts(options) {
|
|
16
|
-
httpOptions = {
|
|
17
|
-
...httpOptions,
|
|
18
|
-
...options
|
|
19
|
-
};
|
|
20
|
-
return httpOptions;
|
|
21
|
-
}
|
|
22
|
-
/** The platform config the router publishes: everything but the TLS file paths. */
|
|
23
|
-
function serializablePlatformConfig() {
|
|
24
|
-
const { ssl: _ssl, ...serializableConfig } = httpOptions;
|
|
25
|
-
return serializableConfig;
|
|
26
|
-
}
|
|
27
|
-
async function startUwsServer(options) {
|
|
28
|
-
const isTest = (0, _mionjs_core.getENV)("NODE_ENV") === "test";
|
|
29
|
-
if (options) setUwsHttpOpts(options);
|
|
30
|
-
if ((0, _mionjs_router.hostOwnsSocket)()) throw new Error("@mionjs/platform-uws does not support middleware mode: uWebSockets.js owns its own listen socket and cannot mount on a host node server. Use @mionjs/platform-node for middleware mode.");
|
|
31
|
-
const url = `${httpOptions.ssl ? "https" : "http"}://localhost${httpOptions.port !== 80 ? `:${httpOptions.port}` : ""}`;
|
|
32
|
-
if (!isTest) console.log(`mion uws server running on ${url}`, {
|
|
33
|
-
port: httpOptions.port,
|
|
34
|
-
httpOptions
|
|
35
|
-
});
|
|
36
|
-
const uws = (0, _mionjs_bin_uws.loadUws)();
|
|
37
|
-
const app = httpOptions.ssl ? uws.SSLApp(httpOptions.ssl) : uws.App();
|
|
38
|
-
app.any("/*", uwsRequestHandler);
|
|
39
|
-
return new Promise((resolve, reject) => {
|
|
40
|
-
app.listen(httpOptions.port, (listenSocket) => {
|
|
41
|
-
if (!listenSocket) {
|
|
42
|
-
reject(/* @__PURE__ */ new Error(`mion uws server failed to listen on port ${httpOptions.port} (port in use?)`));
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
(0, _mionjs_router.setPlatformConfig)(serializablePlatformConfig());
|
|
46
|
-
let closed = false;
|
|
47
|
-
const close = () => {
|
|
48
|
-
if (closed) return;
|
|
49
|
-
closed = true;
|
|
50
|
-
uws.us_listen_socket_close(listenSocket);
|
|
51
|
-
};
|
|
52
|
-
const server = {
|
|
53
|
-
app,
|
|
54
|
-
listenSocket,
|
|
55
|
-
close
|
|
56
|
-
};
|
|
57
|
-
const shutdownHandler = function() {
|
|
58
|
-
if (!isTest) console.log(`Shutting down mion server on ${url}`);
|
|
59
|
-
close();
|
|
60
|
-
process.exit(0);
|
|
61
|
-
};
|
|
62
|
-
process.on("SIGINT", shutdownHandler);
|
|
63
|
-
process.on("SIGTERM", shutdownHandler);
|
|
64
|
-
resolve(server);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
function drainRequestBody(res) {
|
|
69
|
-
res.onData(() => {});
|
|
70
|
-
}
|
|
71
|
-
function uwsRequestHandler(res, req) {
|
|
72
|
-
const state = {
|
|
73
|
-
replied: false,
|
|
74
|
-
aborted: false
|
|
75
|
-
};
|
|
76
|
-
const path = req.getUrl();
|
|
77
|
-
const query = req.getQuery();
|
|
78
|
-
const urlQuery = query === "" ? void 0 : query;
|
|
79
|
-
const reqHeaders = require_src_headers.headersFromUwsRequest(req);
|
|
80
|
-
const respHeaders = (0, _mionjs_router.getDefaultHeaders)();
|
|
81
|
-
res.onAborted(() => {
|
|
82
|
-
state.aborted = true;
|
|
83
|
-
});
|
|
84
|
-
const rawRequest = {
|
|
85
|
-
path,
|
|
86
|
-
urlQuery,
|
|
87
|
-
headers: reqHeaders
|
|
88
|
-
};
|
|
89
|
-
let chain;
|
|
90
|
-
try {
|
|
91
|
-
chain = (0, _mionjs_router.resolveExecutionChain)(path, urlQuery, rawRequest);
|
|
92
|
-
} catch (err) {
|
|
93
|
-
drainRequestBody(res);
|
|
94
|
-
state.replied = true;
|
|
95
|
-
fatalFail(res, state, respHeaders, (0, _mionjs_router.toRpcError)(err));
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
const dispatchBody = (rawBody, readQueryBody) => {
|
|
99
|
-
let reqRawBody = rawBody;
|
|
100
|
-
let reqBodyType = _mionjs_core.SerializerModes.stringifyJson;
|
|
101
|
-
try {
|
|
102
|
-
const queryBody = readQueryBody ? (0, _mionjs_router.decodeQueryBody)(urlQuery, reqRawBody || void 0) : void 0;
|
|
103
|
-
if (queryBody) {
|
|
104
|
-
reqRawBody = queryBody.rawBody;
|
|
105
|
-
reqBodyType = queryBody.bodyType;
|
|
106
|
-
}
|
|
107
|
-
} catch (err) {
|
|
108
|
-
state.replied = true;
|
|
109
|
-
fatalFail(res, state, respHeaders, err);
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
const context = (0, _mionjs_router.createContextFromChain)(chain, path, urlQuery, reqHeaders, respHeaders, reqRawBody, reqBodyType);
|
|
113
|
-
let dispatched;
|
|
114
|
-
try {
|
|
115
|
-
dispatched = (0, _mionjs_router.dispatchMaybeSync)(context, rawRequest, res);
|
|
116
|
-
} catch (err) {
|
|
117
|
-
failWith(err);
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
if (dispatched instanceof Promise) answerWith(dispatched);
|
|
121
|
-
else answer(dispatched);
|
|
122
|
-
};
|
|
123
|
-
const answer = (mionResponse) => {
|
|
124
|
-
if (state.replied) return;
|
|
125
|
-
state.replied = true;
|
|
126
|
-
try {
|
|
127
|
-
reply(res, state, mionResponse);
|
|
128
|
-
} catch {}
|
|
129
|
-
};
|
|
130
|
-
const failWith = (err) => {
|
|
131
|
-
if (state.replied) return;
|
|
132
|
-
state.replied = true;
|
|
133
|
-
fatalFail(res, state, respHeaders, (0, _mionjs_router.toRpcError)(err));
|
|
134
|
-
};
|
|
135
|
-
const answerWith = (dispatched) => {
|
|
136
|
-
dispatched.then(answer).catch(failWith);
|
|
137
|
-
};
|
|
138
|
-
if (!chain.readsBody) {
|
|
139
|
-
drainRequestBody(res);
|
|
140
|
-
dispatchBody("", false);
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
res.collectBody(chain.maxBodySize, (fullBody) => {
|
|
144
|
-
if (state.replied) return;
|
|
145
|
-
if (fullBody === null) {
|
|
146
|
-
answerWith((0, _mionjs_router.dispatchPlatformError)(chain, path, urlQuery, (0, _mionjs_router.requestPayloadTooLarge)(), reqHeaders, respHeaders, rawRequest, res));
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
if (fullBody.byteLength <= UWS_MAX_SINGLE_READ) {
|
|
150
|
-
dispatchBody(fullBody.byteLength === 0 ? "" : Buffer.from(fullBody).toString(), true);
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
queueMicrotask(() => {
|
|
154
|
-
if (state.replied) return;
|
|
155
|
-
if (fullBody.byteLength === 0) {
|
|
156
|
-
state.replied = true;
|
|
157
|
-
fatalFail(res, state, respHeaders, new _mionjs_core.FatalError({
|
|
158
|
-
publicMessage: "Internal Server Error",
|
|
159
|
-
type: "unknown-error",
|
|
160
|
-
errorData: { reason: "uws detached a multi-read body buffer (upstream behavior change) — report to mion" }
|
|
161
|
-
}));
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
dispatchBody(releaseAfterDecode(fullBody), true);
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
/** Frees an ownership-transferred body buffer right after decoding it; the single-read window must never come through here.
|
|
169
|
-
* `transfer(0)` and not `transfer()`: measured on node 26, only `transfer(0)` releases the backing store there and then.
|
|
170
|
-
* Guarded because a future uWS could hand back a buffer that cannot be transferred, which must not fail the request. */
|
|
171
|
-
function releaseAfterDecode(fullBody) {
|
|
172
|
-
const text = Buffer.from(fullBody).toString();
|
|
173
|
-
try {
|
|
174
|
-
fullBody.transfer?.(0);
|
|
175
|
-
} catch {}
|
|
176
|
-
return text;
|
|
177
|
-
}
|
|
178
|
-
function fatalFail(res, state, respHeaders, error) {
|
|
179
|
-
reply(res, state, (0, _mionjs_router.getRouterFatalErrorResponse)(error, respHeaders));
|
|
180
|
-
}
|
|
181
|
-
function isHeaderSafe(text) {
|
|
182
|
-
for (let i = 0; i < text.length; i++) {
|
|
183
|
-
const code = text.charCodeAt(i);
|
|
184
|
-
if (code === 13 || code === 10 || code === 0) return false;
|
|
185
|
-
}
|
|
186
|
-
return true;
|
|
187
|
-
}
|
|
188
|
-
/** The line is a pure function of the code, so it is built once per code rather than per response. */
|
|
189
|
-
var statusLines = /* @__PURE__ */ new Map();
|
|
190
|
-
function statusLine(statusCode) {
|
|
191
|
-
const cached = statusLines.get(statusCode);
|
|
192
|
-
if (cached !== void 0) return cached;
|
|
193
|
-
const statusText = http.STATUS_CODES[statusCode];
|
|
194
|
-
const line = statusText ? `${statusCode} ${statusText}` : `${statusCode}`;
|
|
195
|
-
statusLines.set(statusCode, line);
|
|
196
|
-
return line;
|
|
197
|
-
}
|
|
198
|
-
function reply(res, state, mionResp) {
|
|
199
|
-
if (state.aborted) return;
|
|
200
|
-
const headers = mionResp.headers;
|
|
201
|
-
const payload = JSON.stringify(mionResp.body);
|
|
202
|
-
const write = (name, value) => {
|
|
203
|
-
if (isHeaderSafe(value)) res.writeHeader(name, value);
|
|
204
|
-
};
|
|
205
|
-
res.cork(() => {
|
|
206
|
-
res.writeStatus(statusLine(mionResp.statusCode));
|
|
207
|
-
headers.forEach((name, value) => {
|
|
208
|
-
if (name === "content-length" || name === "content-type" || !isHeaderSafe(name)) return;
|
|
209
|
-
if (typeof value === "string") write(name, value);
|
|
210
|
-
else if (name === "set-cookie") for (const cookie of value) write(name, cookie);
|
|
211
|
-
else write(name, value.join(", "));
|
|
212
|
-
});
|
|
213
|
-
res.writeHeader("content-type", mionResp.contentType);
|
|
214
|
-
res.end(payload);
|
|
215
|
-
});
|
|
216
|
-
(0, _mionjs_router.releaseHeaders)(headers);
|
|
217
|
-
}
|
|
218
|
-
//#endregion
|
|
219
|
-
exports.resetUwsHttpOpts = resetUwsHttpOpts;
|
|
220
|
-
exports.setUwsHttpOpts = setUwsHttpOpts;
|
|
221
|
-
exports.startUwsServer = startUwsServer;
|
|
222
|
-
exports.uwsRequestHandler = uwsRequestHandler;
|
|
223
|
-
|
|
224
|
-
//# sourceMappingURL=uwsHttp.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"uwsHttp.cjs","names":[],"sources":["../../../src/uwsHttp.ts"],"sourcesContent":["/* ########\n * 2022 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {\n dispatchMaybeSync,\n dispatchPlatformError,\n resolveExecutionChain,\n createContextFromChain,\n getRouterFatalErrorResponse,\n toRpcError,\n resetRouter,\n decodeQueryBody,\n setPlatformConfig,\n getDefaultHeaders,\n releaseHeaders,\n requestPayloadTooLarge,\n hostOwnsSocket,\n} from '@mionjs/router';\nimport {STATUS_CODES} from 'http';\nimport {loadUws} from '@mionjs/bin-uws';\nimport type {HttpRequest, HttpResponse, TemplatedApp, us_listen_socket} from '@mionjs/bin-uws';\nimport {DEFAULT_UWS_HTTP_OPTIONS} from './constants.ts';\nimport type {UwsHttpOptions} from './types.ts';\nimport type {MethodsExecutionChain, MionHeaders, MionResponse, RecordResponseHeaders} from '@mionjs/router';\nimport {getENV, SerializerModes} from '@mionjs/core';\nimport type {SerializerCode} from '@mionjs/core';\nimport {RpcError, FatalError} from '@mionjs/core';\nimport {headersFromUwsRequest} from './headers.ts';\n\n// ############# PRIVATE STATE #############\n\nlet httpOptions: Readonly<UwsHttpOptions> = {...DEFAULT_UWS_HTTP_OPTIONS};\n\n// The most bytes one uWS socket read can deliver: uSockets' LIBUS_RECV_BUFFER_LENGTH (512 KiB) at the tag pinned in\n// packages/bin-uws. A LARGER body cannot be single-read, which is what makes the zero-copy branch below safe (a\n// detachment tripwire guards it at runtime). Re-verify against uSockets on a tag bump.\nconst UWS_MAX_SINGLE_READ = 524288;\n\n/** The running server: the uWS app plus the socket handle listen() produced. */\nexport interface UwsServer {\n app: TemplatedApp;\n listenSocket: us_listen_socket;\n close(): void;\n}\n\n// ############# PUBLIC METHODS #############\n\nexport function resetUwsHttpOpts() {\n httpOptions = {...DEFAULT_UWS_HTTP_OPTIONS};\n resetRouter();\n}\n\nexport function setUwsHttpOpts(options?: Partial<UwsHttpOptions>) {\n httpOptions = {\n ...httpOptions,\n ...options,\n };\n\n return httpOptions;\n}\n\n/** The platform config the router publishes: everything but the TLS file paths. */\nfunction serializablePlatformConfig(): Record<string, unknown> {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const {ssl: _ssl, ...serializableConfig} = httpOptions;\n return serializableConfig;\n}\n\nexport async function startUwsServer(options?: Partial<UwsHttpOptions>): Promise<UwsServer> {\n const isTest = getENV('NODE_ENV') === 'test';\n\n if (options) setUwsHttpOpts(options);\n // uWS is its own C++ event loop and owns its listen socket, so it cannot mount on a host node server.\n if (hostOwnsSocket()) {\n throw new Error(\n '@mionjs/platform-uws does not support middleware mode: uWebSockets.js owns its own listen ' +\n 'socket and cannot mount on a host node server. Use @mionjs/platform-node for middleware mode.'\n );\n }\n const protocol = httpOptions.ssl ? 'https' : 'http';\n const port = httpOptions.port !== 80 ? `:${httpOptions.port}` : '';\n const url = `${protocol}://localhost${port}`;\n if (!isTest)\n console.log(`mion uws server running on ${url}`, {\n port: httpOptions.port,\n httpOptions,\n });\n\n const uws = loadUws();\n const app = httpOptions.ssl ? uws.SSLApp(httpOptions.ssl) : uws.App();\n app.any('/*', uwsRequestHandler);\n\n return new Promise<UwsServer>((resolve, reject) => {\n app.listen(httpOptions.port, (listenSocket) => {\n if (!listenSocket) {\n reject(new Error(`mion uws server failed to listen on port ${httpOptions.port} (port in use?)`));\n return;\n }\n\n setPlatformConfig(serializablePlatformConfig());\n\n let closed = false;\n const close = () => {\n if (closed) return;\n closed = true;\n uws.us_listen_socket_close(listenSocket);\n };\n const server: UwsServer = {app, listenSocket, close};\n\n const shutdownHandler = function () {\n if (!isTest) console.log(`Shutting down mion server on ${url}`);\n close();\n process.exit(0);\n };\n process.on('SIGINT', shutdownHandler);\n process.on('SIGTERM', shutdownHandler);\n\n resolve(server);\n });\n });\n}\n\n// ############# PRIVATE METHODS #############\n\n// uWS refreshes a socket's idle timeout only from inside a body-data callback, and runs it only when a reader is registered.\n// Without one, a request answered before its body finished arriving is closed mid-upload, so drop every chunk instead.\nfunction drainRequestBody(res: HttpResponse) {\n res.onData(() => {});\n}\n\n// exported for tests and for mounting on a hand-built uWS app; NOT a middleware handler (see startUwsServer).\n// uWS contract: `req` is valid only synchronously here, so the async dispatch snapshots it before the first await.\nexport function uwsRequestHandler(res: HttpResponse, req: HttpRequest): void {\n const state = {replied: false, aborted: false};\n // Everything read from `req` happens HERE, synchronously.\n const path = req.getUrl();\n const query = req.getQuery();\n const urlQuery = query === '' ? undefined : query;\n const reqHeaders = headersFromUwsRequest(req);\n\n const respHeaders = getDefaultHeaders();\n\n // must be registered before any async work: after a disconnect uWS frees the response, and touching it would crash\n res.onAborted(() => {\n state.aborted = true;\n });\n\n // route resolved BEFORE the body, synchronously: the native read stops at the chain's limit, and a late context stays GC-cheap.\n // The raw request object is built once: the one a pathTransform reads and the one the handlers see.\n const rawRequest = {path, urlQuery, headers: reqHeaders};\n let chain: MethodsExecutionChain;\n try {\n chain = resolveExecutionChain(path, urlQuery, rawRequest);\n } catch (err) {\n drainRequestBody(res);\n state.replied = true;\n fatalFail(res, state, respHeaders, toRpcError(err));\n return;\n }\n\n const dispatchBody = (rawBody: string, readQueryBody: boolean) => {\n let reqRawBody: any = rawBody;\n let reqBodyType: SerializerCode = SerializerModes.stringifyJson;\n // a throw here runs inside uWS' native callback (or a microtask): it must become a response\n try {\n const queryBody = readQueryBody ? decodeQueryBody(urlQuery, reqRawBody || undefined) : undefined;\n if (queryBody) {\n reqRawBody = queryBody.rawBody;\n reqBodyType = queryBody.bodyType;\n }\n } catch (err) {\n state.replied = true;\n fatalFail(res, state, respHeaders, err as RpcError<string>);\n return;\n }\n\n const context = createContextFromChain(chain, path, urlQuery, reqHeaders, respHeaders, reqRawBody, reqBodyType);\n let dispatched: MionResponse | Promise<MionResponse>;\n try {\n dispatched = dispatchMaybeSync(context, rawRequest, res);\n } catch (err) {\n failWith(err);\n return;\n }\n // an all-sync chain answers inside uWS' own callback, with no promise in between\n if (dispatched instanceof Promise) answerWith(dispatched);\n else answer(dispatched);\n };\n\n const answer = (mionResponse: MionResponse) => {\n if (state.replied) return;\n state.replied = true;\n try {\n reply(res, state, mionResponse);\n } catch {\n // already marked replied: a failed write has nothing left to answer with\n }\n };\n\n const failWith = (err: unknown) => {\n if (state.replied) return;\n state.replied = true;\n fatalFail(res, state, respHeaders, toRpcError(err));\n };\n\n const answerWith = (dispatched: Promise<MionResponse>) => {\n dispatched.then(answer).catch(failWith);\n };\n\n // collectBody assembles the body natively (uWS' onDataV2 preallocates) and calls back ONCE, with null past maxSize,\n // which is exactly the maxBodySize contract. A not-found chain has no route to feed: its body is dropped as it arrives.\n if (!chain.readsBody) {\n drainRequestBody(res);\n dispatchBody('', false);\n return;\n }\n\n res.collectBody(chain.maxBodySize, (fullBody) => {\n if (state.replied) return;\n if (fullBody === null) {\n // the route resolved, so the refusal still runs the chain's alwaysRun members\n answerWith(\n dispatchPlatformError(chain, path, urlQuery, requestPayloadTooLarge(), reqHeaders, respHeaders, rawRequest, res)\n );\n return;\n }\n\n // collectBody has two paths (verified in the pinned tag's HttpResponseWrapper.h and by test): a single-read body is a\n // zero-copy window into uWS' receive buffer, DETACHED when this callback returns, so it must be decoded synchronously\n // here; a multi-read body was assembled in C++ and OWNERSHIP-TRANSFERRED to JS, so it can be decoded from a microtask.\n if (fullBody.byteLength <= UWS_MAX_SINGLE_READ) {\n // a body-less request (every GET) skips the view and the decode altogether\n dispatchBody(fullBody.byteLength === 0 ? '' : Buffer.from(fullBody).toString(), true);\n return;\n }\n // bigger than one read can deliver → guaranteed the ownership-transferred path, so the buffer survives the microtask.\n // The zero-length check is a tripwire for an upstream change: fail loudly instead of parsing a neutered buffer.\n queueMicrotask(() => {\n if (state.replied) return;\n if (fullBody.byteLength === 0) {\n state.replied = true;\n const error = new FatalError({\n publicMessage: 'Internal Server Error',\n type: 'unknown-error',\n errorData: {reason: 'uws detached a multi-read body buffer (upstream behavior change) — report to mion'},\n });\n fatalFail(res, state, respHeaders, error);\n return;\n }\n dispatchBody(releaseAfterDecode(fullBody), true);\n });\n });\n}\n\n/** Frees an ownership-transferred body buffer right after decoding it; the single-read window must never come through here.\n * `transfer(0)` and not `transfer()`: measured on node 26, only `transfer(0)` releases the backing store there and then.\n * Guarded because a future uWS could hand back a buffer that cannot be transferred, which must not fail the request. */\nfunction releaseAfterDecode(fullBody: ArrayBuffer): string {\n const text = Buffer.from(fullBody).toString();\n try {\n (fullBody as ReleasableBuffer).transfer?.(0);\n } catch {\n // not transferable: the collector gets it instead, exactly as before\n }\n return text;\n}\n\n/** `transfer` is ES2024 and this package compiles against an ES2023 lib; optional because a runtime without it must work. */\ntype ReleasableBuffer = ArrayBuffer & {transfer?: (newByteLength?: number) => ArrayBuffer};\n\n// only called when there is an http error or weird unhandled route errors\nfunction fatalFail(res: HttpResponse, state: {aborted: boolean}, respHeaders: MionHeaders, error: RpcError<string>) {\n const routeResponse = getRouterFatalErrorResponse(error, respHeaders);\n reply(res, state, routeResponse);\n}\n\nfunction isHeaderSafe(text: string): boolean {\n // one pass, no allocation: a CR, LF or NUL anywhere makes the header unsafe to write\n for (let i = 0; i < text.length; i++) {\n const code = text.charCodeAt(i);\n if (code === 13 || code === 10 || code === 0) return false;\n }\n return true;\n}\n\n/** The line is a pure function of the code, so it is built once per code rather than per response. */\nconst statusLines = new Map<number, string>();\nfunction statusLine(statusCode: number): string {\n const cached = statusLines.get(statusCode);\n if (cached !== undefined) return cached;\n const statusText = STATUS_CODES[statusCode];\n const line = statusText ? `${statusCode} ${statusText}` : `${statusCode}`;\n statusLines.set(statusCode, line);\n return line;\n}\n\nfunction reply(res: HttpResponse, state: {aborted: boolean}, mionResp: MionResponse) {\n // uWS freed the response when the client left; touching it would crash\n if (state.aborted) return;\n const headers = mionResp.headers as RecordResponseHeaders;\n\n // serialized BEFORE the cork: uWS warns a cork buffer must not be held across event loop iterations\n const payload = JSON.stringify(mionResp.body);\n\n // cork makes one syscall; uWS headers are write-only and must all precede end().\n // content-length is skipped: uWS writes its own, and a duplicate corrupts the response.\n // uWS writes headers unchecked, so an echoed CR or LF would be header injection: dropped\n const write = (name: string, value: string) => {\n if (isHeaderSafe(value)) res.writeHeader(name, value);\n };\n res.cork(() => {\n res.writeStatus(statusLine(mionResp.statusCode));\n headers.forEach((name, value) => {\n if (name === 'content-length' || name === 'content-type' || !isHeaderSafe(name)) return;\n if (typeof value === 'string') write(name, value);\n // RFC 6265 forbids folding set-cookie; uWS writes a repeated name as its own line\n else if (name === 'set-cookie') for (const cookie of value) write(name, cookie);\n else write(name, value.join(', '));\n });\n // last, and the store's own skipped above: uWS appends a repeated name instead of replacing it\n res.writeHeader('content-type', mionResp.contentType);\n\n res.end(payload);\n });\n releaseHeaders(headers);\n}\n"],"mappings":"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"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { HttpRequest, HttpResponse, TemplatedApp, us_listen_socket } from '@mionjs/bin-uws';
|
|
2
|
-
import { UwsHttpOptions } from './types.ts';
|
|
3
|
-
export interface UwsServer {
|
|
4
|
-
app: TemplatedApp;
|
|
5
|
-
listenSocket: us_listen_socket;
|
|
6
|
-
close(): void;
|
|
7
|
-
}
|
|
8
|
-
export declare function resetUwsHttpOpts(): void;
|
|
9
|
-
export declare function setUwsHttpOpts(options?: Partial<UwsHttpOptions>): Readonly<UwsHttpOptions>;
|
|
10
|
-
export declare function startUwsServer(options?: Partial<UwsHttpOptions>): Promise<UwsServer>;
|
|
11
|
-
export declare function uwsRequestHandler(res: HttpResponse, req: HttpRequest): void;
|
|
12
|
-
//# sourceMappingURL=uwsHttp.d.ts.map
|