webhoster 0.1.0 → 0.3.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/.eslintrc.json +74 -58
- package/.github/copilot-instructions.md +100 -0
- package/.github/workflows/test-matrix.yml +37 -0
- package/.test/benchmark.js +28 -0
- package/.test/constants.js +4 -0
- package/{test → .test}/http2server.js +1 -1
- package/{test → .test}/httpserver.js +1 -1
- package/{test → .test}/index.js +178 -192
- package/.test/multipromise.js +32 -0
- package/{test → .test}/tls.js +3 -3
- package/{test → .test}/urlencoded.js +3 -0
- package/.vscode/launch.json +24 -3
- package/README.md +116 -90
- package/data/CookieObject.js +14 -14
- package/errata/socketio.js +6 -11
- package/examples/starter.js +11 -0
- package/helpers/HeadersParser.js +7 -8
- package/helpers/HttpListener.js +387 -42
- package/helpers/RequestHeaders.js +43 -36
- package/helpers/RequestReader.js +27 -26
- package/helpers/ResponseHeaders.js +47 -36
- package/jsconfig.json +1 -1
- package/lib/HttpHandler.js +447 -277
- package/lib/HttpRequest.js +383 -39
- package/lib/HttpResponse.js +316 -52
- package/lib/HttpTransaction.js +146 -0
- package/middleware/AutoHeadersMiddleware.js +73 -0
- package/middleware/CORSMiddleware.js +45 -47
- package/middleware/CaseInsensitiveHeadersMiddleware.js +5 -11
- package/middleware/ContentDecoderMiddleware.js +81 -35
- package/middleware/ContentEncoderMiddleware.js +179 -132
- package/middleware/ContentLengthMiddleware.js +66 -43
- package/middleware/ContentWriterMiddleware.js +5 -11
- package/middleware/HashMiddleware.js +68 -40
- package/middleware/HeadMethodMiddleware.js +24 -22
- package/middleware/MethodMiddleware.js +29 -36
- package/middleware/PathMiddleware.js +49 -66
- package/middleware/ReadFormData.js +99 -0
- package/middleware/SendHeadersMiddleware.js +0 -2
- package/middleware/SendJsonMiddleware.js +131 -0
- package/middleware/SendStringMiddleware.js +87 -0
- package/package.json +38 -29
- package/polyfill/FormData.js +164 -0
- package/rollup.config.js +0 -1
- package/scripts/test-all-sync.sh +6 -0
- package/scripts/test-all.sh +7 -0
- package/templates/starter.js +53 -0
- package/test/fixtures/stream.js +68 -0
- package/test/helpers/HttpListener/construct.js +18 -0
- package/test/helpers/HttpListener/customOptions.js +22 -0
- package/test/helpers/HttpListener/doubleCreate.js +40 -0
- package/test/helpers/HttpListener/events.js +77 -0
- package/test/helpers/HttpListener/http.js +31 -0
- package/test/helpers/HttpListener/http2.js +41 -0
- package/test/helpers/HttpListener/https.js +38 -0
- package/test/helpers/HttpListener/startAll.js +31 -0
- package/test/helpers/HttpListener/stopNotStarted.js +23 -0
- package/test/lib/HttpHandler/class.js +8 -0
- package/test/lib/HttpHandler/handleRequest.js +11 -0
- package/test/lib/HttpHandler/middleware.js +941 -0
- package/test/lib/HttpHandler/parse.js +41 -0
- package/test/lib/HttpRequest/class.js +8 -0
- package/test/lib/HttpRequest/downstream.js +171 -0
- package/test/lib/HttpRequest/properties.js +101 -0
- package/test/lib/HttpRequest/read.js +518 -0
- package/test/lib/HttpResponse/class.js +8 -0
- package/test/lib/HttpResponse/properties.js +59 -0
- package/test/lib/HttpResponse/send.js +275 -0
- package/test/lib/HttpTransaction/class.js +8 -0
- package/test/lib/HttpTransaction/ping.js +50 -0
- package/test/lib/HttpTransaction/push.js +89 -0
- package/test/middleware/SendJsonMiddleware.js +222 -0
- package/test/sanity.js +10 -0
- package/test/templates/starter.js +93 -0
- package/tsconfig.json +12 -0
- package/types/index.js +61 -34
- package/types/typings.d.ts +8 -9
- package/utils/AsyncObject.js +6 -3
- package/utils/CaseInsensitiveObject.js +2 -3
- package/utils/function.js +1 -7
- package/utils/headers.js +42 -0
- package/utils/qualityValues.js +1 -1
- package/utils/stream.js +4 -20
- package/index.cjs +0 -3200
- package/test/constants.js +0 -4
- /package/{test → .test}/cookietester.js +0 -0
package/.vscode/launch.json
CHANGED
|
@@ -4,11 +4,32 @@
|
|
|
4
4
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
5
5
|
"version": "0.2.0",
|
|
6
6
|
"configurations": [
|
|
7
|
+
{
|
|
8
|
+
"name": "Debug AVA",
|
|
9
|
+
"request": "launch",
|
|
10
|
+
"runtimeArgs": [
|
|
11
|
+
"run-script",
|
|
12
|
+
"debug-test"
|
|
13
|
+
],
|
|
14
|
+
"runtimeExecutable": "npm",
|
|
15
|
+
"skipFiles": [
|
|
16
|
+
"<node_internals>/**"
|
|
17
|
+
],
|
|
18
|
+
"type": "pwa-node"
|
|
19
|
+
},
|
|
7
20
|
{
|
|
8
21
|
"type": "node",
|
|
9
22
|
"request": "launch",
|
|
10
|
-
"name": "
|
|
11
|
-
"
|
|
12
|
-
|
|
23
|
+
"name": "Debug AVA test file",
|
|
24
|
+
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/ava",
|
|
25
|
+
"runtimeArgs": [
|
|
26
|
+
"--serial",
|
|
27
|
+
"${file}"
|
|
28
|
+
],
|
|
29
|
+
"outputCapture": "std",
|
|
30
|
+
"skipFiles": [
|
|
31
|
+
"<node_internals>/**/*.js"
|
|
32
|
+
]
|
|
33
|
+
},
|
|
13
34
|
]
|
|
14
35
|
}
|
package/README.md
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
     
|
|
2
2
|
|
|
3
|
+
<!-- Per-node test status badges (matrix job names must match `test-matrix.yml` job names) -->
|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
<!-- Note: these badges reflect the job named "Test on Node <version>" in `.github/workflows/test-matrix.yml`. -->
|
|
11
|
+
|
|
3
12
|
# webhoster
|
|
4
13
|
|
|
5
|
-
An opt-in, stream-based approach to Web Hosting with NodeJS.
|
|
14
|
+
An opt-in, stream-based, tree-processing approach to Web Hosting with NodeJS.
|
|
6
15
|
|
|
7
16
|
* Supports HTTP
|
|
8
17
|
* Supports HTTPS
|
|
@@ -10,7 +19,7 @@ An opt-in, stream-based approach to Web Hosting with NodeJS.
|
|
|
10
19
|
|
|
11
20
|
#### Nothing is true; everything is permitted
|
|
12
21
|
|
|
13
|
-
By default, the framework does nothing. It parses no headers. It writes no headers. It never writes to or reads from any stream.
|
|
22
|
+
By default, the framework does nothing. It parses no headers. It writes no headers. It never writes to or reads from any stream. All processing is handled by middleware. All middleware is built to provide maximum throughput and do as little as possible.
|
|
14
23
|
|
|
15
24
|
## install
|
|
16
25
|
|
|
@@ -28,10 +37,9 @@ For now, take a look at [/test/index.js](/test/index.js)
|
|
|
28
37
|
*Class that handles the logic for handling requests and responses*
|
|
29
38
|
|
|
30
39
|
* `.defaultInstance` - (`HttpHandler`) - Returns a instance of `HttpHandler` that can be accessed staticly.
|
|
31
|
-
* `.
|
|
32
|
-
* `.middleware` - (`Set<Middleware>`) - A collection of middleware chains to iterate though when handling a request. It is recommended to create isolated chains (eg: `/images/`; `/api/`; `/views/`; etc.). The use of `Set` type is to avoid mistakenly inserting the same middleware chain twice.
|
|
40
|
+
* `.middleware` - (`Middleware[]`) - An array of middleware operations to iterate through when handling a request. It is recommended to create isolated branches (eg: `/images/`; `/api/`; `/views/`; etc.).
|
|
33
41
|
* `.errorHandlers` - (`MiddlewareErrorHandler[]`) - An array of `MiddlewareErrorHandler` that will handle errors and respond appropriately (eg: `res.status = 500`)
|
|
34
|
-
* `.handleRequest` - (`function(MiddlewareFunctionParams):Promise<HttpResponse>`) - handles logic for calling
|
|
42
|
+
* `.handleRequest` - (`function(MiddlewareFunctionParams):Promise<HttpResponse>`) - handles logic for calling middleware and error handlers. Unlikely to be used directly.
|
|
35
43
|
* `.handleHttp1Request` - (`function(IncomingMessage, ServerResponse):Promise<HttpResponse>`) - constructs a new `HttpRequest` and `HttpResponse` based on the HTTP1 parameters and passes it to `handleRequest`
|
|
36
44
|
* `.handleHttp2Stream` - (`function(ServerHttp2Stream, IncomingHttpHeaders, HttpResponseOptions):Promise<HttpResponse>`) - constructs a new `HttpRequest` and `HttpResponse` based on the HTTP2 parameters and passes it to `handleRequest`
|
|
37
45
|
|
|
@@ -39,21 +47,24 @@ For now, take a look at [/test/index.js](/test/index.js)
|
|
|
39
47
|
|
|
40
48
|
````js
|
|
41
49
|
const handler = HttpHandler.defaultInstance;
|
|
42
|
-
handler.
|
|
43
|
-
new
|
|
44
|
-
new
|
|
45
|
-
new
|
|
46
|
-
new ContentEncoderMiddleware(),
|
|
47
|
-
new
|
|
48
|
-
new
|
|
49
|
-
new
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
handler.middleware.
|
|
53
|
-
|
|
50
|
+
handler.middleware.push(
|
|
51
|
+
new ContentDecoderMiddleware(), // Automatically decodes content
|
|
52
|
+
new SendStringMiddleware(), // Auto convert strings to Buffer
|
|
53
|
+
new SendJsonMiddleware(), // Auto converts objects to JSON
|
|
54
|
+
new ContentEncoderMiddleware(), // Compress anything after
|
|
55
|
+
new HashMiddleware(), // Hash anything after
|
|
56
|
+
new ContentLengthMiddleware(), // Calculate length of anything after
|
|
57
|
+
new AutoHeadersMiddleware(), // Send headers automatically
|
|
58
|
+
new HeadMethodMiddleware(), // Discard body content
|
|
59
|
+
);
|
|
60
|
+
handler.middleware.push(
|
|
61
|
+
imagesMiddleware,
|
|
62
|
+
return404Middleware,
|
|
63
|
+
);
|
|
64
|
+
handler.errorHandlers.push(
|
|
54
65
|
errorLoggerMiddleware,
|
|
55
|
-
return500Middleware
|
|
56
|
-
|
|
66
|
+
return500Middleware,
|
|
67
|
+
);
|
|
57
68
|
http1Server.addListener('request', handler.handleHttp1Request);
|
|
58
69
|
http2Server.addListener('stream', handler.handleHttp2Stream);
|
|
59
70
|
````
|
|
@@ -61,58 +72,66 @@ For now, take a look at [/test/index.js](/test/index.js)
|
|
|
61
72
|
### [HttpRequest.js](/lib/HttpRequest.js)
|
|
62
73
|
*Class that provides the bare-minimum to bridge different protocols for client requests*
|
|
63
74
|
|
|
64
|
-
* `.
|
|
75
|
+
* `.read()` - `Promise<any>` - Returns content as handled by request's content handlers. Returns `.raw()` if no compatible handler found.
|
|
76
|
+
* `.stream` - (`Readable`) - Allows for direct interaction with tail-end of the request stream pipeline. With no middleware, it emits `Buffer` chunks.
|
|
77
|
+
* `.body` - (`ReadableStream`) - Returns request's body as `ReadableStream` (if supported).
|
|
78
|
+
* `.bodyUsed` - (`boolean`) - Returns whether request's body has been read from.
|
|
79
|
+
* `.arrayBuffer()` - (`Promise<ArrayBuffer>`) - Returns a promise fulfilled with request's body as `ArrayBuffer`.
|
|
80
|
+
* `.blob()` - (`Promise<Blob>`) - Returns a promise fulfilled with request's body as `Blob`.
|
|
81
|
+
* `.formData()` - (`Promise<FormData>`) - Returns a promise fulfilled with request's body as `FormData`. Not implemented by default.
|
|
82
|
+
* `.json()` - (`Promise<any>`) - Returns a promise fulfilled with request's body parsed as `JSON`.
|
|
83
|
+
* `.text()` - (`Promise<string>`) - Returns a promise fulfilled with request's body as `string`.
|
|
65
84
|
* `.headers` - (`IncomingHttpHeaders`) - The response headers exactly as presented to the NodeJS Server with no modifications.
|
|
66
|
-
* `.locals` - (`Object<string,any>`) - Object that gets passed in every step of the middleware
|
|
67
|
-
* `.
|
|
85
|
+
* `.locals` - (`Object<string,any>`) - Object that gets passed in every step of the middleware tree. Application-level variables *should* be presented here.
|
|
86
|
+
* `.addDownstream()` - (`function(stream:Readable):Readable`) - adds a downstream to the current pipeline. Used by preprocessor middleware for the purpose of transforming data (eg: JSON-parsing) before reaching logic middleware.
|
|
68
87
|
|
|
69
88
|
### Example
|
|
70
89
|
|
|
71
90
|
````js
|
|
72
|
-
async function onPostComment({
|
|
73
|
-
const content =
|
|
91
|
+
async function onPostComment({ request }) {
|
|
92
|
+
const content = await request.read();
|
|
74
93
|
let comment;
|
|
75
94
|
try {
|
|
76
95
|
comment = new UserComment(content);
|
|
77
96
|
} catch {
|
|
78
|
-
|
|
79
|
-
return 'end';
|
|
97
|
+
return 400;
|
|
80
98
|
}
|
|
81
99
|
try {
|
|
82
100
|
await insertComment(comment);
|
|
83
101
|
} catch {
|
|
84
|
-
|
|
85
|
-
return 'end';
|
|
102
|
+
return 500;
|
|
86
103
|
}
|
|
87
|
-
|
|
88
|
-
res.stream.end({status: 'OK'});
|
|
89
|
-
return 'end';
|
|
104
|
+
return { status: 'OK' };
|
|
90
105
|
}
|
|
91
106
|
````
|
|
92
107
|
|
|
93
108
|
### [HttpResponse.js](/lib/HttpResponse.js)
|
|
94
109
|
*Class that provides the bare-minimum to bridge different protocols for client responses*
|
|
95
110
|
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
111
|
+
* `end(content:any) => HttpHandler.END` - Content to be sent to the client based on the Content Handler configured. The function will call `.stream.end` and return `HttpHandler.END`.
|
|
112
|
+
* `headers` - (`OutgoingHttpHeaders`) - The response headers exactly as presented to the NodeJS Server with no modifications.
|
|
113
|
+
* `content` - (`any`) - Content that will be sent to client.
|
|
114
|
+
* `status` - (`number`) - The response status code
|
|
115
|
+
* `setStatus(statusCode:number) => this` - Sets `.status` while returning `this` (`HttpResponse`). Will throw an `Error` if headers have already been sent.
|
|
116
|
+
* `code(statusCode:number) => this` - Sets `.status` while returning `this` (`HttpResponse).
|
|
117
|
+
* `async send(content:any)` - Similar to `end`, but asynchronous. Return when the client stream ends, confirming the data was sent, or throws an error on failure. Useful for ensuring client received the data.
|
|
118
|
+
* `stream` - (`Writable`) - Used for interacting with the stream. With no custom middleware, it accepts a `Buffer` or `string`.
|
|
119
|
+
* `pipeFrom(source:any)` - Creates a pipeline starting with source. May insert `.pipeProcessors` into pipeline. Returns `HttpHandler.END`;
|
|
120
|
+
* `pipelineFrom(source:any) => Promise<HttpHandler.END>` - Similar to `pipeFrom`, but asynchronous. Return when the client stream ends, confirming the data was sent, or throws an error on failure. Useful for ensuring client received the data.
|
|
121
|
+
* `.finalizers` - Array of body processors that may read and transform `.body`, or simply analyze on `.body`.
|
|
101
122
|
* `.canPushPath` - (`boolean`) - `true` on HTTP/2 streams that support push
|
|
102
123
|
* `.pushPath` - (`function(path:string):Promise`) - Push a new HTTP/2 stream simulating a request for `path`
|
|
103
124
|
|
|
104
125
|
### Example
|
|
105
126
|
|
|
106
127
|
````js
|
|
107
|
-
async function onGetIndexPage(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
res.pushPath('/script.js');
|
|
112
|
-
res.pushPath('/styles.css');
|
|
128
|
+
async function onGetIndexPage(transaction) {
|
|
129
|
+
if (transaction.canPushPath) {
|
|
130
|
+
transaction.pushPath('/script.js').catch(() => { /* Ignore push failure */ });
|
|
131
|
+
transaction.pushPath('/styles.css').catch(() => { /* Ignore push failure */ });;
|
|
113
132
|
}
|
|
114
|
-
|
|
115
|
-
return
|
|
133
|
+
transaction.response.header['content-type'] = 'text/html';
|
|
134
|
+
return await getIndexPage();
|
|
116
135
|
}
|
|
117
136
|
````
|
|
118
137
|
|
|
@@ -120,34 +139,38 @@ async function onGetIndexPage({res}) {
|
|
|
120
139
|
|
|
121
140
|
#### MiddlewareFunction
|
|
122
141
|
|
|
123
|
-
Middleware logic flows in a tree structure, allowing for `
|
|
142
|
+
Middleware logic flows in a tree structure, allowing for `continue`, `break`, or `end`.
|
|
124
143
|
|
|
125
|
-
A `MiddlewareFunction` is a function that accepts a `MiddlewareFunctionParams` object structured as `{ res: HttpRequest, res: HttpResponse }`. The function
|
|
144
|
+
A `MiddlewareFunction` is a function that accepts a `MiddlewareFunctionParams` object structured as `{ res: HttpRequest, res: HttpResponse }`. The function can return a instruction with the step in the tree-based logic, status code, or content body to be handled. It maybe return any of these instructions with any of the values as a literal, a `Promise`, or `PromiseLike`:
|
|
126
145
|
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
146
|
+
* `HttpHandler.CONTINUE`: Continues on the current branch to the next middleware, or moves to the next branch if there are no siblings left. `alias: true|void|null|undefined`
|
|
147
|
+
* `HttpHandler.BREAK`: Breaks from the current middleware branch, and continues to the next branch. `alias: false`
|
|
148
|
+
* `HttpHandler.END`: Terminates the entire middleware tree. `alias: 0`
|
|
149
|
+
* `number`: Sets status code and then ends stream. `alias: res.code(statusCode).end()`
|
|
150
|
+
* `Set|Map`: Add an inline middleware branch.
|
|
151
|
+
* `Array`: Explicitly passed to `HttpResponse.end()`. This is to support sending an `Array` object instead having it becoming an inline middleware branch.
|
|
152
|
+
* `any`: Any other value returned would automatically be passed to `HttpResponse.end()` which, in turn, uses it's own content handlers (eg: `JSON`; `Readable`), and finally terminates the middleware tree.
|
|
130
153
|
|
|
131
|
-
A `MiddlewareFilter` is a function that accepts a `MiddlewareFunctionParams` and returns a `
|
|
154
|
+
A `MiddlewareFilter` is a function that accepts a `MiddlewareFunctionParams` and returns a `boolean` or `Promise<boolean>` signaling whether to continue in the branch. `true` translates to `HttpHandler.CONTINUE`. `false` translates to `HttpHandler.BREAK`. There is no support for `HttpHandler.END` logic in a MiddlewareFilter by design.
|
|
132
155
|
|
|
133
|
-
A `MiddlewareErrorHandler` is an `Object` with a `onError` property. `onError` is like a MiddlewareFunction, but includes an `err` item in its parameter object. When the handler is in an error state, it will bubble upwards while
|
|
156
|
+
A `MiddlewareErrorHandler` is an `Object` with a `onError` property. `onError` is like a MiddlewareFunction, but includes an `err` item in its parameter object. When the handler is in an error state, it will bubble upwards while searching for the next `MiddlewareErrorHandler`.
|
|
134
157
|
|
|
135
|
-
`Middleware` can be a `MiddlewareFunction` or `MiddlewareFilter`. It can also a compatible response value of either: `
|
|
158
|
+
`Middleware` can be a `MiddlewareFunction` or `MiddlewareFilter`. It can also a compatible response value of either: `HttpHandler.CONTINUE|true|null|void|undefined`, `HttpHandler.BREAK|false`, `HttpHandler.END|0`. The response can be the value or a `Promise`.
|
|
136
159
|
|
|
137
|
-
To support branching, `Middleware` can also be a `Iterable<Middleware>` (
|
|
160
|
+
To support branching, `Middleware` can also be a `Iterable<Middleware>` (eg: `Set`) or `Map<any, Middleware>`. The `HttpHandler` will iterate through each and flow based on the `break`, `continue`, or `end` instruction returned by each entry.
|
|
138
161
|
|
|
139
162
|
## Included Middleware
|
|
140
163
|
|
|
141
164
|
### Response Middleware
|
|
142
|
-
* [
|
|
143
|
-
* [ContentLength](
|
|
144
|
-
* [Hash](
|
|
145
|
-
* [ContentEncoder](
|
|
146
|
-
* [
|
|
165
|
+
* [AuthHeaders](./middleware/AutoHeadersMiddleware.js) - Automatically sends response headers before writing or ending a response stream
|
|
166
|
+
* [ContentLength](./middleware/contentLengthMiddleware.js) - Sets `Content-Length` based on response stream content writes
|
|
167
|
+
* [Hash](./middleware/HashMiddleware.js) - Sets `ETag`, `Digest`, and `Content-MD5` response headers automatically
|
|
168
|
+
* [ContentEncoder](./middleware/ContentEncoderMiddleware.js) - Applies `Content-Encoding` to response based on `Accept-Encoding` request header
|
|
169
|
+
* [SendJson](./middleware/SendJsonMiddleware.js) - Adds response content processor that encodes objects and arrays to JSON string. Sets `application/json;charset=utf-8`, if content-type not set.
|
|
170
|
+
* [SendString](./middleware/SendStringMiddleware.js) - Adds response content processor that encodes a string. Uses `charset` or uses `utf-8`, if not set.
|
|
147
171
|
|
|
148
172
|
### Request Middleware
|
|
149
173
|
* [ContentDecoder](/middleware/ContentDecoderMiddleware.js) - Decodes `Content-Encoding` from request streams
|
|
150
|
-
* [ContentReader](/middleware/ContentReaderMiddleware.js) - Adds `Object Mode` read support to request stream, including `string`, `JSON`, and `urlencoded` support. Can cache transformation into `req.local` for convenience.
|
|
151
174
|
|
|
152
175
|
### Logic Middleware
|
|
153
176
|
* [Path](/middleware/PathMiddleware.js) - Creates logic filter based on URL pathname
|
|
@@ -160,30 +183,29 @@ To support branching, `Middleware` can also be a `Iterable<Middleware>` (include
|
|
|
160
183
|
### Examples:
|
|
161
184
|
|
|
162
185
|
````js
|
|
163
|
-
HttpHandler.defaultInstance.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
]
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
186
|
+
HttpHandler.defaultInstance.middleware.push(
|
|
187
|
+
new AutoHeadersMiddleware(),
|
|
188
|
+
new ContentLengthMiddleware(),
|
|
189
|
+
hash: (USE_HASH ? new HashMiddleware() : HttpHandler.CONTINUE),
|
|
190
|
+
);
|
|
191
|
+
HttpHandler.defaultInstance.middleware.push(
|
|
192
|
+
[
|
|
193
|
+
PathMiddleware.SUBPATH('/api'),
|
|
194
|
+
new CORSMiddleware(),
|
|
195
|
+
[MethodMiddleware.GET, myAPIGetFunctions],
|
|
196
|
+
[MethodMiddleware.POST, myAPIPostFunctions],
|
|
197
|
+
],
|
|
198
|
+
[
|
|
199
|
+
new PathMiddleware(/^\/(index\.html?)?$/),
|
|
200
|
+
indexPageMiddleware
|
|
201
|
+
],
|
|
202
|
+
arrayToBePopulatedLater,
|
|
203
|
+
404 // Equivalient of ({response}) => response.code(404).end()
|
|
204
|
+
);
|
|
182
205
|
HttpHandler.defaultInstance.errorHandlers.push({
|
|
183
|
-
onError({
|
|
184
|
-
console.error(
|
|
185
|
-
|
|
186
|
-
return 'end';
|
|
206
|
+
onError({error}) {
|
|
207
|
+
console.error(error);
|
|
208
|
+
return 500;
|
|
187
209
|
},
|
|
188
210
|
});
|
|
189
211
|
````
|
|
@@ -192,17 +214,21 @@ HttpHandler.defaultInstance.errorHandlers.push({
|
|
|
192
214
|
|
|
193
215
|
|
|
194
216
|
````js
|
|
195
|
-
async function checkToken({
|
|
196
|
-
const content =
|
|
197
|
-
req.locals.content = content;
|
|
217
|
+
async function checkToken({request, response, locals}) {
|
|
218
|
+
const content = await req.read();
|
|
198
219
|
try {
|
|
199
220
|
const decoded = await decodeJWT(content.token);
|
|
200
|
-
|
|
201
|
-
delete req.locals.content.token;
|
|
221
|
+
locals.jwt = decoded;
|
|
202
222
|
} catch {
|
|
203
|
-
|
|
204
|
-
return 'end';
|
|
223
|
+
return 401;
|
|
205
224
|
}
|
|
206
|
-
|
|
225
|
+
/**
|
|
226
|
+
* Since we want the logic to continue to the next step,
|
|
227
|
+
* We can either allow the function to implicitly return `undefined`
|
|
228
|
+
* or explicitly use any of the following:
|
|
229
|
+
* * return undefined;
|
|
230
|
+
* * return true;
|
|
231
|
+
* * return HttpHandler.CONTINUE;
|
|
232
|
+
*/
|
|
207
233
|
}
|
|
208
234
|
````
|
package/data/CookieObject.js
CHANGED
|
@@ -25,7 +25,7 @@ export default class CookieObject {
|
|
|
25
25
|
static parse(cookieString) {
|
|
26
26
|
/** @type {Partial<CookieDetails>} */
|
|
27
27
|
const options = {};
|
|
28
|
-
cookieString.split(';').
|
|
28
|
+
for (const [index, pair] of cookieString.split(';').entries()) {
|
|
29
29
|
const indexOfEquals = pair.indexOf('=');
|
|
30
30
|
let key;
|
|
31
31
|
let value;
|
|
@@ -33,28 +33,28 @@ export default class CookieObject {
|
|
|
33
33
|
key = '';
|
|
34
34
|
value = pair.trim();
|
|
35
35
|
} else {
|
|
36
|
-
key = pair.
|
|
37
|
-
value = pair.
|
|
36
|
+
key = pair.slice(0, indexOfEquals).trim();
|
|
37
|
+
value = pair.slice(indexOfEquals + 1).trim();
|
|
38
38
|
}
|
|
39
39
|
const firstQuote = value.indexOf('"');
|
|
40
40
|
const lastQuote = value.lastIndexOf('"');
|
|
41
41
|
if (firstQuote !== -1 && lastQuote !== -1) {
|
|
42
|
-
value = value.
|
|
42
|
+
value = value.slice(firstQuote + 1, lastQuote);
|
|
43
43
|
}
|
|
44
44
|
if (index === 0) {
|
|
45
45
|
options.name = key;
|
|
46
46
|
if (value != null) {
|
|
47
47
|
options.value = value;
|
|
48
48
|
}
|
|
49
|
-
|
|
49
|
+
continue;
|
|
50
50
|
}
|
|
51
51
|
switch (key.toLowerCase()) {
|
|
52
52
|
case 'expires':
|
|
53
53
|
options.expires = new Date(value);
|
|
54
|
-
|
|
54
|
+
continue;
|
|
55
55
|
case 'max-age':
|
|
56
|
-
options.maxAge = parseInt(value, 10);
|
|
57
|
-
|
|
56
|
+
options.maxAge = Number.parseInt(value, 10);
|
|
57
|
+
continue;
|
|
58
58
|
case 'domain':
|
|
59
59
|
options.domain = value;
|
|
60
60
|
break;
|
|
@@ -68,22 +68,22 @@ export default class CookieObject {
|
|
|
68
68
|
options.httpOnly = true;
|
|
69
69
|
break;
|
|
70
70
|
case 'samesite':
|
|
71
|
-
// @ts-
|
|
71
|
+
// @ts-expect-error No cast
|
|
72
72
|
options.sameSite = value;
|
|
73
73
|
break;
|
|
74
74
|
default:
|
|
75
75
|
}
|
|
76
|
-
}
|
|
76
|
+
}
|
|
77
77
|
return new CookieObject(options);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
toString() {
|
|
81
81
|
// eslint-disable-next-line prefer-template
|
|
82
82
|
return (`${this.name ?? ''}=${this.value ?? ''}`)
|
|
83
|
-
+ (this.expires
|
|
84
|
-
+ (this.maxAge
|
|
85
|
-
+ (this.domain
|
|
86
|
-
+ (this.path
|
|
83
|
+
+ (this.expires == null ? '' : `; Expires=${this.expires.toUTCString()}`)
|
|
84
|
+
+ (this.maxAge == null ? '' : `; Max-Age=${this.maxAge}`)
|
|
85
|
+
+ (this.domain == null ? '' : `; Domain=${this.domain}`)
|
|
86
|
+
+ (this.path == null ? '' : `; Path=${this.path}`)
|
|
87
87
|
+ (this.secure ? '; Secure' : '')
|
|
88
88
|
+ (this.httpOnly ? '; HttpOnly' : '')
|
|
89
89
|
+ (this.sameSite ? `; SameSite=${this.sameSite}` : '');
|
package/errata/socketio.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/* eslint-disable import/prefer-default-export */
|
|
2
|
-
|
|
3
1
|
/** @typedef {import('../lib/HttpHandler').default} HttpHandler */
|
|
4
2
|
/** @typedef {import('http2').Http2SecureServer} Http2SecureServer */
|
|
5
3
|
|
|
@@ -8,17 +6,14 @@
|
|
|
8
6
|
* @param {RegExp} [socketioPath] /^\/socket.io\//i
|
|
9
7
|
* @return {void}
|
|
10
8
|
*/
|
|
11
|
-
export function addHttp2Support(httpHandler, socketioPath = /^\/socket
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const headers = args
|
|
9
|
+
export function addHttp2Support(httpHandler, socketioPath = /^\/socket\.io\//i) {
|
|
10
|
+
const previous = httpHandler.handleHttp2Stream;
|
|
11
|
+
// eslint-disable-next-line no-param-reassign, unicorn/prevent-abbreviations
|
|
12
|
+
httpHandler.handleHttp2Stream = (...args) => {
|
|
13
|
+
const [, headers] = args;
|
|
16
14
|
if (headers?.[':path']?.match(socketioPath)) {
|
|
17
15
|
return Promise.resolve(null);
|
|
18
16
|
}
|
|
19
|
-
return
|
|
17
|
+
return previous.call(httpHandler, ...args);
|
|
20
18
|
};
|
|
21
|
-
// @ts-ignore
|
|
22
|
-
// eslint-disable-next-line no-param-reassign
|
|
23
|
-
httpHandler.handleHttp2Stream = newFunction;
|
|
24
19
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as starter from '../templates/starter.js';
|
|
2
|
+
|
|
3
|
+
const middleware = [
|
|
4
|
+
() => 'Hello world!',
|
|
5
|
+
];
|
|
6
|
+
|
|
7
|
+
// Start the singleton HTTP server using the starter convenience API.
|
|
8
|
+
await starter.start({ middleware });
|
|
9
|
+
|
|
10
|
+
// Export nothing — this example file is intended as a tiny runnable demo.
|
|
11
|
+
export {};
|
package/helpers/HeadersParser.js
CHANGED
|
@@ -23,10 +23,10 @@ export default class HeadersParser {
|
|
|
23
23
|
* The `charset` direct of `Content-Type`.
|
|
24
24
|
* The character encoding standard.
|
|
25
25
|
* (Always lowercase)
|
|
26
|
-
|
|
26
|
+
@return {string} */
|
|
27
27
|
get charset() {
|
|
28
28
|
let value = null;
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
this.contentType?.split(';').some((directive) => {
|
|
31
31
|
const parameters = directive.split('=');
|
|
32
32
|
if (parameters[0].trim().toLowerCase() !== 'charset') {
|
|
@@ -36,7 +36,7 @@ export default class HeadersParser {
|
|
|
36
36
|
const firstQuote = value.indexOf('"');
|
|
37
37
|
const lastQuote = value.lastIndexOf('"');
|
|
38
38
|
if (firstQuote !== -1 && lastQuote !== -1) {
|
|
39
|
-
value = value.
|
|
39
|
+
value = value.slice(firstQuote + 1, lastQuote);
|
|
40
40
|
}
|
|
41
41
|
return true;
|
|
42
42
|
});
|
|
@@ -46,7 +46,7 @@ export default class HeadersParser {
|
|
|
46
46
|
/** @return {string} */
|
|
47
47
|
get boundary() {
|
|
48
48
|
let value = null;
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
this.contentType?.split(';').some((directive) => {
|
|
51
51
|
const parameters = directive.split('=');
|
|
52
52
|
if (parameters[0].trim().toLowerCase() !== 'boundary') {
|
|
@@ -56,16 +56,15 @@ export default class HeadersParser {
|
|
|
56
56
|
const firstQuote = value.indexOf('"');
|
|
57
57
|
const lastQuote = value.lastIndexOf('"');
|
|
58
58
|
if (firstQuote !== -1 && lastQuote !== -1) {
|
|
59
|
-
value = value.
|
|
59
|
+
value = value.slice(firstQuote + 1, lastQuote);
|
|
60
60
|
}
|
|
61
61
|
return true;
|
|
62
62
|
});
|
|
63
63
|
return value;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
/** @return {number} */
|
|
66
|
+
/** @return {?number} */
|
|
68
67
|
get contentLength() {
|
|
69
|
-
return parseInt(this.headers['content-length'], 10) || null;
|
|
68
|
+
return Number.parseInt(this.headers['content-length'], 10) || null;
|
|
70
69
|
}
|
|
71
70
|
}
|