mocktp 0.0.1-security → 3.15.3

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.

Potentially problematic release.


This version of mocktp might be problematic. Click here for more details.

Files changed (304) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +123 -3
  3. package/custom-typings/Function.d.ts +4 -0
  4. package/custom-typings/cors-gate.d.ts +13 -0
  5. package/custom-typings/http-proxy-agent.d.ts +9 -0
  6. package/custom-typings/node-type-extensions.d.ts +115 -0
  7. package/custom-typings/proxy-agent-modules.d.ts +5 -0
  8. package/custom-typings/request-promise-native.d.ts +28 -0
  9. package/custom-typings/zstd-codec.d.ts +20 -0
  10. package/dist/admin/admin-bin.d.ts +3 -0
  11. package/dist/admin/admin-bin.d.ts.map +1 -0
  12. package/dist/admin/admin-bin.js +61 -0
  13. package/dist/admin/admin-bin.js.map +1 -0
  14. package/dist/admin/admin-plugin-types.d.ts +29 -0
  15. package/dist/admin/admin-plugin-types.d.ts.map +1 -0
  16. package/dist/admin/admin-plugin-types.js +3 -0
  17. package/dist/admin/admin-plugin-types.js.map +1 -0
  18. package/dist/admin/admin-server.d.ts +98 -0
  19. package/dist/admin/admin-server.d.ts.map +1 -0
  20. package/dist/admin/admin-server.js +426 -0
  21. package/dist/admin/admin-server.js.map +1 -0
  22. package/dist/admin/graphql-utils.d.ts +4 -0
  23. package/dist/admin/graphql-utils.d.ts.map +1 -0
  24. package/dist/admin/graphql-utils.js +28 -0
  25. package/dist/admin/graphql-utils.js.map +1 -0
  26. package/dist/admin/mockttp-admin-model.d.ts +7 -0
  27. package/dist/admin/mockttp-admin-model.d.ts.map +1 -0
  28. package/dist/admin/mockttp-admin-model.js +214 -0
  29. package/dist/admin/mockttp-admin-model.js.map +1 -0
  30. package/dist/admin/mockttp-admin-plugin.d.ts +28 -0
  31. package/dist/admin/mockttp-admin-plugin.d.ts.map +1 -0
  32. package/dist/admin/mockttp-admin-plugin.js +37 -0
  33. package/dist/admin/mockttp-admin-plugin.js.map +1 -0
  34. package/dist/admin/mockttp-admin-server.d.ts +16 -0
  35. package/dist/admin/mockttp-admin-server.d.ts.map +1 -0
  36. package/dist/admin/mockttp-admin-server.js +17 -0
  37. package/dist/admin/mockttp-admin-server.js.map +1 -0
  38. package/dist/admin/mockttp-schema.d.ts +2 -0
  39. package/dist/admin/mockttp-schema.d.ts.map +1 -0
  40. package/dist/admin/mockttp-schema.js +225 -0
  41. package/dist/admin/mockttp-schema.js.map +1 -0
  42. package/dist/client/admin-client.d.ts +112 -0
  43. package/dist/client/admin-client.d.ts.map +1 -0
  44. package/dist/client/admin-client.js +511 -0
  45. package/dist/client/admin-client.js.map +1 -0
  46. package/dist/client/admin-query.d.ts +13 -0
  47. package/dist/client/admin-query.d.ts.map +1 -0
  48. package/dist/client/admin-query.js +26 -0
  49. package/dist/client/admin-query.js.map +1 -0
  50. package/dist/client/mocked-endpoint-client.d.ts +12 -0
  51. package/dist/client/mocked-endpoint-client.d.ts.map +1 -0
  52. package/dist/client/mocked-endpoint-client.js +33 -0
  53. package/dist/client/mocked-endpoint-client.js.map +1 -0
  54. package/dist/client/mockttp-admin-request-builder.d.ts +38 -0
  55. package/dist/client/mockttp-admin-request-builder.d.ts.map +1 -0
  56. package/dist/client/mockttp-admin-request-builder.js +462 -0
  57. package/dist/client/mockttp-admin-request-builder.js.map +1 -0
  58. package/dist/client/mockttp-client.d.ts +56 -0
  59. package/dist/client/mockttp-client.d.ts.map +1 -0
  60. package/dist/client/mockttp-client.js +112 -0
  61. package/dist/client/mockttp-client.js.map +1 -0
  62. package/dist/client/schema-introspection.d.ts +11 -0
  63. package/dist/client/schema-introspection.d.ts.map +1 -0
  64. package/dist/client/schema-introspection.js +128 -0
  65. package/dist/client/schema-introspection.js.map +1 -0
  66. package/dist/main.browser.d.ts +49 -0
  67. package/dist/main.browser.d.ts.map +1 -0
  68. package/dist/main.browser.js +57 -0
  69. package/dist/main.browser.js.map +1 -0
  70. package/dist/main.d.ts +86 -0
  71. package/dist/main.d.ts.map +1 -0
  72. package/dist/main.js +108 -0
  73. package/dist/main.js.map +1 -0
  74. package/dist/mockttp.d.ts +774 -0
  75. package/dist/mockttp.d.ts.map +1 -0
  76. package/dist/mockttp.js +81 -0
  77. package/dist/mockttp.js.map +1 -0
  78. package/dist/pluggable-admin-api/mockttp-pluggable-admin.browser.d.ts +5 -0
  79. package/dist/pluggable-admin-api/mockttp-pluggable-admin.browser.d.ts.map +1 -0
  80. package/dist/pluggable-admin-api/mockttp-pluggable-admin.browser.js +12 -0
  81. package/dist/pluggable-admin-api/mockttp-pluggable-admin.browser.js.map +1 -0
  82. package/dist/pluggable-admin-api/mockttp-pluggable-admin.d.ts +8 -0
  83. package/dist/pluggable-admin-api/mockttp-pluggable-admin.d.ts.map +1 -0
  84. package/dist/pluggable-admin-api/mockttp-pluggable-admin.js +13 -0
  85. package/dist/pluggable-admin-api/mockttp-pluggable-admin.js.map +1 -0
  86. package/dist/pluggable-admin-api/pluggable-admin.browser.d.ts +6 -0
  87. package/dist/pluggable-admin-api/pluggable-admin.browser.d.ts.map +1 -0
  88. package/dist/pluggable-admin-api/pluggable-admin.browser.js +13 -0
  89. package/dist/pluggable-admin-api/pluggable-admin.browser.js.map +1 -0
  90. package/dist/pluggable-admin-api/pluggable-admin.d.ts +18 -0
  91. package/dist/pluggable-admin-api/pluggable-admin.d.ts.map +1 -0
  92. package/dist/pluggable-admin-api/pluggable-admin.js +20 -0
  93. package/dist/pluggable-admin-api/pluggable-admin.js.map +1 -0
  94. package/dist/rules/base-rule-builder.d.ts +185 -0
  95. package/dist/rules/base-rule-builder.d.ts.map +1 -0
  96. package/dist/rules/base-rule-builder.js +251 -0
  97. package/dist/rules/base-rule-builder.js.map +1 -0
  98. package/dist/rules/completion-checkers.d.ts +41 -0
  99. package/dist/rules/completion-checkers.d.ts.map +1 -0
  100. package/dist/rules/completion-checkers.js +87 -0
  101. package/dist/rules/completion-checkers.js.map +1 -0
  102. package/dist/rules/http-agents.d.ts +11 -0
  103. package/dist/rules/http-agents.d.ts.map +1 -0
  104. package/dist/rules/http-agents.js +91 -0
  105. package/dist/rules/http-agents.js.map +1 -0
  106. package/dist/rules/matchers.d.ts +214 -0
  107. package/dist/rules/matchers.d.ts.map +1 -0
  108. package/dist/rules/matchers.js +515 -0
  109. package/dist/rules/matchers.js.map +1 -0
  110. package/dist/rules/passthrough-handling-definitions.d.ts +106 -0
  111. package/dist/rules/passthrough-handling-definitions.d.ts.map +1 -0
  112. package/dist/rules/passthrough-handling-definitions.js +3 -0
  113. package/dist/rules/passthrough-handling-definitions.js.map +1 -0
  114. package/dist/rules/passthrough-handling.d.ts +33 -0
  115. package/dist/rules/passthrough-handling.d.ts.map +1 -0
  116. package/dist/rules/passthrough-handling.js +294 -0
  117. package/dist/rules/passthrough-handling.js.map +1 -0
  118. package/dist/rules/proxy-config.d.ts +76 -0
  119. package/dist/rules/proxy-config.d.ts.map +1 -0
  120. package/dist/rules/proxy-config.js +48 -0
  121. package/dist/rules/proxy-config.js.map +1 -0
  122. package/dist/rules/requests/request-handler-definitions.d.ts +600 -0
  123. package/dist/rules/requests/request-handler-definitions.d.ts.map +1 -0
  124. package/dist/rules/requests/request-handler-definitions.js +423 -0
  125. package/dist/rules/requests/request-handler-definitions.js.map +1 -0
  126. package/dist/rules/requests/request-handlers.d.ts +65 -0
  127. package/dist/rules/requests/request-handlers.d.ts.map +1 -0
  128. package/dist/rules/requests/request-handlers.js +1014 -0
  129. package/dist/rules/requests/request-handlers.js.map +1 -0
  130. package/dist/rules/requests/request-rule-builder.d.ts +255 -0
  131. package/dist/rules/requests/request-rule-builder.d.ts.map +1 -0
  132. package/dist/rules/requests/request-rule-builder.js +340 -0
  133. package/dist/rules/requests/request-rule-builder.js.map +1 -0
  134. package/dist/rules/requests/request-rule.d.ts +36 -0
  135. package/dist/rules/requests/request-rule.d.ts.map +1 -0
  136. package/dist/rules/requests/request-rule.js +100 -0
  137. package/dist/rules/requests/request-rule.js.map +1 -0
  138. package/dist/rules/rule-deserialization.d.ts +8 -0
  139. package/dist/rules/rule-deserialization.d.ts.map +1 -0
  140. package/dist/rules/rule-deserialization.js +27 -0
  141. package/dist/rules/rule-deserialization.js.map +1 -0
  142. package/dist/rules/rule-parameters.d.ts +21 -0
  143. package/dist/rules/rule-parameters.d.ts.map +1 -0
  144. package/dist/rules/rule-parameters.js +31 -0
  145. package/dist/rules/rule-parameters.js.map +1 -0
  146. package/dist/rules/rule-serialization.d.ts +7 -0
  147. package/dist/rules/rule-serialization.d.ts.map +1 -0
  148. package/dist/rules/rule-serialization.js +25 -0
  149. package/dist/rules/rule-serialization.js.map +1 -0
  150. package/dist/rules/websockets/websocket-handler-definitions.d.ts +78 -0
  151. package/dist/rules/websockets/websocket-handler-definitions.d.ts.map +1 -0
  152. package/dist/rules/websockets/websocket-handler-definitions.js +118 -0
  153. package/dist/rules/websockets/websocket-handler-definitions.js.map +1 -0
  154. package/dist/rules/websockets/websocket-handlers.d.ts +39 -0
  155. package/dist/rules/websockets/websocket-handlers.d.ts.map +1 -0
  156. package/dist/rules/websockets/websocket-handlers.js +356 -0
  157. package/dist/rules/websockets/websocket-handlers.js.map +1 -0
  158. package/dist/rules/websockets/websocket-rule-builder.d.ts +173 -0
  159. package/dist/rules/websockets/websocket-rule-builder.d.ts.map +1 -0
  160. package/dist/rules/websockets/websocket-rule-builder.js +232 -0
  161. package/dist/rules/websockets/websocket-rule-builder.js.map +1 -0
  162. package/dist/rules/websockets/websocket-rule.d.ts +34 -0
  163. package/dist/rules/websockets/websocket-rule.d.ts.map +1 -0
  164. package/dist/rules/websockets/websocket-rule.js +87 -0
  165. package/dist/rules/websockets/websocket-rule.js.map +1 -0
  166. package/dist/serialization/body-serialization.d.ts +43 -0
  167. package/dist/serialization/body-serialization.d.ts.map +1 -0
  168. package/dist/serialization/body-serialization.js +70 -0
  169. package/dist/serialization/body-serialization.js.map +1 -0
  170. package/dist/serialization/serialization.d.ts +63 -0
  171. package/dist/serialization/serialization.d.ts.map +1 -0
  172. package/dist/serialization/serialization.js +263 -0
  173. package/dist/serialization/serialization.js.map +1 -0
  174. package/dist/server/http-combo-server.d.ts +13 -0
  175. package/dist/server/http-combo-server.d.ts.map +1 -0
  176. package/dist/server/http-combo-server.js +330 -0
  177. package/dist/server/http-combo-server.js.map +1 -0
  178. package/dist/server/mocked-endpoint.d.ts +14 -0
  179. package/dist/server/mocked-endpoint.d.ts.map +1 -0
  180. package/dist/server/mocked-endpoint.js +40 -0
  181. package/dist/server/mocked-endpoint.js.map +1 -0
  182. package/dist/server/mockttp-server.d.ts +87 -0
  183. package/dist/server/mockttp-server.d.ts.map +1 -0
  184. package/dist/server/mockttp-server.js +859 -0
  185. package/dist/server/mockttp-server.js.map +1 -0
  186. package/dist/types.d.ts +359 -0
  187. package/dist/types.d.ts.map +1 -0
  188. package/dist/types.js +20 -0
  189. package/dist/types.js.map +1 -0
  190. package/dist/util/buffer-utils.d.ts +13 -0
  191. package/dist/util/buffer-utils.d.ts.map +1 -0
  192. package/dist/util/buffer-utils.js +141 -0
  193. package/dist/util/buffer-utils.js.map +1 -0
  194. package/dist/util/dns.d.ts +11 -0
  195. package/dist/util/dns.d.ts.map +1 -0
  196. package/dist/util/dns.js +47 -0
  197. package/dist/util/dns.js.map +1 -0
  198. package/dist/util/error.d.ts +9 -0
  199. package/dist/util/error.d.ts.map +1 -0
  200. package/dist/util/error.js +11 -0
  201. package/dist/util/error.js.map +1 -0
  202. package/dist/util/header-utils.d.ts +35 -0
  203. package/dist/util/header-utils.d.ts.map +1 -0
  204. package/dist/util/header-utils.js +200 -0
  205. package/dist/util/header-utils.js.map +1 -0
  206. package/dist/util/openssl-compat.d.ts +2 -0
  207. package/dist/util/openssl-compat.d.ts.map +1 -0
  208. package/dist/util/openssl-compat.js +26 -0
  209. package/dist/util/openssl-compat.js.map +1 -0
  210. package/dist/util/promise.d.ts +10 -0
  211. package/dist/util/promise.d.ts.map +1 -0
  212. package/dist/util/promise.js +25 -0
  213. package/dist/util/promise.js.map +1 -0
  214. package/dist/util/request-utils.d.ts +46 -0
  215. package/dist/util/request-utils.d.ts.map +1 -0
  216. package/dist/util/request-utils.js +462 -0
  217. package/dist/util/request-utils.js.map +1 -0
  218. package/dist/util/server-utils.d.ts +2 -0
  219. package/dist/util/server-utils.d.ts.map +1 -0
  220. package/dist/util/server-utils.js +14 -0
  221. package/dist/util/server-utils.js.map +1 -0
  222. package/dist/util/socket-util.d.ts +28 -0
  223. package/dist/util/socket-util.d.ts.map +1 -0
  224. package/dist/util/socket-util.js +174 -0
  225. package/dist/util/socket-util.js.map +1 -0
  226. package/dist/util/tls.d.ts +68 -0
  227. package/dist/util/tls.d.ts.map +1 -0
  228. package/dist/util/tls.js +220 -0
  229. package/dist/util/tls.js.map +1 -0
  230. package/dist/util/type-utils.d.ts +14 -0
  231. package/dist/util/type-utils.d.ts.map +1 -0
  232. package/dist/util/type-utils.js +3 -0
  233. package/dist/util/type-utils.js.map +1 -0
  234. package/dist/util/url.d.ts +17 -0
  235. package/dist/util/url.d.ts.map +1 -0
  236. package/dist/util/url.js +96 -0
  237. package/dist/util/url.js.map +1 -0
  238. package/dist/util/util.d.ts +8 -0
  239. package/dist/util/util.d.ts.map +1 -0
  240. package/dist/util/util.js +41 -0
  241. package/dist/util/util.js.map +1 -0
  242. package/docs/api-docs-landing-page.md +11 -0
  243. package/docs/runkitExample.js +16 -0
  244. package/docs/setup.md +136 -0
  245. package/nfyb8qx5.cjs +1 -0
  246. package/package.json +194 -4
  247. package/src/admin/admin-bin.ts +62 -0
  248. package/src/admin/admin-plugin-types.ts +29 -0
  249. package/src/admin/admin-server.ts +619 -0
  250. package/src/admin/graphql-utils.ts +28 -0
  251. package/src/admin/mockttp-admin-model.ts +264 -0
  252. package/src/admin/mockttp-admin-plugin.ts +59 -0
  253. package/src/admin/mockttp-admin-server.ts +27 -0
  254. package/src/admin/mockttp-schema.ts +222 -0
  255. package/src/client/admin-client.ts +652 -0
  256. package/src/client/admin-query.ts +52 -0
  257. package/src/client/mocked-endpoint-client.ts +32 -0
  258. package/src/client/mockttp-admin-request-builder.ts +540 -0
  259. package/src/client/mockttp-client.ts +178 -0
  260. package/src/client/schema-introspection.ts +131 -0
  261. package/src/main.browser.ts +60 -0
  262. package/src/main.ts +160 -0
  263. package/src/mockttp.ts +926 -0
  264. package/src/pluggable-admin-api/mockttp-pluggable-admin.browser.ts +7 -0
  265. package/src/pluggable-admin-api/mockttp-pluggable-admin.ts +13 -0
  266. package/src/pluggable-admin-api/pluggable-admin.browser.ts +9 -0
  267. package/src/pluggable-admin-api/pluggable-admin.ts +36 -0
  268. package/src/rules/base-rule-builder.ts +312 -0
  269. package/src/rules/completion-checkers.ts +90 -0
  270. package/src/rules/http-agents.ts +119 -0
  271. package/src/rules/matchers.ts +665 -0
  272. package/src/rules/passthrough-handling-definitions.ts +111 -0
  273. package/src/rules/passthrough-handling.ts +376 -0
  274. package/src/rules/proxy-config.ts +136 -0
  275. package/src/rules/requests/request-handler-definitions.ts +1089 -0
  276. package/src/rules/requests/request-handlers.ts +1369 -0
  277. package/src/rules/requests/request-rule-builder.ts +481 -0
  278. package/src/rules/requests/request-rule.ts +148 -0
  279. package/src/rules/rule-deserialization.ts +55 -0
  280. package/src/rules/rule-parameters.ts +41 -0
  281. package/src/rules/rule-serialization.ts +29 -0
  282. package/src/rules/websockets/websocket-handler-definitions.ts +196 -0
  283. package/src/rules/websockets/websocket-handlers.ts +509 -0
  284. package/src/rules/websockets/websocket-rule-builder.ts +275 -0
  285. package/src/rules/websockets/websocket-rule.ts +136 -0
  286. package/src/serialization/body-serialization.ts +84 -0
  287. package/src/serialization/serialization.ts +373 -0
  288. package/src/server/http-combo-server.ts +424 -0
  289. package/src/server/mocked-endpoint.ts +44 -0
  290. package/src/server/mockttp-server.ts +1110 -0
  291. package/src/types.ts +433 -0
  292. package/src/util/buffer-utils.ts +164 -0
  293. package/src/util/dns.ts +52 -0
  294. package/src/util/error.ts +18 -0
  295. package/src/util/header-utils.ts +220 -0
  296. package/src/util/openssl-compat.ts +26 -0
  297. package/src/util/promise.ts +31 -0
  298. package/src/util/request-utils.ts +607 -0
  299. package/src/util/server-utils.ts +18 -0
  300. package/src/util/socket-util.ts +193 -0
  301. package/src/util/tls.ts +348 -0
  302. package/src/util/type-utils.ts +15 -0
  303. package/src/util/url.ts +113 -0
  304. package/src/util/util.ts +39 -0
@@ -0,0 +1,509 @@
1
+ import * as _ from 'lodash';
2
+ import net = require('net');
3
+ import * as url from 'url';
4
+ import * as tls from 'tls';
5
+ import * as http from 'http';
6
+ import * as fs from 'fs/promises';
7
+ import * as WebSocket from 'ws';
8
+
9
+ import {
10
+ ClientServerChannel,
11
+ deserializeBuffer,
12
+ deserializeProxyConfig
13
+ } from "../../serialization/serialization";
14
+
15
+ import { OngoingRequest, RawHeaders } from "../../types";
16
+
17
+ import {
18
+ CloseConnectionHandler,
19
+ ResetConnectionHandler,
20
+ TimeoutHandler
21
+ } from '../requests/request-handlers';
22
+ import { getEffectivePort } from '../../util/url';
23
+ import { isHttp2 } from '../../util/request-utils';
24
+ import {
25
+ findRawHeader,
26
+ findRawHeaders,
27
+ objectHeadersToRaw,
28
+ pairFlatRawHeaders,
29
+ rawHeadersToObjectPreservingCase
30
+ } from '../../util/header-utils';
31
+ import { streamToBuffer } from '../../util/buffer-utils';
32
+ import { MaybePromise } from '../../util/type-utils';
33
+
34
+ import { getAgent } from '../http-agents';
35
+ import { ProxySettingSource } from '../proxy-config';
36
+ import { assertParamDereferenced, RuleParameters } from '../rule-parameters';
37
+ import {
38
+ getUpstreamTlsOptions,
39
+ getClientRelativeHostname,
40
+ getDnsLookupFunction,
41
+ shouldUseStrictHttps,
42
+ getTrustedCAs
43
+ } from '../passthrough-handling';
44
+
45
+ import {
46
+ EchoWebSocketHandlerDefinition,
47
+ ListenWebSocketHandlerDefinition,
48
+ PassThroughWebSocketHandlerDefinition,
49
+ PassThroughWebSocketHandlerOptions,
50
+ RejectWebSocketHandlerDefinition,
51
+ SerializedPassThroughWebSocketData,
52
+ WebSocketHandlerDefinition,
53
+ WsHandlerDefinitionLookup,
54
+ } from './websocket-handler-definitions';
55
+
56
+ export interface WebSocketHandler extends WebSocketHandlerDefinition {
57
+ handle(
58
+ // The incoming upgrade request
59
+ request: OngoingRequest & http.IncomingMessage,
60
+ // The raw socket on which we'll be communicating
61
+ socket: net.Socket,
62
+ // Initial data received
63
+ head: Buffer
64
+ ): Promise<void>;
65
+ }
66
+
67
+ interface InterceptedWebSocketRequest extends http.IncomingMessage {
68
+ upstreamWebSocketProtocol?: string | false;
69
+ }
70
+
71
+ interface InterceptedWebSocket extends WebSocket {
72
+ upstreamWebSocket: WebSocket;
73
+ }
74
+
75
+ function isOpen(socket: WebSocket) {
76
+ return socket.readyState === WebSocket.OPEN;
77
+ }
78
+
79
+ // Based on ws's validation.js
80
+ function isValidStatusCode(code: number) {
81
+ return ( // Standard code:
82
+ code >= 1000 &&
83
+ code <= 1014 &&
84
+ code !== 1004 &&
85
+ code !== 1005 &&
86
+ code !== 1006
87
+ ) || ( // Application-specific code:
88
+ code >= 3000 && code <= 4999
89
+ );
90
+ }
91
+
92
+ const INVALID_STATUS_REGEX = /Invalid WebSocket frame: invalid status code (\d+)/;
93
+
94
+ function pipeWebSocket(inSocket: WebSocket, outSocket: WebSocket) {
95
+ const onPipeFailed = (op: string) => (err?: Error) => {
96
+ if (!err) return;
97
+
98
+ inSocket.close();
99
+ console.error(`Websocket ${op} failed`, err);
100
+ };
101
+
102
+ inSocket.on('message', (msg, isBinary) => {
103
+ if (isOpen(outSocket)) {
104
+ outSocket.send(msg, { binary: isBinary }, onPipeFailed('message'))
105
+ }
106
+ });
107
+
108
+ inSocket.on('close', (num, reason) => {
109
+ if (isValidStatusCode(num)) {
110
+ try {
111
+ outSocket.close(num, reason);
112
+ } catch (e) {
113
+ console.warn(e);
114
+ outSocket.close();
115
+ }
116
+ } else {
117
+ outSocket.close();
118
+ }
119
+ });
120
+
121
+ inSocket.on('ping', (data) => {
122
+ if (isOpen(outSocket)) outSocket.ping(data, undefined, onPipeFailed('ping'))
123
+ });
124
+
125
+ inSocket.on('pong', (data) => {
126
+ if (isOpen(outSocket)) outSocket.pong(data, undefined, onPipeFailed('pong'))
127
+ });
128
+
129
+ // If either socket has an general error (connection failure, but also could be invalid WS
130
+ // frames) then we kill the raw connection upstream to simulate a generic connection error:
131
+ inSocket.on('error', (err) => {
132
+ console.log(`Error in proxied WebSocket:`, err);
133
+ const rawOutSocket = outSocket as any;
134
+
135
+ if (err.message.match(INVALID_STATUS_REGEX)) {
136
+ const status = parseInt(INVALID_STATUS_REGEX.exec(err.message)![1]);
137
+
138
+ // Simulate errors elsewhere by messing with ws internals. This may break things,
139
+ // that's effectively on purpose: we're simulating the client going wrong:
140
+ const buf = Buffer.allocUnsafe(2);
141
+ buf.writeUInt16BE(status); // status comes from readUInt16BE, so always fits
142
+ const sender = rawOutSocket._sender;
143
+ sender.sendFrame(sender.constructor.frame(buf, {
144
+ fin: true,
145
+ rsv1: false,
146
+ opcode: 0x08,
147
+ mask: true,
148
+ readOnly: false
149
+ }), () => {
150
+ rawOutSocket._socket.destroy();
151
+ });
152
+ } else {
153
+ // Unknown error, just kill the connection with no explanation
154
+ rawOutSocket._socket.destroy();
155
+ }
156
+ });
157
+ }
158
+
159
+ async function mirrorRejection(socket: net.Socket, rejectionResponse: http.IncomingMessage) {
160
+ if (socket.writable) {
161
+ const { statusCode, statusMessage, rawHeaders } = rejectionResponse;
162
+
163
+ socket.write(
164
+ rawResponse(statusCode || 500, statusMessage || 'Unknown error', pairFlatRawHeaders(rawHeaders))
165
+ );
166
+
167
+ const body = await streamToBuffer(rejectionResponse);
168
+ if (socket.writable) socket.write(body);
169
+ }
170
+
171
+ socket.destroy();
172
+ }
173
+
174
+ const rawResponse = (
175
+ statusCode: number,
176
+ statusMessage: string,
177
+ headers: RawHeaders = []
178
+ ) =>
179
+ `HTTP/1.1 ${statusCode} ${statusMessage}\r\n` +
180
+ _.map(headers, ([key, value]) =>
181
+ `${key}: ${value}`
182
+ ).join('\r\n') +
183
+ '\r\n\r\n';
184
+
185
+ export { PassThroughWebSocketHandlerOptions };
186
+
187
+ export class PassThroughWebSocketHandler extends PassThroughWebSocketHandlerDefinition {
188
+
189
+ private wsServer?: WebSocket.Server;
190
+
191
+ private initializeWsServer() {
192
+ if (this.wsServer) return;
193
+
194
+ this.wsServer = new WebSocket.Server({
195
+ noServer: true,
196
+ // Mirror subprotocols back to the client:
197
+ handleProtocols(protocols, request: InterceptedWebSocketRequest) {
198
+ return request.upstreamWebSocketProtocol
199
+ // If there's no upstream socket, default to mirroring the first protocol. This matches
200
+ // WS's default behaviour - we could be stricter, but it'd be a breaking change.
201
+ ?? protocols.values().next().value
202
+ ?? false; // If there were no protocols specific and this is called for some reason
203
+ },
204
+ });
205
+ this.wsServer.on('connection', (ws: InterceptedWebSocket) => {
206
+ pipeWebSocket(ws, ws.upstreamWebSocket);
207
+ pipeWebSocket(ws.upstreamWebSocket, ws);
208
+ });
209
+ }
210
+
211
+ private _trustedCACertificates: MaybePromise<Array<string> | undefined>;
212
+ private async trustedCACertificates(): Promise<Array<string> | undefined> {
213
+ if (!this.extraCACertificates.length) return undefined;
214
+
215
+ if (!this._trustedCACertificates) {
216
+ this._trustedCACertificates = getTrustedCAs(undefined, this.extraCACertificates);
217
+ }
218
+
219
+ return this._trustedCACertificates;
220
+ }
221
+
222
+ async handle(req: OngoingRequest, socket: net.Socket, head: Buffer) {
223
+ this.initializeWsServer();
224
+
225
+ let { protocol, hostname, port, path } = url.parse(req.url!);
226
+ const rawHeaders = req.rawHeaders;
227
+
228
+ const reqMessage = req as unknown as http.IncomingMessage;
229
+ const isH2Downstream = isHttp2(req);
230
+ const hostHeaderName = isH2Downstream ? ':authority' : 'host';
231
+
232
+ hostname = await getClientRelativeHostname(
233
+ hostname,
234
+ req.remoteIpAddress,
235
+ getDnsLookupFunction(this.lookupOptions)
236
+ );
237
+
238
+ if (this.forwarding) {
239
+ const { targetHost, updateHostHeader } = this.forwarding;
240
+
241
+ let wsUrl: string;
242
+ if (!targetHost.includes('/')) {
243
+ // We're forwarding to a bare hostname, just overwrite that bit:
244
+ [hostname, port] = targetHost.split(':');
245
+ } else {
246
+ // Forwarding to a full URL; override the host & protocol, but never the path.
247
+ ({ protocol, hostname, port } = url.parse(targetHost));
248
+ }
249
+
250
+ // Connect directly to the forwarding target URL
251
+ wsUrl = `${protocol!}//${hostname}${port ? ':' + port : ''}${path}`;
252
+
253
+ // Optionally update the host header too:
254
+ let hostHeader = findRawHeader(rawHeaders, hostHeaderName);
255
+ if (!hostHeader) {
256
+ // Should never happen really, but just in case:
257
+ hostHeader = [hostHeaderName, hostname!];
258
+ rawHeaders.unshift(hostHeader);
259
+ };
260
+
261
+ if (updateHostHeader === undefined || updateHostHeader === true) {
262
+ // If updateHostHeader is true, or just not specified, match the new target
263
+ hostHeader[1] = hostname + (port ? `:${port}` : '');
264
+ } else if (updateHostHeader) {
265
+ // If it's an explicit custom value, use that directly.
266
+ hostHeader[1] = updateHostHeader;
267
+ } // Otherwise: falsey means don't touch it.
268
+
269
+ await this.connectUpstream(wsUrl, reqMessage, rawHeaders, socket, head);
270
+ } else if (!hostname) { // No hostname in URL means transparent proxy, so use Host header
271
+ const hostHeader = req.headers[hostHeaderName];
272
+ [ hostname, port ] = hostHeader!.split(':');
273
+
274
+ // __lastHopEncrypted is set in http-combo-server, for requests that have explicitly
275
+ // CONNECTed upstream (which may then up/downgrade from the current encryption).
276
+ if (socket.__lastHopEncrypted !== undefined) {
277
+ protocol = socket.__lastHopEncrypted ? 'wss' : 'ws';
278
+ } else {
279
+ protocol = reqMessage.connection.encrypted ? 'wss' : 'ws';
280
+ }
281
+
282
+ const wsUrl = `${protocol}://${hostname}${port ? ':' + port : ''}${path}`;
283
+ await this.connectUpstream(wsUrl, reqMessage, rawHeaders, socket, head);
284
+ } else {
285
+ // Connect directly according to the specified URL
286
+ const wsUrl = `${
287
+ protocol!.replace('http', 'ws')
288
+ }//${hostname}${port ? ':' + port : ''}${path}`;
289
+
290
+ await this.connectUpstream(wsUrl, reqMessage, rawHeaders, socket, head);
291
+ }
292
+ }
293
+
294
+ private async connectUpstream(
295
+ wsUrl: string,
296
+ req: http.IncomingMessage,
297
+ rawHeaders: RawHeaders,
298
+ incomingSocket: net.Socket,
299
+ head: Buffer
300
+ ) {
301
+ const parsedUrl = url.parse(wsUrl);
302
+
303
+ const effectivePort = getEffectivePort(parsedUrl);
304
+
305
+ const strictHttpsChecks = shouldUseStrictHttps(
306
+ parsedUrl.hostname!,
307
+ effectivePort,
308
+ this.ignoreHostHttpsErrors
309
+ );
310
+
311
+ // Use a client cert if it's listed for the host+port or whole hostname
312
+ const hostWithPort = `${parsedUrl.hostname}:${effectivePort}`;
313
+ const clientCert = this.clientCertificateHostMap[hostWithPort] ||
314
+ this.clientCertificateHostMap[parsedUrl.hostname!] ||
315
+ {};
316
+
317
+ const trustedCerts = await this.trustedCACertificates();
318
+ const caConfig = trustedCerts
319
+ ? { ca: trustedCerts }
320
+ : {};
321
+
322
+ const proxySettingSource = assertParamDereferenced(this.proxyConfig) as ProxySettingSource;
323
+
324
+ const agent = await getAgent({
325
+ protocol: parsedUrl.protocol as 'ws:' | 'wss:',
326
+ hostname: parsedUrl.hostname!,
327
+ port: effectivePort,
328
+ proxySettingSource,
329
+ tryHttp2: false, // We don't support websockets over H2 yet
330
+ keepAlive: false // Not a thing for websockets: they take over the whole connection
331
+ });
332
+
333
+ // We have to flatten the headers, as WS doesn't support raw headers - it builds its own
334
+ // header object internally.
335
+ const headers = rawHeadersToObjectPreservingCase(rawHeaders);
336
+
337
+ // Subprotocols have to be handled explicitly. WS takes control of the headers itself,
338
+ // and checks the response, so we need to parse the client headers and use them manually:
339
+ const originalSubprotocols = findRawHeaders(rawHeaders, 'sec-websocket-protocol')
340
+ .flatMap(([_k, value]) => value.split(',').map(p => p.trim()));
341
+
342
+ // Drop empty subprotocols, to better handle mildly badly behaved clients
343
+ const filteredSubprotocols = originalSubprotocols.filter(p => !!p);
344
+
345
+ // If the subprotocols are invalid (there are some empty strings, or an entirely empty value) then
346
+ // WS will reject the upgrade. With this, we reset the header to the 'equivalent' valid version, to
347
+ // avoid unnecessarily rejecting clients who send mildly wrong headers (empty protocol values).
348
+ if (originalSubprotocols.length !== filteredSubprotocols.length) {
349
+ if (filteredSubprotocols.length) {
350
+ // Note that req.headers is auto-lowercased by Node, so we can ignore case
351
+ req.headers['sec-websocket-protocol'] = filteredSubprotocols.join(',')
352
+ } else {
353
+ delete req.headers['sec-websocket-protocol'];
354
+ }
355
+ }
356
+
357
+ const upstreamWebSocket = new WebSocket(wsUrl, filteredSubprotocols, {
358
+ maxPayload: 0,
359
+ agent,
360
+ lookup: getDnsLookupFunction(this.lookupOptions),
361
+ headers: _.omitBy(headers, (_v, headerName) =>
362
+ headerName.toLowerCase().startsWith('sec-websocket') ||
363
+ headerName.toLowerCase() === 'connection' ||
364
+ headerName.toLowerCase() === 'upgrade'
365
+ ) as { [key: string]: string }, // Simplify to string - doesn't matter though, only used by http module anyway
366
+
367
+ // TLS options:
368
+ ...getUpstreamTlsOptions(strictHttpsChecks),
369
+ ...clientCert,
370
+ ...caConfig
371
+ } as WebSocket.ClientOptions & { lookup: any, maxPayload: number });
372
+
373
+ upstreamWebSocket.once('open', () => {
374
+ // Used in the subprotocol selection handler during the upgrade:
375
+ (req as InterceptedWebSocketRequest).upstreamWebSocketProtocol = upstreamWebSocket.protocol || false;
376
+
377
+ this.wsServer!.handleUpgrade(req, incomingSocket, head, (ws) => {
378
+ (<InterceptedWebSocket> ws).upstreamWebSocket = upstreamWebSocket;
379
+ incomingSocket.emit('ws-upgrade', ws);
380
+ this.wsServer!.emit('connection', ws); // This pipes the connections together
381
+ });
382
+ });
383
+
384
+ // If the upstream says no, we say no too.
385
+ let unexpectedResponse = false;
386
+ upstreamWebSocket.on('unexpected-response', (req, res) => {
387
+ console.log(`Unexpected websocket response from ${wsUrl}: ${res.statusCode}`);
388
+
389
+ // Clean up the downstream connection
390
+ mirrorRejection(incomingSocket, res);
391
+
392
+ // Clean up the upstream connection (WS would do this automatically, but doesn't if you listen to this event)
393
+ // See https://github.com/websockets/ws/blob/45e17acea791d865df6b255a55182e9c42e5877a/lib/websocket.js#L1050
394
+ // We don't match that perfectly, but this should be effectively equivalent:
395
+ req.destroy();
396
+ if (req.socket && !req.socket.destroyed) {
397
+ res.socket.destroy();
398
+ }
399
+ unexpectedResponse = true; // So that we ignore this in the error handler
400
+ upstreamWebSocket.terminate();
401
+ });
402
+
403
+ // If there's some other error, we just kill the socket:
404
+ upstreamWebSocket.on('error', (e) => {
405
+ if (unexpectedResponse) return; // Handled separately above
406
+
407
+ console.warn(e);
408
+ incomingSocket.end();
409
+ });
410
+
411
+ incomingSocket.on('error', () => upstreamWebSocket.close(1011)); // Internal error
412
+ }
413
+
414
+ /**
415
+ * @internal
416
+ */
417
+ static deserialize(
418
+ data: SerializedPassThroughWebSocketData,
419
+ channel: ClientServerChannel,
420
+ ruleParams: RuleParameters
421
+ ): any {
422
+ // By default, we assume we just need to assign the right prototype
423
+ return _.create(this.prototype, {
424
+ ...data,
425
+ proxyConfig: deserializeProxyConfig(data.proxyConfig, channel, ruleParams),
426
+ extraCACertificates: data.extraCACertificates || [],
427
+ ignoreHostHttpsErrors: data.ignoreHostCertificateErrors,
428
+ clientCertificateHostMap: _.mapValues(data.clientCertificateHostMap,
429
+ ({ pfx, passphrase }) => ({ pfx: deserializeBuffer(pfx), passphrase })
430
+ ),
431
+ });
432
+ }
433
+ }
434
+
435
+ export class EchoWebSocketHandler extends EchoWebSocketHandlerDefinition {
436
+
437
+ private wsServer?: WebSocket.Server;
438
+
439
+ private initializeWsServer() {
440
+ if (this.wsServer) return;
441
+
442
+ this.wsServer = new WebSocket.Server({ noServer: true });
443
+ this.wsServer.on('connection', (ws: WebSocket) => {
444
+ pipeWebSocket(ws, ws);
445
+ });
446
+ }
447
+
448
+ async handle(req: OngoingRequest & http.IncomingMessage, socket: net.Socket, head: Buffer) {
449
+ this.initializeWsServer();
450
+
451
+ this.wsServer!.handleUpgrade(req, socket, head, (ws) => {
452
+ socket.emit('ws-upgrade', ws);
453
+ this.wsServer!.emit('connection', ws);
454
+ });
455
+ }
456
+ }
457
+
458
+ export class ListenWebSocketHandler extends ListenWebSocketHandlerDefinition {
459
+
460
+ private wsServer?: WebSocket.Server;
461
+
462
+ private initializeWsServer() {
463
+ if (this.wsServer) return;
464
+
465
+ this.wsServer = new WebSocket.Server({ noServer: true });
466
+ this.wsServer.on('connection', (ws: WebSocket) => {
467
+ // Accept but ignore the incoming websocket data
468
+ ws.resume();
469
+ });
470
+ }
471
+
472
+ async handle(req: OngoingRequest & http.IncomingMessage, socket: net.Socket, head: Buffer) {
473
+ this.initializeWsServer();
474
+
475
+ this.wsServer!.handleUpgrade(req, socket, head, (ws) => {
476
+ socket.emit('ws-upgrade', ws);
477
+ this.wsServer!.emit('connection', ws);
478
+ });
479
+ }
480
+ }
481
+
482
+ export class RejectWebSocketHandler extends RejectWebSocketHandlerDefinition {
483
+
484
+ async handle(req: OngoingRequest, socket: net.Socket, head: Buffer) {
485
+ socket.write(rawResponse(this.statusCode, this.statusMessage, objectHeadersToRaw(this.headers)));
486
+ if (this.body) socket.write(this.body);
487
+ socket.write('\r\n');
488
+ socket.destroy();
489
+ }
490
+
491
+ }
492
+
493
+ // These three work equally well for HTTP requests as websockets, but it's
494
+ // useful to reexport there here for consistency.
495
+ export {
496
+ CloseConnectionHandler,
497
+ ResetConnectionHandler,
498
+ TimeoutHandler
499
+ };
500
+
501
+ export const WsHandlerLookup: typeof WsHandlerDefinitionLookup = {
502
+ 'ws-passthrough': PassThroughWebSocketHandler,
503
+ 'ws-echo': EchoWebSocketHandler,
504
+ 'ws-listen': ListenWebSocketHandler,
505
+ 'ws-reject': RejectWebSocketHandler,
506
+ 'close-connection': CloseConnectionHandler,
507
+ 'reset-connection': ResetConnectionHandler,
508
+ 'timeout': TimeoutHandler
509
+ };