webhoster 0.3.4 → 0.3.5

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.
Files changed (125) hide show
  1. package/package.json +12 -1
  2. package/types/data/CookieObject.d.ts +34 -0
  3. package/types/data/CookieObject.d.ts.map +1 -0
  4. package/types/data/custom-types.d.ts +111 -0
  5. package/types/data/custom-types.d.ts.map +1 -0
  6. package/types/errata/socketio.d.ts +11 -0
  7. package/types/errata/socketio.d.ts.map +1 -0
  8. package/types/examples/starter.d.ts +2 -0
  9. package/types/examples/starter.d.ts.map +1 -0
  10. package/types/helpers/HeadersParser.d.ts +27 -0
  11. package/types/helpers/HeadersParser.d.ts.map +1 -0
  12. package/types/helpers/HttpListener.d.ts +84 -0
  13. package/types/helpers/HttpListener.d.ts.map +1 -0
  14. package/types/helpers/RequestHeaders.d.ts +25 -0
  15. package/types/helpers/RequestHeaders.d.ts.map +1 -0
  16. package/types/helpers/RequestReader.d.ts +52 -0
  17. package/types/helpers/RequestReader.d.ts.map +1 -0
  18. package/types/helpers/ResponseHeaders.d.ts +126 -0
  19. package/types/helpers/ResponseHeaders.d.ts.map +1 -0
  20. package/types/helpers/index.d.ts +6 -0
  21. package/types/helpers/index.d.ts.map +1 -0
  22. package/types/lib/HttpHandler.d.ts +79 -0
  23. package/types/lib/HttpHandler.d.ts.map +1 -0
  24. package/types/lib/HttpRequest.d.ts +175 -0
  25. package/types/lib/HttpRequest.d.ts.map +1 -0
  26. package/types/lib/HttpResponse.d.ts +117 -0
  27. package/types/lib/HttpResponse.d.ts.map +1 -0
  28. package/types/lib/HttpTransaction.d.ts +114 -0
  29. package/types/lib/HttpTransaction.d.ts.map +1 -0
  30. package/types/middleware/AutoHeadersMiddleware.d.ts +27 -0
  31. package/types/middleware/AutoHeadersMiddleware.d.ts.map +1 -0
  32. package/types/middleware/CORSMiddleware.d.ts +65 -0
  33. package/types/middleware/CORSMiddleware.d.ts.map +1 -0
  34. package/types/middleware/CaseInsensitiveHeadersMiddleware.d.ts +27 -0
  35. package/types/middleware/CaseInsensitiveHeadersMiddleware.d.ts.map +1 -0
  36. package/types/middleware/ContentDecoderMiddleware.d.ts +17 -0
  37. package/types/middleware/ContentDecoderMiddleware.d.ts.map +1 -0
  38. package/types/middleware/ContentEncoderMiddleware.d.ts +48 -0
  39. package/types/middleware/ContentEncoderMiddleware.d.ts.map +1 -0
  40. package/types/middleware/ContentLengthMiddleware.d.ts +38 -0
  41. package/types/middleware/ContentLengthMiddleware.d.ts.map +1 -0
  42. package/types/middleware/HashMiddleware.d.ts +28 -0
  43. package/types/middleware/HashMiddleware.d.ts.map +1 -0
  44. package/types/middleware/HeadMethodMiddleware.d.ts +12 -0
  45. package/types/middleware/HeadMethodMiddleware.d.ts.map +1 -0
  46. package/types/middleware/MethodMiddleware.d.ts +40 -0
  47. package/types/middleware/MethodMiddleware.d.ts.map +1 -0
  48. package/types/middleware/PathMiddleware.d.ts +81 -0
  49. package/types/middleware/PathMiddleware.d.ts.map +1 -0
  50. package/types/middleware/ReadFormData.d.ts +7 -0
  51. package/types/middleware/ReadFormData.d.ts.map +1 -0
  52. package/types/middleware/SendJsonMiddleware.d.ts +51 -0
  53. package/types/middleware/SendJsonMiddleware.d.ts.map +1 -0
  54. package/types/middleware/SendStringMiddleware.d.ts +35 -0
  55. package/types/middleware/SendStringMiddleware.d.ts.map +1 -0
  56. package/types/templates/starter.d.ts +15 -0
  57. package/types/templates/starter.d.ts.map +1 -0
  58. package/types/utils/AsyncObject.d.ts +38 -0
  59. package/types/utils/AsyncObject.d.ts.map +1 -0
  60. package/types/utils/CaseInsensitiveObject.d.ts +9 -0
  61. package/types/utils/CaseInsensitiveObject.d.ts.map +1 -0
  62. package/types/utils/function.d.ts +2 -0
  63. package/types/utils/function.d.ts.map +1 -0
  64. package/types/utils/headers.d.ts +7 -0
  65. package/types/utils/headers.d.ts.map +1 -0
  66. package/types/utils/qualityValues.d.ts +12 -0
  67. package/types/utils/qualityValues.d.ts.map +1 -0
  68. package/types/utils/stream.d.ts +6 -0
  69. package/types/utils/stream.d.ts.map +1 -0
  70. package/.eslintrc.json +0 -96
  71. package/.github/FUNDING.yml +0 -4
  72. package/.github/copilot-instructions.md +0 -100
  73. package/.github/workflows/publish.yml +0 -29
  74. package/.github/workflows/test-matrix.yml +0 -37
  75. package/.test/benchmark.js +0 -28
  76. package/.test/constants.js +0 -4
  77. package/.test/cookietester.js +0 -19
  78. package/.test/http2server.js +0 -35
  79. package/.test/httpserver.js +0 -32
  80. package/.test/index.js +0 -498
  81. package/.test/multipromise.js +0 -32
  82. package/.test/tls.js +0 -75
  83. package/.test/urlencoded.js +0 -54
  84. package/.vscode/launch.json +0 -35
  85. package/.vscode/settings.json +0 -5
  86. package/CHANGELOG.md +0 -10
  87. package/CODE_OF_CONDUCT.md +0 -76
  88. package/examples/starter.js +0 -11
  89. package/jsconfig.json +0 -12
  90. package/polyfill/FormData.js +0 -164
  91. package/rollup.config.js +0 -8
  92. package/scripts/check-teapot.mjs +0 -40
  93. package/scripts/test-all-sync.sh +0 -6
  94. package/scripts/test-all.sh +0 -7
  95. package/test/fixtures/stream.js +0 -68
  96. package/test/helpers/HttpListener/construct.js +0 -18
  97. package/test/helpers/HttpListener/customOptions.js +0 -22
  98. package/test/helpers/HttpListener/doubleCreate.js +0 -40
  99. package/test/helpers/HttpListener/events.js +0 -77
  100. package/test/helpers/HttpListener/http.js +0 -31
  101. package/test/helpers/HttpListener/http2.js +0 -41
  102. package/test/helpers/HttpListener/https.js +0 -38
  103. package/test/helpers/HttpListener/startAll.js +0 -31
  104. package/test/helpers/HttpListener/stopNotStarted.js +0 -23
  105. package/test/lib/HttpHandler/class.js +0 -8
  106. package/test/lib/HttpHandler/handleRequest.js +0 -11
  107. package/test/lib/HttpHandler/middleware.js +0 -941
  108. package/test/lib/HttpHandler/parse.js +0 -41
  109. package/test/lib/HttpRequest/class.js +0 -8
  110. package/test/lib/HttpRequest/downstream.js +0 -171
  111. package/test/lib/HttpRequest/properties.js +0 -101
  112. package/test/lib/HttpRequest/read.js +0 -518
  113. package/test/lib/HttpResponse/async-iterable-middleware.js +0 -37
  114. package/test/lib/HttpResponse/async-iterable.js +0 -31
  115. package/test/lib/HttpResponse/class.js +0 -8
  116. package/test/lib/HttpResponse/properties.js +0 -59
  117. package/test/lib/HttpResponse/send.js +0 -275
  118. package/test/lib/HttpTransaction/class.js +0 -8
  119. package/test/lib/HttpTransaction/ping.js +0 -50
  120. package/test/lib/HttpTransaction/push.js +0 -89
  121. package/test/middleware/SendJsonMiddleware.js +0 -222
  122. package/test/sanity.js +0 -10
  123. package/test/templates/error-teapot.js +0 -47
  124. package/test/templates/starter.js +0 -93
  125. package/tsconfig.json +0 -29
@@ -1,29 +0,0 @@
1
- name: Publish Package
2
-
3
- on:
4
- push:
5
- tags:
6
- - 'v*'
7
-
8
- permissions:
9
- id-token: write # Required for OIDC
10
- contents: read
11
-
12
- jobs:
13
- publish:
14
- runs-on: ubuntu-latest
15
- steps:
16
- - uses: actions/checkout@v4
17
-
18
- - uses: actions/setup-node@v4
19
- with:
20
- node-version: '20'
21
- registry-url: 'https://registry.npmjs.org'
22
-
23
- # Ensure npm 11.5.1 or later is installed
24
- - name: Update npm
25
- run: npm install -g npm@latest
26
- - run: npm ci
27
- - run: npm run build --if-present
28
- - run: npm test
29
- - run: npm publish
@@ -1,37 +0,0 @@
1
- name: Test matrix
2
-
3
- on:
4
- push:
5
- branches: ["master"]
6
- pull_request:
7
- branches: ["master"]
8
-
9
- jobs:
10
- test:
11
- name: Test on Node ${{ matrix.node-version }}
12
- runs-on: ubuntu-latest
13
- strategy:
14
- matrix:
15
- node-version: ["16.13", "16", "18", "20", "22"]
16
- steps:
17
- - name: Checkout
18
- uses: actions/checkout@v4
19
-
20
- - name: Setup Node
21
- uses: actions/setup-node@v4
22
- with:
23
- node-version: ${{ matrix.node-version }}
24
- cache: 'npm'
25
-
26
- - name: Install dependencies
27
- run: npm ci
28
-
29
- - name: Run tests
30
- run: npm run test
31
-
32
- - name: Upload coverage artifact
33
- if: always()
34
- uses: actions/upload-artifact@v4
35
- with:
36
- name: coverage-${{ matrix.node-version }}
37
- path: coverage
@@ -1,28 +0,0 @@
1
- import HttpListener from '../helpers/HttpListener.js';
2
- import HttpHandler from '../lib/HttpHandler.js';
3
- import AutoHeadersMiddleware from '../middleware/AutoHeadersMiddleware.js';
4
- import SendJsonMiddleware from '../middleware/SendJsonMiddleware.js';
5
- import ContentLengthMiddleware from '../middleware/ContentLengthMiddleware.js';
6
-
7
- const { middleware } = HttpHandler.defaultInstance;
8
- middleware.push(
9
- // new HeadMethodMiddleware(), // Discard body content
10
- new SendJsonMiddleware(),
11
- // new ContentLengthMiddleware({ delayCycle: false }), // Calculate length of anything after
12
- // new AutoHeadersMiddleware(), // Send headers automatically
13
- // new HashMiddleware(), // Hash anything after
14
- // new ContentEncoderMiddleware(), // Compress anything after
15
- // new ContentWriterMiddleware({ setCharset: true, setJSON: true }),
16
- // new ContentDecoderMiddleware(),
17
- // Automatically reads text, JSON, and form-url-encoded from requests
18
- // new ContentReaderMiddleware({
19
- // buildString: true,
20
- // defaultMediaType: 'application/json',
21
- // parseJSON: true,
22
- // formURLEncodedFormat: 'object',
23
- // }),
24
- { hello: "world" },
25
- );
26
-
27
- const listener = new HttpListener({ insecurePort: 3000 });
28
- listener.startAll();
@@ -1,4 +0,0 @@
1
- export const HTTP_PORT = Number.parseInt(process.env.HTTP_PORT || process.env.PORT || '8080', 10);
2
- export const HTTPS_PORT = Number.parseInt(process.env.HTTPS_PORT || '8443', 10);
3
- export const HTTP_HOST = process.env.HTTP_HOST || process.env.HOST || '0.0.0.0';
4
- export const HTTPS_HOST = process.env.HTTPS_HOST || process.env.HOST || '0.0.0.0';
@@ -1,19 +0,0 @@
1
- import CookieObject from '../data/CookieObject.js';
2
-
3
- [
4
- 'id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT',
5
- 'id=a3fWa; Max-Age=2592000',
6
- 'qwerty=219ffwef9w0f; Domain=somecompany.co.uk',
7
- 'sessionId=e8bb43229de9; Domain=foo.example.com',
8
- '__Secure-ID=123; Secure; Domain=example.com',
9
- '__Host-ID=123; Secure; Path=/',
10
- 'test="abc1234";',
11
- 'random',
12
- ].forEach((cookie) => {
13
- const handler = new CookieObject(cookie);
14
- const rebuilt = new CookieObject(handler.toJSON());
15
- console.log(cookie);
16
- console.log(rebuilt.toString());
17
- console.dir(handler);
18
- console.dir(rebuilt);
19
- });
@@ -1,35 +0,0 @@
1
- import http2 from 'node:http2';
2
-
3
- import { HTTPS_HOST, HTTPS_PORT } from './constants.js';
4
-
5
- /** @type {http2.Http2SecureServer} */
6
- let http2Server = null;
7
-
8
- /**
9
- * @param {http2.SecureServerOptions} [options]
10
- * @return {Promise<http2.Http2SecureServer>}
11
- */
12
- export function start(options = {}) {
13
- return new Promise((resolve, reject) => {
14
- http2Server = http2.createSecureServer(options);
15
- http2Server.listen(HTTPS_PORT, HTTPS_HOST, () => resolve(http2Server));
16
- http2Server.addListener('error', reject);
17
- });
18
- }
19
-
20
- /** @return {Promise<void>} */
21
- export function stop() {
22
- return new Promise((resolve, reject) => {
23
- if (!http2Server) {
24
- resolve();
25
- return;
26
- }
27
- http2Server.close((err) => {
28
- if (err) {
29
- reject(err);
30
- return;
31
- }
32
- resolve();
33
- });
34
- });
35
- }
@@ -1,32 +0,0 @@
1
- import http from 'node:http';
2
-
3
- import { HTTP_HOST, HTTP_PORT } from './constants.js';
4
-
5
- /** @type {http.Server} */
6
- let httpServer = null;
7
-
8
- /** @return {Promise<http.Server>} */
9
- export function start() {
10
- return new Promise((resolve, reject) => {
11
- httpServer = http.createServer();
12
- httpServer.listen(HTTP_PORT, HTTP_HOST, () => resolve(httpServer));
13
- httpServer.addListener('error', reject);
14
- });
15
- }
16
-
17
- /** @return {Promise<void>} */
18
- export function stop() {
19
- return new Promise((resolve, reject) => {
20
- if (!httpServer) {
21
- resolve();
22
- return;
23
- }
24
- httpServer.close((err) => {
25
- if (err) {
26
- reject(err);
27
- return;
28
- }
29
- resolve();
30
- });
31
- });
32
- }
package/.test/index.js DELETED
@@ -1,498 +0,0 @@
1
- // @ts-check
2
-
3
- /* eslint-disable no-console */
4
-
5
- import { existsSync, readFileSync } from 'node:fs';
6
-
7
- import HttpListener from '../helpers/HttpListener.js';
8
- import RequestHeaders from '../helpers/RequestHeaders.js';
9
- import ResponseHeaders from '../helpers/ResponseHeaders.js';
10
- import HttpHandler from '../lib/HttpHandler.js';
11
- import AutoHeadersMiddleware from '../middleware/AutoHeadersMiddleware.js';
12
- import CORSMiddleware from '../middleware/CORSMiddleware.js';
13
- import ContentDecoderMiddleware from '../middleware/ContentDecoderMiddleware.js';
14
- import ContentEncoderMiddleware from '../middleware/ContentEncoderMiddleware.js';
15
- import ContentLengthMiddleware from '../middleware/ContentLengthMiddleware.js';
16
- import HashMiddleware from '../middleware/HashMiddleware.js';
17
- import HeadMethodMiddleware from '../middleware/HeadMethodMiddleware.js';
18
- import MethodMiddleware from '../middleware/MethodMiddleware.js';
19
- import PathMiddleware from '../middleware/PathMiddleware.js';
20
- import SendJsonMiddleware from '../middleware/SendJsonMiddleware.js';
21
- import SendStringMiddleware from '../middleware/SendStringMiddleware.js';
22
-
23
- import {
24
- HTTPS_HOST, HTTPS_PORT, HTTP_HOST, HTTP_PORT,
25
- } from './constants.js';
26
- import * as tls from './tls.js';
27
- import { ServerResponse } from 'node:http';
28
-
29
- /** @typedef {import('../data/custom-types.js').MiddlewareFunction} MiddlewareFunction */
30
-
31
- /**
32
- * Redirect to HTTPS/2
33
- * @type {MiddlewareFunction}
34
- */
35
- function redirectHttpsMiddleware({request,response}) {
36
- if (request.protocol === 'https:') {
37
- return true;
38
- }
39
- const url = new URL(request.url);
40
- url.protocol = 'https:';
41
- url.port = HTTPS_PORT.toString(10);
42
- const Location = url.href;
43
- response.headers.location = Location;
44
- return 301;
45
- }
46
-
47
- /** @type {MiddlewareFunction} */
48
- function indexMiddleware(transaction) {
49
- console.log('indexMiddleware');
50
- transaction.response.headers['content-type'] = 'text/html';
51
- if (transaction.canPushPath) {
52
- transaction.pushPath('/script.js').catch(console.error);
53
- transaction.pushPath('/fake.png').catch(console.error);
54
- }
55
- return /* html */ `
56
- <html>
57
- <head><script src="script.js"></script></head>
58
- </body>
59
- ${new Date()}
60
- <img src="fake.png"/>
61
- <form action="form" method="POST"><input name="field"><input type="submit" value="POST"></form>
62
- <form action="form" method="GET"><input name="field"><input type="submit" value="GET"></form>
63
- <ul>
64
- <li><a href="/cors.html">/cors.html</a></li>
65
- <li><a href="/large.html">/large.html</a></li>
66
- <li><a href="/chunk.html">/chunk.html</a></li>
67
- <li><a href="/blank.html">/blank.html</a></li>
68
- <li><a href="/nocontent.html">/nocontent.html</a></li>
69
- <li><a href="/gzip.html">/gzip.html</a></li>
70
- <li><a href="/plaintext.html">/plaintext.html</a></li>
71
- <li><a href="/get.json">/get.json</a></li>
72
- </ul>
73
- </body>
74
- </html>
75
- `;
76
- }
77
-
78
- /** @type {MiddlewareFunction} */
79
- function largeMiddleware({request}) {
80
- console.log('largeMiddleware');
81
- request.headers['content-type'] = 'text/html';
82
- let block = '';
83
- const len = Number.parseInt(request.searchParams.get('lines'), 10) || 10_000;
84
- for (let i = 0; i < len; i += 1) {
85
- block += `<pre>${i.toString(36)}</pre><br>`;
86
- }
87
- return /* html */ `
88
- <html>
89
- <head></head>
90
- </body>
91
- ${block}
92
- </body>
93
- </html>
94
- `;
95
- }
96
-
97
- /** @type {MiddlewareFunction} */
98
- function chunkMiddleware({ request, response }) {
99
- console.log('chunkMiddleware');
100
- response.headers['content-type'] = 'text/html';
101
- let block = '';
102
- for (let i = 0; i < 1000; i += 1) {
103
- block += `<pre>${Math.random().toString(36).slice(2, 18)}</pre><br>`;
104
- }
105
- const delay = Number.parseInt(request.searchParams.get('delay'), 10) || 0;
106
- let stream = response.getPipeline();
107
- return new Promise((resolve) => {
108
- const timingFn = delay ? setTimeout : process.nextTick;
109
- timingFn(() => {
110
- console.log('write1');
111
- stream.write(/* html */ `
112
- <html>
113
- <head></head>
114
- </body>
115
- `);
116
- }, delay);
117
- timingFn(() => {
118
- console.log('write2');
119
- stream.write(block);
120
- }, delay * 2);
121
- timingFn(() => {
122
- console.log('write3');
123
- stream.write(/* html */ `
124
- </body>
125
- </html>
126
- `);
127
- }, delay * 3);
128
- timingFn(() => {
129
- stream.end();
130
- resolve(HttpHandler.END);
131
- }, delay * 4);
132
- });
133
- }
134
-
135
- /** @type {MiddlewareFunction} */
136
- function plainTextMiddleware({ response }) {
137
- console.log('plainTextMiddleware');
138
- response.status = 200;
139
- response.headers['content-type'] = 'text/html';
140
- response.headers['content-encoding'] = 'identity';
141
- return 'This is always in plaintext';
142
- }
143
-
144
- /** @type {MiddlewareFunction} */
145
- function blankMiddleware({ response }) {
146
- console.log('blankMiddleware');
147
- response.headers['content-type'] = 'text/html';
148
- response.headers['content-length'] = '0';
149
- return 200;
150
- }
151
-
152
- /** @type {MiddlewareFunction} */
153
- function gzipMiddleware({ response }) {
154
- console.log('gzipMiddleware');
155
- response.statusCode = 200;
156
- response.headers['content-type'] = 'text/html';
157
- response.headers['content-encoding'] = 'gzip';
158
- return 'This is always compressed with gzip.';
159
- }
160
-
161
- /** @type {MiddlewareFunction} */
162
- function corsTest({ response }) {
163
- console.log('cors');
164
- response.statusCode = 200;
165
- response.headers['content-type'] = 'text/html';
166
- return /* html */ `
167
- <html>
168
- <head>
169
- <script type="text/javascript">
170
- fetch('http://127.0.0.1:8080/input.json', {
171
- headers: [['Content-Type', '']],
172
- method: 'POST', body: JSON.stringify({test: 'content'}),
173
- })
174
- .then((response) => response.json())
175
- .then(console.log)
176
- .catch(console.error);
177
- </script>
178
- </head>
179
- </body>
180
- <h1>CORS TEST</h1>
181
- ${new Date()}
182
- </body>
183
- </html>
184
- `;
185
- }
186
-
187
- /** @type {MiddlewareFunction} */
188
- function scriptMiddleware(transaction) {
189
- const response = transaction.response;
190
- console.log('scriptMiddleware');
191
- console.log('Holding script');
192
- return new Promise((resolve) => {
193
- setTimeout(() => {
194
- response.statusCode = 200;
195
- response.headers['content-type'] = 'text/javascript';
196
- if (transaction.canPushPath) {
197
- transaction.pushPath('/fake.js').catch(console.error);
198
- } else {
199
- console.log('RIP Push');
200
- }
201
- console.log('Releasing script');
202
- response.end(/* js */ `
203
- console.log('hello');
204
- let data = '';
205
- for(let i = 0; i < 10 ; i++) {
206
- data += Math.random().toString(36).slice(2, 18);
207
- }
208
- console.log(data);
209
- fetch('http://127.0.0.1:8080/echo.json', {
210
- method: 'POST',
211
- body: JSON.stringify({data}),
212
- headers: [['Content-Type', 'application/json']],
213
- }).then((response) => response.json())
214
- .then((response) => console.log('match', response.data === data))
215
- .catch(console.error);
216
- `);
217
- resolve(HttpHandler.END);
218
- }, 0);
219
- });
220
- }
221
-
222
- ServerResponse
223
-
224
- /** @type {MiddlewareFunction} */
225
- function outputMiddleware({ request, response }) {
226
- const reqHeaders = new RequestHeaders(request);
227
- console.log(request.headers.cookie);
228
- console.log('reqHeaders.cookieEntries.test', reqHeaders.cookieEntries.test);
229
- console.log('req.headers.cookie', request.headers.cookie);
230
- console.log('reqHeaders.cookies.get("test")', reqHeaders.cookies.get('test'));
231
- console.log('reqHeaders.cookies.all("test")', reqHeaders.cookies.all('test'));
232
- console.log('reqHeaders.cookieEntries.test?.[0]', reqHeaders.cookieEntries.test?.[0]);
233
- console.log('reqHeaders.cookieEntries.test?.[1]', reqHeaders.cookieEntries.test?.[1]);
234
- console.log('reqHeaders.cookieEntries.test2?.[0]', reqHeaders.cookieEntries.test2?.[0]);
235
- console.log('reqHeaders.cookieEntries.test4', reqHeaders.cookieEntries.test4);
236
- console.log("'test' in reqHeaders.cookieEntries", 'test' in reqHeaders.cookieEntries);
237
- console.log("'test4' in reqHeaders.cookieEntries", 'test4' in reqHeaders.cookieEntries);
238
- console.log('req.headers.cookie', request.headers.cookie);
239
-
240
- const resHeaders = new ResponseHeaders(response);
241
- resHeaders.cookies.set({
242
- name: 'test',
243
- value: Date.now().toString(),
244
- });
245
- resHeaders.cookies.set({ name: 'test2', value: Date.now().toString() });
246
- resHeaders.cookies.set(`test3=${Date.now()}`);
247
- resHeaders.cookies.set('test=newtest;Path=/test/*');
248
- const testCookieObject = resHeaders.cookies.get('test');
249
- testCookieObject.value = 'replaced';
250
- resHeaders.cookies.expire('test4');
251
- console.log('all', JSON.stringify(resHeaders.cookies.findAll(), null, 2));
252
- resHeaders.cookies.expireAll('test');
253
- resHeaders.cookieEntries.forEach((c) => console.log(c.toString()));
254
- console.log('wiping');
255
- resHeaders.cookieEntries.splice(0, resHeaders.cookieEntries.length);
256
- resHeaders.cookieEntries.forEach((c) => console.log(c.toString()));
257
- console.log('pushing 1 ');
258
- const held = resHeaders.cookies.set(`held=${Date.now()}`);
259
- resHeaders.cookies.remove(held);
260
- resHeaders.cookieEntries.forEach((c) => console.log(c.toString()));
261
- console.log('modifying held');
262
- held.secure = true;
263
- // console.log('unshifting 1 ');
264
- // resHeaders.cookieEntries.unshift(new CookieObject(`unshift=${Date.now()}`));
265
- resHeaders.cookieEntries.forEach((c) => console.log(c.toString()));
266
- console.log('putting back');
267
- resHeaders.cookieEntries.unshift(held);
268
- resHeaders.cookieEntries.forEach((c) => console.log(c.toString()));
269
- resHeaders.cookies.set('novalue=');
270
- resHeaders.cookies.set('noname');
271
- resHeaders.cookies.set('=blankname;path=/');
272
- resHeaders.cookies.set('quotedblank=""');
273
- resHeaders.cookieEntries.forEach((c) => console.log(c.toString()));
274
- // res.statusCode = 200;
275
- // resHeaders.mediaType = 'application/json';
276
- return { now: new Date() };
277
- }
278
-
279
- /** @type {MiddlewareFunction} */
280
- async function inputMiddleware({ request, response }) {
281
- console.log('inputMiddleware');
282
-
283
- console.log('stalled processing for 1000ms');
284
- // await new Promise((resolve) => setTimeout(resolve, 1000));
285
- response.statusCode = 200;
286
- console.log('reading post data from input.json');
287
- // Pipe it back and read at the same time
288
- request.stream.pipe(response.getPipeline());
289
- // res.pipeFrom(req.stream);
290
- const value = await request.read();
291
- console.log('got input.json', typeof value, value);
292
- return HttpHandler.END;
293
- }
294
-
295
- /** @type {MiddlewareFunction} */
296
- async function echoMiddleware({ request, response }) {
297
- console.log('echoMiddleware');
298
- return request.stream;
299
- }
300
-
301
- /** @type {MiddlewareFunction} */
302
- async function formGetMiddleware({ request, response }) {
303
- console.log('formGetMiddleware');
304
-
305
- console.log('stalled processing for 1000ms');
306
- await new Promise((resolve) => setTimeout(resolve, 1000));
307
- const data = Object.fromEntries(request.searchParams.entries());
308
- console.log('returning', data);
309
- return response.send(data);
310
- }
311
-
312
- /** @type {MiddlewareFunction} */
313
- function outputURLMiddleware({ request, response, state }) {
314
- console.log('outputURLMiddleware', state.path);
315
- return response.code(200).end(request.pathname);
316
- }
317
-
318
- /** @type {MiddlewareFunction} */
319
- async function formPostMiddleware({ request, response }) {
320
- console.log('formPostMiddleware');
321
- /** @type {FormData} */
322
- const value = await request.read();
323
- const stringData = JSON.stringify(value);
324
- return `type: ${request.headers['content-type']}\n${stringData.toString()}`;
325
- }
326
-
327
- /** @type {MiddlewareFunction} */
328
- function getJSONMiddleware() {
329
- console.log('getJSONMiddleware');
330
- return { now: new Date() };
331
- }
332
-
333
- const USE_HTTPS_REDIRECT = false;
334
- const SHOULD_CRASH = false;
335
-
336
- /**
337
- *
338
- */
339
- function setupHandler() {
340
- const { middleware, errorHandlers } = HttpHandler.defaultInstance;
341
- // Conditional statement
342
- middleware.push(USE_HTTPS_REDIRECT ? redirectHttpsMiddleware : null);
343
- const middlewareObject = new Set([
344
- // Allow Cross-Origin Resource Sharing
345
- new CORSMiddleware({ allowOrigin: ['http://localhost:8080', 'https://localhost:8443'] }),
346
-
347
- new ContentDecoderMiddleware(), // Automatically decodes content
348
-
349
- new SendJsonMiddleware({
350
- defaultCharset: 'utf-8',
351
- setCharset: true,
352
- setMediaType: true,
353
- }), // Auto converts objects to JSON
354
- new SendStringMiddleware({
355
- defaultCharset: 'utf-8',
356
- setCharset: true,
357
- }), // Auto converts strings to buffer
358
- new ContentEncoderMiddleware(), // Compress anything after
359
- new HashMiddleware(), // Hash anything after
360
- new ContentLengthMiddleware(), // Calculate length of anything after
361
- new AutoHeadersMiddleware(), // Send headers automatically
362
- new HeadMethodMiddleware(), // Discard body content
363
-
364
- ]);
365
- middleware.push(middlewareObject);
366
- const setTest = new Set();
367
- middleware.push(setTest);
368
- /** @type {any} */
369
- const getMiddlewareArray = [
370
- MethodMiddleware.GET,
371
- [new PathMiddleware(/^\/(index.html?)?$/), indexMiddleware],
372
- [new PathMiddleware('/large.html'), largeMiddleware],
373
- [new PathMiddleware('/chunk.html'), chunkMiddleware],
374
- [new PathMiddleware('/blank.html'), blankMiddleware],
375
- [new PathMiddleware('/nocontent.html'), 204],
376
- [new PathMiddleware('/gzip.html'), gzipMiddleware],
377
- [new PathMiddleware('/plaintext.html'), plainTextMiddleware],
378
- [new PathMiddleware('/get.json'), getJSONMiddleware],
379
- [new PathMiddleware('/form'), formGetMiddleware],
380
- ];
381
- setTest.add(getMiddlewareArray);
382
- // Modify after insertion
383
- getMiddlewareArray.push(
384
- [new PathMiddleware({ path: '/script.js' }), scriptMiddleware],
385
- );
386
- // Add terminator middleware
387
- getMiddlewareArray.push(
388
- [new PathMiddleware({ path: /\/output.json$/ }), outputMiddleware],
389
- );
390
-
391
- // Predefined Route
392
- const route = [
393
- [new PathMiddleware('/'), outputURLMiddleware],
394
- [new PathMiddleware('/foo'), outputURLMiddleware],
395
- [new PathMiddleware('/bar'), outputURLMiddleware],
396
- [new PathMiddleware('/baz'), outputURLMiddleware],
397
- ];
398
-
399
- // Automatic Path Routing
400
- middleware.push([
401
- MethodMiddleware.GET,
402
- [new PathMiddleware({ path: /^(\/subpath)\/?.*/, subPath: true }), [
403
- [new PathMiddleware('/'), outputURLMiddleware],
404
- [new PathMiddleware('/foo'), outputURLMiddleware],
405
- [new PathMiddleware('/bar'), outputURLMiddleware],
406
- ]],
407
- [PathMiddleware.SUBPATH('/subpath2'), [
408
- [new PathMiddleware('/'), outputURLMiddleware],
409
- [new PathMiddleware('/foo'), outputURLMiddleware],
410
- [new PathMiddleware('/baz'), outputURLMiddleware],
411
- ]],
412
- [PathMiddleware.SUBPATH('/subpath3'), route],
413
- [PathMiddleware.SUBPATH('/foo'), [
414
- [new PathMiddleware('/'), outputURLMiddleware],
415
- [PathMiddleware.SUBPATH('/bar'), [
416
- [new PathMiddleware('/'), outputURLMiddleware],
417
- [PathMiddleware.SUBPATH('/baz'), route],
418
- ]],
419
- ]],
420
- ]);
421
-
422
- // Add error handler
423
- middleware.push([MethodMiddleware.GET,
424
- new PathMiddleware('/error'),
425
- function throwError() {
426
- throw new Error('unexpected error!');
427
- }]);
428
- middleware.push([
429
- MethodMiddleware.GET,
430
- new PathMiddleware('/catch'),
431
- function throwError() {
432
- throw new Error('EXCEPTION!');
433
- },
434
- {
435
- onError: ({ error }) => {
436
- console.log('I catch and rethrow errors.');
437
- throw error;
438
- },
439
- },
440
- {
441
- onError: ({ error }) => {
442
- console.warn('Caught exception. Allowing continue.', error);
443
- return HttpHandler.CONTINUE;
444
- },
445
- },
446
- 'Error was caught.'
447
- ]);
448
-
449
- // Inline middleware and filter adding
450
- middleware.push(
451
- [
452
- MethodMiddleware.POST,
453
- [new PathMiddleware('/input.json'), inputMiddleware],
454
- [new PathMiddleware('/echo.json'), echoMiddleware],
455
- [new PathMiddleware('/form'), formPostMiddleware],
456
- ],
457
- [
458
- () => SHOULD_CRASH,
459
- () => { throw new Error('Break not called!'); },
460
- ],
461
- [
462
- MethodMiddleware.GET,
463
- new PathMiddleware('/cors.html'),
464
- corsTest,
465
- HttpHandler.END,
466
- ],
467
- ({ request }) => {
468
- console.log('Unknown', request.url);
469
- return 404;
470
- },
471
- );
472
-
473
- errorHandlers.push({
474
- onError({ error }) {
475
- console.error('Uncaught exception');
476
- console.error(error);
477
- return 500;
478
- },
479
- });
480
- console.dir([
481
- middleware,
482
- errorHandlers,
483
- ], { colors: true, depth: null });
484
- }
485
-
486
- const listener = new HttpListener({
487
- insecureHost: HTTP_HOST,
488
- insecurePort: HTTP_PORT,
489
- secureHost: HTTPS_HOST,
490
- securePort: HTTPS_PORT,
491
- tlsOptions: tls.setup({
492
- key: existsSync('./certificates/localhost-privkey.pem') && readFileSync('./certificates/localhost-privkey.pem'),
493
- cert: existsSync('./certificates/localhost-privkey.pem') && readFileSync('./certificates/localhost-cert.pem'),
494
- }),
495
- });
496
-
497
- setupHandler();
498
- listener.startAll().then(() => console.log('Ready.'));