@duplojs/http 0.6.2 → 0.7.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/client/getBody.cjs +1 -1
- package/dist/client/getBody.mjs +1 -1
- package/dist/client/hooks.d.ts +1 -25
- package/dist/client/httpClient.d.ts +29 -25
- package/dist/client/promiseRequest.cjs +3 -0
- package/dist/client/promiseRequest.d.ts +31 -39
- package/dist/client/promiseRequest.mjs +4 -1
- package/dist/client/types/clientResponse.d.ts +6 -6
- package/dist/client/types/hooks.d.ts +25 -0
- package/dist/client/types/index.cjs +2 -0
- package/dist/client/types/index.d.ts +2 -0
- package/dist/client/types/index.mjs +2 -0
- package/dist/client/types/promiseRequestParams.cjs +2 -0
- package/dist/client/types/promiseRequestParams.d.ts +9 -0
- package/dist/client/types/promiseRequestParams.mjs +1 -0
- package/dist/client/types/serverRoute.d.ts +19 -1
- package/dist/client/unexpectedResponseError.d.ts +1 -2
- package/dist/core/builders/preflight/route.cjs +1 -0
- package/dist/core/builders/preflight/route.d.ts +4 -2
- package/dist/core/builders/preflight/route.mjs +1 -0
- package/dist/core/builders/route/builder.cjs +1 -0
- package/dist/core/builders/route/builder.d.ts +4 -2
- package/dist/core/builders/route/builder.mjs +1 -0
- package/dist/core/clean/newType.d.ts +1 -1
- package/dist/core/clean/primitive.d.ts +1 -1
- package/dist/core/defaultHooks/index.cjs +50 -0
- package/dist/core/defaultHooks/index.d.ts +5 -0
- package/dist/core/defaultHooks/index.mjs +48 -0
- package/dist/{interfaces/node/error → core/errors}/bodyParseWrongChunkReceived.cjs +6 -4
- package/dist/core/errors/bodyParseWrongChunkReceived.d.ts +9 -0
- package/dist/core/errors/bodyParseWrongChunkReceived.mjs +14 -0
- package/dist/{interfaces/node/error → core/errors}/bodySizeExceedsLimitError.cjs +2 -2
- package/dist/{interfaces/node/error → core/errors}/bodySizeExceedsLimitError.d.ts +2 -2
- package/dist/{interfaces/node/error → core/errors}/bodySizeExceedsLimitError.mjs +2 -2
- package/dist/{interfaces/node/error → core/errors}/index.cjs +6 -4
- package/dist/{interfaces/node/error → core/errors}/index.d.ts +3 -2
- package/dist/{interfaces/node/error → core/errors}/index.mjs +3 -2
- package/dist/core/errors/parseJsonError.cjs +16 -0
- package/dist/core/errors/parseJsonError.d.ts +9 -0
- package/dist/core/errors/parseJsonError.mjs +14 -0
- package/dist/core/errors/wrongContentTypeError.cjs +16 -0
- package/dist/core/errors/wrongContentTypeError.d.ts +9 -0
- package/dist/core/errors/wrongContentTypeError.mjs +14 -0
- package/dist/core/functionsBuilders/route/create.d.ts +2 -2
- package/dist/core/functionsBuilders/route/default.cjs +0 -11
- package/dist/core/functionsBuilders/route/default.mjs +0 -11
- package/dist/core/functionsBuilders/route/hook.d.ts +2 -2
- package/dist/core/functionsBuilders/steps/create.d.ts +2 -2
- package/dist/core/functionsBuilders/steps/defaults/cutStep.cjs +15 -18
- package/dist/core/functionsBuilders/steps/defaults/cutStep.mjs +15 -18
- package/dist/core/functionsBuilders/steps/defaults/extractStep.cjs +43 -19
- package/dist/core/functionsBuilders/steps/defaults/extractStep.d.ts +2 -1
- package/dist/core/functionsBuilders/steps/defaults/extractStep.mjs +44 -20
- package/dist/core/functionsBuilders/steps/defaults/handlerStep.cjs +14 -8
- package/dist/core/functionsBuilders/steps/defaults/handlerStep.mjs +14 -8
- package/dist/core/hub/defaultBodyController.cjs +8 -0
- package/dist/core/hub/defaultBodyController.d.ts +1 -0
- package/dist/core/hub/defaultBodyController.mjs +6 -0
- package/dist/core/hub/hooks.cjs +3 -1
- package/dist/core/hub/hooks.d.ts +2 -3
- package/dist/core/hub/hooks.mjs +3 -1
- package/dist/core/hub/index.cjs +101 -127
- package/dist/core/hub/index.d.ts +33 -34
- package/dist/core/hub/index.mjs +100 -128
- package/dist/core/implementHttpServer.cjs +5 -5
- package/dist/core/implementHttpServer.d.ts +2 -1
- package/dist/core/implementHttpServer.mjs +5 -5
- package/dist/core/index.cjs +34 -10
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.mjs +13 -2
- package/dist/core/request/bodyController/base.cjs +36 -0
- package/dist/core/request/bodyController/base.d.ts +28 -0
- package/dist/core/request/bodyController/base.mjs +34 -0
- package/dist/core/request/bodyController/formData.cjs +28 -0
- package/dist/core/request/bodyController/formData.d.ts +22 -0
- package/dist/core/request/bodyController/formData.mjs +25 -0
- package/dist/core/request/bodyController/index.cjs +13 -0
- package/dist/core/request/bodyController/index.d.ts +3 -0
- package/dist/core/request/bodyController/index.mjs +3 -0
- package/dist/core/request/bodyController/text.cjs +14 -0
- package/dist/core/request/bodyController/text.d.ts +10 -0
- package/dist/core/request/bodyController/text.mjs +11 -0
- package/dist/core/{request.cjs → request/index.cjs} +21 -3
- package/dist/core/{request.d.ts → request/index.d.ts} +10 -3
- package/dist/core/request/index.mjs +50 -0
- package/dist/core/response/contract.d.ts +1 -1
- package/dist/core/route/hooks.d.ts +0 -2
- package/dist/core/route/index.d.ts +2 -1
- package/dist/core/router/index.cjs +27 -8
- package/dist/core/router/index.d.ts +2 -1
- package/dist/core/router/index.mjs +28 -10
- package/dist/core/router/notFoundBodyReaderImplementationError.cjs +16 -0
- package/dist/core/router/notFoundBodyReaderImplementationError.d.ts +11 -0
- package/dist/core/router/notFoundBodyReaderImplementationError.mjs +14 -0
- package/dist/core/router/types/buildedRouter.d.ts +3 -3
- package/dist/core/steps/extract.d.ts +3 -1
- package/dist/core/steps/types/steps.d.ts +1 -3
- package/dist/core/types/hosts.cjs +2 -0
- package/dist/core/types/hosts.d.ts +4 -0
- package/dist/core/types/hosts.mjs +1 -0
- package/dist/core/types/httpServerParams.cjs +2 -0
- package/dist/core/types/httpServerParams.d.ts +11 -0
- package/dist/core/types/httpServerParams.mjs +1 -0
- package/dist/core/types/index.cjs +2 -0
- package/dist/core/types/index.d.ts +2 -0
- package/dist/core/types/index.mjs +2 -0
- package/dist/interfaces/bun/types/request.cjs +1 -1
- package/dist/interfaces/bun/types/request.mjs +1 -1
- package/dist/interfaces/node/bodyReaders/formData/error.cjs +14 -0
- package/dist/interfaces/node/bodyReaders/formData/error.d.ts +8 -0
- package/dist/interfaces/node/bodyReaders/formData/error.mjs +12 -0
- package/dist/interfaces/node/bodyReaders/formData/index.cjs +94 -0
- package/dist/interfaces/node/bodyReaders/formData/index.d.ts +4 -0
- package/dist/interfaces/node/bodyReaders/formData/index.mjs +90 -0
- package/dist/interfaces/node/bodyReaders/formData/readRequestFormData.cjs +175 -0
- package/dist/interfaces/node/bodyReaders/formData/readRequestFormData.d.ts +21 -0
- package/dist/interfaces/node/bodyReaders/formData/readRequestFormData.mjs +173 -0
- package/dist/interfaces/node/bodyReaders/index.cjs +9 -0
- package/dist/interfaces/node/bodyReaders/index.d.ts +2 -0
- package/dist/interfaces/node/bodyReaders/index.mjs +2 -0
- package/dist/interfaces/node/bodyReaders/text/index.cjs +41 -0
- package/dist/interfaces/node/bodyReaders/text/index.d.ts +3 -0
- package/dist/interfaces/node/bodyReaders/text/index.mjs +38 -0
- package/dist/interfaces/node/bodyReaders/text/readRequestText.cjs +37 -0
- package/dist/interfaces/node/bodyReaders/text/readRequestText.d.ts +6 -0
- package/dist/interfaces/node/bodyReaders/text/readRequestText.mjs +35 -0
- package/dist/interfaces/node/createHttpServer.cjs +13 -5
- package/dist/interfaces/node/createHttpServer.d.ts +13 -12
- package/dist/interfaces/node/createHttpServer.mjs +13 -5
- package/dist/interfaces/node/hooks/index.cjs +47 -0
- package/dist/interfaces/node/hooks/index.d.ts +5 -0
- package/dist/interfaces/node/hooks/index.mjs +45 -0
- package/dist/interfaces/node/index.cjs +13 -9
- package/dist/interfaces/node/index.d.ts +1 -1
- package/dist/interfaces/node/index.mjs +7 -5
- package/dist/interfaces/node/types/index.cjs +0 -1
- package/dist/interfaces/node/types/index.d.ts +0 -1
- package/dist/interfaces/node/types/index.mjs +0 -1
- package/dist/interfaces/node/types/request.cjs +1 -1
- package/dist/interfaces/node/types/request.mjs +1 -1
- package/dist/plugins/codeGenerator/index.cjs +2 -0
- package/dist/plugins/codeGenerator/index.mjs +1 -1
- package/dist/plugins/codeGenerator/plugin.cjs +8 -4
- package/dist/plugins/codeGenerator/plugin.mjs +9 -5
- package/dist/plugins/codeGenerator/routeToDataParser.cjs +24 -1
- package/dist/plugins/codeGenerator/routeToDataParser.d.ts +34 -0
- package/dist/plugins/codeGenerator/routeToDataParser.mjs +24 -3
- package/dist/plugins/codeGenerator/typescriptTransfomer.cjs +9 -0
- package/dist/plugins/codeGenerator/typescriptTransfomer.d.ts +1 -0
- package/dist/plugins/codeGenerator/typescriptTransfomer.mjs +7 -0
- package/dist/plugins/openApiGenerator/makeOpenApiRoute.d.ts +3 -0
- package/dist/plugins/openApiGenerator/plugin.cjs +3 -3
- package/dist/plugins/openApiGenerator/plugin.mjs +4 -4
- package/dist/plugins/openApiGenerator/routeToOpenApi.cjs +16 -1
- package/dist/plugins/openApiGenerator/routeToOpenApi.d.ts +10 -1
- package/dist/plugins/openApiGenerator/routeToOpenApi.mjs +16 -1
- package/dist/plugins/openApiGenerator/types/entrypoint.d.ts +6 -1
- package/dist/plugins/openApiGenerator/types/openApiMethod.d.ts +1 -1
- package/package.json +5 -6
- package/dist/core/request.mjs +0 -32
- package/dist/interfaces/node/error/bodyParseUnknownError.cjs +0 -16
- package/dist/interfaces/node/error/bodyParseUnknownError.d.ts +0 -9
- package/dist/interfaces/node/error/bodyParseUnknownError.mjs +0 -14
- package/dist/interfaces/node/error/bodyParseWrongChunkReceived.d.ts +0 -8
- package/dist/interfaces/node/error/bodyParseWrongChunkReceived.mjs +0 -12
- package/dist/interfaces/node/hooks.cjs +0 -126
- package/dist/interfaces/node/hooks.d.ts +0 -8
- package/dist/interfaces/node/hooks.mjs +0 -124
- package/dist/interfaces/node/types/host.d.ts +0 -4
- /package/dist/{interfaces/node/types/host.cjs → client/types/hooks.cjs} +0 -0
- /package/dist/{interfaces/node/types/host.mjs → client/types/hooks.mjs} +0 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
require('../../../../core/request/index.cjs');
|
|
4
|
+
var serverUtils = require('@duplojs/server-utils');
|
|
5
|
+
var utils = require('@duplojs/utils');
|
|
6
|
+
var readRequestFormData = require('./readRequestFormData.cjs');
|
|
7
|
+
var node_fs = require('node:fs');
|
|
8
|
+
require('../../../../core/errors/index.cjs');
|
|
9
|
+
var error = require('./error.cjs');
|
|
10
|
+
var formData = require('../../../../core/request/bodyController/formData.cjs');
|
|
11
|
+
var wrongContentTypeError = require('../../../../core/errors/wrongContentTypeError.cjs');
|
|
12
|
+
|
|
13
|
+
function createFormDataBodyReaderImplementation(serverParams) {
|
|
14
|
+
const serverMaxBodySize = utils.stringToBytes(serverParams.maxBodySize);
|
|
15
|
+
function addValue(mapResult, fieldName, newValue) {
|
|
16
|
+
const value = mapResult.get(fieldName);
|
|
17
|
+
if (value === undefined) {
|
|
18
|
+
mapResult.set(fieldName, newValue);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
mapResult.set(fieldName, utils.A.push(utils.A.coalescing(value), newValue));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return formData.FormDataBodyController.createReaderImplementation(async (request, params) => {
|
|
25
|
+
if (!request.headers["content-type"]?.includes("multipart/form-data")) {
|
|
26
|
+
return utils.E.error(new wrongContentTypeError.WrongContentTypeError("multipart/form-data", utils.A.join(utils.A.coalescing(request.headers["content-type"] ?? ""), " ")));
|
|
27
|
+
}
|
|
28
|
+
const filesAttache = [];
|
|
29
|
+
request.filesAttache = filesAttache;
|
|
30
|
+
const result = await readRequestFormData.readRequestFormData(request.raw.request, new Map(), {
|
|
31
|
+
maxBodySize: params.bodyMaxSize ?? serverMaxBodySize,
|
|
32
|
+
fileMaxSize: params.fileMaxSize ?? Infinity,
|
|
33
|
+
maxFileQuantity: params.maxFileQuantity,
|
|
34
|
+
mimeType: params.mimeType,
|
|
35
|
+
maxBufferSize: params.maxBufferSize,
|
|
36
|
+
maxKeyLength: params.maxKeyLength,
|
|
37
|
+
}, (header) => {
|
|
38
|
+
const fieldName = header.name;
|
|
39
|
+
if (header.filename) {
|
|
40
|
+
const extension = utils.Path.getExtensionName(header.filename);
|
|
41
|
+
const displayExtension = extension ? `.${extension}` : "";
|
|
42
|
+
const filePath = utils.Path.resolveRelative([
|
|
43
|
+
serverParams.uploadFolder,
|
|
44
|
+
`${Math.random().toString(36).slice(2, 10)}-${Date.now()}${displayExtension}`,
|
|
45
|
+
]);
|
|
46
|
+
filesAttache.push(filePath);
|
|
47
|
+
const currentFile = node_fs.createWriteStream(filePath, {
|
|
48
|
+
highWaterMark: request.raw.request.readableHighWaterMark,
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
onReceiveChunk: (chunk) => new Promise((resolve, reject) => void currentFile.write(chunk, (result) => {
|
|
52
|
+
if (result instanceof Error) {
|
|
53
|
+
return void reject(result);
|
|
54
|
+
}
|
|
55
|
+
return void resolve();
|
|
56
|
+
})),
|
|
57
|
+
onEndPart: (valueAccumulator) => {
|
|
58
|
+
currentFile.end();
|
|
59
|
+
addValue(valueAccumulator, fieldName, serverUtils.SF.createFileInterface(currentFile.path.toString()));
|
|
60
|
+
return valueAccumulator;
|
|
61
|
+
},
|
|
62
|
+
onError: () => void currentFile.end(),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
let currentValue = "";
|
|
66
|
+
return {
|
|
67
|
+
onReceiveChunk: (chunk) => {
|
|
68
|
+
currentValue += chunk.toString("utf-8");
|
|
69
|
+
},
|
|
70
|
+
onEndPart: (valueAccumulator) => {
|
|
71
|
+
addValue(valueAccumulator, fieldName, currentValue);
|
|
72
|
+
return valueAccumulator;
|
|
73
|
+
},
|
|
74
|
+
onError: null,
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
if (utils.E.isLeft(result)) {
|
|
78
|
+
// mandatory in case of error to avoid monopolizing the client connection if a stream is not finished.
|
|
79
|
+
request.raw.response.setHeader("Connection", "close");
|
|
80
|
+
if (utils.E.hasInformation(result, "server-error")) {
|
|
81
|
+
throw utils.unwrap(result);
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
if (request.headers["content-type-options"]?.includes("advanced")) {
|
|
86
|
+
return utils.E.success(utils.TheFormData.fromEntries(result.entries(), params.maxIndexArray));
|
|
87
|
+
}
|
|
88
|
+
return utils.E.success(utils.O.fromEntries(result.entries()));
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
exports.readRequestFormData = readRequestFormData.readRequestFormData;
|
|
93
|
+
exports.BodyParseFormDataError = error.BodyParseFormDataError;
|
|
94
|
+
exports.createFormDataBodyReaderImplementation = createFormDataBodyReaderImplementation;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type HttpServerParams } from "../../../../core/types";
|
|
2
|
+
export * from "./error";
|
|
3
|
+
export * from "./readRequestFormData";
|
|
4
|
+
export declare function createFormDataBodyReaderImplementation(serverParams: HttpServerParams): import("../../../../core/request").BodyReaderImplementation<"formData", import("../../../../core/request").FormDataBodyReaderParams>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import '../../../../core/request/index.mjs';
|
|
2
|
+
import { SF } from '@duplojs/server-utils';
|
|
3
|
+
import { stringToBytes, A, E, Path, unwrap, TheFormData, O } from '@duplojs/utils';
|
|
4
|
+
import { readRequestFormData } from './readRequestFormData.mjs';
|
|
5
|
+
import { createWriteStream } from 'node:fs';
|
|
6
|
+
import '../../../../core/errors/index.mjs';
|
|
7
|
+
export { BodyParseFormDataError } from './error.mjs';
|
|
8
|
+
import { FormDataBodyController } from '../../../../core/request/bodyController/formData.mjs';
|
|
9
|
+
import { WrongContentTypeError } from '../../../../core/errors/wrongContentTypeError.mjs';
|
|
10
|
+
|
|
11
|
+
function createFormDataBodyReaderImplementation(serverParams) {
|
|
12
|
+
const serverMaxBodySize = stringToBytes(serverParams.maxBodySize);
|
|
13
|
+
function addValue(mapResult, fieldName, newValue) {
|
|
14
|
+
const value = mapResult.get(fieldName);
|
|
15
|
+
if (value === undefined) {
|
|
16
|
+
mapResult.set(fieldName, newValue);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
mapResult.set(fieldName, A.push(A.coalescing(value), newValue));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return FormDataBodyController.createReaderImplementation(async (request, params) => {
|
|
23
|
+
if (!request.headers["content-type"]?.includes("multipart/form-data")) {
|
|
24
|
+
return E.error(new WrongContentTypeError("multipart/form-data", A.join(A.coalescing(request.headers["content-type"] ?? ""), " ")));
|
|
25
|
+
}
|
|
26
|
+
const filesAttache = [];
|
|
27
|
+
request.filesAttache = filesAttache;
|
|
28
|
+
const result = await readRequestFormData(request.raw.request, new Map(), {
|
|
29
|
+
maxBodySize: params.bodyMaxSize ?? serverMaxBodySize,
|
|
30
|
+
fileMaxSize: params.fileMaxSize ?? Infinity,
|
|
31
|
+
maxFileQuantity: params.maxFileQuantity,
|
|
32
|
+
mimeType: params.mimeType,
|
|
33
|
+
maxBufferSize: params.maxBufferSize,
|
|
34
|
+
maxKeyLength: params.maxKeyLength,
|
|
35
|
+
}, (header) => {
|
|
36
|
+
const fieldName = header.name;
|
|
37
|
+
if (header.filename) {
|
|
38
|
+
const extension = Path.getExtensionName(header.filename);
|
|
39
|
+
const displayExtension = extension ? `.${extension}` : "";
|
|
40
|
+
const filePath = Path.resolveRelative([
|
|
41
|
+
serverParams.uploadFolder,
|
|
42
|
+
`${Math.random().toString(36).slice(2, 10)}-${Date.now()}${displayExtension}`,
|
|
43
|
+
]);
|
|
44
|
+
filesAttache.push(filePath);
|
|
45
|
+
const currentFile = createWriteStream(filePath, {
|
|
46
|
+
highWaterMark: request.raw.request.readableHighWaterMark,
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
onReceiveChunk: (chunk) => new Promise((resolve, reject) => void currentFile.write(chunk, (result) => {
|
|
50
|
+
if (result instanceof Error) {
|
|
51
|
+
return void reject(result);
|
|
52
|
+
}
|
|
53
|
+
return void resolve();
|
|
54
|
+
})),
|
|
55
|
+
onEndPart: (valueAccumulator) => {
|
|
56
|
+
currentFile.end();
|
|
57
|
+
addValue(valueAccumulator, fieldName, SF.createFileInterface(currentFile.path.toString()));
|
|
58
|
+
return valueAccumulator;
|
|
59
|
+
},
|
|
60
|
+
onError: () => void currentFile.end(),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
let currentValue = "";
|
|
64
|
+
return {
|
|
65
|
+
onReceiveChunk: (chunk) => {
|
|
66
|
+
currentValue += chunk.toString("utf-8");
|
|
67
|
+
},
|
|
68
|
+
onEndPart: (valueAccumulator) => {
|
|
69
|
+
addValue(valueAccumulator, fieldName, currentValue);
|
|
70
|
+
return valueAccumulator;
|
|
71
|
+
},
|
|
72
|
+
onError: null,
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
if (E.isLeft(result)) {
|
|
76
|
+
// mandatory in case of error to avoid monopolizing the client connection if a stream is not finished.
|
|
77
|
+
request.raw.response.setHeader("Connection", "close");
|
|
78
|
+
if (E.hasInformation(result, "server-error")) {
|
|
79
|
+
throw unwrap(result);
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
if (request.headers["content-type-options"]?.includes("advanced")) {
|
|
84
|
+
return E.success(TheFormData.fromEntries(result.entries(), params.maxIndexArray));
|
|
85
|
+
}
|
|
86
|
+
return E.success(O.fromEntries(result.entries()));
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export { createFormDataBodyReaderImplementation, readRequestFormData };
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var utils = require('@duplojs/utils');
|
|
4
|
+
var error = require('./error.cjs');
|
|
5
|
+
require('../../../../core/errors/index.cjs');
|
|
6
|
+
var bodySizeExceedsLimitError = require('../../../../core/errors/bodySizeExceedsLimitError.cjs');
|
|
7
|
+
var bodyParseWrongChunkReceived = require('../../../../core/errors/bodyParseWrongChunkReceived.cjs');
|
|
8
|
+
|
|
9
|
+
const endHeaderPart = Buffer.from("\r\n\r\n");
|
|
10
|
+
const bufferStart = Buffer.from("\r\n");
|
|
11
|
+
const regexBoundary = /boundary=(?<boundary>[^; ]+)/i;
|
|
12
|
+
const regexHeaderPart = /name="(?<name>(?:\\"|[^"])+)"(?:; filename="(?<filename>(?:\\"|[^"])+)")?(?:;\s+filename\*=[^']+'[^']*'(?<encodedFilename>[^;\r\n\s]+))?/i;
|
|
13
|
+
function safeDecode(value) {
|
|
14
|
+
try {
|
|
15
|
+
return decodeURIComponent(value);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async function readRequestFormData(request, firstValueAccumulator, params, onReceiveHeader) {
|
|
22
|
+
const boundary = utils.S.extract(request.headers["content-type"] ?? "", regexBoundary)?.namedGroups?.boundary;
|
|
23
|
+
if (!boundary) {
|
|
24
|
+
return utils.E.error(new error.BodyParseFormDataError("Wrong boundary."));
|
|
25
|
+
}
|
|
26
|
+
let valueAccumulator = firstValueAccumulator;
|
|
27
|
+
const startPart = Buffer.from(`\r\n--${boundary}`);
|
|
28
|
+
const endMultiPart = Buffer.from(`\r\n--${boundary}--`);
|
|
29
|
+
let currentBuffer = bufferStart;
|
|
30
|
+
let size = 0;
|
|
31
|
+
const keep = endMultiPart.length - 1;
|
|
32
|
+
let currentStream = undefined;
|
|
33
|
+
let fileQuantity = 0;
|
|
34
|
+
let currentFileSize = undefined;
|
|
35
|
+
const checkSize = (receivedChunk) => {
|
|
36
|
+
size += receivedChunk.length;
|
|
37
|
+
return size > params.maxBodySize
|
|
38
|
+
? new bodySizeExceedsLimitError.BodySizeExceedsLimitError(params.maxBodySize)
|
|
39
|
+
: true;
|
|
40
|
+
};
|
|
41
|
+
const flushReceiveHeader = async (headerPart) => {
|
|
42
|
+
valueAccumulator = await currentStream?.onEndPart(valueAccumulator) ?? valueAccumulator;
|
|
43
|
+
const sizeResult = checkSize(headerPart);
|
|
44
|
+
if (sizeResult !== true) {
|
|
45
|
+
return sizeResult;
|
|
46
|
+
}
|
|
47
|
+
const extract = utils.S.extract(headerPart.toString("utf-8"), regexHeaderPart)?.namedGroups;
|
|
48
|
+
const header = extract?.name
|
|
49
|
+
? {
|
|
50
|
+
name: extract.name.trim(),
|
|
51
|
+
filename: (extract.encodedFilename !== undefined
|
|
52
|
+
? safeDecode(extract.encodedFilename)
|
|
53
|
+
: extract.filename)?.trim(),
|
|
54
|
+
}
|
|
55
|
+
: null;
|
|
56
|
+
if (!header) {
|
|
57
|
+
return new error.BodyParseFormDataError("Bad content header part.");
|
|
58
|
+
}
|
|
59
|
+
if (header.name.length > params.maxKeyLength) {
|
|
60
|
+
return new error.BodyParseFormDataError("key length exceeds limit.");
|
|
61
|
+
}
|
|
62
|
+
if (header.filename !== undefined) {
|
|
63
|
+
currentFileSize = 0;
|
|
64
|
+
fileQuantity++;
|
|
65
|
+
if (fileQuantity > params.maxFileQuantity) {
|
|
66
|
+
return new error.BodyParseFormDataError("File quantity exceeds limit.");
|
|
67
|
+
}
|
|
68
|
+
else if (params.mimeType !== undefined
|
|
69
|
+
&& !params.mimeType.test(utils.Path.getExtensionName(header.filename) ?? "")) {
|
|
70
|
+
return new error.BodyParseFormDataError("File have wrong mimeType.");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
currentFileSize = undefined;
|
|
75
|
+
}
|
|
76
|
+
const newStream = await onReceiveHeader(header);
|
|
77
|
+
if (newStream instanceof Error) {
|
|
78
|
+
return newStream;
|
|
79
|
+
}
|
|
80
|
+
currentStream = newStream;
|
|
81
|
+
return true;
|
|
82
|
+
};
|
|
83
|
+
const flushReceiveChunk = async (chunk) => {
|
|
84
|
+
if (chunk.length === 0) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
const sizeResult = checkSize(chunk);
|
|
88
|
+
if (sizeResult !== true) {
|
|
89
|
+
return sizeResult;
|
|
90
|
+
}
|
|
91
|
+
if (!currentStream) {
|
|
92
|
+
return new error.BodyParseFormDataError("Receive chunk before header part.");
|
|
93
|
+
}
|
|
94
|
+
if (typeof currentFileSize === "number") {
|
|
95
|
+
currentFileSize += chunk.length;
|
|
96
|
+
if (params.fileMaxSize !== undefined && currentFileSize > params.fileMaxSize) {
|
|
97
|
+
return new error.BodyParseFormDataError("File size exceeds limit.");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
await currentStream.onReceiveChunk(chunk);
|
|
101
|
+
return true;
|
|
102
|
+
};
|
|
103
|
+
const treatError = async (error) => {
|
|
104
|
+
await currentStream?.onError?.(error, valueAccumulator);
|
|
105
|
+
return utils.E.error(error);
|
|
106
|
+
};
|
|
107
|
+
try {
|
|
108
|
+
for await (const chunk of request) {
|
|
109
|
+
if (!(chunk instanceof Buffer)) {
|
|
110
|
+
return await treatError(new bodyParseWrongChunkReceived.BodyParseWrongChunkReceived("Buffer.", chunk));
|
|
111
|
+
}
|
|
112
|
+
currentBuffer = Buffer.concat([currentBuffer, chunk]);
|
|
113
|
+
if (currentBuffer.length > params.maxBufferSize) {
|
|
114
|
+
return await treatError(new error.BodyParseFormDataError("Buffer size exceeds limit."));
|
|
115
|
+
}
|
|
116
|
+
while (true) {
|
|
117
|
+
const endMultiPartIndex = currentBuffer.indexOf(endMultiPart);
|
|
118
|
+
if (endMultiPartIndex !== -1) {
|
|
119
|
+
// check if buffer contain end of transmissions
|
|
120
|
+
currentBuffer = currentBuffer.subarray(0, endMultiPartIndex);
|
|
121
|
+
}
|
|
122
|
+
const startPartIndex = currentBuffer.indexOf(startPart);
|
|
123
|
+
const endHeaderPartIndex = currentBuffer.indexOf(endHeaderPart);
|
|
124
|
+
if (startPartIndex !== -1 && endHeaderPartIndex !== -1) {
|
|
125
|
+
// check if buffer contain an entire header of part
|
|
126
|
+
const resultChunk = await flushReceiveChunk(currentBuffer.subarray(0, startPartIndex));
|
|
127
|
+
if (resultChunk !== true) {
|
|
128
|
+
return await treatError(resultChunk);
|
|
129
|
+
}
|
|
130
|
+
const endIndex = endHeaderPartIndex + endHeaderPart.length;
|
|
131
|
+
const resultHeader = await flushReceiveHeader(currentBuffer.subarray(startPartIndex, endIndex));
|
|
132
|
+
if (resultHeader !== true) {
|
|
133
|
+
return await treatError(resultHeader);
|
|
134
|
+
}
|
|
135
|
+
currentBuffer = currentBuffer.subarray(endIndex);
|
|
136
|
+
}
|
|
137
|
+
else if (startPartIndex === -1 && endHeaderPartIndex === -1) {
|
|
138
|
+
// check if buffer contain only data
|
|
139
|
+
if (currentBuffer.length > keep) {
|
|
140
|
+
const bufferRestIndex = currentBuffer.length - keep;
|
|
141
|
+
const resultChunk = await flushReceiveChunk(currentBuffer.subarray(0, bufferRestIndex));
|
|
142
|
+
if (resultChunk !== true) {
|
|
143
|
+
return await treatError(resultChunk);
|
|
144
|
+
}
|
|
145
|
+
currentBuffer = currentBuffer.subarray(bufferRestIndex);
|
|
146
|
+
}
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
else if (startPartIndex !== -1 && endHeaderPartIndex === -1) {
|
|
150
|
+
// check if buffer contain start of header but not contain end
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
// check if buffer contain only end of header part
|
|
155
|
+
return await treatError(new error.BodyParseFormDataError("Wrong content."));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const resultChunk = await flushReceiveChunk(currentBuffer);
|
|
160
|
+
if (resultChunk !== true) {
|
|
161
|
+
return await treatError(resultChunk);
|
|
162
|
+
}
|
|
163
|
+
valueAccumulator = await currentStream?.onEndPart(valueAccumulator) ?? valueAccumulator;
|
|
164
|
+
return valueAccumulator;
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
await currentStream?.onError?.(error, valueAccumulator);
|
|
168
|
+
return utils.E.left("server-error", error);
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
request.destroy();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
exports.readRequestFormData = readRequestFormData;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { E, type MaybePromise } from "@duplojs/utils";
|
|
2
|
+
import type http from "http";
|
|
3
|
+
interface HeaderPartInformation {
|
|
4
|
+
name: string;
|
|
5
|
+
filename?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ReadRequestFormDataStreamChunkEvent<GenericValueAccumulator extends unknown = unknown> {
|
|
8
|
+
onReceiveChunk(chunk: Buffer): MaybePromise<void>;
|
|
9
|
+
onEndPart(valueAccumulator: GenericValueAccumulator): MaybePromise<GenericValueAccumulator>;
|
|
10
|
+
onError: ((error: unknown, valueAccumulator: GenericValueAccumulator) => MaybePromise<void>) | null;
|
|
11
|
+
}
|
|
12
|
+
export interface ReadRequestFormDataParams {
|
|
13
|
+
maxBodySize: number;
|
|
14
|
+
maxFileQuantity: number;
|
|
15
|
+
maxBufferSize: number;
|
|
16
|
+
maxKeyLength: number;
|
|
17
|
+
fileMaxSize?: number;
|
|
18
|
+
mimeType?: RegExp;
|
|
19
|
+
}
|
|
20
|
+
export declare function readRequestFormData<GenericValueAccumulator extends unknown, GenericOutputHeader extends E.Left = never>(request: http.IncomingMessage, firstValueAccumulator: GenericValueAccumulator, params: ReadRequestFormDataParams, onReceiveHeader: (header: HeaderPartInformation) => MaybePromise<ReadRequestFormDataStreamChunkEvent<GenericValueAccumulator> | Error>): Promise<E.Left<"server-error", unknown> | GenericOutputHeader | E.Error<Error> | GenericValueAccumulator>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { S, E, Path } from '@duplojs/utils';
|
|
2
|
+
import { BodyParseFormDataError } from './error.mjs';
|
|
3
|
+
import '../../../../core/errors/index.mjs';
|
|
4
|
+
import { BodySizeExceedsLimitError } from '../../../../core/errors/bodySizeExceedsLimitError.mjs';
|
|
5
|
+
import { BodyParseWrongChunkReceived } from '../../../../core/errors/bodyParseWrongChunkReceived.mjs';
|
|
6
|
+
|
|
7
|
+
const endHeaderPart = Buffer.from("\r\n\r\n");
|
|
8
|
+
const bufferStart = Buffer.from("\r\n");
|
|
9
|
+
const regexBoundary = /boundary=(?<boundary>[^; ]+)/i;
|
|
10
|
+
const regexHeaderPart = /name="(?<name>(?:\\"|[^"])+)"(?:; filename="(?<filename>(?:\\"|[^"])+)")?(?:;\s+filename\*=[^']+'[^']*'(?<encodedFilename>[^;\r\n\s]+))?/i;
|
|
11
|
+
function safeDecode(value) {
|
|
12
|
+
try {
|
|
13
|
+
return decodeURIComponent(value);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async function readRequestFormData(request, firstValueAccumulator, params, onReceiveHeader) {
|
|
20
|
+
const boundary = S.extract(request.headers["content-type"] ?? "", regexBoundary)?.namedGroups?.boundary;
|
|
21
|
+
if (!boundary) {
|
|
22
|
+
return E.error(new BodyParseFormDataError("Wrong boundary."));
|
|
23
|
+
}
|
|
24
|
+
let valueAccumulator = firstValueAccumulator;
|
|
25
|
+
const startPart = Buffer.from(`\r\n--${boundary}`);
|
|
26
|
+
const endMultiPart = Buffer.from(`\r\n--${boundary}--`);
|
|
27
|
+
let currentBuffer = bufferStart;
|
|
28
|
+
let size = 0;
|
|
29
|
+
const keep = endMultiPart.length - 1;
|
|
30
|
+
let currentStream = undefined;
|
|
31
|
+
let fileQuantity = 0;
|
|
32
|
+
let currentFileSize = undefined;
|
|
33
|
+
const checkSize = (receivedChunk) => {
|
|
34
|
+
size += receivedChunk.length;
|
|
35
|
+
return size > params.maxBodySize
|
|
36
|
+
? new BodySizeExceedsLimitError(params.maxBodySize)
|
|
37
|
+
: true;
|
|
38
|
+
};
|
|
39
|
+
const flushReceiveHeader = async (headerPart) => {
|
|
40
|
+
valueAccumulator = await currentStream?.onEndPart(valueAccumulator) ?? valueAccumulator;
|
|
41
|
+
const sizeResult = checkSize(headerPart);
|
|
42
|
+
if (sizeResult !== true) {
|
|
43
|
+
return sizeResult;
|
|
44
|
+
}
|
|
45
|
+
const extract = S.extract(headerPart.toString("utf-8"), regexHeaderPart)?.namedGroups;
|
|
46
|
+
const header = extract?.name
|
|
47
|
+
? {
|
|
48
|
+
name: extract.name.trim(),
|
|
49
|
+
filename: (extract.encodedFilename !== undefined
|
|
50
|
+
? safeDecode(extract.encodedFilename)
|
|
51
|
+
: extract.filename)?.trim(),
|
|
52
|
+
}
|
|
53
|
+
: null;
|
|
54
|
+
if (!header) {
|
|
55
|
+
return new BodyParseFormDataError("Bad content header part.");
|
|
56
|
+
}
|
|
57
|
+
if (header.name.length > params.maxKeyLength) {
|
|
58
|
+
return new BodyParseFormDataError("key length exceeds limit.");
|
|
59
|
+
}
|
|
60
|
+
if (header.filename !== undefined) {
|
|
61
|
+
currentFileSize = 0;
|
|
62
|
+
fileQuantity++;
|
|
63
|
+
if (fileQuantity > params.maxFileQuantity) {
|
|
64
|
+
return new BodyParseFormDataError("File quantity exceeds limit.");
|
|
65
|
+
}
|
|
66
|
+
else if (params.mimeType !== undefined
|
|
67
|
+
&& !params.mimeType.test(Path.getExtensionName(header.filename) ?? "")) {
|
|
68
|
+
return new BodyParseFormDataError("File have wrong mimeType.");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
currentFileSize = undefined;
|
|
73
|
+
}
|
|
74
|
+
const newStream = await onReceiveHeader(header);
|
|
75
|
+
if (newStream instanceof Error) {
|
|
76
|
+
return newStream;
|
|
77
|
+
}
|
|
78
|
+
currentStream = newStream;
|
|
79
|
+
return true;
|
|
80
|
+
};
|
|
81
|
+
const flushReceiveChunk = async (chunk) => {
|
|
82
|
+
if (chunk.length === 0) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
const sizeResult = checkSize(chunk);
|
|
86
|
+
if (sizeResult !== true) {
|
|
87
|
+
return sizeResult;
|
|
88
|
+
}
|
|
89
|
+
if (!currentStream) {
|
|
90
|
+
return new BodyParseFormDataError("Receive chunk before header part.");
|
|
91
|
+
}
|
|
92
|
+
if (typeof currentFileSize === "number") {
|
|
93
|
+
currentFileSize += chunk.length;
|
|
94
|
+
if (params.fileMaxSize !== undefined && currentFileSize > params.fileMaxSize) {
|
|
95
|
+
return new BodyParseFormDataError("File size exceeds limit.");
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
await currentStream.onReceiveChunk(chunk);
|
|
99
|
+
return true;
|
|
100
|
+
};
|
|
101
|
+
const treatError = async (error) => {
|
|
102
|
+
await currentStream?.onError?.(error, valueAccumulator);
|
|
103
|
+
return E.error(error);
|
|
104
|
+
};
|
|
105
|
+
try {
|
|
106
|
+
for await (const chunk of request) {
|
|
107
|
+
if (!(chunk instanceof Buffer)) {
|
|
108
|
+
return await treatError(new BodyParseWrongChunkReceived("Buffer.", chunk));
|
|
109
|
+
}
|
|
110
|
+
currentBuffer = Buffer.concat([currentBuffer, chunk]);
|
|
111
|
+
if (currentBuffer.length > params.maxBufferSize) {
|
|
112
|
+
return await treatError(new BodyParseFormDataError("Buffer size exceeds limit."));
|
|
113
|
+
}
|
|
114
|
+
while (true) {
|
|
115
|
+
const endMultiPartIndex = currentBuffer.indexOf(endMultiPart);
|
|
116
|
+
if (endMultiPartIndex !== -1) {
|
|
117
|
+
// check if buffer contain end of transmissions
|
|
118
|
+
currentBuffer = currentBuffer.subarray(0, endMultiPartIndex);
|
|
119
|
+
}
|
|
120
|
+
const startPartIndex = currentBuffer.indexOf(startPart);
|
|
121
|
+
const endHeaderPartIndex = currentBuffer.indexOf(endHeaderPart);
|
|
122
|
+
if (startPartIndex !== -1 && endHeaderPartIndex !== -1) {
|
|
123
|
+
// check if buffer contain an entire header of part
|
|
124
|
+
const resultChunk = await flushReceiveChunk(currentBuffer.subarray(0, startPartIndex));
|
|
125
|
+
if (resultChunk !== true) {
|
|
126
|
+
return await treatError(resultChunk);
|
|
127
|
+
}
|
|
128
|
+
const endIndex = endHeaderPartIndex + endHeaderPart.length;
|
|
129
|
+
const resultHeader = await flushReceiveHeader(currentBuffer.subarray(startPartIndex, endIndex));
|
|
130
|
+
if (resultHeader !== true) {
|
|
131
|
+
return await treatError(resultHeader);
|
|
132
|
+
}
|
|
133
|
+
currentBuffer = currentBuffer.subarray(endIndex);
|
|
134
|
+
}
|
|
135
|
+
else if (startPartIndex === -1 && endHeaderPartIndex === -1) {
|
|
136
|
+
// check if buffer contain only data
|
|
137
|
+
if (currentBuffer.length > keep) {
|
|
138
|
+
const bufferRestIndex = currentBuffer.length - keep;
|
|
139
|
+
const resultChunk = await flushReceiveChunk(currentBuffer.subarray(0, bufferRestIndex));
|
|
140
|
+
if (resultChunk !== true) {
|
|
141
|
+
return await treatError(resultChunk);
|
|
142
|
+
}
|
|
143
|
+
currentBuffer = currentBuffer.subarray(bufferRestIndex);
|
|
144
|
+
}
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
else if (startPartIndex !== -1 && endHeaderPartIndex === -1) {
|
|
148
|
+
// check if buffer contain start of header but not contain end
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
// check if buffer contain only end of header part
|
|
153
|
+
return await treatError(new BodyParseFormDataError("Wrong content."));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const resultChunk = await flushReceiveChunk(currentBuffer);
|
|
158
|
+
if (resultChunk !== true) {
|
|
159
|
+
return await treatError(resultChunk);
|
|
160
|
+
}
|
|
161
|
+
valueAccumulator = await currentStream?.onEndPart(valueAccumulator) ?? valueAccumulator;
|
|
162
|
+
return valueAccumulator;
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
await currentStream?.onError?.(error, valueAccumulator);
|
|
166
|
+
return E.left("server-error", error);
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
request.destroy();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export { readRequestFormData };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var index = require('./formData/index.cjs');
|
|
4
|
+
var index$1 = require('./text/index.cjs');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
exports.createFormDataBodyReaderImplementation = index.createFormDataBodyReaderImplementation;
|
|
9
|
+
exports.createTextBodyReaderImplementation = index$1.createTextBodyReaderImplementation;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
require('../../../../core/request/index.cjs');
|
|
4
|
+
var readRequestText = require('./readRequestText.cjs');
|
|
5
|
+
var utils = require('@duplojs/utils');
|
|
6
|
+
require('../../../../core/errors/index.cjs');
|
|
7
|
+
var text = require('../../../../core/request/bodyController/text.cjs');
|
|
8
|
+
var wrongContentTypeError = require('../../../../core/errors/wrongContentTypeError.cjs');
|
|
9
|
+
var parseJsonError = require('../../../../core/errors/parseJsonError.cjs');
|
|
10
|
+
|
|
11
|
+
function createTextBodyReaderImplementation(serverParams) {
|
|
12
|
+
const serverMaxBodySize = utils.stringToBytes(serverParams.maxBodySize);
|
|
13
|
+
return text.TextBodyController.createReaderImplementation(async (request, params) => {
|
|
14
|
+
if (!request.headers["content-type"]?.includes("application/json")
|
|
15
|
+
&& !request.headers["content-type"]?.includes("text/plain")) {
|
|
16
|
+
return utils.E.error(new wrongContentTypeError.WrongContentTypeError("application/json or text/plain", utils.A.join(utils.A.coalescing(request.headers["content-type"] ?? ""), " ")));
|
|
17
|
+
}
|
|
18
|
+
const result = await readRequestText.readRequestText(request.raw.request, { maxBodySize: params.bodyMaxSize ?? serverMaxBodySize }, (result) => {
|
|
19
|
+
if (request.headers["content-type"]?.includes("application/json")) {
|
|
20
|
+
try {
|
|
21
|
+
return utils.E.success(JSON.parse(result));
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
return utils.E.error(new parseJsonError.ParseJsonError(result, error));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return utils.E.success(result);
|
|
28
|
+
});
|
|
29
|
+
if (utils.E.isLeft(result)) {
|
|
30
|
+
// mandatory in case of error to avoid monopolizing the client connection if a stream is not finished.
|
|
31
|
+
request.raw.response.setHeader("Connection", "close");
|
|
32
|
+
}
|
|
33
|
+
if (utils.E.hasInformation(result, "server-error")) {
|
|
34
|
+
throw utils.unwrap(result);
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
exports.readRequestText = readRequestText.readRequestText;
|
|
41
|
+
exports.createTextBodyReaderImplementation = createTextBodyReaderImplementation;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type HttpServerParams } from "../../../../core/types";
|
|
2
|
+
export * from "./readRequestText";
|
|
3
|
+
export declare function createTextBodyReaderImplementation(serverParams: HttpServerParams): import("../../../../core/request").BodyReaderImplementation<"text", import("../../../../core/request").TextBodyReaderParams>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import '../../../../core/request/index.mjs';
|
|
2
|
+
import { readRequestText } from './readRequestText.mjs';
|
|
3
|
+
import { stringToBytes, E, A, unwrap } from '@duplojs/utils';
|
|
4
|
+
import '../../../../core/errors/index.mjs';
|
|
5
|
+
import { TextBodyController } from '../../../../core/request/bodyController/text.mjs';
|
|
6
|
+
import { WrongContentTypeError } from '../../../../core/errors/wrongContentTypeError.mjs';
|
|
7
|
+
import { ParseJsonError } from '../../../../core/errors/parseJsonError.mjs';
|
|
8
|
+
|
|
9
|
+
function createTextBodyReaderImplementation(serverParams) {
|
|
10
|
+
const serverMaxBodySize = stringToBytes(serverParams.maxBodySize);
|
|
11
|
+
return TextBodyController.createReaderImplementation(async (request, params) => {
|
|
12
|
+
if (!request.headers["content-type"]?.includes("application/json")
|
|
13
|
+
&& !request.headers["content-type"]?.includes("text/plain")) {
|
|
14
|
+
return E.error(new WrongContentTypeError("application/json or text/plain", A.join(A.coalescing(request.headers["content-type"] ?? ""), " ")));
|
|
15
|
+
}
|
|
16
|
+
const result = await readRequestText(request.raw.request, { maxBodySize: params.bodyMaxSize ?? serverMaxBodySize }, (result) => {
|
|
17
|
+
if (request.headers["content-type"]?.includes("application/json")) {
|
|
18
|
+
try {
|
|
19
|
+
return E.success(JSON.parse(result));
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
return E.error(new ParseJsonError(result, error));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return E.success(result);
|
|
26
|
+
});
|
|
27
|
+
if (E.isLeft(result)) {
|
|
28
|
+
// mandatory in case of error to avoid monopolizing the client connection if a stream is not finished.
|
|
29
|
+
request.raw.response.setHeader("Connection", "close");
|
|
30
|
+
}
|
|
31
|
+
if (E.hasInformation(result, "server-error")) {
|
|
32
|
+
throw unwrap(result);
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { createTextBodyReaderImplementation, readRequestText };
|