@dooboostore/simple-boot-http-server 1.0.14 → 1.0.16
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/README.MD +40 -6
- package/SimpleBootHttpServer.d.ts +0 -2
- package/SimpleBootHttpServer.js +1 -1
- package/decorators/MethodMapping.js +1 -1
- package/endpoints/CrossDomainHeaderEndPoint.js +1 -1
- package/filters/CacheFilter.js +16 -16
- package/filters/HttpStatusFilter.js +4 -4
- package/filters/OpenApi3Filter.js +4 -4
- package/filters/ResourceFilter.js +6 -6
- package/filters/ThrowFilter.js +4 -4
- package/models/RequestResponse.d.ts +2 -7
- package/models/datas/MultipartData.d.ts +0 -2
- package/models/datas/ReqHeader.d.ts +0 -1
- package/option/HttpServerOption.d.ts +0 -2
- package/package.json +9 -9
- package/session/SessionManager.js +5 -5
package/README.MD
CHANGED
|
@@ -198,23 +198,57 @@ const option = new HttpServerOption({
|
|
|
198
198
|
|
|
199
199
|
Define global exception handlers in an `Advice` class. This class is managed by `@Sim` and can catch errors thrown from anywhere in the application.
|
|
200
200
|
|
|
201
|
+
### Preventing `ERR_HTTP_HEADERS_SENT`
|
|
202
|
+
|
|
203
|
+
A common issue in asynchronous frameworks is the `ERR_HTTP_HEADERS_SENT` error. This occurs when an error is thrown *after* a response has already been sent by another part of the application (like a filter). The global exception handler then tries to send a *second* response, causing a crash.
|
|
204
|
+
|
|
205
|
+
To prevent this, **always check if a response has already been sent** before sending a new one from your exception handler. The `RequestResponse` object provides the `resIsDone()` method for this purpose.
|
|
206
|
+
|
|
201
207
|
```typescript
|
|
202
|
-
import { Sim, ExceptionHandler } from '@dooboostore/simple-boot';
|
|
203
|
-
import { RequestResponse, NotFoundError } from '@dooboostore/simple-boot-http-server';
|
|
208
|
+
import { Sim, ExceptionHandler, Inject, ExceptionHandlerSituationType } from '@dooboostore/simple-boot';
|
|
209
|
+
import { RequestResponse, NotFoundError, HttpError, InternalServerError, Mimes, HttpStatus } from '@dooboostore/simple-boot-http-server';
|
|
204
210
|
|
|
205
211
|
@Sim
|
|
206
212
|
export class GlobalErrorHandler {
|
|
213
|
+
|
|
207
214
|
@ExceptionHandler({ type: NotFoundError })
|
|
208
215
|
handleNotFound(rr: RequestResponse, e: NotFoundError) {
|
|
216
|
+
// Critical check
|
|
217
|
+
if (rr.resIsDone()) {
|
|
218
|
+
console.error(`Not found error for URL: ${rr.reqUrl}, but response already sent.`, e);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
209
221
|
rr.resStatusCode(404).resSetHeader('X-Error-Message', 'Resource not found');
|
|
210
222
|
console.error(`Not found error for URL: ${rr.reqUrl}`, e);
|
|
223
|
+
rr.resEnd(JSON.stringify({ error: 'Resource not found' }));
|
|
211
224
|
}
|
|
212
225
|
|
|
213
226
|
@ExceptionHandler()
|
|
214
|
-
|
|
215
|
-
console.error(
|
|
216
|
-
|
|
217
|
-
|
|
227
|
+
async catch(@Inject({situationType: ExceptionHandlerSituationType.ERROR_OBJECT}) e: any, rr: RequestResponse) {
|
|
228
|
+
console.error(`GlobalAdvice.catch ${rr.reqUrl}`, e);
|
|
229
|
+
|
|
230
|
+
// Critical check to prevent writing to an already-sent response
|
|
231
|
+
if (rr.resIsDone()) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
rr.resStatusCode(HttpStatus.InternalServerError);
|
|
236
|
+
rr.resSetHeader('Content-Type', Mimes.ApplicationJson);
|
|
237
|
+
|
|
238
|
+
let data = '';
|
|
239
|
+
if (e instanceof HttpError) {
|
|
240
|
+
rr.resStatusCode(e.status);
|
|
241
|
+
data = JSON.stringify(e);
|
|
242
|
+
} else if (e instanceof Error){
|
|
243
|
+
const error = new InternalServerError();
|
|
244
|
+
error.data = {message: e.message, stack: e.stack}; // Be careful with stack in production
|
|
245
|
+
data = JSON.stringify(error);
|
|
246
|
+
} else {
|
|
247
|
+
const error = new InternalServerError();
|
|
248
|
+
error.data = e;
|
|
249
|
+
data = JSON.stringify(error);
|
|
250
|
+
}
|
|
251
|
+
await rr.resEnd(data);
|
|
218
252
|
}
|
|
219
253
|
}
|
|
220
254
|
|
package/SimpleBootHttpServer.js
CHANGED
|
@@ -18,7 +18,7 @@ import { HttpHeaders } from './codes/HttpHeaders';
|
|
|
18
18
|
import { Mimes } from './codes/Mimes';
|
|
19
19
|
import { ExceptionHandlerSituationType, targetExceptionHandler } from '@dooboostore/simple-boot/decorators/exception/ExceptionDecorator';
|
|
20
20
|
import { getInject, SituationTypeContainer, SituationTypeContainers } from '@dooboostore/simple-boot/decorators/inject/Inject';
|
|
21
|
-
import { ReflectUtils } from '@dooboostore/
|
|
21
|
+
import { ReflectUtils } from '@dooboostore/core/reflect/ReflectUtils';
|
|
22
22
|
import { ReqFormUrlBody } from './models/datas/body/ReqFormUrlBody';
|
|
23
23
|
import { ReqJsonBody } from './models/datas/body/ReqJsonBody';
|
|
24
24
|
import { ReqHeader } from './models/datas/ReqHeader';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
|
-
import { ReflectUtils } from '@dooboostore/
|
|
2
|
+
import { ReflectUtils } from '@dooboostore/core/reflect/ReflectUtils';
|
|
3
3
|
import { HttpMethod } from '../codes/HttpMethod';
|
|
4
4
|
export var UrlMappingSituationType;
|
|
5
5
|
(function (UrlMappingSituationType) {
|
|
@@ -13,8 +13,8 @@ export class CrossDomainHeaderEndPoint {
|
|
|
13
13
|
this.config = config;
|
|
14
14
|
}
|
|
15
15
|
endPoint(rr, app) {
|
|
16
|
-
var _a, _b, _c, _d;
|
|
17
16
|
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
+
var _a, _b, _c, _d;
|
|
18
18
|
if ((_a = this.config) === null || _a === void 0 ? void 0 : _a.accessControlAllowOrigin) {
|
|
19
19
|
rr.resSetHeader(HttpHeaders.AccessControlAllowOrigin, Array.isArray(this.config.accessControlAllowOrigin) ? this.config.accessControlAllowOrigin.join(', ') : this.config.accessControlAllowOrigin);
|
|
20
20
|
}
|
package/filters/CacheFilter.js
CHANGED
|
@@ -22,11 +22,11 @@ export class CacheFilter {
|
|
|
22
22
|
return __awaiter(this, void 0, void 0, function* () {
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
|
-
proceedBefore(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const filter = (
|
|
25
|
+
proceedBefore(_a) {
|
|
26
|
+
return __awaiter(this, arguments, void 0, function* ({ rr, app, carrier }) {
|
|
27
|
+
var _b, _c, _d, _e, _f, _g, _h;
|
|
28
|
+
var _j, _k, _l;
|
|
29
|
+
const filter = (_c = (_b = this.config).filter) === null || _c === void 0 ? void 0 : _c.call(_b, rr, app);
|
|
30
30
|
if (!filter) {
|
|
31
31
|
return true;
|
|
32
32
|
}
|
|
@@ -41,16 +41,16 @@ export class CacheFilter {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
const date = new Date().toISOString();
|
|
44
|
-
(
|
|
44
|
+
(_d = this.cacheConfig) !== null && _d !== void 0 ? _d : (this.cacheConfig = {
|
|
45
45
|
createDate: date,
|
|
46
46
|
updateDate: date,
|
|
47
47
|
caches: {}
|
|
48
48
|
});
|
|
49
|
-
(
|
|
50
|
-
(
|
|
51
|
-
(
|
|
49
|
+
(_e = (_j = this.cacheConfig).createDate) !== null && _e !== void 0 ? _e : (_j.createDate = date);
|
|
50
|
+
(_f = (_k = this.cacheConfig).updateDate) !== null && _f !== void 0 ? _f : (_k.updateDate = date);
|
|
51
|
+
(_g = (_l = this.cacheConfig).caches) !== null && _g !== void 0 ? _g : (_l.caches = {});
|
|
52
52
|
const rkey = this.config.key(rr, app);
|
|
53
|
-
const sessionData = { key: rkey.key, lifeTime: (
|
|
53
|
+
const sessionData = { key: rkey.key, lifeTime: (_h = rkey.lifeTime) !== null && _h !== void 0 ? _h : this.config.config.lifeTime, isUpdate: false, responseHeaders: rkey.responseHeaders };
|
|
54
54
|
const cache = this.cacheConfig.caches[sessionData.key];
|
|
55
55
|
if (cache) {
|
|
56
56
|
const existes = this.config.config.cacheDir ? FileUtils.existesSync(cache.path) : this.storage.has(cache.path);
|
|
@@ -89,10 +89,10 @@ export class CacheFilter {
|
|
|
89
89
|
getChunk(path) {
|
|
90
90
|
return this.config.config.cacheDir ? FileUtils.readSync(path) : this.storage.get(path);
|
|
91
91
|
}
|
|
92
|
-
proceedAfter(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
92
|
+
proceedAfter(_a) {
|
|
93
|
+
return __awaiter(this, arguments, void 0, function* ({ rr, app, carrier }) {
|
|
94
|
+
var _b, _c;
|
|
95
|
+
var _d;
|
|
96
96
|
const sessionData = carrier.get(CacheFilter.CARRIER_KEY_NAME);
|
|
97
97
|
if (sessionData && sessionData.isUpdate) {
|
|
98
98
|
try {
|
|
@@ -108,10 +108,10 @@ export class CacheFilter {
|
|
|
108
108
|
p = this.writeChunk(destFileUUID, resBody);
|
|
109
109
|
const updateDateNow = Date.now();
|
|
110
110
|
const updateDate = new Date(updateDateNow).toISOString();
|
|
111
|
-
(
|
|
111
|
+
(_b = (_d = this.cacheConfig).createDate) !== null && _b !== void 0 ? _b : (_d.createDate = updateDate);
|
|
112
112
|
this.cacheConfig.updateDate = updateDate;
|
|
113
113
|
const responseHeaders = {};
|
|
114
|
-
(
|
|
114
|
+
(_c = sessionData.responseHeaders) === null || _c === void 0 ? void 0 : _c.map(it => it.toLowerCase()).forEach(it => {
|
|
115
115
|
responseHeaders[it] = rr.resHeader(it);
|
|
116
116
|
});
|
|
117
117
|
this.cacheConfig.caches[sessionData.key] = {
|
|
@@ -11,15 +11,15 @@ export class HttpStatusFilter {
|
|
|
11
11
|
constructor(httpStatus) {
|
|
12
12
|
this.httpStatus = httpStatus;
|
|
13
13
|
}
|
|
14
|
-
proceedBefore(
|
|
15
|
-
return __awaiter(this,
|
|
14
|
+
proceedBefore(_a) {
|
|
15
|
+
return __awaiter(this, arguments, void 0, function* ({ rr, app }) {
|
|
16
16
|
rr.resStatusCode(this.httpStatus);
|
|
17
17
|
rr.resEnd();
|
|
18
18
|
return false;
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
|
-
proceedAfter(
|
|
22
|
-
return __awaiter(this,
|
|
21
|
+
proceedAfter(_a) {
|
|
22
|
+
return __awaiter(this, arguments, void 0, function* ({ rr, app }) {
|
|
23
23
|
return true;
|
|
24
24
|
});
|
|
25
25
|
}
|
|
@@ -25,8 +25,8 @@ export class OpenApi3Filter {
|
|
|
25
25
|
return __awaiter(this, void 0, void 0, function* () {
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
|
-
proceedBefore(
|
|
29
|
-
return __awaiter(this,
|
|
28
|
+
proceedBefore(_a) {
|
|
29
|
+
return __awaiter(this, arguments, void 0, function* ({ rr, app }) {
|
|
30
30
|
if (rr.reqUrl === this.config.path) {
|
|
31
31
|
const map = app.routerManager.routingMap();
|
|
32
32
|
const data = {
|
|
@@ -48,8 +48,8 @@ export class OpenApi3Filter {
|
|
|
48
48
|
return true;
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
|
-
proceedAfter(
|
|
52
|
-
return __awaiter(this,
|
|
51
|
+
proceedAfter(_a) {
|
|
52
|
+
return __awaiter(this, arguments, void 0, function* ({ rr, app }) {
|
|
53
53
|
return true;
|
|
54
54
|
});
|
|
55
55
|
}
|
|
@@ -21,10 +21,10 @@ export class ResourceFilter {
|
|
|
21
21
|
return __awaiter(this, void 0, void 0, function* () {
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
|
-
proceedBefore(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const url = ((
|
|
24
|
+
proceedBefore(_a) {
|
|
25
|
+
return __awaiter(this, arguments, void 0, function* ({ rr, app }) {
|
|
26
|
+
var _b;
|
|
27
|
+
const url = ((_b = rr.reqUrlPathName) !== null && _b !== void 0 ? _b : '').replace(/\.\./g, '');
|
|
28
28
|
const urlObj = rr.reqUrlObject;
|
|
29
29
|
let sw = true;
|
|
30
30
|
const regExps = this.resourceRegex.filter(it => {
|
|
@@ -63,8 +63,8 @@ export class ResourceFilter {
|
|
|
63
63
|
return sw;
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
|
-
proceedAfter(
|
|
67
|
-
return __awaiter(this,
|
|
66
|
+
proceedAfter(_a) {
|
|
67
|
+
return __awaiter(this, arguments, void 0, function* ({ rr, app }) {
|
|
68
68
|
return true;
|
|
69
69
|
});
|
|
70
70
|
}
|
package/filters/ThrowFilter.js
CHANGED
|
@@ -15,13 +15,13 @@ export class ThrowFilter {
|
|
|
15
15
|
return __awaiter(this, void 0, void 0, function* () {
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
|
-
proceedBefore(
|
|
19
|
-
return __awaiter(this,
|
|
18
|
+
proceedBefore(_a) {
|
|
19
|
+
return __awaiter(this, arguments, void 0, function* ({ rr, app }) {
|
|
20
20
|
return true;
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
|
-
proceedAfter(
|
|
24
|
-
return __awaiter(this,
|
|
23
|
+
proceedAfter(_a) {
|
|
24
|
+
return __awaiter(this, arguments, void 0, function* ({ rr, app }) {
|
|
25
25
|
const sw = rr.resIsDone();
|
|
26
26
|
if (!sw) {
|
|
27
27
|
throw this.error;
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
/// <reference types="node" />
|
|
3
|
-
/// <reference types="node" />
|
|
4
|
-
/// <reference types="node" />
|
|
5
|
-
/// <reference types="node" />
|
|
6
1
|
import { IncomingMessage, OutgoingHttpHeader, OutgoingHttpHeaders, ServerResponse } from 'http';
|
|
7
2
|
import { HttpHeaders } from '../codes/HttpHeaders';
|
|
8
3
|
import { Mimes } from '../codes/Mimes';
|
|
@@ -78,8 +73,8 @@ export declare class RequestResponse {
|
|
|
78
73
|
reqSessionSet(key: string, value: any): void;
|
|
79
74
|
reqSessionGet<T = any>(key: string): T | undefined;
|
|
80
75
|
resSetStatusCode(statusCode: number): RequestResponseChain<number>;
|
|
81
|
-
resWrite(chunk: string | Buffer | any, encoding?: BufferEncoding): RequestResponseChain<string | Buffer
|
|
82
|
-
resWriteJson(chunk: any, encoding?: BufferEncoding): RequestResponseChain<string | Buffer
|
|
76
|
+
resWrite(chunk: string | Buffer | any, encoding?: BufferEncoding): RequestResponseChain<string | Buffer<ArrayBufferLike>>;
|
|
77
|
+
resWriteJson(chunk: any, encoding?: BufferEncoding): RequestResponseChain<string | Buffer<ArrayBufferLike>>;
|
|
83
78
|
resSetHeader(key: HttpHeaders | string, value: string | string[]): RequestResponseChain<ServerResponse<IncomingMessage>>;
|
|
84
79
|
resAddHeader(key: HttpHeaders | string, value: string | string[]): RequestResponseChain<ServerResponse<IncomingMessage>>;
|
|
85
80
|
resSetHeaders(headers: {
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dooboostore/simple-boot-http-server",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"main": "dist/
|
|
3
|
+
"version": "1.0.16",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
5
6
|
"license": "MIT",
|
|
6
7
|
"description": "back end http server frameworks",
|
|
7
8
|
"files": [
|
|
@@ -51,7 +52,7 @@
|
|
|
51
52
|
"ts-node": "^10.0.0",
|
|
52
53
|
"ts-node-dev": "^1.1.8",
|
|
53
54
|
"tsconfig-paths": "^3.9.0",
|
|
54
|
-
"typescript": "^
|
|
55
|
+
"typescript": "^5.9.2"
|
|
55
56
|
},
|
|
56
57
|
"dependencies": {
|
|
57
58
|
"@types/node-gzip": "^1.1.0",
|
|
@@ -59,17 +60,16 @@
|
|
|
59
60
|
"mime-types": "^2.1.34",
|
|
60
61
|
"node-gzip": "^1.1.2",
|
|
61
62
|
"reflect-metadata": "^0.2.2",
|
|
62
|
-
"
|
|
63
|
-
"@dooboostore/
|
|
63
|
+
"@dooboostore/core": "1.0.8",
|
|
64
|
+
"@dooboostore/dom-render": "1.0.102",
|
|
64
65
|
"@dooboostore/core-node": "1.0.1",
|
|
65
66
|
"@dooboostore/core-web": "1.0.1",
|
|
66
|
-
"@dooboostore/
|
|
67
|
-
"@dooboostore/simple-boot": "1.0.43"
|
|
67
|
+
"@dooboostore/simple-boot": "1.0.44"
|
|
68
68
|
},
|
|
69
69
|
"scripts": {
|
|
70
|
-
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
70
|
+
"tsc:build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
71
71
|
"clean": "rm -rf dist",
|
|
72
|
-
"npm:build": "pnpm run build && cp README.MD dist/ && pnpm pack && TARBALL_NAME=$(ls dooboostore-simple-boot-http-server-*.tgz | head -n 1) && tar -xzf \"$TARBALL_NAME\" -C dist --strip-components=1 package/package.json && rm \"$TARBALL_NAME\"",
|
|
72
|
+
"npm:build": "pnpm run clean && pnpm run tsc:build && cp README.MD dist/ && pnpm pack && TARBALL_NAME=$(ls dooboostore-simple-boot-http-server-*.tgz | head -n 1) && tar -xzf \"$TARBALL_NAME\" -C dist --strip-components=1 package/package.json && rm \"$TARBALL_NAME\"",
|
|
73
73
|
"npm:clean": "rm -rf dist",
|
|
74
74
|
"npm:publish": "pnpm run npm:build && cd dist && pnpm publish --no-git-checks",
|
|
75
75
|
"npm:publish:clean": "pnpm run npm:publish && rm -rf dist",
|
|
@@ -25,8 +25,8 @@ export class SessionManager {
|
|
|
25
25
|
}), this.sessionOption.expiredTime);
|
|
26
26
|
}
|
|
27
27
|
getSessionUUIDs() {
|
|
28
|
-
var _a;
|
|
29
28
|
return __awaiter(this, void 0, void 0, function* () {
|
|
29
|
+
var _a;
|
|
30
30
|
if ((_a = this.sessionOption.provider) === null || _a === void 0 ? void 0 : _a.uuids) {
|
|
31
31
|
return this.sessionOption.provider.uuids();
|
|
32
32
|
}
|
|
@@ -36,8 +36,8 @@ export class SessionManager {
|
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
38
|
deleteSession(uuid) {
|
|
39
|
-
var _a;
|
|
40
39
|
return __awaiter(this, void 0, void 0, function* () {
|
|
40
|
+
var _a;
|
|
41
41
|
if ((_a = this.sessionOption.provider) === null || _a === void 0 ? void 0 : _a.delete) {
|
|
42
42
|
this.sessionOption.provider.delete(uuid);
|
|
43
43
|
}
|
|
@@ -47,8 +47,8 @@ export class SessionManager {
|
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
49
|
getSessionDataSet(uuid) {
|
|
50
|
-
var _a;
|
|
51
50
|
return __awaiter(this, void 0, void 0, function* () {
|
|
51
|
+
var _a;
|
|
52
52
|
if ((_a = this.sessionOption.provider) === null || _a === void 0 ? void 0 : _a.get) {
|
|
53
53
|
return this.sessionOption.provider.get(uuid);
|
|
54
54
|
}
|
|
@@ -58,8 +58,8 @@ export class SessionManager {
|
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
60
|
setSessionDataSet(uuid, dataSet) {
|
|
61
|
-
var _a;
|
|
62
61
|
return __awaiter(this, void 0, void 0, function* () {
|
|
62
|
+
var _a;
|
|
63
63
|
if ((_a = this.sessionOption.provider) === null || _a === void 0 ? void 0 : _a.set) {
|
|
64
64
|
this.sessionOption.provider.set(uuid, dataSet);
|
|
65
65
|
}
|
|
@@ -75,8 +75,8 @@ export class SessionManager {
|
|
|
75
75
|
return RandomUtils.uuid('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
|
|
76
76
|
}
|
|
77
77
|
session(rrOrUUID) {
|
|
78
|
-
var _a;
|
|
79
78
|
return __awaiter(this, void 0, void 0, function* () {
|
|
79
|
+
var _a;
|
|
80
80
|
let uuid;
|
|
81
81
|
if (rrOrUUID instanceof RequestResponse) {
|
|
82
82
|
uuid = (_a = rrOrUUID.reqCookieGet(this.sessionOption.key)) !== null && _a !== void 0 ? _a : this.makeUUID();
|