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
package/src/types.ts ADDED
@@ -0,0 +1,433 @@
1
+ import stream = require('stream');
2
+ import http = require('http');
3
+ import { EventEmitter } from 'events';
4
+
5
+ export const DEFAULT_ADMIN_SERVER_PORT = 45454;
6
+
7
+ export enum Method {
8
+ GET,
9
+ POST,
10
+ PUT,
11
+ DELETE,
12
+ PATCH,
13
+ HEAD,
14
+ OPTIONS
15
+ }
16
+
17
+ export enum RulePriority {
18
+ FALLBACK = 0,
19
+ DEFAULT = 1
20
+ }
21
+
22
+ export interface Headers {
23
+ // An arbitrary set of headers that are known to
24
+ // only ever appear once (for valid requests).
25
+ host?: string;
26
+ 'content-length'?: string;
27
+ 'content-type'?: string;
28
+ 'user-agent'?: string;
29
+ cookie?: string;
30
+ ':method'?: string;
31
+ ':scheme'?: string;
32
+ ':authority'?: string;
33
+ ':path'?: string;
34
+
35
+ // In general there may be 0+ of any header
36
+ [key: string]: undefined | string | string[];
37
+ }
38
+
39
+ export interface Trailers {
40
+ // 0+ of any trailer
41
+ [key: string]: undefined | string | string[];
42
+ }
43
+
44
+ export type RawHeaders = Array<[key: string, value: string]>;
45
+ export type RawTrailers = RawHeaders; // Just a convenient alias
46
+
47
+ export interface Request {
48
+ id: string;
49
+ matchedRuleId?: string;
50
+
51
+ protocol: string;
52
+ httpVersion?: string; // Like timingEvents - not set remotely with older servers
53
+ method: string;
54
+ url: string;
55
+ path: string;
56
+
57
+ remoteIpAddress?: string; // Not set remotely with older servers
58
+ remotePort?: number; // Not set remotely with older servers
59
+
60
+ // Exists only if a host header is sent. A strong candidate for deprecation
61
+ // in future, since it's not clear that this comes from headers not the URL, and
62
+ // either way it duplicates existing data.
63
+ hostname?: string;
64
+
65
+ headers: Headers;
66
+ rawHeaders: RawHeaders;
67
+
68
+ timingEvents: TimingEvents;
69
+ tags: string[];
70
+ }
71
+
72
+ export interface TlsConnectionEvent {
73
+ hostname?: string;
74
+ remoteIpAddress: string;
75
+ remotePort: number;
76
+ tags: string[];
77
+ timingEvents: TlsTimingEvents;
78
+ tlsMetadata: TlsSocketMetadata;
79
+ }
80
+
81
+ export interface TlsSocketMetadata {
82
+ sniHostname?: string;
83
+ connectHostname?: string;
84
+ connectPort?: string;
85
+ clientAlpn?: string[];
86
+ ja3Fingerprint?: string;
87
+ }
88
+
89
+ export interface TlsPassthroughEvent extends TlsConnectionEvent {
90
+ id: string;
91
+ upstreamPort: number;
92
+ }
93
+
94
+ export interface TlsHandshakeFailure extends TlsConnectionEvent {
95
+ failureCause:
96
+ | 'closed'
97
+ | 'reset'
98
+ | 'cert-rejected'
99
+ | 'no-shared-cipher'
100
+ | 'handshake-timeout'
101
+ | 'unknown';
102
+ timingEvents: TlsFailureTimingEvents;
103
+ }
104
+
105
+ export interface TlsTimingEvents {
106
+ /**
107
+ * When the socket initially connected, in MS since the unix
108
+ * epoch.
109
+ */
110
+ startTime: number;
111
+
112
+ /**
113
+ * When the socket initially connected, equivalent to startTime.
114
+ *
115
+ * High-precision floating-point monotonically increasing timestamps.
116
+ * Comparable and precise, but not related to specific current time.
117
+ */
118
+ connectTimestamp: number;
119
+
120
+ /**
121
+ * When Mockttp's handshake for this connection was completed (if there
122
+ * was one). This is not set for passed through connections.
123
+ */
124
+ handshakeTimestamp?: number;
125
+
126
+ /**
127
+ * When the outer tunnel (e.g. a preceeding CONNECT request) was created,
128
+ * if there was one.
129
+ */
130
+ tunnelTimestamp?: number;
131
+
132
+ /**
133
+ * When the connection was closed, if it has been closed.
134
+ */
135
+ disconnectTimestamp?: number;
136
+ }
137
+
138
+ export interface TlsFailureTimingEvents extends TlsTimingEvents {
139
+ /**
140
+ * When the TLS connection failed. This may be due to a failed handshake
141
+ * (in which case `handshakeTimestamp` will be undefined) or due to a
142
+ * subsequent error which means the TLS connection was not usable (like
143
+ * an immediate closure due to an async certificate rejection).
144
+ */
145
+ failureTimestamp: number;
146
+ }
147
+
148
+ // Internal representation of an ongoing HTTP request whilst it's being processed
149
+ export interface OngoingRequest extends Request, EventEmitter {
150
+ body: OngoingBody;
151
+ rawTrailers?: RawHeaders;
152
+ }
153
+
154
+ export interface OngoingBody {
155
+ asStream: () => stream.Readable;
156
+ asBuffer: () => Promise<Buffer>;
157
+ asDecodedBuffer: () => Promise<Buffer>;
158
+ asText: () => Promise<string>;
159
+ asJson: () => Promise<object>;
160
+ asFormData: () => Promise<{ [key: string]: string | string[] | undefined }>;
161
+ }
162
+
163
+ export interface CompletedBody {
164
+ /**
165
+ * The raw bytes of the response. If a content encoding was used, this is
166
+ * the raw encoded data.
167
+ */
168
+ buffer: Buffer;
169
+
170
+ /**
171
+ * The decoded bytes of the response. If no encoding was used, this is the
172
+ * same as `.buffer`. The response is decoded and returned asynchronously
173
+ * as a Promise.
174
+ */
175
+ getDecodedBuffer(): Promise<Buffer | undefined>;
176
+
177
+ /**
178
+ * The contents of the response, decoded and parsed as a UTF-8 string.
179
+ * The response is decoded and returned asynchronously as a Promise.
180
+ */
181
+ getText(): Promise<string | undefined>;
182
+
183
+ /**
184
+ * The contents of the response, decoded, parsed as UTF-8 string, and
185
+ * then parsed a JSON. The response is decoded and returned asynchronously
186
+ * as a Promise.
187
+ */
188
+ getJson(): Promise<object | undefined>;
189
+
190
+ /**
191
+ * The contents of the response, decoded, and then parsed automatically as
192
+ * either one of the form encoding types (either URL-encoded or multipart),
193
+ * determined automatically from the message content-type header.
194
+ *
195
+ * This method is convenient and offers a single mechanism to parse both
196
+ * formats, but you may want to consider parsing on format explicitly with
197
+ * the `getUrlEncodedFormData()` or `getMultipartFormData()` methods instead.
198
+ *
199
+ * After parsing & decoding, the result is returned asynchronously as a
200
+ * Promise for a key-value(s) object.
201
+ */
202
+ getFormData(): Promise<{ [key: string]: string | string[] | undefined } | undefined>;
203
+
204
+ /**
205
+ * The contents of the response, decoded, parsed as UTF-8 string, and then
206
+ * parsed as URL-encoded form data. After parsing & decoding, the result is
207
+ * returned asynchronously as a Promise for a key-value(s) object.
208
+ */
209
+ getUrlEncodedFormData(): Promise<{ [key: string]: string | string[] | undefined } | undefined>;
210
+
211
+ /**
212
+ * The contents of the response, decoded, and then parsed as multi-part
213
+ * form data. The response is result is returned asynchronously as a
214
+ * Promise for an array of parts with their names, data and metadata.
215
+ */
216
+ getMultipartFormData(): Promise<Array<{ name?: string, filename?: string, type?: string, data: Buffer }> | undefined>;
217
+ }
218
+
219
+ // Internal & external representation of an initiated (no body yet received) HTTP request.
220
+ export type InitiatedRequest = Request;
221
+
222
+ export interface AbortedRequest extends InitiatedRequest {
223
+ error?: {
224
+ name?: string;
225
+ code?: string;
226
+ message?: string;
227
+ stack?: string;
228
+ };
229
+ }
230
+
231
+ // Internal & external representation of a fully completed HTTP request
232
+ export interface CompletedRequest extends Request {
233
+ body: CompletedBody;
234
+ rawTrailers: RawTrailers;
235
+ trailers: Trailers;
236
+ }
237
+
238
+ export interface TimingEvents {
239
+ // Milliseconds since unix epoch
240
+ startTime: number;
241
+
242
+ // High-precision floating-point monotonically increasing timestamps.
243
+ // Comparable and precise, but not related to specific current time.
244
+ startTimestamp: number; // When the request was initially received
245
+ bodyReceivedTimestamp?: number; // When the request body was fully received
246
+ headersSentTimestamp?: number; // When the response headers were sent
247
+ responseSentTimestamp?: number; // When the response was fully completed
248
+
249
+ wsAcceptedTimestamp?: number; // When the websocket was accepted
250
+ wsClosedTimestamp?: number; // When the websocket was closed
251
+
252
+ abortedTimestamp?: number; // When the connected was aborted
253
+ }
254
+
255
+ export interface OngoingResponse extends http.ServerResponse {
256
+ id: string;
257
+ getHeaders(): Headers;
258
+ getRawHeaders(): RawHeaders;
259
+ body: OngoingBody;
260
+ getRawTrailers(): RawTrailers;
261
+ timingEvents: TimingEvents;
262
+ tags: string[];
263
+ }
264
+
265
+ export interface CompletedResponse {
266
+ id: string;
267
+ statusCode: number;
268
+ statusMessage: string;
269
+ headers: Headers;
270
+ rawHeaders: RawHeaders;
271
+ body: CompletedBody;
272
+ rawTrailers: RawTrailers;
273
+ trailers: Trailers;
274
+ timingEvents: TimingEvents;
275
+ tags: string[];
276
+ }
277
+
278
+ export interface WebSocketMessage {
279
+ /**
280
+ * The id of this websocket stream. This will match the id of the request,
281
+ * the initial connection response, and any other WebSocket events for the
282
+ * same connection stream.
283
+ */
284
+ streamId: string;
285
+
286
+ /**
287
+ * Whether the message was sent by Mockttp, or received from a Mockttp client.
288
+ */
289
+ direction: 'sent' | 'received';
290
+
291
+ /**
292
+ * The contents of the message as a raw buffer. This is already decompressed,
293
+ * if the WebSocket uses compression.
294
+ */
295
+ content: Uint8Array;
296
+
297
+ /**
298
+ * Whether this is a string message or a raw binary data message.
299
+ */
300
+ isBinary: boolean;
301
+
302
+ /**
303
+ * A high-precision floating-point monotonically increasing timestamp.
304
+ * Comparable and precise, but not related to specific current time.
305
+ *
306
+ * To link this to the current time, compare it to `timingEvents.startTime`.
307
+ */
308
+ eventTimestamp: number;
309
+
310
+ timingEvents: TimingEvents;
311
+ tags: string[];
312
+ }
313
+
314
+ export interface WebSocketClose {
315
+ /**
316
+ * The id of this websocket stream. This will match the id of the request,
317
+ * the initial connection response, and any other WebSocket events for the
318
+ * same connection stream.
319
+ */
320
+ streamId: string;
321
+
322
+ /**
323
+ * The close code of the shutdown. This is the close code that was received
324
+ * from the remote client (either initiated remotely, or echoing our own sent
325
+ * close frame).
326
+ *
327
+ * This may be undefined only if a close frame was received but did not contain
328
+ * any close code. If no close frame was received before the connection was
329
+ * lost (i.e. the connection was not cleanly closed) this event will not
330
+ * fire at all, and an 'abort' event will fire instead.
331
+ */
332
+ closeCode: number | undefined;
333
+
334
+ /**
335
+ * The close reason of the shutdown.
336
+ */
337
+ closeReason: string;
338
+
339
+ timingEvents: TimingEvents;
340
+ tags: string[];
341
+ }
342
+
343
+ /**
344
+ * A client error event describes a request (or our best guess at parsing it),
345
+ * that wasn't correctly completed, and the error response it received, or
346
+ * 'aborted' if the connection was disconnected before we could respond.
347
+ */
348
+ export interface ClientError {
349
+ errorCode?: string;
350
+ request: {
351
+ id: string;
352
+ timingEvents: TimingEvents;
353
+ tags: string[];
354
+
355
+ // All of these are best guess, depending on what's parseable:
356
+ protocol?: string;
357
+ httpVersion?: string;
358
+ method?: string;
359
+ url?: string;
360
+ path?: string;
361
+
362
+ headers: Headers;
363
+ rawHeaders: RawHeaders;
364
+
365
+ remoteIpAddress?: string;
366
+ remotePort?: number;
367
+ };
368
+ response: CompletedResponse | 'aborted';
369
+ }
370
+
371
+ /**
372
+ * An event fired from an individual rule during request processing.
373
+ */
374
+ export interface RuleEvent<T = unknown> {
375
+ requestId: string;
376
+ ruleId: string;
377
+ eventType: string;
378
+ eventData: T;
379
+ }
380
+
381
+ /**
382
+ * A mocked endpoint provides methods to see the current state of
383
+ * a mock rule.
384
+ */
385
+ export interface MockedEndpoint {
386
+ id: string;
387
+
388
+ /**
389
+ * Get the requests that this endpoint has seen so far.
390
+ *
391
+ * This method returns a promise, which resolves with the requests seen
392
+ * up until now, once all ongoing requests have terminated. The returned
393
+ * lists are immutable, so won't change if more requests arrive in future.
394
+ * Call `getSeenRequests` again later to get an updated list.
395
+ *
396
+ * Requests are included here once the response is completed, even if the request
397
+ * itself failed, the responses failed or exceptions are thrown elsewhere. To
398
+ * watch for errors or detailed response info, look at the various server.on(event)
399
+ * methods.
400
+ */
401
+ getSeenRequests(): Promise<CompletedRequest[]>;
402
+
403
+ /**
404
+ * Reports whether this endpoint is still pending: if it either hasn't seen the
405
+ * specified number of requests (if one was specified e.g. with .twice())
406
+ * or if it hasn't seen at least one request, by default.
407
+ *
408
+ * This method returns a promise, which resolves with the result once all
409
+ * ongoing requests have terminated.
410
+ */
411
+ isPending(): Promise<boolean>;
412
+ }
413
+
414
+ export interface MockedEndpointData {
415
+ id: string;
416
+ explanation?: string;
417
+ seenRequests: CompletedRequest[];
418
+ isPending: boolean;
419
+ }
420
+
421
+ export interface Explainable {
422
+ explain(): string;
423
+ }
424
+
425
+ export interface ProxyEnvConfig {
426
+ HTTP_PROXY: string;
427
+ HTTPS_PROXY: string;
428
+ }
429
+
430
+ // A slightly weird one: this is necessary because we export types that inherit from EventEmitter,
431
+ // so the docs include EventEmitter's methods, which @link to this type, that's otherwise not
432
+ // defined in this module. Reexporting the values avoids warnings for that.
433
+ export type defaultMaxListeners = typeof EventEmitter.defaultMaxListeners;
@@ -0,0 +1,164 @@
1
+ import * as _ from 'lodash';
2
+ import { EventEmitter } from 'events';
3
+ import * as stream from 'stream';
4
+
5
+ import { isNode } from './util';
6
+
7
+ const MAX_BUFFER_SIZE = isNode
8
+ ? require('buffer').constants.MAX_LENGTH
9
+ : Infinity;
10
+
11
+ export const asBuffer = (input: Buffer | Uint8Array | string) =>
12
+ Buffer.isBuffer(input)
13
+ ? input
14
+ : typeof input === "string"
15
+ ? Buffer.from(input, 'utf8')
16
+ // Is Array:
17
+ : Buffer.from(input);
18
+
19
+ export type BufferInProgress = Promise<Buffer> & {
20
+ currentChunks: Buffer[]; // Stores the body chunks as they arrive
21
+ failedWith?: Error; // Stores the error that killed the stream, if one did
22
+ events: EventEmitter; // Emits events - notably 'truncate' if data is truncated
23
+ };
24
+
25
+ // Takes a buffer and a stream, returns a simple stream that outputs the buffer then the stream. The stream
26
+ // is lazy, so doesn't read data in from the buffer or input until something here starts reading.
27
+ export const bufferThenStream = (buffer: BufferInProgress, inputStream: stream.Readable): stream.Readable => {
28
+ let active = false;
29
+
30
+ const outputStream = new stream.PassThrough({
31
+ // Note we use the default highWaterMark, which means this applies backpressure, pushing buffering
32
+ // onto the OS & backpressure on network instead of accepting data before we're ready to stream it.
33
+
34
+ // Without changing behaviour, we listen for read() events, and don't start streaming until we get one.
35
+ read(size) {
36
+ // On the first actual read of this stream, we pull from the buffer
37
+ // and then hook it up to the input.
38
+ if (!active) {
39
+ if (buffer.failedWith) {
40
+ outputStream.destroy(buffer.failedWith);
41
+ } else {
42
+ // First stream everything that's been buffered so far:
43
+ outputStream.write(Buffer.concat(buffer.currentChunks));
44
+
45
+ // Then start streaming all future incoming data:
46
+ inputStream.pipe(outputStream);
47
+
48
+ if (inputStream.readableEnded) outputStream.end();
49
+ if (inputStream.readableAborted) outputStream.destroy();
50
+
51
+ // Forward any future errors from the input stream:
52
+ inputStream.on('error', (e) => {
53
+ outputStream.emit('error', e)
54
+ });
55
+
56
+ // Silence 'unhandled rejection' warnings here, since we'll handle
57
+ // them on the stream instead
58
+ buffer.catch(() => {});
59
+ }
60
+ active = true;
61
+ }
62
+
63
+ // Except for the first activation logic (above) do the default transform read() steps just
64
+ // like a normal PassThrough stream.
65
+ return stream.Transform.prototype._read.call(this, size);
66
+ }
67
+ });
68
+
69
+ buffer.events.on('truncate', (chunks) => {
70
+ // If the stream hasn't started yet, start it now, so it grabs the buffer
71
+ // data before it gets truncated:
72
+ if (!active) outputStream.read(0);
73
+ });
74
+
75
+ return outputStream;
76
+ };
77
+
78
+ export const bufferToStream = (buffer: Buffer): stream.Readable => {
79
+ const outputStream = new stream.PassThrough();
80
+ outputStream.end(buffer);
81
+ return outputStream;
82
+ };
83
+
84
+ export const streamToBuffer = (input: stream.Readable, maxSize = MAX_BUFFER_SIZE) => {
85
+ let chunks: Buffer[] = [];
86
+
87
+ const bufferPromise = <BufferInProgress> new Promise(
88
+ (resolve, reject) => {
89
+ function failWithAbortError() {
90
+ bufferPromise.failedWith = new Error('Aborted');
91
+ reject(bufferPromise.failedWith);
92
+ }
93
+
94
+ // If stream has already finished/aborted, resolve accordingly immediately:
95
+ if (input.readableEnded) return resolve(Buffer.from([]));
96
+ if (input.readableAborted) return failWithAbortError();
97
+
98
+ let currentSize = 0;
99
+ const onData = (d: Buffer) => {
100
+ currentSize += d.length;
101
+ chunks.push(d);
102
+
103
+ // If we go over maxSize, drop the whole stream, so the buffer
104
+ // resolves empty. MaxSize should be large, so this is rare,
105
+ // and only happens as an alternative to crashing the process.
106
+ if (currentSize > maxSize) {
107
+ // Announce truncation, so that other mechanisms (asStream) can
108
+ // capture this data if they're interested in it.
109
+ bufferPromise.events.emit('truncate', chunks);
110
+
111
+ // Drop all the data so far & stop reading
112
+ bufferPromise.currentChunks = chunks = [];
113
+ input.removeListener('data', onData);
114
+
115
+ // We then resolve immediately - the buffer is done, even if the body
116
+ // might still be streaming in we're not listening to it. This means
117
+ // that requests can 'complete' for event/callback purposes while
118
+ // they're actually still streaming, but only in this scenario where
119
+ // the data is too large to really be used by the events/callbacks.
120
+
121
+ // If we don't resolve, then cases which intentionally don't consume
122
+ // the raw stream but do consume the buffer (beforeRequest) would
123
+ // deadlock: beforeRequest must complete to begin streaming the
124
+ // full body to the target clients.
125
+
126
+ resolve(Buffer.from([]));
127
+ }
128
+ };
129
+ input.on('data', onData);
130
+
131
+ input.once('end', () => {
132
+ resolve(Buffer.concat(chunks));
133
+ });
134
+ input.once('aborted', failWithAbortError);
135
+ input.on('error', (e) => {
136
+ bufferPromise.failedWith = bufferPromise.failedWith || e;
137
+ reject(e);
138
+ });
139
+ }
140
+ );
141
+ bufferPromise.currentChunks = chunks;
142
+ bufferPromise.events = new EventEmitter();
143
+ return bufferPromise;
144
+ };
145
+
146
+ export function splitBuffer(input: Buffer, splitter: string, maxParts = Infinity) {
147
+ const parts: Buffer[] = [];
148
+
149
+ let remainingBuffer = input;
150
+ while (remainingBuffer.length) {
151
+ let endOfPart = remainingBuffer.indexOf(splitter);
152
+ if (endOfPart === -1) endOfPart = remainingBuffer.length;
153
+
154
+ parts.push(remainingBuffer.slice(0, endOfPart));
155
+ remainingBuffer = remainingBuffer.slice(endOfPart + splitter.length);
156
+
157
+ if (parts.length === maxParts - 1) {
158
+ parts.push(remainingBuffer);
159
+ break;
160
+ }
161
+ }
162
+
163
+ return parts;
164
+ }
@@ -0,0 +1,52 @@
1
+ import * as dns from 'dns';
2
+
3
+ // We exclude the __promisify__ type hack from the official DNS type, to give us a type
4
+ // that otherwise enforces correctness. We still have to cast past the missing 'field'
5
+ // at the end of the day, but this allows us to avoid that until the last minute without
6
+ // sacrificing any strictness the rest of the time.
7
+ export type DnsLookupFunction = Omit<typeof dns.lookup, '__promisify__'>;
8
+
9
+ // A drop-in alternative to dns.lookup, but where results are briefly cached to avoid completely
10
+ // unnecessary lookups, while remaining fairly reactive to actual host file changes etc.
11
+ export class CachedDns {
12
+
13
+ private cache = new Map<string, [address: string | dns.LookupAddress[], family: number]>();
14
+
15
+ constructor(
16
+ private cacheDurationMs: number
17
+ ) {}
18
+
19
+ private cacheKey(hostname: Parameters<typeof dns.lookup>[0], options?: dns.LookupAllOptions | dns.LookupOneOptions) {
20
+ return `${hostname}-${options?.all}-${options?.family}-${options?.hints}-${options?.verbatim}`;
21
+ }
22
+
23
+ lookup: DnsLookupFunction = (...args: Parameters<typeof dns.lookup>) => {
24
+ const [hostname, options] = args.slice(0, -1) as [string, dns.LookupOptions | undefined];
25
+ const cb = args[args.length - 1] as (err: NodeJS.ErrnoException | null, address: string | dns.LookupAddress[], family: number) => void;
26
+
27
+ const key = this.cacheKey(hostname, options);
28
+ const cachedResult = this.cache.get(key);
29
+ if (cachedResult) {
30
+ setImmediate(() => cb(null, ...cachedResult));
31
+ } else {
32
+ dns.lookup(hostname, options ?? {}, (err, ...cbArgs) => {
33
+ if (!err) {
34
+ this.cache.set(key, cbArgs);
35
+ // Always refresh Xms after initially inserted - no LRU or similar
36
+ setTimeout(() => this.cache.delete(key), this.cacheDurationMs).unref();
37
+ }
38
+ return cb(err, ...cbArgs);
39
+ });
40
+ }
41
+ }
42
+
43
+ }
44
+
45
+ export function dnsLookup(lookupFn: typeof dns.lookup | DnsLookupFunction, hostname: string) {
46
+ return new Promise<string>((resolve, reject) => {
47
+ (lookupFn as typeof dns.lookup)(hostname!, (err, address) => {
48
+ if (err) reject(err);
49
+ else resolve(address);
50
+ });
51
+ })
52
+ }
@@ -0,0 +1,18 @@
1
+ export type ErrorLike = Partial<Error> & {
2
+ // Various properties we might want to look for on errors:
3
+ code?: string;
4
+ cmd?: string;
5
+ signal?: string;
6
+ statusCode?: number;
7
+ statusMessage?: string;
8
+ };
9
+
10
+ // Useful to easily cast and then examine errors that are otherwise 'unknown':
11
+ export function isErrorLike(error: any): error is ErrorLike {
12
+ return typeof error === 'object' && (
13
+ error instanceof Error ||
14
+ error.message ||
15
+ error.code ||
16
+ error.stack
17
+ )
18
+ }