@h-ai/serv 0.1.0-alpha.74 → 0.1.0-alpha.76
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.en.md +2 -2
- package/README.ja.md +2 -0
- package/README.md +2 -0
- package/dist/{chunk-IRZVJW4V.js → chunk-65XNI7TH.js} +3 -3
- package/dist/{chunk-IRZVJW4V.js.map → chunk-65XNI7TH.js.map} +1 -1
- package/dist/{chunk-N6IOKLQY.js → chunk-JAQIEQJS.js} +13 -5
- package/dist/chunk-JAQIEQJS.js.map +1 -0
- package/dist/features/ai.js +1 -1
- package/dist/features/feedback.js +5 -3
- package/dist/features/feedback.js.map +1 -1
- package/dist/features/iam.js +11 -3
- package/dist/features/iam.js.map +1 -1
- package/dist/features/storage.js +2 -2
- package/dist/index.d.ts +8 -37
- package/dist/index.js +82 -73
- package/dist/index.js.map +1 -1
- package/package.json +16 -16
- package/dist/chunk-N6IOKLQY.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -250,6 +250,8 @@ interface ServRpcHttpConfig {
|
|
|
250
250
|
interface ServTransportRuntimeConfig {
|
|
251
251
|
/** 双向明文 UTF-8 字节上限;省略使用 1 MiB,范围 1 KiB~64 MiB。 */
|
|
252
252
|
readonly maxPlaintextBytes?: number;
|
|
253
|
+
/** 按完整路径段匹配的明文容量;* 匹配单段。 */
|
|
254
|
+
readonly routePlaintextBytes?: Readonly<Record<string, number>>;
|
|
253
255
|
readonly keyExchangePath: `/${string}`;
|
|
254
256
|
readonly excludePaths: readonly `/${string}`[];
|
|
255
257
|
}
|
|
@@ -337,6 +339,7 @@ type ServCorsRuntimeConfigInput = Partial<ServCorsRuntimeConfig>;
|
|
|
337
339
|
*/
|
|
338
340
|
type ServTransportRuntimeConfigInput = Partial<{
|
|
339
341
|
readonly maxPlaintextBytes: number | 'auto';
|
|
342
|
+
readonly routePlaintextBytes: Readonly<Record<string, number>>;
|
|
340
343
|
readonly keyExchangePath: `/${string}`;
|
|
341
344
|
readonly excludePaths: readonly `/${string}`[];
|
|
342
345
|
}>;
|
|
@@ -432,6 +435,7 @@ declare const ServConfigSchema: z.ZodPipe<z.ZodObject<{
|
|
|
432
435
|
}, z.core.$strip>>;
|
|
433
436
|
transport: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodObject<{
|
|
434
437
|
maxPlaintextBytes: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodLiteral<"auto">]>, z.ZodTransform<number | undefined, number | "auto">>>;
|
|
438
|
+
routePlaintextBytes: z.ZodOptional<z.ZodRecord<z.ZodCustom<`/${string}`, `/${string}`>, z.ZodNumber>>;
|
|
435
439
|
keyExchangePath: z.ZodDefault<z.ZodCustom<`/${string}`, `/${string}`>>;
|
|
436
440
|
excludePaths: z.ZodDefault<z.ZodArray<z.ZodCustom<`/${string}`, `/${string}`>>>;
|
|
437
441
|
}, z.core.$strip>]>>;
|
|
@@ -477,27 +481,10 @@ declare const ServConfigSchema: z.ZodPipe<z.ZodObject<{
|
|
|
477
481
|
keyExchangePath: `/${string}`;
|
|
478
482
|
excludePaths: `/${string}`[];
|
|
479
483
|
maxPlaintextBytes?: number | undefined;
|
|
484
|
+
routePlaintextBytes?: Record<`/${string}`, number> | undefined;
|
|
480
485
|
} | undefined;
|
|
481
486
|
}>>;
|
|
482
487
|
|
|
483
|
-
/**
|
|
484
|
-
* @h-ai/serv — 传输加密集成
|
|
485
|
-
*
|
|
486
|
-
* 提供 Hono 中间件 + 密钥协商端点,直接依赖 `@h-ai/crypto` 的
|
|
487
|
-
* `TransportEncryptionManager` 与 `TRANSPORT_PROTOCOL` 协议常量,
|
|
488
|
-
* 确保前后端协议不漂移。
|
|
489
|
-
*
|
|
490
|
-
* 单次请求的处理顺序:
|
|
491
|
-
* 1. 特判密钥协商端点
|
|
492
|
-
* 2. 跳过不参与加解密的路径
|
|
493
|
-
* 3. 校验 clientId 与已注册公钥
|
|
494
|
-
* 4. 解密请求体并把明文 request 回写给下游
|
|
495
|
-
* 5. 执行业务逻辑
|
|
496
|
-
* 6. 尝试加密 JSON 响应
|
|
497
|
-
*
|
|
498
|
-
* @module serv-transport
|
|
499
|
-
*/
|
|
500
|
-
|
|
501
488
|
/**
|
|
502
489
|
* {@link createApp} 的 `transport` 配置项。
|
|
503
490
|
*
|
|
@@ -518,6 +505,8 @@ interface ServTransportConfig {
|
|
|
518
505
|
/** 受管服务端密钥环,仅运行时从私有 Secret 注入。 */
|
|
519
506
|
/** 双向业务明文 UTF-8 字节上限;默认 1 MiB,整数范围 1 KiB~64 MiB。 */
|
|
520
507
|
readonly maxPlaintextBytes?: number;
|
|
508
|
+
/** 路由明文容量;* 匹配一个完整路径段。 */
|
|
509
|
+
routePlaintextBytes?: Readonly<Record<string, number>>;
|
|
521
510
|
readonly serverKeyRing: TransportServerKeyRing;
|
|
522
511
|
/**
|
|
523
512
|
* crypto 服务实例。serv 通过 `crypto.transport.createServer()` 创建
|
|
@@ -537,24 +526,6 @@ interface ServTransportConfig {
|
|
|
537
526
|
readonly keyStore: TransportKeyStore;
|
|
538
527
|
}
|
|
539
528
|
|
|
540
|
-
/**
|
|
541
|
-
* @h-ai/serv — HTTP app 装配
|
|
542
|
-
*
|
|
543
|
-
* 将 oRPC contract + procedures + http 配置组合为可运行的 ServHttpApp,
|
|
544
|
-
* 自动挂载安全响应头、健康检查、OpenAPI JSON、Scalar 文档页与 oRPC handler。
|
|
545
|
-
*
|
|
546
|
-
* 关键装配顺序:
|
|
547
|
-
* 1. 解析 HTTP 配置
|
|
548
|
-
* 2. 选择请求上下文工厂
|
|
549
|
-
* 3. 挂基础安全 middleware
|
|
550
|
-
* 4. 挂自定义 middleware(若提供)
|
|
551
|
-
* 5. (可选)挂传输加密 middleware
|
|
552
|
-
* 6. 挂基础路由(health / refresh-cookie)
|
|
553
|
-
* 7. 挂业务路由(oRPC / RPC)
|
|
554
|
-
* 8. 挂文档路由(openapi / docs)
|
|
555
|
-
* @module serv-app
|
|
556
|
-
*/
|
|
557
|
-
|
|
558
529
|
/**
|
|
559
530
|
* `serv.createApp()` 的自定义 middleware 挂载项。
|
|
560
531
|
*
|
|
@@ -1325,7 +1296,7 @@ declare const serv: {
|
|
|
1325
1296
|
validateInputOrFail: typeof validateInputOrFail;
|
|
1326
1297
|
WILDCARD_PERMISSION: string;
|
|
1327
1298
|
WILDCARD_ROLE: string;
|
|
1328
|
-
m: (key: "$schema" | "serv_validationFailed" | "serv_validationRequired" | "serv_validationInvalid" | "serv_validationInvalidType" | "serv_validationStringMin" | "serv_validationStringMax" | "serv_validationNumberMin" | "serv_validationNumberMax" | "serv_validationArrayMin" | "serv_validationArrayMax" | "serv_validationTooSmall" | "serv_validationTooBig" | "serv_validationEmail" | "serv_validationUrl" | "serv_validationUuid" | "serv_validationEnum" | "serv_errorUnauthorized" | "serv_errorForbidden" | "serv_errorInternal" | "serv_storageKeyRequired" | "serv_storageKeyTooLong" | "serv_storageKeyIllegalChars" | "serv_storageKeyDotSegments" | "serv_iamCannotDeleteCurrentUser" | "serv_transportInvalidPayload" | "serv_transportUnknownClient" | "serv_transportClientIdRequired" | "serv_transportClientKeyNotFound" | "serv_transportDecryptFailed" | "serv_transportEncryptFailed" | "serv_transportKeyExchangeFailed", options?: _h_ai_core.MessageOptions) => string;
|
|
1299
|
+
m: (key: "$schema" | "serv_validationFailed" | "serv_validationRequired" | "serv_validationInvalid" | "serv_validationInvalidType" | "serv_validationStringMin" | "serv_validationStringMax" | "serv_validationNumberMin" | "serv_validationNumberMax" | "serv_validationArrayMin" | "serv_validationArrayMax" | "serv_validationTooSmall" | "serv_validationTooBig" | "serv_validationEmail" | "serv_validationUrl" | "serv_validationUuid" | "serv_validationEnum" | "serv_errorUnauthorized" | "serv_errorForbidden" | "serv_errorInternal" | "serv_storageKeyRequired" | "serv_storageKeyTooLong" | "serv_storageKeyIllegalChars" | "serv_storageKeyDotSegments" | "serv_iamCannotDeleteCurrentUser" | "serv_transportInvalidPayload" | "serv_transportUnknownClient" | "serv_transportClientIdRequired" | "serv_transportClientKeyNotFound" | "serv_transportDecryptFailed" | "serv_transportEncryptFailed" | "serv_transportKeyExchangeFailed" | "serv_transportBusy" | "serv_feedbackUnavailable", options?: _h_ai_core.MessageOptions) => string;
|
|
1329
1300
|
};
|
|
1330
1301
|
|
|
1331
1302
|
export { type AudioTicketGrant, type AudioTicketVerification, type AuthorizedAudioRequest, type CreateServAppOptions, CreateServContext, type GenerateOpenAPISpecOptions, RefreshCookieConfig, type ServAudioConfig, type ServConfig, type ServConfigInput, ServConfigSchema, ServContext, type ServCorsConfig, type ServCorsRuntimeConfig, type ServCorsRuntimeConfigInput, type ServDocsHttpConfig, type ServFetchHandler, type ServHealthHttpConfig, type ServHttpApp, type ServHttpConfig, type ServHttpConfigInput, ServIam, type ServListenOptions, type ServMiddleware, type ServMiddlewareFactory, type ServMiddlewareMount, type ServNodeServer, type ServOpenAPIHttpConfig, type ServProcedureHandler, type ServProcedureOptions, ServRequestMetadata, type ServRpcHttpConfig, type ServRuntimeSecurityPolicy, type ServRuntimeSecurityPolicyInput, type ServServerConfig, type ServServerConfigInput, ServSession, type ServStorageAssetsConfig, type ServTransportConfig, type ServTransportRuntimeConfig, type ServTransportRuntimeConfigInput, type ServValidationFailureBody, serv };
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { validateInputOrFail, resolveRequestLocale, localizeZodError } from './chunk-
|
|
2
|
-
import { servM, WILDCARD_ROLE, WILDCARD_PERMISSION, implement, buildHaiErrorBody } from './chunk-
|
|
1
|
+
import { validateInputOrFail, resolveRequestLocale, localizeZodError } from './chunk-65XNI7TH.js';
|
|
2
|
+
import { servM, WILDCARD_ROLE, WILDCARD_PERMISSION, implement, buildHaiErrorBody } from './chunk-JAQIEQJS.js';
|
|
3
3
|
import process from 'process';
|
|
4
4
|
import { upgradeWebSocket, serve } from '@hono/node-server';
|
|
5
5
|
import { apiContract, AUDIO_WS_PATH, AudioWsClientMessageSchema } from '@h-ai/api-contract';
|
|
6
6
|
import { audit } from '@h-ai/audit';
|
|
7
7
|
import { core, HaiCommonError, err, ok } from '@h-ai/core';
|
|
8
|
+
import { TRANSPORT_PLAINTEXT_RANGE, createTransportBodyPolicy, TRANSPORT_PROTOCOL, authenticateTransportRequest, TransportTimeError, createTransportErrorResponse, TransportBodyTooLargeError, readTransportBody, TRANSPORT_BODY_LIMITS, isValidSM2PublicKey } from '@h-ai/crypto';
|
|
8
9
|
import { OpenAPIHandler } from '@orpc/openapi/fetch';
|
|
9
10
|
import { RPCHandler } from '@orpc/server/fetch';
|
|
10
11
|
import { Hono } from 'hono';
|
|
11
12
|
import { WebSocketServer } from 'ws';
|
|
12
13
|
import { Buffer } from 'buffer';
|
|
13
14
|
import { BlockList, isIP } from 'net';
|
|
14
|
-
import { TRANSPORT_PLAINTEXT_RANGE, TRANSPORT_PROTOCOL, authenticateTransportRequest, TransportTimeError, createTransportErrorResponse, TransportBodyTooLargeError, readTransportBody, TRANSPORT_BODY_LIMITS, isValidSM2PublicKey } from '@h-ai/crypto';
|
|
15
15
|
import { z } from 'zod';
|
|
16
16
|
import { getCookie } from 'hono/cookie';
|
|
17
17
|
import { readFile } from 'fs/promises';
|
|
@@ -725,7 +725,8 @@ var RpcHttpConfigInputSchema = z.union([
|
|
|
725
725
|
var TransportRuntimeConfigInputSchema = z.union([
|
|
726
726
|
z.literal(false),
|
|
727
727
|
z.object({
|
|
728
|
-
maxPlaintextBytes: z.union([z.number().int().min(TRANSPORT_PLAINTEXT_RANGE.min).max(TRANSPORT_PLAINTEXT_RANGE.max), z.literal("auto")]).transform((value) => value === "auto" ? void 0 : value).optional().describe("\u53CC\u5411\u660E\u6587\u4E0A\u9650\uFF1Bauto \
|
|
728
|
+
maxPlaintextBytes: z.union([z.number().int().min(TRANSPORT_PLAINTEXT_RANGE.min).max(TRANSPORT_PLAINTEXT_RANGE.max), z.literal("auto")]).transform((value) => value === "auto" ? void 0 : value).optional().describe("\u666E\u901A\u4E1A\u52A1\u53CC\u5411\u660E\u6587\u4E0A\u9650\uFF1Bauto \u4E3A 1 MiB\uFF1B\u5927\u6750\u6599\u4F7F\u7528 routePlaintextBytes \u5355\u72EC\u58F0\u660E").meta({ default: "auto" }),
|
|
729
|
+
routePlaintextBytes: z.record(AbsolutePathSchema, z.number().int().min(TRANSPORT_PLAINTEXT_RANGE.min).max(TRANSPORT_PLAINTEXT_RANGE.max)).optional().describe("\u5B8C\u6574\u8DEF\u5F84\u6BB5\u5230\u53CC\u5411\u660E\u6587\u5B57\u8282\u4E0A\u9650\u7684\u6620\u5C04\uFF1B\u661F\u53F7\u5339\u914D\u5355\u6BB5\uFF0C\u4F9B\u9644\u4EF6\u548C\u5F52\u6863\u72EC\u7ACB\u5BB9\u91CF"),
|
|
729
730
|
keyExchangePath: AbsolutePathSchema.default("/_hai/key-exchange"),
|
|
730
731
|
excludePaths: z.array(AbsolutePathSchema).default([])
|
|
731
732
|
})
|
|
@@ -1062,7 +1063,7 @@ function readScalarBrowserEntry(packageInfo) {
|
|
|
1062
1063
|
}
|
|
1063
1064
|
var logger2 = core.logger.child({ module: "serv", scope: "transport" });
|
|
1064
1065
|
var ENCRYPTABLE_RESPONSE_CONTENT_TYPES = ["application/json"];
|
|
1065
|
-
function createTransportMiddleware(manager, keyExchangePath, excludePaths = []) {
|
|
1066
|
+
function createTransportMiddleware(manager, keyExchangePath, excludePaths = [], policy = createTransportBodyPolicy(manager.bodyLimits.plaintextBytes)) {
|
|
1066
1067
|
return async (c, next) => {
|
|
1067
1068
|
const pathname = new URL(c.req.url).pathname;
|
|
1068
1069
|
if (pathname === keyExchangePath && c.req.method === "POST")
|
|
@@ -1072,76 +1073,83 @@ function createTransportMiddleware(manager, keyExchangePath, excludePaths = [])
|
|
|
1072
1073
|
const clientId = c.req.header(TRANSPORT_PROTOCOL.CLIENT_ID_HEADER);
|
|
1073
1074
|
if (!clientId)
|
|
1074
1075
|
return jsonError(servM("serv_transportClientIdRequired"), 400);
|
|
1075
|
-
|
|
1076
|
+
const lease = policy.acquire(pathname);
|
|
1077
|
+
if (!lease)
|
|
1078
|
+
return jsonError(servM("serv_transportBusy"), 503);
|
|
1076
1079
|
try {
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1080
|
+
let requestContext;
|
|
1081
|
+
try {
|
|
1082
|
+
const verified = await authenticateTransportRequest(manager, c.req.raw, lease.limits);
|
|
1083
|
+
if (!verified.success) {
|
|
1084
|
+
if (verified.error.cause instanceof TransportTimeError && verified.error.cause.context)
|
|
1085
|
+
return createTransportErrorResponse(verified.error.cause, { manager, context: verified.error.cause.context });
|
|
1086
|
+
return jsonError(servM("serv_transportDecryptFailed"), 400);
|
|
1087
|
+
}
|
|
1088
|
+
requestContext = verified.data.context;
|
|
1089
|
+
if (c.req.method !== "GET" && c.req.method !== "HEAD")
|
|
1090
|
+
replaceRequestBody(c, verified.data.plaintext);
|
|
1091
|
+
} catch (error) {
|
|
1092
|
+
if (error instanceof TransportBodyTooLargeError)
|
|
1093
|
+
return createTransportErrorResponse(error);
|
|
1094
|
+
return jsonError(servM("serv_transportInvalidPayload"), error instanceof RangeError ? 413 : 400);
|
|
1082
1095
|
}
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
c.header("Content-Length", void 0);
|
|
1101
|
-
c.header("Content-Encoding", void 0);
|
|
1102
|
-
c.header(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER, void 0);
|
|
1103
|
-
c.header(TRANSPORT_PROTOCOL.ENVELOPE_HEADER, void 0);
|
|
1104
|
-
if (!bodyless && !canEncryptResponseContentType(contentType)) {
|
|
1105
|
-
void response.body?.cancel().catch(() => {
|
|
1106
|
-
});
|
|
1107
|
-
c.res = jsonError(servM("serv_transportEncryptFailed"), 500);
|
|
1108
|
-
return;
|
|
1109
|
-
}
|
|
1110
|
-
let bodyText;
|
|
1111
|
-
try {
|
|
1112
|
-
bodyText = bodyless ? "" : await readTransportBody(response.body, {
|
|
1113
|
-
maxBytes: manager.bodyLimits.plaintextBytes,
|
|
1114
|
-
direction: "response",
|
|
1115
|
-
signal: c.req.raw.signal
|
|
1116
|
-
});
|
|
1117
|
-
} catch (cause) {
|
|
1118
|
-
if (cause instanceof TransportBodyTooLargeError) {
|
|
1119
|
-
c.res = await createTransportErrorResponse(cause, { manager, context: requestContext });
|
|
1096
|
+
await next();
|
|
1097
|
+
const response = c.res;
|
|
1098
|
+
if (!response)
|
|
1099
|
+
return;
|
|
1100
|
+
const contentType = response.headers.get("Content-Type") ?? "";
|
|
1101
|
+
const bodyless = c.req.method === "HEAD" || isEmptyResponse(response);
|
|
1102
|
+
if (bodyless)
|
|
1103
|
+
void response.body?.cancel().catch(() => {
|
|
1104
|
+
});
|
|
1105
|
+
c.header("Content-Length", void 0);
|
|
1106
|
+
c.header("Content-Encoding", void 0);
|
|
1107
|
+
c.header(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER, void 0);
|
|
1108
|
+
c.header(TRANSPORT_PROTOCOL.ENVELOPE_HEADER, void 0);
|
|
1109
|
+
if (!bodyless && !canEncryptResponseContentType(contentType)) {
|
|
1110
|
+
void response.body?.cancel().catch(() => {
|
|
1111
|
+
});
|
|
1112
|
+
c.res = jsonError(servM("serv_transportEncryptFailed"), 500);
|
|
1120
1113
|
return;
|
|
1121
1114
|
}
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1115
|
+
let bodyText;
|
|
1116
|
+
try {
|
|
1117
|
+
bodyText = bodyless ? "" : await readTransportBody(response.body, {
|
|
1118
|
+
maxBytes: lease.limits.plaintextBytes,
|
|
1119
|
+
direction: "response",
|
|
1120
|
+
signal: c.req.raw.signal
|
|
1121
|
+
});
|
|
1122
|
+
} catch (cause) {
|
|
1123
|
+
if (cause instanceof TransportBodyTooLargeError) {
|
|
1124
|
+
c.res = await createTransportErrorResponse(cause, { manager, context: requestContext });
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
logger2.warn("Failed to read response body for encryption", { error: cause });
|
|
1128
|
+
c.res = jsonError(servM("serv_transportEncryptFailed"), 500);
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
const headers = new Headers(response.headers);
|
|
1132
|
+
headers.delete(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER);
|
|
1133
|
+
headers.delete("Content-Length");
|
|
1134
|
+
headers.delete("Content-Encoding");
|
|
1135
|
+
const encResult = await manager.encryptResponse(requestContext, bodyText, response.status);
|
|
1136
|
+
if (!encResult.success) {
|
|
1137
|
+
logger2.warn("Failed to encrypt response", { error: encResult.error });
|
|
1138
|
+
c.res = jsonError(servM("serv_transportEncryptFailed"), 500);
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
headers.set("Content-Type", "application/json; charset=utf-8");
|
|
1142
|
+
headers.set(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER, TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE);
|
|
1143
|
+
if (bodyless)
|
|
1144
|
+
headers.set(TRANSPORT_PROTOCOL.ENVELOPE_HEADER, JSON.stringify(encResult.data));
|
|
1145
|
+
c.res = new Response(bodyless ? null : JSON.stringify(encResult.data), {
|
|
1146
|
+
status: response.status,
|
|
1147
|
+
statusText: response.statusText,
|
|
1148
|
+
headers
|
|
1149
|
+
});
|
|
1150
|
+
} finally {
|
|
1151
|
+
lease.release();
|
|
1135
1152
|
}
|
|
1136
|
-
headers.set("Content-Type", "application/json; charset=utf-8");
|
|
1137
|
-
headers.set(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER, TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE);
|
|
1138
|
-
if (bodyless)
|
|
1139
|
-
headers.set(TRANSPORT_PROTOCOL.ENVELOPE_HEADER, JSON.stringify(encResult.data));
|
|
1140
|
-
c.res = new Response(bodyless ? null : JSON.stringify(encResult.data), {
|
|
1141
|
-
status: response.status,
|
|
1142
|
-
statusText: response.statusText,
|
|
1143
|
-
headers
|
|
1144
|
-
});
|
|
1145
1153
|
};
|
|
1146
1154
|
}
|
|
1147
1155
|
async function handleKeyExchange(request, manager) {
|
|
@@ -1235,7 +1243,7 @@ function createApp(options) {
|
|
|
1235
1243
|
userAgent: c.req.header("user-agent")?.slice(0, 4096)
|
|
1236
1244
|
}, async () => {
|
|
1237
1245
|
await next();
|
|
1238
|
-
if (c.res.status >= 400) {
|
|
1246
|
+
if (audit.isInitialized && c.res.status >= 400) {
|
|
1239
1247
|
const trusted = requestAuditContexts.get(c.req.raw);
|
|
1240
1248
|
const recorded = await audit.capture({
|
|
1241
1249
|
action: "http.reject",
|
|
@@ -1275,10 +1283,11 @@ function createApp(options) {
|
|
|
1275
1283
|
if (options.middlewares)
|
|
1276
1284
|
mountCustomMiddlewares(app, options.middlewares);
|
|
1277
1285
|
if (options.transport) {
|
|
1286
|
+
const policy = createTransportBodyPolicy(options.transport.maxPlaintextBytes, options.transport.routePlaintextBytes);
|
|
1278
1287
|
const mgr = options.transport.crypto.transport.createServer({
|
|
1279
1288
|
serverKeyRing: options.transport.serverKeyRing,
|
|
1280
1289
|
keyStore: options.transport.keyStore,
|
|
1281
|
-
maxPlaintextBytes:
|
|
1290
|
+
maxPlaintextBytes: policy.maximum.plaintextBytes
|
|
1282
1291
|
});
|
|
1283
1292
|
if (!mgr.success) {
|
|
1284
1293
|
throw core.error.buildHaiErrorInst(
|
|
@@ -1288,7 +1297,7 @@ function createApp(options) {
|
|
|
1288
1297
|
);
|
|
1289
1298
|
}
|
|
1290
1299
|
const keyExchangePath = `${http.apiPrefix}${options.transport.keyExchangePath ?? "/_hai/key-exchange"}`;
|
|
1291
|
-
app.use("*", createTransportMiddleware(mgr.data, keyExchangePath, options.transport.excludePaths));
|
|
1300
|
+
app.use("*", createTransportMiddleware(mgr.data, keyExchangePath, options.transport.excludePaths, policy));
|
|
1292
1301
|
}
|
|
1293
1302
|
if (http.health !== false)
|
|
1294
1303
|
mountHealthRoutes(app, http.health);
|