@dooboostore/simple-boot-http-server 1.0.34 → 1.0.35
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/cjs/SimpleBootHttpServer.js +6 -3
- package/dist/cjs/SimpleBootHttpServer.js.map +2 -2
- package/dist/cjs/models/RequestResponse.js +38 -23
- package/dist/cjs/models/RequestResponse.js.map +2 -2
- package/dist/esm/SimpleBootHttpServer.js +6 -3
- package/dist/esm/SimpleBootHttpServer.js.map +2 -2
- package/dist/esm/models/RequestResponse.js +38 -23
- package/dist/esm/models/RequestResponse.js.map +2 -2
- package/dist/esm-bundle/dooboostore-simple-boot-http-server.esm.js +45 -27
- package/dist/esm-bundle/dooboostore-simple-boot-http-server.esm.js.map +2 -2
- package/dist/types/SimpleBootHttpServer.d.ts.map +1 -1
- package/dist/types/models/RequestResponse.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-simple-boot-http-server.umd.js +45 -27
- package/dist/umd-bundle/dooboostore-simple-boot-http-server.umd.js.map +2 -2
- package/package.json +4 -4
- package/src/SimpleBootHttpServer.ts +12 -3
- package/src/models/RequestResponse.ts +58 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dooboostore/simple-boot-http-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.35",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -100,11 +100,11 @@
|
|
|
100
100
|
"mime-types": "^2.1.34",
|
|
101
101
|
"node-gzip": "^1.1.2",
|
|
102
102
|
"ws": "^8.18.3",
|
|
103
|
+
"@dooboostore/core-node": "1.0.14",
|
|
104
|
+
"@dooboostore/dom-render": "1.0.115",
|
|
103
105
|
"@dooboostore/core": "1.0.33",
|
|
104
|
-
"@dooboostore/core-web": "1.0.14",
|
|
105
106
|
"@dooboostore/simple-boot": "1.0.60",
|
|
106
|
-
"@dooboostore/
|
|
107
|
-
"@dooboostore/core-node": "1.0.14"
|
|
107
|
+
"@dooboostore/core-web": "1.0.14"
|
|
108
108
|
},
|
|
109
109
|
"scripts": {
|
|
110
110
|
"clean": "rm -rf dist",
|
|
@@ -372,14 +372,18 @@ export class SimpleBootHttpServer extends SimpleApplication {
|
|
|
372
372
|
headers[HttpHeaders.ContentType] = contentType;
|
|
373
373
|
}
|
|
374
374
|
rr.resSetHeaders(headers);
|
|
375
|
-
|
|
375
|
+
if (resConfig?.status !== undefined) {
|
|
376
|
+
// Explicit decorator status wins. The Node default (200) is left
|
|
377
|
+
// alone otherwise so a handler-set status (e.g. via resEnd(Response))
|
|
378
|
+
// is not clobbered back to 200.
|
|
379
|
+
rr.resSetStatusCode(resConfig.status);
|
|
380
|
+
}
|
|
376
381
|
await rr.resStream(data, { compress: stream?.compress });
|
|
377
382
|
} else {
|
|
378
383
|
if (it.config?.resolver) {
|
|
379
384
|
const execute = typeof it.config.resolver === 'function' ? this.simstanceManager.getOrNewSim({target: it.config.resolver}) : it.config.resolver;
|
|
380
385
|
data = await execute?.resolve?.(data, rr, resConfig);
|
|
381
386
|
}
|
|
382
|
-
const status = resConfig?.status ?? HttpStatus.Ok;
|
|
383
387
|
const headers = resConfig?.header ?? {};
|
|
384
388
|
if (resConfig?.contentType) {
|
|
385
389
|
headers[HttpHeaders.ContentType] = resConfig.contentType;
|
|
@@ -390,7 +394,12 @@ export class SimpleBootHttpServer extends SimpleApplication {
|
|
|
390
394
|
data = JSON.stringify(data);
|
|
391
395
|
}
|
|
392
396
|
rr.resSetHeaders(headers)
|
|
393
|
-
|
|
397
|
+
if (resConfig?.status !== undefined) {
|
|
398
|
+
// An explicit decorator status wins. Otherwise leave the status
|
|
399
|
+
// alone (Node defaults to 200) so a handler-set status is not
|
|
400
|
+
// clobbered back to 200.
|
|
401
|
+
rr.resSetStatusCode(resConfig.status);
|
|
402
|
+
}
|
|
394
403
|
rr.resWrite(data);
|
|
395
404
|
}
|
|
396
405
|
}
|
|
@@ -18,6 +18,14 @@ import { Blob } from 'node:buffer';
|
|
|
18
18
|
import * as fs from 'fs';
|
|
19
19
|
import * as os from 'os';
|
|
20
20
|
import { RandomUtils } from '@dooboostore/core';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Responses with a send already in flight. Keyed by the underlying
|
|
24
|
+
* ServerResponse (shared by RequestResponse and its chains), so concurrent
|
|
25
|
+
* resEnd() calls for one response serialize instead of racing each other
|
|
26
|
+
* through gzip/setHeader into ERR_HTTP_HEADERS_SENT.
|
|
27
|
+
*/
|
|
28
|
+
const resEndingSet = new WeakSet<object>();
|
|
21
29
|
import { FileUtils } from '@dooboostore/core-node';
|
|
22
30
|
import { ConvertUtils as CoreConvertUtils } from '@dooboostore/core';
|
|
23
31
|
import { ConvertUtils } from '@dooboostore/core-node';
|
|
@@ -491,6 +499,12 @@ export class RequestResponse {
|
|
|
491
499
|
}
|
|
492
500
|
|
|
493
501
|
resWrite(chunk: string | Buffer | any, encoding: BufferEncoding = 'utf8') {
|
|
502
|
+
if (chunk === undefined) {
|
|
503
|
+
// A handler that already wrote via rr.resEnd()/resWrite() and returns
|
|
504
|
+
// undefined must not have its buffered body clobbered by the pipeline's
|
|
505
|
+
// return-value write (express#7510-class probe caught this).
|
|
506
|
+
return this.createRequestResponseChain(this.resWriteChunk);
|
|
507
|
+
}
|
|
494
508
|
this.resWriteChunk = chunk;
|
|
495
509
|
return this.createRequestResponseChain(this.resWriteChunk);
|
|
496
510
|
}
|
|
@@ -532,32 +546,52 @@ export class RequestResponse {
|
|
|
532
546
|
}
|
|
533
547
|
|
|
534
548
|
async resEnd(chunk?: string | Buffer | Blob | ArrayBuffer | Response) {
|
|
535
|
-
if (
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
}
|
|
543
|
-
this.res.statusCode = chunk.status;
|
|
544
|
-
this.resWriteChunk = Buffer.from(await chunk.arrayBuffer());
|
|
545
|
-
} else if (chunk instanceof Blob) {
|
|
546
|
-
this.resWriteChunk = Buffer.from(await chunk.arrayBuffer());
|
|
547
|
-
if (!this.resHeader(HttpHeaders.ContentType)) {
|
|
548
|
-
this.res.setHeader(HttpHeaders.ContentType, chunk.type);
|
|
549
|
-
}
|
|
550
|
-
} else if (chunk instanceof ArrayBuffer) {
|
|
551
|
-
this.resWriteChunk = Buffer.from(chunk);
|
|
552
|
-
} else {
|
|
553
|
-
this.resWriteChunk = chunk ?? this.resWriteChunk;
|
|
549
|
+
if (resEndingSet.has(this.res) || this.resIsDone()) {
|
|
550
|
+
// A send is already in flight (or finished): concurrent resEnd() calls
|
|
551
|
+
// — e.g. a handler-sent body followed by the pipeline's final flush —
|
|
552
|
+
// must not start a second send. Without this guard the two sends race
|
|
553
|
+
// through gzip/setHeader and the loser throws ERR_HTTP_HEADERS_SENT.
|
|
554
|
+
// The set is keyed by the underlying response (shared by chains).
|
|
555
|
+
return;
|
|
554
556
|
}
|
|
557
|
+
resEndingSet.add(this.res);
|
|
558
|
+
try {
|
|
559
|
+
if (typeof ArrayBuffer !== 'undefined' && ArrayBuffer.isView(chunk) && !Buffer.isBuffer(chunk)) {
|
|
560
|
+
// Typed-array views (incl. DataView, which Node's res.end() rejects outright)
|
|
561
|
+
// are normalized to their exact byte range. Native res.end() already honors
|
|
562
|
+
// byteOffset for typed arrays, but DataView throws ERR_INVALID_ARG_TYPE,
|
|
563
|
+
// so every non-Buffer view goes through here (expressjs/express#7510 class).
|
|
564
|
+
const view = chunk as unknown as { buffer: ArrayBuffer; byteOffset: number; byteLength: number };
|
|
565
|
+
this.resWriteChunk = Buffer.from(view.buffer, view.byteOffset, view.byteLength);
|
|
566
|
+
} else if (chunk instanceof Response) {
|
|
567
|
+
chunk.headers.forEach((value, key) => {
|
|
568
|
+
this.res.setHeader(`ORIGIN-${key}`, value);
|
|
569
|
+
});
|
|
570
|
+
if (!this.resHeader(HttpHeaders.ContentType)) {
|
|
571
|
+
const contentType = chunk.headers.get(HttpHeaders.ContentType);
|
|
572
|
+
if (contentType) this.res.setHeader(HttpHeaders.ContentType, contentType);
|
|
573
|
+
}
|
|
574
|
+
this.res.statusCode = chunk.status;
|
|
575
|
+
this.resWriteChunk = Buffer.from(await chunk.arrayBuffer());
|
|
576
|
+
} else if (chunk instanceof Blob) {
|
|
577
|
+
this.resWriteChunk = Buffer.from(await chunk.arrayBuffer());
|
|
578
|
+
if (!this.resHeader(HttpHeaders.ContentType)) {
|
|
579
|
+
this.res.setHeader(HttpHeaders.ContentType, chunk.type);
|
|
580
|
+
}
|
|
581
|
+
} else if (chunk instanceof ArrayBuffer) {
|
|
582
|
+
this.resWriteChunk = Buffer.from(chunk);
|
|
583
|
+
} else {
|
|
584
|
+
this.resWriteChunk = chunk ?? this.resWriteChunk;
|
|
585
|
+
}
|
|
555
586
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
587
|
+
if (this.req.readable) {
|
|
588
|
+
await this.reqBodyData();
|
|
589
|
+
await this.resEndChunk();
|
|
590
|
+
} else {
|
|
591
|
+
await this.resEndChunk();
|
|
592
|
+
}
|
|
593
|
+
} finally {
|
|
594
|
+
resEndingSet.delete(this.res);
|
|
561
595
|
}
|
|
562
596
|
}
|
|
563
597
|
|