nstarter-http-request 0.1.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.
Files changed (263) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +160 -0
  3. package/dist/cjs/adapter/axios.adapter.js +224 -0
  4. package/dist/cjs/adapter/axios.adapter.js.map +1 -0
  5. package/dist/cjs/adapter/base.js +134 -0
  6. package/dist/cjs/adapter/base.js.map +1 -0
  7. package/dist/cjs/adapter/index.js +20 -0
  8. package/dist/cjs/adapter/index.js.map +1 -0
  9. package/dist/cjs/adapter/undici.adapter.js +272 -0
  10. package/dist/cjs/adapter/undici.adapter.js.map +1 -0
  11. package/dist/cjs/client.js +409 -0
  12. package/dist/cjs/client.js.map +1 -0
  13. package/dist/cjs/config.js +69 -0
  14. package/dist/cjs/config.js.map +1 -0
  15. package/dist/cjs/const/config.const.js +53 -0
  16. package/dist/cjs/const/config.const.js.map +1 -0
  17. package/dist/cjs/const/dns.const.js +16 -0
  18. package/dist/cjs/const/dns.const.js.map +1 -0
  19. package/dist/cjs/const/enum.const.js +22 -0
  20. package/dist/cjs/const/enum.const.js.map +1 -0
  21. package/dist/cjs/const/index.js +21 -0
  22. package/dist/cjs/const/index.js.map +1 -0
  23. package/dist/cjs/const/ip.const.js +110 -0
  24. package/dist/cjs/const/ip.const.js.map +1 -0
  25. package/dist/cjs/index.js +25 -0
  26. package/dist/cjs/index.js.map +1 -0
  27. package/dist/cjs/logger/base.js +60 -0
  28. package/dist/cjs/logger/base.js.map +1 -0
  29. package/dist/cjs/logger/http_client.logger.js +209 -0
  30. package/dist/cjs/logger/http_client.logger.js.map +1 -0
  31. package/dist/cjs/logger/index.js +20 -0
  32. package/dist/cjs/logger/index.js.map +1 -0
  33. package/dist/cjs/logger/log.filter.js +126 -0
  34. package/dist/cjs/logger/log.filter.js.map +1 -0
  35. package/dist/cjs/security/dns.validator.js +137 -0
  36. package/dist/cjs/security/dns.validator.js.map +1 -0
  37. package/dist/cjs/security/index.js +21 -0
  38. package/dist/cjs/security/index.js.map +1 -0
  39. package/dist/cjs/security/ip.validator.js +107 -0
  40. package/dist/cjs/security/ip.validator.js.map +1 -0
  41. package/dist/cjs/security/ssrf.guard.js +180 -0
  42. package/dist/cjs/security/ssrf.guard.js.map +1 -0
  43. package/dist/cjs/security/url.validator.js +170 -0
  44. package/dist/cjs/security/url.validator.js.map +1 -0
  45. package/dist/cjs/tsconfig.tsbuildinfo +1 -0
  46. package/dist/cjs/types/adapter.js +6 -0
  47. package/dist/cjs/types/adapter.js.map +1 -0
  48. package/dist/cjs/types/client.js +6 -0
  49. package/dist/cjs/types/client.js.map +1 -0
  50. package/dist/cjs/types/config.js +6 -0
  51. package/dist/cjs/types/config.js.map +1 -0
  52. package/dist/cjs/types/errors.js +35 -0
  53. package/dist/cjs/types/errors.js.map +1 -0
  54. package/dist/cjs/types/index.js +35 -0
  55. package/dist/cjs/types/index.js.map +1 -0
  56. package/dist/cjs/types/ip.js +6 -0
  57. package/dist/cjs/types/ip.js.map +1 -0
  58. package/dist/cjs/types/logger.js +6 -0
  59. package/dist/cjs/types/logger.js.map +1 -0
  60. package/dist/cjs/types/request_response.js +6 -0
  61. package/dist/cjs/types/request_response.js.map +1 -0
  62. package/dist/cjs/types/security.js +6 -0
  63. package/dist/cjs/types/security.js.map +1 -0
  64. package/dist/cjs/types/trace.js +14 -0
  65. package/dist/cjs/types/trace.js.map +1 -0
  66. package/dist/cjs/utils/common.js +31 -0
  67. package/dist/cjs/utils/common.js.map +1 -0
  68. package/dist/cjs/utils/domain.js +79 -0
  69. package/dist/cjs/utils/domain.js.map +1 -0
  70. package/dist/cjs/utils/index.js +44 -0
  71. package/dist/cjs/utils/index.js.map +1 -0
  72. package/dist/cjs/utils/ip.range.js +200 -0
  73. package/dist/cjs/utils/ip.range.js.map +1 -0
  74. package/dist/cjs/utils/trace.context.js +213 -0
  75. package/dist/cjs/utils/trace.context.js.map +1 -0
  76. package/dist/esm/adapter/axios.adapter.js +184 -0
  77. package/dist/esm/adapter/axios.adapter.js.map +1 -0
  78. package/dist/esm/adapter/base.js +130 -0
  79. package/dist/esm/adapter/base.js.map +1 -0
  80. package/dist/esm/adapter/index.js +4 -0
  81. package/dist/esm/adapter/index.js.map +1 -0
  82. package/dist/esm/adapter/undici.adapter.js +235 -0
  83. package/dist/esm/adapter/undici.adapter.js.map +1 -0
  84. package/dist/esm/client.js +405 -0
  85. package/dist/esm/client.js.map +1 -0
  86. package/dist/esm/config.js +65 -0
  87. package/dist/esm/config.js.map +1 -0
  88. package/dist/esm/const/config.const.js +50 -0
  89. package/dist/esm/const/config.const.js.map +1 -0
  90. package/dist/esm/const/dns.const.js +13 -0
  91. package/dist/esm/const/dns.const.js.map +1 -0
  92. package/dist/esm/const/enum.const.js +19 -0
  93. package/dist/esm/const/enum.const.js.map +1 -0
  94. package/dist/esm/const/index.js +5 -0
  95. package/dist/esm/const/index.js.map +1 -0
  96. package/dist/esm/const/ip.const.js +107 -0
  97. package/dist/esm/const/ip.const.js.map +1 -0
  98. package/dist/esm/index.js +9 -0
  99. package/dist/esm/index.js.map +1 -0
  100. package/dist/esm/logger/base.js +55 -0
  101. package/dist/esm/logger/base.js.map +1 -0
  102. package/dist/esm/logger/http_client.logger.js +205 -0
  103. package/dist/esm/logger/http_client.logger.js.map +1 -0
  104. package/dist/esm/logger/index.js +4 -0
  105. package/dist/esm/logger/index.js.map +1 -0
  106. package/dist/esm/logger/log.filter.js +122 -0
  107. package/dist/esm/logger/log.filter.js.map +1 -0
  108. package/dist/esm/security/dns.validator.js +133 -0
  109. package/dist/esm/security/dns.validator.js.map +1 -0
  110. package/dist/esm/security/index.js +5 -0
  111. package/dist/esm/security/index.js.map +1 -0
  112. package/dist/esm/security/ip.validator.js +103 -0
  113. package/dist/esm/security/ip.validator.js.map +1 -0
  114. package/dist/esm/security/ssrf.guard.js +176 -0
  115. package/dist/esm/security/ssrf.guard.js.map +1 -0
  116. package/dist/esm/security/url.validator.js +166 -0
  117. package/dist/esm/security/url.validator.js.map +1 -0
  118. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -0
  119. package/dist/esm/types/adapter.js +5 -0
  120. package/dist/esm/types/adapter.js.map +1 -0
  121. package/dist/esm/types/client.js +5 -0
  122. package/dist/esm/types/client.js.map +1 -0
  123. package/dist/esm/types/config.js +5 -0
  124. package/dist/esm/types/config.js.map +1 -0
  125. package/dist/esm/types/errors.js +30 -0
  126. package/dist/esm/types/errors.js.map +1 -0
  127. package/dist/esm/types/index.js +19 -0
  128. package/dist/esm/types/index.js.map +1 -0
  129. package/dist/esm/types/ip.js +5 -0
  130. package/dist/esm/types/ip.js.map +1 -0
  131. package/dist/esm/types/logger.js +5 -0
  132. package/dist/esm/types/logger.js.map +1 -0
  133. package/dist/esm/types/request_response.js +5 -0
  134. package/dist/esm/types/request_response.js.map +1 -0
  135. package/dist/esm/types/security.js +5 -0
  136. package/dist/esm/types/security.js.map +1 -0
  137. package/dist/esm/types/trace.js +11 -0
  138. package/dist/esm/types/trace.js.map +1 -0
  139. package/dist/esm/utils/common.js +27 -0
  140. package/dist/esm/utils/common.js.map +1 -0
  141. package/dist/esm/utils/domain.js +71 -0
  142. package/dist/esm/utils/domain.js.map +1 -0
  143. package/dist/esm/utils/index.js +7 -0
  144. package/dist/esm/utils/index.js.map +1 -0
  145. package/dist/esm/utils/ip.range.js +187 -0
  146. package/dist/esm/utils/ip.range.js.map +1 -0
  147. package/dist/esm/utils/trace.context.js +199 -0
  148. package/dist/esm/utils/trace.context.js.map +1 -0
  149. package/dist/types/adapter/axios.adapter.d.ts +51 -0
  150. package/dist/types/adapter/axios.adapter.d.ts.map +1 -0
  151. package/dist/types/adapter/base.d.ts +56 -0
  152. package/dist/types/adapter/base.d.ts.map +1 -0
  153. package/dist/types/adapter/index.d.ts +4 -0
  154. package/dist/types/adapter/index.d.ts.map +1 -0
  155. package/dist/types/adapter/undici.adapter.d.ts +68 -0
  156. package/dist/types/adapter/undici.adapter.d.ts.map +1 -0
  157. package/dist/types/client.d.ts +105 -0
  158. package/dist/types/client.d.ts.map +1 -0
  159. package/dist/types/config.d.ts +14 -0
  160. package/dist/types/config.d.ts.map +1 -0
  161. package/dist/types/const/config.const.d.ts +23 -0
  162. package/dist/types/const/config.const.d.ts.map +1 -0
  163. package/dist/types/const/dns.const.d.ts +13 -0
  164. package/dist/types/const/dns.const.d.ts.map +1 -0
  165. package/dist/types/const/enum.const.d.ts +17 -0
  166. package/dist/types/const/enum.const.d.ts.map +1 -0
  167. package/dist/types/const/index.d.ts +5 -0
  168. package/dist/types/const/index.d.ts.map +1 -0
  169. package/dist/types/const/ip.const.d.ts +42 -0
  170. package/dist/types/const/ip.const.d.ts.map +1 -0
  171. package/dist/types/index.d.ts +9 -0
  172. package/dist/types/index.d.ts.map +1 -0
  173. package/dist/types/logger/base.d.ts +42 -0
  174. package/dist/types/logger/base.d.ts.map +1 -0
  175. package/dist/types/logger/http_client.logger.d.ts +49 -0
  176. package/dist/types/logger/http_client.logger.d.ts.map +1 -0
  177. package/dist/types/logger/index.d.ts +4 -0
  178. package/dist/types/logger/index.d.ts.map +1 -0
  179. package/dist/types/logger/log.filter.d.ts +56 -0
  180. package/dist/types/logger/log.filter.d.ts.map +1 -0
  181. package/dist/types/security/dns.validator.d.ts +61 -0
  182. package/dist/types/security/dns.validator.d.ts.map +1 -0
  183. package/dist/types/security/index.d.ts +5 -0
  184. package/dist/types/security/index.d.ts.map +1 -0
  185. package/dist/types/security/ip.validator.d.ts +31 -0
  186. package/dist/types/security/ip.validator.d.ts.map +1 -0
  187. package/dist/types/security/ssrf.guard.d.ts +54 -0
  188. package/dist/types/security/ssrf.guard.d.ts.map +1 -0
  189. package/dist/types/security/url.validator.d.ts +76 -0
  190. package/dist/types/security/url.validator.d.ts.map +1 -0
  191. package/dist/types/types/adapter.d.ts +30 -0
  192. package/dist/types/types/adapter.d.ts.map +1 -0
  193. package/dist/types/types/client.d.ts +85 -0
  194. package/dist/types/types/client.d.ts.map +1 -0
  195. package/dist/types/types/config.d.ts +99 -0
  196. package/dist/types/types/config.d.ts.map +1 -0
  197. package/dist/types/types/errors.d.ts +23 -0
  198. package/dist/types/types/errors.d.ts.map +1 -0
  199. package/dist/types/types/index.d.ts +10 -0
  200. package/dist/types/types/index.d.ts.map +1 -0
  201. package/dist/types/types/ip.d.ts +32 -0
  202. package/dist/types/types/ip.d.ts.map +1 -0
  203. package/dist/types/types/logger.d.ts +136 -0
  204. package/dist/types/types/logger.d.ts.map +1 -0
  205. package/dist/types/types/request_response.d.ts +54 -0
  206. package/dist/types/types/request_response.d.ts.map +1 -0
  207. package/dist/types/types/security.d.ts +115 -0
  208. package/dist/types/types/security.d.ts.map +1 -0
  209. package/dist/types/types/trace.d.ts +34 -0
  210. package/dist/types/types/trace.d.ts.map +1 -0
  211. package/dist/types/utils/common.d.ts +14 -0
  212. package/dist/types/utils/common.d.ts.map +1 -0
  213. package/dist/types/utils/domain.d.ts +39 -0
  214. package/dist/types/utils/domain.d.ts.map +1 -0
  215. package/dist/types/utils/index.d.ts +6 -0
  216. package/dist/types/utils/index.d.ts.map +1 -0
  217. package/dist/types/utils/ip.range.d.ts +61 -0
  218. package/dist/types/utils/ip.range.d.ts.map +1 -0
  219. package/dist/types/utils/trace.context.d.ts +106 -0
  220. package/dist/types/utils/trace.context.d.ts.map +1 -0
  221. package/docs/adapters.md +53 -0
  222. package/docs/configuration.md +149 -0
  223. package/docs/logging.md +70 -0
  224. package/docs/proxy.md +44 -0
  225. package/docs/security.md +56 -0
  226. package/docs/trace-context.md +436 -0
  227. package/package.json +50 -0
  228. package/src/adapter/axios.adapter.ts +228 -0
  229. package/src/adapter/base.ts +180 -0
  230. package/src/adapter/index.ts +3 -0
  231. package/src/adapter/undici.adapter.ts +282 -0
  232. package/src/client.ts +552 -0
  233. package/src/config.ts +86 -0
  234. package/src/const/config.const.ts +60 -0
  235. package/src/const/dns.const.ts +15 -0
  236. package/src/const/enum.const.ts +17 -0
  237. package/src/const/index.ts +4 -0
  238. package/src/const/ip.const.ts +139 -0
  239. package/src/index.ts +8 -0
  240. package/src/logger/base.ts +75 -0
  241. package/src/logger/http_client.logger.ts +272 -0
  242. package/src/logger/index.ts +3 -0
  243. package/src/logger/log.filter.ts +149 -0
  244. package/src/security/dns.validator.ts +170 -0
  245. package/src/security/index.ts +4 -0
  246. package/src/security/ip.validator.ts +124 -0
  247. package/src/security/ssrf.guard.ts +224 -0
  248. package/src/security/url.validator.ts +192 -0
  249. package/src/types/adapter.ts +38 -0
  250. package/src/types/client.ts +119 -0
  251. package/src/types/config.ts +110 -0
  252. package/src/types/errors.ts +38 -0
  253. package/src/types/index.ts +27 -0
  254. package/src/types/ip.ts +34 -0
  255. package/src/types/logger.ts +150 -0
  256. package/src/types/request_response.ts +65 -0
  257. package/src/types/security.ts +126 -0
  258. package/src/types/trace.ts +35 -0
  259. package/src/utils/common.ts +28 -0
  260. package/src/utils/domain.ts +78 -0
  261. package/src/utils/index.ts +7 -0
  262. package/src/utils/ip.range.ts +218 -0
  263. package/src/utils/trace.context.ts +240 -0
@@ -0,0 +1,272 @@
1
+ "use strict";
2
+ /**
3
+ * Undici 适配器实现
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.UndiciAdapter = void 0;
40
+ const types_1 = require("../types");
41
+ const base_1 = require("./base");
42
+ const undici_1 = __importStar(require("undici"));
43
+ const http_1 = require("http");
44
+ const const_1 = require("../const");
45
+ /**
46
+ * Undici 适配器
47
+ */
48
+ class UndiciAdapter extends base_1.BaseHttpAdapter {
49
+ constructor() {
50
+ super(...arguments);
51
+ this.name = const_1.EClientAdapter.undici;
52
+ }
53
+ /**
54
+ * 检查 Undici 是否可用
55
+ */
56
+ isAvailable() {
57
+ return !!undici_1.default;
58
+ }
59
+ /**
60
+ * 发送请求
61
+ * @param options 请求选项
62
+ */
63
+ async sendRequest(options) {
64
+ if (!this.isAvailable()) {
65
+ throw new Error('Undici is not available. Please install it: npm install undici');
66
+ }
67
+ const fullUrl = this.buildUrl(options.url, options.params);
68
+ const headers = this.prepareHeaders(options.headers);
69
+ const body = this.prepareBody(options.body, headers);
70
+ const dispatcher = this.buildDispatcher(options);
71
+ const undiciOptions = this.buildUndiciOptions(options, headers, body, dispatcher);
72
+ try {
73
+ const startTime = Date.now();
74
+ const response = await undici_1.default.request(fullUrl, undiciOptions);
75
+ const duration = Date.now() - startTime;
76
+ this.logger?.debug('HTTP request completed', {
77
+ method: options.method,
78
+ url: fullUrl,
79
+ status: response.statusCode,
80
+ duration
81
+ });
82
+ const data = await this.readResponseBody(response, options.responseType);
83
+ const httpResponse = {
84
+ status: response.statusCode,
85
+ statusText: this.getStatusText(response.statusCode),
86
+ headers: this.normalizeHeaders(response.headers),
87
+ data,
88
+ config: options
89
+ };
90
+ return httpResponse;
91
+ }
92
+ catch (error) {
93
+ throw this.handleUndiciError(error, options);
94
+ }
95
+ }
96
+ /**
97
+ * 准备请求头
98
+ * @param headers 原始请求头
99
+ */
100
+ prepareHeaders(headers) {
101
+ const prepared = {};
102
+ if (headers) {
103
+ for (const [key, value] of Object.entries(headers)) {
104
+ prepared[key] = typeof value === 'number' ? String(value) : value;
105
+ }
106
+ }
107
+ return prepared;
108
+ }
109
+ /**
110
+ * 准备请求体
111
+ * @param body 原始请求体
112
+ * @param headers 请求头
113
+ */
114
+ prepareBody(body, headers) {
115
+ if (body === undefined) {
116
+ return undefined;
117
+ }
118
+ if (typeof body === 'object' && !(body instanceof Buffer)) {
119
+ // 自动设置 Content-Type
120
+ if (!headers['content-type'] && !headers['Content-Type']) {
121
+ headers['Content-Type'] = 'application/json';
122
+ }
123
+ return JSON.stringify(body);
124
+ }
125
+ return body;
126
+ }
127
+ /**
128
+ * 构建 Dispatcher(代理或 TLS 配置)
129
+ * @param options 请求选项
130
+ */
131
+ buildDispatcher(options) {
132
+ let dispatcher;
133
+ // 如果 rejectUnauthorized 为 false,需要创建 Agent 来设置 TLS 选项
134
+ if (!this.securityConfig.rejectUnauthorized) {
135
+ dispatcher = new undici_1.Agent({
136
+ connect: {
137
+ rejectUnauthorized: false
138
+ }
139
+ });
140
+ }
141
+ // 有代理时,创建 ProxyAgent
142
+ if (this.requestConfig.proxy) {
143
+ dispatcher = this.createGlobalProxyAgent();
144
+ }
145
+ // 设置正向代理(如果请求元数据中包含代理信息,会覆盖全局代理配置)
146
+ const forwardProxyMeta = options.meta?.__forwardProxy;
147
+ if (forwardProxyMeta) {
148
+ dispatcher = this.createForwardProxyAgent(forwardProxyMeta.proxyUrl);
149
+ }
150
+ return dispatcher;
151
+ }
152
+ /**
153
+ * 创建全局代理 Agent
154
+ */
155
+ createGlobalProxyAgent() {
156
+ const { protocol, host, port, auth } = this.requestConfig.proxy;
157
+ let proxyUrl = `${protocol}://${host}:${port}`;
158
+ if (auth) {
159
+ const encodedUsername = encodeURIComponent(auth.username);
160
+ const encodedPassword = encodeURIComponent(auth.password);
161
+ proxyUrl = `${protocol}://${encodedUsername}:${encodedPassword}@${host}:${port}`;
162
+ }
163
+ const agent = new undici_1.ProxyAgent({
164
+ uri: proxyUrl,
165
+ connect: {
166
+ rejectUnauthorized: this.securityConfig.rejectUnauthorized
167
+ }
168
+ });
169
+ this.logger?.debug('Using ProxyAgent with global proxy config', {
170
+ host,
171
+ port,
172
+ protocol,
173
+ rejectUnauthorized: this.securityConfig.rejectUnauthorized
174
+ });
175
+ return agent;
176
+ }
177
+ /**
178
+ * 创建正向代理 Agent
179
+ * @param proxyUrl 代理 URL
180
+ */
181
+ createForwardProxyAgent(proxyUrl) {
182
+ const agent = new undici_1.ProxyAgent({
183
+ uri: proxyUrl,
184
+ connect: {
185
+ rejectUnauthorized: this.securityConfig.rejectUnauthorized
186
+ }
187
+ });
188
+ this.logger?.debug('Using ProxyAgent for forward proxy (overrides global proxy)', {
189
+ proxyUrl,
190
+ rejectUnauthorized: this.securityConfig.rejectUnauthorized
191
+ });
192
+ return agent;
193
+ }
194
+ /**
195
+ * 构建 Undici 请求选项
196
+ */
197
+ buildUndiciOptions(options, headers, body, dispatcher) {
198
+ return {
199
+ method: options.method,
200
+ headers,
201
+ body,
202
+ bodyTimeout: options.timeout ?? this.requestConfig.timeout,
203
+ headersTimeout: options.timeout ?? this.requestConfig.timeout,
204
+ maxRedirections: this.requestConfig.maxRedirects,
205
+ ...(dispatcher && { dispatcher }),
206
+ };
207
+ }
208
+ /**
209
+ * 处理 Undici 错误
210
+ * @param error 错误对象
211
+ * @param options 请求选项
212
+ */
213
+ handleUndiciError(error, options) {
214
+ const errorMessage = error instanceof Error ? error.message : 'Undici request failed';
215
+ const errorCode = (error && typeof error === 'object' && 'code' in error && typeof error.code === 'string')
216
+ ? error.code
217
+ : 'UNDICI_ERROR';
218
+ return new types_1.HttpError(errorMessage, errorCode, undefined, undefined, options);
219
+ }
220
+ /**
221
+ * 读取响应体
222
+ * @param response Undici 响应对象
223
+ * @param responseType 响应类型
224
+ */
225
+ async readResponseBody(response, responseType) {
226
+ try {
227
+ switch (responseType) {
228
+ case 'text': {
229
+ return (await response.body.text());
230
+ }
231
+ case 'arraybuffer': {
232
+ return (await response.body.arrayBuffer());
233
+ }
234
+ case 'stream': {
235
+ // undici 返回 BodyReadable (继承自 Node.js Readable)
236
+ return response.body;
237
+ }
238
+ case 'blob': {
239
+ return (await response.body.blob());
240
+ }
241
+ case 'formdata': {
242
+ return (await response.body.formData());
243
+ }
244
+ case 'json': {
245
+ return (await response.body.json());
246
+ }
247
+ default: {
248
+ const text = await response.body.text();
249
+ try {
250
+ return JSON.parse(text);
251
+ }
252
+ catch {
253
+ // 如果不是有效的 JSON,返回原文本
254
+ return text;
255
+ }
256
+ }
257
+ }
258
+ }
259
+ catch (error) {
260
+ throw new Error(`Failed to read response body: ${error instanceof Error ? error.message : String(error)}`);
261
+ }
262
+ }
263
+ /**
264
+ * 获取状态文本
265
+ * @param statusCode 状态码
266
+ */
267
+ getStatusText(statusCode) {
268
+ return http_1.STATUS_CODES[statusCode] || 'Unknown';
269
+ }
270
+ }
271
+ exports.UndiciAdapter = UndiciAdapter;
272
+ //# sourceMappingURL=undici.adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"undici.adapter.js","sourceRoot":"","sources":["../../../src/adapter/undici.adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,409 @@
1
+ "use strict";
2
+ /**
3
+ * HTTP 客户端核心类
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.NsHttpClient = void 0;
7
+ const config_1 = require("./config");
8
+ const security_1 = require("./security");
9
+ const adapter_1 = require("./adapter");
10
+ const logger_1 = require("./logger");
11
+ const const_1 = require("./const");
12
+ const types_1 = require("./types");
13
+ const utils_1 = require("./utils");
14
+ const node_stream_1 = require("node:stream");
15
+ /**
16
+ * HTTP 客户端
17
+ */
18
+ class NsHttpClient {
19
+ constructor(userConfig = {}) {
20
+ // 合并配置
21
+ this.config = (0, config_1.mergeConfig)(userConfig);
22
+ // 验证配置
23
+ (0, config_1.validateConfig)(this.config);
24
+ // 初始化日志记录器
25
+ this.logger = this.initLogger();
26
+ this.httpLogger = this.initHttpLogger();
27
+ // 初始化 SSRF 防护
28
+ this.ssrfGuard = new security_1.SsrfGuard(this.config.security);
29
+ // 初始化适配器
30
+ this.adapter = this.initAdapter();
31
+ this.logger.debug('NsHttpClient initialized', {
32
+ adapter: this.config.adapter,
33
+ security: {
34
+ enableDnsValidation: this.config.security.enableDnsValidation,
35
+ enableIpValidation: this.config.security.enableIpValidation,
36
+ allowPrivateIp: this.config.security.allowPrivateIp
37
+ }
38
+ });
39
+ }
40
+ /**
41
+ * 从配置创建 HTTP 客户端
42
+ * @param config
43
+ */
44
+ static fromHttpClientConfig(config = {}) {
45
+ return new NsHttpClient(config);
46
+ }
47
+ /**
48
+ * 挂载适配器.
49
+ * @param adapter
50
+ */
51
+ withAdapter(adapter) {
52
+ this.config.adapter = adapter;
53
+ this.adapter = this.initAdapter();
54
+ return this;
55
+ }
56
+ /**
57
+ * 挂载正向代理.
58
+ * @param proxy
59
+ */
60
+ withForwardProxy(proxy) {
61
+ this.forwardProxy = proxy;
62
+ this.logger.info('Forward proxy configured', {
63
+ enabled: proxy.enabled,
64
+ http_proxy: proxy.http_proxy,
65
+ https_proxy: proxy.https_proxy,
66
+ no_proxy_count: proxy.no_proxy?.length || 0
67
+ });
68
+ return this;
69
+ }
70
+ /**
71
+ * 初始化日志记录器
72
+ */
73
+ initLogger() {
74
+ if (!this.config.logging.enabled) {
75
+ return new logger_1.NullLogger();
76
+ }
77
+ return this.config.logging.logger || new logger_1.NullLogger();
78
+ }
79
+ /**
80
+ * 初始化 HTTP 客户端日志记录器
81
+ */
82
+ initHttpLogger() {
83
+ if (!this.config.logging.enabled) {
84
+ return undefined;
85
+ }
86
+ // 如果用户提供了自定义的 httpLogger,直接使用
87
+ if (this.config.logging.httpLogger) {
88
+ return this.config.logging.httpLogger;
89
+ }
90
+ // 否则,使用 NsHttpClientLogger 包装通用的 logger
91
+ return logger_1.NsHttpClientLogger.create(this.logger, this.config.logging.filter, {
92
+ logRequestBody: this.config.logging.logRequestBody,
93
+ logResponseBody: this.config.logging.logResponseBody,
94
+ bodyMaxLength: this.config.logging.bodyMaxLength
95
+ });
96
+ }
97
+ /**
98
+ * 初始化适配器
99
+ */
100
+ initAdapter() {
101
+ const adapter = this.config.adapter === const_1.EClientAdapter.axios
102
+ ? new adapter_1.AxiosAdapter(this.config.request, this.config.security, this.logger)
103
+ : new adapter_1.UndiciAdapter(this.config.request, this.config.security, this.logger);
104
+ if (!adapter.isAvailable()) {
105
+ throw new Error(`Adapter "${this.config.adapter}" is not available. ` +
106
+ `Please install it: npm install ${this.config.adapter}`);
107
+ }
108
+ return adapter;
109
+ }
110
+ /**
111
+ * 发送 HTTP 请求
112
+ * @param options 请求选项
113
+ * @returns 响应结果
114
+ */
115
+ async request(options) {
116
+ const startTime = Date.now();
117
+ try {
118
+ // 安全验证
119
+ const { safeUrl, result: ssrfResult } = await this.ssrfGuard.validateAndGetSafeUrl(options.url);
120
+ this.logger.debug('URL validation passed', {
121
+ originalUrl: options.url,
122
+ safeUrl,
123
+ resolvedIps: ssrfResult.resolvedIps,
124
+ scenario: options.scenario
125
+ });
126
+ // 使用安全的 URL 发起请求
127
+ const safeOptions = {
128
+ ...options,
129
+ url: safeUrl
130
+ };
131
+ // 如果 URL 被替换为 IP,需要设置 Host 头
132
+ if (ssrfResult.safeIp && ssrfResult.hostname !== ssrfResult.safeIp) {
133
+ safeOptions.headers = {
134
+ ...safeOptions.headers,
135
+ Host: ssrfResult.hostname
136
+ };
137
+ }
138
+ // 处理 W3C Trace Context traceparent 头
139
+ if (options.traceparent) {
140
+ // 验证 traceparent 格式
141
+ if (!utils_1.TraceContextUtils.isValidTraceParent(options.traceparent)) {
142
+ this.logger.warn('Invalid traceparent format, ignoring', {
143
+ traceparent: options.traceparent
144
+ });
145
+ }
146
+ else {
147
+ safeOptions.headers = {
148
+ ...safeOptions.headers,
149
+ traceparent: options.traceparent
150
+ };
151
+ this.logger.debug('Traceparent header added', {
152
+ traceparent: options.traceparent,
153
+ sampled: utils_1.TraceContextUtils.isSampled(options.traceparent)
154
+ });
155
+ }
156
+ }
157
+ // 应用正向代理配置
158
+ if (this.forwardProxy) {
159
+ this.applyForwardProxy(safeOptions);
160
+ }
161
+ // 发起请求
162
+ const response = await this.adapter.request(safeOptions);
163
+ // 首次响应时间(TTFB)
164
+ const ttfb = Date.now() - startTime;
165
+ // 如果是流式响应,包装流对象以追踪完整生命周期
166
+ if (options.responseType === 'stream' && this.isStreamResponse(response.data)) {
167
+ return this.wrapStreamResponse(safeOptions, response, startTime, ttfb);
168
+ }
169
+ // 记录非流式响应日志
170
+ this.logResponse(safeOptions, response, ttfb);
171
+ return response;
172
+ }
173
+ catch (error) {
174
+ // 记录错误日志
175
+ this.logError(options, error, Date.now() - startTime);
176
+ throw error;
177
+ }
178
+ }
179
+ /**
180
+ * 便捷方法:GET 请求
181
+ */
182
+ async get(url, options) {
183
+ return this.request({ ...options, method: 'GET', url });
184
+ }
185
+ /**
186
+ * 便捷方法:POST 请求
187
+ */
188
+ async post(url, body, options) {
189
+ return this.request({ ...options, method: 'POST', url, body });
190
+ }
191
+ /**
192
+ * 便捷方法:PUT 请求
193
+ */
194
+ async put(url, body, options) {
195
+ return this.request({ ...options, method: 'PUT', url, body });
196
+ }
197
+ /**
198
+ * 便捷方法:DELETE 请求
199
+ */
200
+ async delete(url, options) {
201
+ return this.request({ ...options, method: 'DELETE', url });
202
+ }
203
+ /**
204
+ * 便捷方法:PATCH 请求
205
+ */
206
+ async patch(url, body, options) {
207
+ return this.request({ ...options, method: 'PATCH', url, body });
208
+ }
209
+ /**
210
+ * 记录响应日志
211
+ */
212
+ logResponse(options, response, duration, streamMetrics) {
213
+ if (!this.httpLogger) {
214
+ return;
215
+ }
216
+ // 计算实际重试次数
217
+ const retryCount = options.retries ?? this.config.request.retries;
218
+ this.httpLogger.logSuccess({
219
+ request: {
220
+ method: options.method,
221
+ url: options.url,
222
+ headers: options.headers,
223
+ body: options.body,
224
+ scenario: options.scenario,
225
+ meta: options.meta
226
+ },
227
+ response,
228
+ duration,
229
+ retryCount,
230
+ streamMetrics
231
+ });
232
+ }
233
+ /**
234
+ * 判断响应数据是否为流对象(Node.js Readable)
235
+ */
236
+ isStreamResponse(data) {
237
+ // undici 和 axios 都返回 Node.js Readable
238
+ return data instanceof node_stream_1.Readable;
239
+ }
240
+ /**
241
+ * 包装流式响应,追踪流的完整生命周期
242
+ */
243
+ /* eslint-disable max-lines-per-function */
244
+ wrapStreamResponse(options, response, requestStartTime, ttfb) {
245
+ const stream = response.data;
246
+ let bytesTransferred = 0;
247
+ // 记录首次响应日志(流开始)
248
+ this.logResponse(options, response, ttfb, {
249
+ ttfb,
250
+ completed: false
251
+ });
252
+ this.logger.debug('Stream response started', {
253
+ method: options.method,
254
+ url: options.url,
255
+ ttfb,
256
+ status: response.status
257
+ });
258
+ // 所有适配器都返回 Node.js Readable
259
+ const originalStream = stream;
260
+ const wrappedStream = new node_stream_1.PassThrough();
261
+ // 监听数据事件统计字节数
262
+ originalStream.on('data', (chunk) => {
263
+ bytesTransferred += chunk.length;
264
+ wrappedStream.write(chunk);
265
+ });
266
+ // 监听结束事件记录日志
267
+ originalStream.on('end', () => {
268
+ const totalDuration = Date.now() - requestStartTime;
269
+ wrappedStream.end();
270
+ // 记录流完成日志
271
+ this.logStreamCompleted(options, response, ttfb, totalDuration, bytesTransferred);
272
+ });
273
+ // 监听错误事件
274
+ originalStream.on('error', (error) => {
275
+ const totalDuration = Date.now() - requestStartTime;
276
+ this.logger.error('Stream read error', {
277
+ method: options.method,
278
+ url: options.url,
279
+ ttfb,
280
+ totalDuration,
281
+ bytesTransferred,
282
+ error: error.message
283
+ });
284
+ wrappedStream.destroy(error);
285
+ });
286
+ // 使用 pipe 连接流
287
+ originalStream.pipe(wrappedStream, { end: false });
288
+ // 返回包装后的响应
289
+ return {
290
+ ...response,
291
+ data: wrappedStream
292
+ };
293
+ }
294
+ /**
295
+ * 记录流完成日志
296
+ */
297
+ logStreamCompleted(options, response, ttfb, totalDuration, bytesTransferred) {
298
+ // 记录流完成日志
299
+ this.logResponse(options, response, totalDuration, {
300
+ ttfb,
301
+ completed: true,
302
+ totalDuration,
303
+ bytesTransferred
304
+ });
305
+ this.logger.debug('Stream response completed', {
306
+ method: options.method,
307
+ url: options.url,
308
+ ttfb,
309
+ totalDuration,
310
+ streamDuration: totalDuration - ttfb,
311
+ bytesTransferred,
312
+ throughput: bytesTransferred > 0 && totalDuration > ttfb
313
+ ? `${(bytesTransferred / 1024 / ((totalDuration - ttfb) / 1000)).toFixed(2)} KB/s`
314
+ : 'N/A'
315
+ });
316
+ }
317
+ /**
318
+ * 记录错误日志
319
+ */
320
+ logError(options, error, duration) {
321
+ if (!this.httpLogger) {
322
+ return;
323
+ }
324
+ // 计算实际重试次数
325
+ const retryCount = options.retries ?? this.config.request.retries;
326
+ const httpError = error instanceof types_1.HttpError ? error : undefined;
327
+ this.httpLogger.logFailed({
328
+ request: {
329
+ method: options.method,
330
+ url: options.url,
331
+ headers: options.headers,
332
+ body: options.body,
333
+ scenario: options.scenario,
334
+ meta: options.meta
335
+ },
336
+ error: error instanceof Error ? error : new Error(String(error)),
337
+ errorCode: httpError?.code || 'UNKNOWN_ERROR',
338
+ status: httpError?.status,
339
+ response: httpError?.response,
340
+ duration,
341
+ retryCount
342
+ });
343
+ }
344
+ /**
345
+ * 应用正向代理配置
346
+ * @param options 请求选项
347
+ */
348
+ applyForwardProxy(options) {
349
+ if (!this.forwardProxy || !this.forwardProxy.enabled) {
350
+ return;
351
+ }
352
+ const { https_proxy, http_proxy, no_proxy } = this.forwardProxy;
353
+ // 解析目标 URL
354
+ const targetUrl = new URL(options.url);
355
+ const hostname = targetUrl.hostname;
356
+ // 检查 no_proxy 规则(使用传统匹配模式)
357
+ const noProxyRegExps = utils_1.DomainUtils.createLegacyDomainMatchers(no_proxy ?? []);
358
+ if (noProxyRegExps.length > 0 && hostname) {
359
+ for (const noProxyRegex of noProxyRegExps) {
360
+ if (noProxyRegex.test(hostname)) {
361
+ this.logger.debug('Skip proxy for no_proxy matched host', {
362
+ hostname,
363
+ pattern: noProxyRegex.source
364
+ });
365
+ return;
366
+ }
367
+ }
368
+ }
369
+ const protocol = targetUrl.protocol || 'http:';
370
+ const isHttps = /https:?/.test(protocol);
371
+ const proxyUrl = isHttps ? https_proxy : http_proxy;
372
+ if (!proxyUrl) {
373
+ this.logger.warn('Forward proxy enabled but proxy URL not configured', {
374
+ protocol
375
+ });
376
+ return;
377
+ }
378
+ // 将代理信息附加到请求元数据中,由适配器处理
379
+ options.meta = {
380
+ ...options.meta,
381
+ __forwardProxy: {
382
+ proxyUrl,
383
+ isHttps,
384
+ protocol
385
+ }
386
+ };
387
+ this.logger.debug('Applied forward proxy', {
388
+ url: options.url,
389
+ proxyUrl,
390
+ isHttps
391
+ });
392
+ }
393
+ /**
394
+ * 清空 DNS 缓存
395
+ */
396
+ clearDnsCache() {
397
+ this.ssrfGuard.clearDnsCache();
398
+ this.logger.debug('DNS cache cleared');
399
+ }
400
+ /**
401
+ * 获取 DNS 缓存大小
402
+ * @returns 缓存条目数量
403
+ */
404
+ getDnsCacheSize() {
405
+ return this.ssrfGuard.getDnsCacheSize();
406
+ }
407
+ }
408
+ exports.NsHttpClient = NsHttpClient;
409
+ //# sourceMappingURL=client.js.map