@jaredwray/mockhttp 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +1657 -1188
- package/package.json +77 -82
package/dist/index.js
CHANGED
|
@@ -6,152 +6,13 @@ import process from "process";
|
|
|
6
6
|
|
|
7
7
|
// src/mock-http.ts
|
|
8
8
|
import path from "path";
|
|
9
|
-
import
|
|
9
|
+
import fastifyCookie from "@fastify/cookie";
|
|
10
10
|
import fastifyHelmet from "@fastify/helmet";
|
|
11
|
+
import fastifyStatic from "@fastify/static";
|
|
11
12
|
import { fastifySwagger } from "@fastify/swagger";
|
|
12
|
-
import
|
|
13
|
+
import { detect } from "detect-port";
|
|
13
14
|
import Fastify from "fastify";
|
|
14
15
|
import { Hookified } from "hookified";
|
|
15
|
-
import { detect } from "detect-port";
|
|
16
|
-
|
|
17
|
-
// src/swagger.ts
|
|
18
|
-
import { fastifySwaggerUi } from "@fastify/swagger-ui";
|
|
19
|
-
|
|
20
|
-
// package.json
|
|
21
|
-
var package_default = {
|
|
22
|
-
name: "@jaredwray/mockhttp",
|
|
23
|
-
version: "0.6.0",
|
|
24
|
-
description: "Mock Http - Easy way to mock http with httpbin replacement",
|
|
25
|
-
type: "module",
|
|
26
|
-
main: "dist/index.js",
|
|
27
|
-
directories: {
|
|
28
|
-
test: "test"
|
|
29
|
-
},
|
|
30
|
-
scripts: {
|
|
31
|
-
test: "xo --fix && vitest run --coverage",
|
|
32
|
-
dev: "xo --fix && tsx src/index.ts --watch",
|
|
33
|
-
build: "rimraf ./dist && tsup src/index.ts --format esm --dts --clean",
|
|
34
|
-
"docker:build": "docker build -t jaredwray/mockhttp .",
|
|
35
|
-
"docker:run": "docker run -p 3000:3000 jaredwray/mockhttp",
|
|
36
|
-
"docker:push": "docker push jaredwray/mockhttp",
|
|
37
|
-
"docker:stop": 'docker stop $(docker ps -a -q --filter ancestor=jaredwray/mockhttp --format="{{.ID}}")',
|
|
38
|
-
"docker:readme": "tsx ./scripts/create-docker-readme.ts",
|
|
39
|
-
"docker:compose:up": "docker-compose up -d",
|
|
40
|
-
"docker:compose:down": "docker-compose down",
|
|
41
|
-
prepare: "pnpm run build",
|
|
42
|
-
start: "node dist/index.js"
|
|
43
|
-
},
|
|
44
|
-
repository: {
|
|
45
|
-
type: "git",
|
|
46
|
-
url: "git+https://github.com/jaredwray/mockhttp.git"
|
|
47
|
-
},
|
|
48
|
-
keywords: [
|
|
49
|
-
"mock",
|
|
50
|
-
"http",
|
|
51
|
-
"httpbin",
|
|
52
|
-
"test"
|
|
53
|
-
],
|
|
54
|
-
author: "Jared Wray <me@jaredwray.com>",
|
|
55
|
-
license: "MIT",
|
|
56
|
-
bugs: {
|
|
57
|
-
url: "https://github.com/jaredwray/mockhttp/issues"
|
|
58
|
-
},
|
|
59
|
-
homepage: "https://github.com/jaredwray/mockhttp#readme",
|
|
60
|
-
devDependencies: {
|
|
61
|
-
"@swc/core": "^1.12.1",
|
|
62
|
-
"@types/html-escaper": "^3.0.4",
|
|
63
|
-
"@vitest/coverage-v8": "^3.2.3",
|
|
64
|
-
rimraf: "^6.0.1",
|
|
65
|
-
tsup: "^8.5.0",
|
|
66
|
-
tsx: "^4.20.3",
|
|
67
|
-
typescript: "^5.8.3",
|
|
68
|
-
vitest: "^3.2.3",
|
|
69
|
-
xo: "^1.1.0"
|
|
70
|
-
},
|
|
71
|
-
dependencies: {
|
|
72
|
-
"@fastify/cookie": "^11.0.2",
|
|
73
|
-
"@fastify/helmet": "^13.0.1",
|
|
74
|
-
"@fastify/static": "^8.2.0",
|
|
75
|
-
"@fastify/swagger": "^9.5.1",
|
|
76
|
-
"@fastify/swagger-ui": "^5.2.3",
|
|
77
|
-
"@scalar/api-reference": "^1.31.11",
|
|
78
|
-
"detect-port": "^2.1.0",
|
|
79
|
-
fastify: "^5.4.0",
|
|
80
|
-
hookified: "^1.9.1",
|
|
81
|
-
"html-escaper": "^3.0.3",
|
|
82
|
-
pino: "^9.7.0",
|
|
83
|
-
"pino-pretty": "^13.0.0"
|
|
84
|
-
},
|
|
85
|
-
files: [
|
|
86
|
-
"dist",
|
|
87
|
-
"public",
|
|
88
|
-
"LICENCE"
|
|
89
|
-
],
|
|
90
|
-
pnpm: {
|
|
91
|
-
onlyBuiltDependencies: [
|
|
92
|
-
"@swc/core",
|
|
93
|
-
"esbuild",
|
|
94
|
-
"unrs-resolver",
|
|
95
|
-
"vue-demi"
|
|
96
|
-
]
|
|
97
|
-
},
|
|
98
|
-
xo: {
|
|
99
|
-
rules: {
|
|
100
|
-
"@typescript-eslint/no-unsafe-call": "off",
|
|
101
|
-
"@typescript-eslint/no-unsafe-assignment": "off"
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
// src/swagger.ts
|
|
107
|
-
var description = `
|
|
108
|
-
A simple HTTP server that can be used to mock HTTP responses for testing purposes. Inspired by [httpbin](https://httpbin.org/) and built using \`nodejs\` and \`fastify\` with the idea of running it via https://mockhttp.org, via docker \`jaredwray/mockhttp\`, or nodejs \`npm install jaredwray/mockhttp\`.
|
|
109
|
-
|
|
110
|
-
* [GitHub Repository](https://github.com/jaredwray/mockhttp)
|
|
111
|
-
* [Docker Image](https://hub.docker.com/r/jaredwray/mockhttp)
|
|
112
|
-
* [NPM Package](https://www.npmjs.com/package/jaredwray/mockhttp)
|
|
113
|
-
|
|
114
|
-
# About mockhttp.org
|
|
115
|
-
|
|
116
|
-
[mockhttp.org](https://mockhttp.org) is a free service that runs this codebase and allows you to use it for testing purposes. It is a simple way to mock HTTP responses for testing purposes. Ran via [Cloudflare](https://cloudflare.com) and [Google Cloud Run](https://cloud.google.com/run/) across 7 regions globally and can do millions of requests per second.
|
|
117
|
-
`;
|
|
118
|
-
var fastifySwaggerConfig = {
|
|
119
|
-
openapi: {
|
|
120
|
-
info: {
|
|
121
|
-
title: "Mock HTTP API",
|
|
122
|
-
description,
|
|
123
|
-
version: package_default.version
|
|
124
|
-
},
|
|
125
|
-
consumes: [
|
|
126
|
-
"application/json"
|
|
127
|
-
],
|
|
128
|
-
produces: [
|
|
129
|
-
"application/json"
|
|
130
|
-
]
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
var registerSwaggerUi = /* @__PURE__ */ __name(async (fastify) => {
|
|
134
|
-
await fastify.register(fastifySwaggerUi, {
|
|
135
|
-
routePrefix: "/docs",
|
|
136
|
-
uiConfig: {
|
|
137
|
-
docExpansion: "none",
|
|
138
|
-
deepLinking: false
|
|
139
|
-
},
|
|
140
|
-
uiHooks: {
|
|
141
|
-
/* c8 ignore next 6 */
|
|
142
|
-
onRequest(_request, _reply, next) {
|
|
143
|
-
next();
|
|
144
|
-
},
|
|
145
|
-
preHandler(_request, _reply, next) {
|
|
146
|
-
next();
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
150
|
-
staticCSP: true,
|
|
151
|
-
transformSpecification: /* @__PURE__ */ __name((swaggerObject, _request, _reply) => swaggerObject, "transformSpecification"),
|
|
152
|
-
transformSpecificationClone: true
|
|
153
|
-
});
|
|
154
|
-
}, "registerSwaggerUi");
|
|
155
16
|
|
|
156
17
|
// src/fastify-config.ts
|
|
157
18
|
var fastifyConfig = {
|
|
@@ -168,949 +29,1017 @@ var fastifyConfig = {
|
|
|
168
29
|
}
|
|
169
30
|
};
|
|
170
31
|
|
|
171
|
-
// src/routes/index.ts
|
|
172
|
-
var
|
|
173
|
-
|
|
174
|
-
schema: {
|
|
175
|
-
hide: true
|
|
176
|
-
}
|
|
177
|
-
}, async (_request, reply) => {
|
|
178
|
-
const redocHtml = `
|
|
179
|
-
<!doctype html>
|
|
180
|
-
<html>
|
|
181
|
-
<head>
|
|
182
|
-
<title>{ mockhttp }</title>
|
|
183
|
-
<meta charset="utf-8" />
|
|
184
|
-
<meta
|
|
185
|
-
name="viewport"
|
|
186
|
-
content="width=device-width, initial-scale=1" />
|
|
187
|
-
<style>
|
|
188
|
-
:root {
|
|
189
|
-
--scalar-custom-header-height: 70px;
|
|
190
|
-
}
|
|
191
|
-
.custom-header {
|
|
192
|
-
height: var(--scalar-custom-header-height);
|
|
193
|
-
background-color: var(--scalar-background-1);
|
|
194
|
-
box-shadow: inset 0 -1px 0 var(--scalar-border-color);
|
|
195
|
-
color: var(--scalar-color-1);
|
|
196
|
-
font-size: var(--scalar-font-size-2);
|
|
197
|
-
padding: 0 18px;
|
|
198
|
-
position: sticky;
|
|
199
|
-
justify-content: space-between;
|
|
200
|
-
top: 0;
|
|
201
|
-
z-index: 100;
|
|
202
|
-
}
|
|
203
|
-
.custom-header,
|
|
204
|
-
.custom-header nav {
|
|
205
|
-
display: flex;
|
|
206
|
-
align-items: center;
|
|
207
|
-
gap: 18px;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
.custom-header img {
|
|
211
|
-
height: var(--scalar-custom-header-height);
|
|
212
|
-
object-fit: contain;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
.custom-header a:hover {
|
|
216
|
-
color: var(--scalar-color-2);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
.github-corner {
|
|
220
|
-
fill: var(--scalar-border-color) !important;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
</style>
|
|
224
|
-
</head>
|
|
225
|
-
<body>
|
|
226
|
-
<header class="custom-header scalar-app">
|
|
227
|
-
<a href="/"><img src="/logo.svg" alt="{ mockhttp }" /></a>
|
|
228
|
-
<a href="https://github.com/jaredwray/mockhttp" class="github-corner" aria-label="View source on GitHub">
|
|
229
|
-
<svg width="70" height="70" viewBox="0 0 250 250" style="color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true">
|
|
230
|
-
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
|
|
231
|
-
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
|
|
232
|
-
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
|
|
233
|
-
</header>
|
|
234
|
-
|
|
235
|
-
<script
|
|
236
|
-
id="api-reference"
|
|
237
|
-
data-url="/docs/json"></script>
|
|
238
|
-
|
|
239
|
-
<script src="/scalar/browser/standalone.js"></script>
|
|
240
|
-
</body>
|
|
241
|
-
</html>
|
|
242
|
-
`;
|
|
243
|
-
await reply.type("text/html").send(redocHtml);
|
|
244
|
-
});
|
|
245
|
-
}, "indexRoute");
|
|
246
|
-
|
|
247
|
-
// src/routes/sitemap.ts
|
|
248
|
-
var sitemapRoute = /* @__PURE__ */ __name((fastify) => {
|
|
249
|
-
fastify.get("/sitemap.xml", {
|
|
250
|
-
schema: {
|
|
251
|
-
hide: true
|
|
252
|
-
}
|
|
253
|
-
}, async (_request, reply) => {
|
|
254
|
-
try {
|
|
255
|
-
const host = _request.headers.host ?? "mockhttp.org";
|
|
256
|
-
const isSecure = _request.headers["x-forwarded-proto"]?.includes("https");
|
|
257
|
-
const protocol = isSecure ? "https" : "http";
|
|
258
|
-
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
259
|
-
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
260
|
-
<url>
|
|
261
|
-
<loc>${protocol}://${host}/</loc>
|
|
262
|
-
<changefreq>monthly</changefreq>
|
|
263
|
-
<priority>1.0</priority>
|
|
264
|
-
</url>
|
|
265
|
-
</urlset>`;
|
|
266
|
-
await reply.type("text/xml").send(xml);
|
|
267
|
-
} catch (error) {
|
|
268
|
-
fastify.log.error(error);
|
|
269
|
-
await reply.status(500).send({
|
|
270
|
-
error: "Internal Server Error"
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
}, "sitemapRoute");
|
|
275
|
-
|
|
276
|
-
// src/routes/http-methods/get.ts
|
|
277
|
-
var getSchema = {
|
|
278
|
-
description: "Returns request information for GET requests",
|
|
32
|
+
// src/routes/anything/index.ts
|
|
33
|
+
var anythingSchema = {
|
|
34
|
+
description: "Will return anything that you send it",
|
|
279
35
|
tags: [
|
|
280
|
-
"
|
|
36
|
+
"Anything"
|
|
281
37
|
],
|
|
282
|
-
querystring: {
|
|
283
|
-
type: "object",
|
|
284
|
-
additionalProperties: true,
|
|
285
|
-
properties: {},
|
|
286
|
-
description: "Query parameters sent in the request"
|
|
287
|
-
},
|
|
288
|
-
headers: {
|
|
289
|
-
type: "object",
|
|
290
|
-
additionalProperties: true,
|
|
291
|
-
properties: {},
|
|
292
|
-
description: "Headers sent in the request"
|
|
293
|
-
},
|
|
294
38
|
response: {
|
|
295
|
-
// eslint-disable-next-line
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
296
40
|
200: {
|
|
297
41
|
type: "object",
|
|
298
42
|
properties: {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
},
|
|
302
|
-
headers: {
|
|
43
|
+
// Echo back all your params under `args`
|
|
44
|
+
args: {
|
|
303
45
|
type: "object",
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
46
|
+
description: "Route parameters",
|
|
47
|
+
additionalProperties: true
|
|
307
48
|
},
|
|
308
|
-
|
|
49
|
+
// Echo back whatever was in the request.body
|
|
50
|
+
data: {
|
|
309
51
|
type: "object",
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
headers: request.headers,
|
|
329
|
-
queryParams: request.query
|
|
330
|
-
}));
|
|
331
|
-
}, "getRoute");
|
|
332
|
-
|
|
333
|
-
// src/routes/http-methods/post.ts
|
|
334
|
-
var postSchema = {
|
|
335
|
-
description: "Handles a POST request and returns request information",
|
|
336
|
-
tags: [
|
|
337
|
-
"HTTP Methods"
|
|
338
|
-
],
|
|
339
|
-
body: {
|
|
340
|
-
type: "object",
|
|
341
|
-
additionalProperties: true,
|
|
342
|
-
description: "The body of the POST request"
|
|
343
|
-
},
|
|
344
|
-
response: {
|
|
345
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
346
|
-
200: {
|
|
347
|
-
type: "object",
|
|
348
|
-
properties: {
|
|
349
|
-
method: {
|
|
350
|
-
type: "string"
|
|
52
|
+
description: "Request body data",
|
|
53
|
+
additionalProperties: true
|
|
54
|
+
},
|
|
55
|
+
// If you’re using fastify-multipart, you’ll get
|
|
56
|
+
// files via request.files(); this just allows
|
|
57
|
+
// you to mirror them back
|
|
58
|
+
files: {
|
|
59
|
+
type: "object",
|
|
60
|
+
description: "Uploaded files",
|
|
61
|
+
additionalProperties: true
|
|
62
|
+
},
|
|
63
|
+
// If you parse application/x-www-form-urlencoded
|
|
64
|
+
// these fields land in request.body too, but you can
|
|
65
|
+
// mirror them separately if you like
|
|
66
|
+
form: {
|
|
67
|
+
type: "object",
|
|
68
|
+
description: "Form data",
|
|
69
|
+
additionalProperties: true
|
|
351
70
|
},
|
|
352
71
|
headers: {
|
|
353
72
|
type: "object",
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
73
|
+
description: "Request headers",
|
|
74
|
+
additionalProperties: true
|
|
357
75
|
},
|
|
358
|
-
|
|
76
|
+
json: {
|
|
359
77
|
type: "object",
|
|
78
|
+
description: "Parsed JSON body",
|
|
360
79
|
additionalProperties: true
|
|
80
|
+
},
|
|
81
|
+
method: {
|
|
82
|
+
type: "string",
|
|
83
|
+
description: "HTTP method used"
|
|
84
|
+
},
|
|
85
|
+
origin: {
|
|
86
|
+
type: "string",
|
|
87
|
+
description: "Origin of the request"
|
|
88
|
+
},
|
|
89
|
+
url: {
|
|
90
|
+
type: "string",
|
|
91
|
+
description: "Request URL"
|
|
361
92
|
}
|
|
362
93
|
},
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
"headers",
|
|
366
|
-
"body"
|
|
367
|
-
]
|
|
94
|
+
// Don’t allow any other top-level keys in your response
|
|
95
|
+
additionalProperties: false
|
|
368
96
|
}
|
|
369
97
|
}
|
|
370
98
|
};
|
|
371
|
-
var
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
99
|
+
var anythingRoute = /* @__PURE__ */ __name((fastify) => {
|
|
100
|
+
const handler = /* @__PURE__ */ __name(async (request, reply) => {
|
|
101
|
+
const response = {
|
|
102
|
+
args: request.query,
|
|
103
|
+
data: request.body ?? {},
|
|
104
|
+
// biome-ignore lint/suspicious/noExplicitAny: expected
|
|
105
|
+
files: request.raw?.files ?? {},
|
|
106
|
+
form: request.body ?? {},
|
|
107
|
+
headers: request.headers,
|
|
108
|
+
json: request.body ?? {},
|
|
109
|
+
method: request.method,
|
|
110
|
+
origin: request.headers.origin ?? request.headers.host ?? "",
|
|
111
|
+
url: request.url
|
|
112
|
+
};
|
|
113
|
+
await reply.send(response);
|
|
114
|
+
}, "handler");
|
|
115
|
+
const noValidation = {
|
|
116
|
+
schema: anythingSchema,
|
|
117
|
+
// Skip AJV on *this* route
|
|
118
|
+
validatorCompiler: /* @__PURE__ */ __name(() => () => true, "validatorCompiler")
|
|
119
|
+
};
|
|
120
|
+
fastify.get("/anything", noValidation, handler);
|
|
121
|
+
fastify.post("/anything", noValidation, handler);
|
|
122
|
+
fastify.put("/anything", noValidation, handler);
|
|
123
|
+
fastify.patch("/anything", noValidation, handler);
|
|
124
|
+
fastify.delete("/anything", noValidation, handler);
|
|
125
|
+
}, "anythingRoute");
|
|
380
126
|
|
|
381
|
-
// src/routes/
|
|
382
|
-
|
|
383
|
-
|
|
127
|
+
// src/routes/auth/basic.ts
|
|
128
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
129
|
+
import { timingSafeEqual } from "crypto";
|
|
130
|
+
var parseBasic = /* @__PURE__ */ __name((header) => {
|
|
131
|
+
if (!header || typeof header !== "string") {
|
|
132
|
+
return void 0;
|
|
133
|
+
}
|
|
134
|
+
const spaceIndex = header.indexOf(" ");
|
|
135
|
+
if (spaceIndex === -1) {
|
|
136
|
+
return void 0;
|
|
137
|
+
}
|
|
138
|
+
const scheme = header.slice(0, spaceIndex);
|
|
139
|
+
const value = header.slice(spaceIndex + 1);
|
|
140
|
+
if (scheme.toLowerCase() !== "basic" || !value) {
|
|
141
|
+
return void 0;
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const decoded = Buffer2.from(value, "base64").toString("utf8");
|
|
145
|
+
const idx = decoded.indexOf(":");
|
|
146
|
+
if (idx === -1) {
|
|
147
|
+
return void 0;
|
|
148
|
+
}
|
|
149
|
+
const username = decoded.slice(0, idx);
|
|
150
|
+
const password = decoded.slice(idx + 1);
|
|
151
|
+
if (username === "") {
|
|
152
|
+
return void 0;
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
username,
|
|
156
|
+
password
|
|
157
|
+
};
|
|
158
|
+
} catch {
|
|
159
|
+
return void 0;
|
|
160
|
+
}
|
|
161
|
+
}, "parseBasic");
|
|
162
|
+
var safeCompare = /* @__PURE__ */ __name((a, b) => {
|
|
163
|
+
if (a.length !== b.length) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
return timingSafeEqual(Buffer2.from(a, "utf8"), Buffer2.from(b, "utf8"));
|
|
168
|
+
} catch {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}, "safeCompare");
|
|
172
|
+
var basicAuthSchema = {
|
|
173
|
+
description: "HTTP Basic authentication. Succeeds only if the user/pass provided in the path matches the Basic Authorization header.",
|
|
384
174
|
tags: [
|
|
385
|
-
"
|
|
175
|
+
"Auth"
|
|
386
176
|
],
|
|
387
|
-
|
|
177
|
+
params: {
|
|
388
178
|
type: "object",
|
|
389
|
-
|
|
390
|
-
|
|
179
|
+
properties: {
|
|
180
|
+
user: {
|
|
181
|
+
type: "string"
|
|
182
|
+
},
|
|
183
|
+
passwd: {
|
|
184
|
+
type: "string"
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
required: [
|
|
188
|
+
"user",
|
|
189
|
+
"passwd"
|
|
190
|
+
]
|
|
391
191
|
},
|
|
392
192
|
response: {
|
|
393
193
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
394
194
|
200: {
|
|
395
195
|
type: "object",
|
|
396
196
|
properties: {
|
|
397
|
-
|
|
398
|
-
type: "
|
|
197
|
+
authenticated: {
|
|
198
|
+
type: "boolean"
|
|
399
199
|
},
|
|
400
|
-
|
|
401
|
-
type: "
|
|
402
|
-
additionalProperties: {
|
|
403
|
-
type: "string"
|
|
404
|
-
}
|
|
405
|
-
},
|
|
406
|
-
body: {
|
|
407
|
-
type: "object",
|
|
408
|
-
additionalProperties: true
|
|
200
|
+
user: {
|
|
201
|
+
type: "string"
|
|
409
202
|
}
|
|
410
203
|
},
|
|
411
204
|
required: [
|
|
412
|
-
"
|
|
413
|
-
"
|
|
414
|
-
"body"
|
|
205
|
+
"authenticated",
|
|
206
|
+
"user"
|
|
415
207
|
]
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
};
|
|
419
|
-
var deleteRoute = /* @__PURE__ */ __name((fastify) => {
|
|
420
|
-
fastify.delete("/delete", {
|
|
421
|
-
schema: deleteSchema
|
|
422
|
-
}, async (request) => ({
|
|
423
|
-
method: "DELETE",
|
|
424
|
-
headers: request.headers,
|
|
425
|
-
body: request.body
|
|
426
|
-
}));
|
|
427
|
-
}, "deleteRoute");
|
|
428
|
-
|
|
429
|
-
// src/routes/http-methods/put.ts
|
|
430
|
-
var putSchema = {
|
|
431
|
-
description: "Handles a PUT request and returns request information",
|
|
432
|
-
tags: [
|
|
433
|
-
"HTTP Methods"
|
|
434
|
-
],
|
|
435
|
-
body: {
|
|
436
|
-
type: "object",
|
|
437
|
-
additionalProperties: true,
|
|
438
|
-
description: "The body of the PUT request"
|
|
439
|
-
},
|
|
440
|
-
response: {
|
|
208
|
+
},
|
|
441
209
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
442
|
-
|
|
210
|
+
401: {
|
|
443
211
|
type: "object",
|
|
444
212
|
properties: {
|
|
445
|
-
|
|
213
|
+
message: {
|
|
446
214
|
type: "string"
|
|
447
|
-
},
|
|
448
|
-
headers: {
|
|
449
|
-
type: "object",
|
|
450
|
-
additionalProperties: {
|
|
451
|
-
type: "string"
|
|
452
|
-
}
|
|
453
|
-
},
|
|
454
|
-
body: {
|
|
455
|
-
type: "object",
|
|
456
|
-
additionalProperties: true
|
|
457
215
|
}
|
|
458
216
|
},
|
|
459
217
|
required: [
|
|
460
|
-
"
|
|
461
|
-
"headers",
|
|
462
|
-
"body"
|
|
218
|
+
"message"
|
|
463
219
|
]
|
|
464
220
|
}
|
|
465
221
|
}
|
|
466
222
|
};
|
|
467
|
-
var
|
|
468
|
-
fastify.
|
|
469
|
-
schema:
|
|
470
|
-
}, async (request) =>
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
223
|
+
var basicAuthRoute = /* @__PURE__ */ __name((fastify) => {
|
|
224
|
+
fastify.get("/basic-auth/:user/:passwd", {
|
|
225
|
+
schema: basicAuthSchema
|
|
226
|
+
}, async (request, reply) => {
|
|
227
|
+
const { user, passwd } = request.params;
|
|
228
|
+
if (!user || typeof user !== "string" || typeof passwd !== "string") {
|
|
229
|
+
void reply.header("WWW-Authenticate", 'Basic realm="mockhttp"');
|
|
230
|
+
return reply.status(401).send({
|
|
231
|
+
message: "Unauthorized"
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
const parsed = parseBasic(request.headers.authorization);
|
|
235
|
+
if (!parsed || !safeCompare(parsed.username, user) || !safeCompare(parsed.password, passwd)) {
|
|
236
|
+
void reply.header("WWW-Authenticate", 'Basic realm="mockhttp"');
|
|
237
|
+
return reply.status(401).send({
|
|
238
|
+
message: "Unauthorized"
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
return reply.send({
|
|
242
|
+
authenticated: true,
|
|
243
|
+
user
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
}, "basicAuthRoute");
|
|
476
247
|
|
|
477
|
-
// src/routes/
|
|
478
|
-
var
|
|
479
|
-
|
|
248
|
+
// src/routes/auth/bearer.ts
|
|
249
|
+
var parseBearer = /* @__PURE__ */ __name((header) => {
|
|
250
|
+
if (!header) {
|
|
251
|
+
return void 0;
|
|
252
|
+
}
|
|
253
|
+
const [scheme, token] = header.split(" ");
|
|
254
|
+
if (!scheme || scheme.toLowerCase() !== "bearer" || !token) {
|
|
255
|
+
return void 0;
|
|
256
|
+
}
|
|
257
|
+
return token;
|
|
258
|
+
}, "parseBearer");
|
|
259
|
+
var bearerAuthSchema = {
|
|
260
|
+
description: "HTTP Bearer authentication. Succeeds if any Bearer token is present unless ?required=true is provided.",
|
|
480
261
|
tags: [
|
|
481
|
-
"
|
|
262
|
+
"Auth"
|
|
482
263
|
],
|
|
483
|
-
|
|
264
|
+
querystring: {
|
|
484
265
|
type: "object",
|
|
485
|
-
|
|
486
|
-
|
|
266
|
+
properties: {
|
|
267
|
+
required: {
|
|
268
|
+
type: "boolean",
|
|
269
|
+
default: true
|
|
270
|
+
}
|
|
271
|
+
}
|
|
487
272
|
},
|
|
488
273
|
response: {
|
|
489
274
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
490
275
|
200: {
|
|
491
276
|
type: "object",
|
|
492
277
|
properties: {
|
|
493
|
-
|
|
494
|
-
type: "
|
|
495
|
-
},
|
|
496
|
-
headers: {
|
|
497
|
-
type: "object",
|
|
498
|
-
additionalProperties: {
|
|
499
|
-
type: "string"
|
|
500
|
-
}
|
|
278
|
+
authenticated: {
|
|
279
|
+
type: "boolean"
|
|
501
280
|
},
|
|
502
|
-
|
|
503
|
-
type: "
|
|
504
|
-
additionalProperties: true
|
|
281
|
+
token: {
|
|
282
|
+
type: "string"
|
|
505
283
|
}
|
|
506
284
|
},
|
|
507
285
|
required: [
|
|
508
|
-
"
|
|
509
|
-
"
|
|
510
|
-
|
|
286
|
+
"authenticated",
|
|
287
|
+
"token"
|
|
288
|
+
]
|
|
289
|
+
},
|
|
290
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
291
|
+
401: {
|
|
292
|
+
type: "object",
|
|
293
|
+
properties: {
|
|
294
|
+
message: {
|
|
295
|
+
type: "string"
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
required: [
|
|
299
|
+
"message"
|
|
511
300
|
]
|
|
512
301
|
}
|
|
513
302
|
}
|
|
514
303
|
};
|
|
515
|
-
var
|
|
516
|
-
fastify.
|
|
517
|
-
schema:
|
|
518
|
-
}, async (request) =>
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
304
|
+
var bearerAuthRoute = /* @__PURE__ */ __name((fastify) => {
|
|
305
|
+
fastify.get("/bearer", {
|
|
306
|
+
schema: bearerAuthSchema
|
|
307
|
+
}, async (request, reply) => {
|
|
308
|
+
const token = parseBearer(request.headers.authorization);
|
|
309
|
+
const qs = request.query;
|
|
310
|
+
const required = qs?.required ?? true;
|
|
311
|
+
if (!token && required) {
|
|
312
|
+
void reply.header("WWW-Authenticate", "Bearer");
|
|
313
|
+
return reply.status(401).send({
|
|
314
|
+
message: "Unauthorized"
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
if (!token) {
|
|
318
|
+
return reply.send({
|
|
319
|
+
authenticated: false,
|
|
320
|
+
token: ""
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
return reply.send({
|
|
324
|
+
authenticated: true,
|
|
325
|
+
token
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
}, "bearerAuthRoute");
|
|
524
329
|
|
|
525
|
-
// src/routes/
|
|
526
|
-
import
|
|
527
|
-
var
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
}
|
|
544
|
-
{
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
330
|
+
// src/routes/auth/digest.ts
|
|
331
|
+
import crypto from "crypto";
|
|
332
|
+
var makeNonce = /* @__PURE__ */ __name(() => crypto.randomBytes(16).toString("hex"), "makeNonce");
|
|
333
|
+
var algorithms = /* @__PURE__ */ new Set([
|
|
334
|
+
"MD5",
|
|
335
|
+
"md5",
|
|
336
|
+
"SHA-256",
|
|
337
|
+
"sha-256",
|
|
338
|
+
"SHA-512",
|
|
339
|
+
"sha-512"
|
|
340
|
+
]);
|
|
341
|
+
var hashAlg = /* @__PURE__ */ __name((alg) => {
|
|
342
|
+
if (!alg) {
|
|
343
|
+
return "md5";
|
|
344
|
+
}
|
|
345
|
+
const a = alg.toLowerCase();
|
|
346
|
+
if (a === "md5") {
|
|
347
|
+
return "md5";
|
|
348
|
+
}
|
|
349
|
+
if (a === "sha-256") {
|
|
350
|
+
return "sha256";
|
|
351
|
+
}
|
|
352
|
+
if (a === "sha-512") {
|
|
353
|
+
return "sha512";
|
|
354
|
+
}
|
|
355
|
+
return "md5";
|
|
356
|
+
}, "hashAlg");
|
|
357
|
+
var h = /* @__PURE__ */ __name((algo, data) => crypto.createHash(algo).update(data).digest("hex"), "h");
|
|
358
|
+
var parseDigest = /* @__PURE__ */ __name((header) => {
|
|
359
|
+
if (!header) {
|
|
360
|
+
return void 0;
|
|
361
|
+
}
|
|
362
|
+
const spaceIndex = header.indexOf(" ");
|
|
363
|
+
if (spaceIndex === -1) {
|
|
364
|
+
return void 0;
|
|
365
|
+
}
|
|
366
|
+
const scheme = header.slice(0, spaceIndex);
|
|
367
|
+
const rest = header.slice(spaceIndex + 1);
|
|
368
|
+
if (!scheme || scheme.toLowerCase() !== "digest" || !rest) {
|
|
369
|
+
return void 0;
|
|
370
|
+
}
|
|
371
|
+
const out = {};
|
|
372
|
+
const parts = [];
|
|
373
|
+
let current = "";
|
|
374
|
+
let inQuotes = false;
|
|
375
|
+
for (const char of rest) {
|
|
376
|
+
if (char === '"') {
|
|
377
|
+
inQuotes = !inQuotes;
|
|
378
|
+
current += char;
|
|
379
|
+
} else if (char === "," && !inQuotes) {
|
|
380
|
+
if (current.trim()) {
|
|
381
|
+
parts.push(current.trim());
|
|
382
|
+
}
|
|
383
|
+
current = "";
|
|
384
|
+
} else {
|
|
385
|
+
current += char;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
if (current.trim()) {
|
|
389
|
+
parts.push(current.trim());
|
|
390
|
+
}
|
|
391
|
+
if (parts.length === 0) {
|
|
392
|
+
return out;
|
|
393
|
+
}
|
|
394
|
+
for (const p of parts) {
|
|
395
|
+
const equalIndex = p.indexOf("=");
|
|
396
|
+
if (equalIndex === -1) {
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
const k = p.slice(0, equalIndex).trim();
|
|
400
|
+
const v = p.slice(equalIndex + 1).trim();
|
|
401
|
+
let value = v;
|
|
402
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
403
|
+
value = value.slice(1, -1);
|
|
404
|
+
}
|
|
405
|
+
out[k] = value;
|
|
406
|
+
}
|
|
407
|
+
return out;
|
|
408
|
+
}, "parseDigest");
|
|
409
|
+
var digestAuthSchema = {
|
|
410
|
+
description: "HTTP Digest authentication. Mirrors httpbin behavior for testing clients.",
|
|
411
|
+
tags: [
|
|
412
|
+
"Auth"
|
|
413
|
+
],
|
|
414
|
+
params: {
|
|
415
|
+
type: "object",
|
|
416
|
+
properties: {
|
|
417
|
+
qop: {
|
|
418
|
+
type: "string"
|
|
419
|
+
},
|
|
420
|
+
user: {
|
|
421
|
+
type: "string"
|
|
422
|
+
},
|
|
423
|
+
passwd: {
|
|
424
|
+
type: "string"
|
|
425
|
+
},
|
|
426
|
+
algorithm: {
|
|
427
|
+
type: "string"
|
|
428
|
+
},
|
|
429
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
430
|
+
stale_after: {
|
|
431
|
+
type: "string"
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
required: [
|
|
435
|
+
"qop",
|
|
436
|
+
"user",
|
|
437
|
+
"passwd"
|
|
438
|
+
]
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
var digestAuthRoute = /* @__PURE__ */ __name((fastify) => {
|
|
442
|
+
fastify.get("/digest-auth/:qop/:user/:passwd", {
|
|
443
|
+
schema: digestAuthSchema
|
|
444
|
+
}, async (request, reply) => {
|
|
445
|
+
const { user, passwd } = request.params;
|
|
446
|
+
const realm = "mockhttp";
|
|
447
|
+
const nonce = makeNonce();
|
|
448
|
+
const auth = parseDigest(request.headers.authorization);
|
|
449
|
+
if (!auth) {
|
|
450
|
+
void reply.header("WWW-Authenticate", `Digest realm="${realm}", qop="auth,auth-int", nonce="${nonce}", opaque="${makeNonce()}"`);
|
|
451
|
+
return reply.status(401).send({
|
|
452
|
+
message: "Unauthorized"
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
if (auth.username !== user) {
|
|
456
|
+
void reply.header("WWW-Authenticate", `Digest realm="${realm}", qop="auth,auth-int", nonce="${nonce}", opaque="${makeNonce()}"`);
|
|
457
|
+
return reply.status(401).send({
|
|
458
|
+
message: "Unauthorized"
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
const method = "GET";
|
|
462
|
+
const uri = auth.uri ?? request.url.replace(/^https?:\/\/[^/]+/, "");
|
|
463
|
+
const algo = hashAlg(auth.algorithm);
|
|
464
|
+
const ha1 = h(algo, `${user}:${realm}:${passwd}`);
|
|
465
|
+
const ha2 = h(algo, `${method}:${uri}`);
|
|
466
|
+
let expected = "";
|
|
467
|
+
expected = auth.qop ? h(algo, `${ha1}:${auth.nonce}:${auth.nc}:${auth.cnonce}:${auth.qop}:${ha2}`) : h(algo, `${ha1}:${auth.nonce}:${ha2}`);
|
|
468
|
+
if (!auth.response || auth.response !== expected) {
|
|
469
|
+
void reply.header("WWW-Authenticate", `Digest realm="${realm}", qop="auth,auth-int", nonce="${nonce}", opaque="${makeNonce()}"`);
|
|
470
|
+
return reply.status(401).send({
|
|
471
|
+
message: "Unauthorized"
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
return reply.send({
|
|
475
|
+
authenticated: true,
|
|
476
|
+
user
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
fastify.get("/digest-auth/:qop/:user/:passwd/:algorithm", {
|
|
480
|
+
schema: digestAuthSchema
|
|
481
|
+
}, async (request, reply) => {
|
|
482
|
+
const { user, passwd, algorithm } = request.params;
|
|
483
|
+
const realm = "mockhttp";
|
|
484
|
+
const algoHeader = algorithms.has(String(algorithm)) ? String(algorithm) : "MD5";
|
|
485
|
+
const nonce = makeNonce();
|
|
486
|
+
const auth = parseDigest(request.headers.authorization);
|
|
487
|
+
if (!auth) {
|
|
488
|
+
void reply.header("WWW-Authenticate", `Digest realm="${realm}", qop="auth,auth-int", algorithm=${algoHeader}, nonce="${nonce}", opaque="${makeNonce()}"`);
|
|
489
|
+
return reply.status(401).send({
|
|
490
|
+
message: "Unauthorized"
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
if (auth.username !== user) {
|
|
494
|
+
void reply.header("WWW-Authenticate", `Digest realm="${realm}", qop="auth,auth-int", algorithm=${algoHeader}, nonce="${nonce}", opaque="${makeNonce()}"`);
|
|
495
|
+
return reply.status(401).send({
|
|
496
|
+
message: "Unauthorized"
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
const method = "GET";
|
|
500
|
+
const uri = auth.uri ?? request.url.replace(/^https?:\/\/[^/]+/, "");
|
|
501
|
+
const algo = hashAlg(auth.algorithm ?? algoHeader);
|
|
502
|
+
const ha1 = h(algo, `${user}:${realm}:${passwd}`);
|
|
503
|
+
const ha2 = h(algo, `${method}:${uri}`);
|
|
504
|
+
const expected = auth.qop ? h(algo, `${ha1}:${auth.nonce}:${auth.nc}:${auth.cnonce}:${auth.qop}:${ha2}`) : h(algo, `${ha1}:${auth.nonce}:${ha2}`);
|
|
505
|
+
if (!auth.response || auth.response !== expected) {
|
|
506
|
+
void reply.header("WWW-Authenticate", `Digest realm="${realm}", qop="auth,auth-int", algorithm=${algoHeader}, nonce="${nonce}", opaque="${makeNonce()}"`);
|
|
507
|
+
return reply.status(401).send({
|
|
508
|
+
message: "Unauthorized"
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
return reply.send({
|
|
512
|
+
authenticated: true,
|
|
513
|
+
user
|
|
514
|
+
});
|
|
515
|
+
});
|
|
516
|
+
}, "digestAuthRoute");
|
|
517
|
+
|
|
518
|
+
// src/routes/auth/hidden-basic.ts
|
|
519
|
+
import { Buffer as Buffer3 } from "buffer";
|
|
520
|
+
var parseBasic2 = /* @__PURE__ */ __name((header) => {
|
|
521
|
+
if (!header) {
|
|
522
|
+
return void 0;
|
|
523
|
+
}
|
|
524
|
+
const [scheme, value] = header.split(" ");
|
|
525
|
+
if (!scheme || scheme.toLowerCase() !== "basic" || !value) {
|
|
526
|
+
return void 0;
|
|
527
|
+
}
|
|
528
|
+
try {
|
|
529
|
+
const decoded = Buffer3.from(value, "base64").toString("utf8");
|
|
530
|
+
const idx = decoded.indexOf(":");
|
|
531
|
+
if (idx === -1) {
|
|
532
|
+
return void 0;
|
|
533
|
+
}
|
|
534
|
+
return {
|
|
535
|
+
username: decoded.slice(0, idx),
|
|
536
|
+
password: decoded.slice(idx + 1)
|
|
537
|
+
};
|
|
538
|
+
} catch {
|
|
539
|
+
return void 0;
|
|
540
|
+
}
|
|
541
|
+
}, "parseBasic");
|
|
542
|
+
var hiddenBasicAuthSchema = {
|
|
543
|
+
description: "HTTP Basic authentication with docs hidden. Mirrors /basic-auth but hides from schema.",
|
|
544
|
+
tags: [
|
|
545
|
+
"Auth"
|
|
546
|
+
],
|
|
547
|
+
hide: true,
|
|
548
|
+
params: {
|
|
549
|
+
type: "object",
|
|
550
|
+
properties: {
|
|
551
|
+
user: {
|
|
552
|
+
type: "string"
|
|
553
|
+
},
|
|
554
|
+
passwd: {
|
|
555
|
+
type: "string"
|
|
556
|
+
}
|
|
557
|
+
},
|
|
558
|
+
required: [
|
|
559
|
+
"user",
|
|
560
|
+
"passwd"
|
|
561
|
+
]
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
var hiddenBasicAuthRoute = /* @__PURE__ */ __name((fastify) => {
|
|
565
|
+
fastify.get("/hidden-basic-auth/:user/:passwd", {
|
|
566
|
+
schema: hiddenBasicAuthSchema
|
|
567
|
+
}, async (request, reply) => {
|
|
568
|
+
const { user, passwd } = request.params;
|
|
569
|
+
const parsed = parseBasic2(request.headers.authorization);
|
|
570
|
+
if (!parsed || parsed.username !== user || parsed.password !== passwd) {
|
|
571
|
+
void reply.header("WWW-Authenticate", 'Basic realm="mockhttp"');
|
|
572
|
+
return reply.status(401).send({
|
|
573
|
+
message: "Unauthorized"
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
return reply.send({
|
|
577
|
+
authenticated: true,
|
|
578
|
+
user
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
}, "hiddenBasicAuthRoute");
|
|
582
|
+
|
|
583
|
+
// src/routes/cookies/delete.ts
|
|
584
|
+
var cookiesDeleteSchema = {
|
|
585
|
+
description: "Delete a cookie",
|
|
586
|
+
tags: [
|
|
587
|
+
"Cookies"
|
|
588
|
+
],
|
|
589
|
+
querystring: {
|
|
590
|
+
type: "object",
|
|
591
|
+
properties: {
|
|
592
|
+
name: {
|
|
593
|
+
type: "string"
|
|
594
|
+
}
|
|
595
|
+
},
|
|
596
|
+
required: [
|
|
597
|
+
"name"
|
|
598
|
+
],
|
|
599
|
+
additionalProperties: false
|
|
571
600
|
},
|
|
572
|
-
{
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
{
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
{
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
{
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
},
|
|
652
|
-
{
|
|
653
|
-
code: 408,
|
|
654
|
-
message: "Request Timeout"
|
|
655
|
-
},
|
|
656
|
-
{
|
|
657
|
-
code: 409,
|
|
658
|
-
message: "Conflict"
|
|
659
|
-
},
|
|
660
|
-
{
|
|
661
|
-
code: 410,
|
|
662
|
-
message: "Gone"
|
|
663
|
-
},
|
|
664
|
-
{
|
|
665
|
-
code: 411,
|
|
666
|
-
message: "Length Required"
|
|
667
|
-
},
|
|
668
|
-
{
|
|
669
|
-
code: 412,
|
|
670
|
-
message: "Precondition Failed"
|
|
671
|
-
},
|
|
672
|
-
{
|
|
673
|
-
code: 413,
|
|
674
|
-
message: "Payload Too Large"
|
|
675
|
-
},
|
|
676
|
-
{
|
|
677
|
-
code: 414,
|
|
678
|
-
message: "URI Too Long"
|
|
679
|
-
},
|
|
680
|
-
{
|
|
681
|
-
code: 415,
|
|
682
|
-
message: "Unsupported Media Type"
|
|
683
|
-
},
|
|
684
|
-
{
|
|
685
|
-
code: 416,
|
|
686
|
-
message: "Range Not Satisfiable"
|
|
687
|
-
},
|
|
688
|
-
{
|
|
689
|
-
code: 417,
|
|
690
|
-
message: "Expectation Failed"
|
|
691
|
-
},
|
|
692
|
-
{
|
|
693
|
-
code: 418,
|
|
694
|
-
message: "I'm a teapot"
|
|
695
|
-
},
|
|
696
|
-
{
|
|
697
|
-
code: 421,
|
|
698
|
-
message: "Misdirected Request"
|
|
699
|
-
},
|
|
700
|
-
{
|
|
701
|
-
code: 422,
|
|
702
|
-
message: "Unprocessable Entity"
|
|
703
|
-
},
|
|
704
|
-
{
|
|
705
|
-
code: 423,
|
|
706
|
-
message: "Locked"
|
|
707
|
-
},
|
|
708
|
-
{
|
|
709
|
-
code: 424,
|
|
710
|
-
message: "Failed Dependency"
|
|
711
|
-
},
|
|
712
|
-
{
|
|
713
|
-
code: 425,
|
|
714
|
-
message: "Too Early"
|
|
715
|
-
},
|
|
716
|
-
{
|
|
717
|
-
code: 426,
|
|
718
|
-
message: "Upgrade Required"
|
|
719
|
-
},
|
|
720
|
-
{
|
|
721
|
-
code: 428,
|
|
722
|
-
message: "Precondition Required"
|
|
723
|
-
},
|
|
724
|
-
{
|
|
725
|
-
code: 429,
|
|
726
|
-
message: "Too Many Requests"
|
|
727
|
-
},
|
|
728
|
-
{
|
|
729
|
-
code: 431,
|
|
730
|
-
message: "Request Header Fields Too Large"
|
|
731
|
-
},
|
|
732
|
-
{
|
|
733
|
-
code: 451,
|
|
734
|
-
message: "Unavailable For Legal Reasons"
|
|
735
|
-
},
|
|
736
|
-
{
|
|
737
|
-
code: 500,
|
|
738
|
-
message: "Internal Server Error"
|
|
739
|
-
},
|
|
740
|
-
{
|
|
741
|
-
code: 501,
|
|
742
|
-
message: "Not Implemented"
|
|
743
|
-
},
|
|
744
|
-
{
|
|
745
|
-
code: 502,
|
|
746
|
-
message: "Bad Gateway"
|
|
747
|
-
},
|
|
748
|
-
{
|
|
749
|
-
code: 503,
|
|
750
|
-
message: "Service Unavailable"
|
|
751
|
-
},
|
|
752
|
-
{
|
|
753
|
-
code: 504,
|
|
754
|
-
message: "Gateway Timeout"
|
|
755
|
-
},
|
|
756
|
-
{
|
|
757
|
-
code: 505,
|
|
758
|
-
message: "HTTP Version Not Supported"
|
|
759
|
-
},
|
|
760
|
-
{
|
|
761
|
-
code: 506,
|
|
762
|
-
message: "Variant Also Negotiates"
|
|
763
|
-
},
|
|
764
|
-
{
|
|
765
|
-
code: 507,
|
|
766
|
-
message: "Insufficient Storage"
|
|
767
|
-
},
|
|
768
|
-
{
|
|
769
|
-
code: 508,
|
|
770
|
-
message: "Loop Detected"
|
|
771
|
-
},
|
|
772
|
-
{
|
|
773
|
-
code: 510,
|
|
774
|
-
message: "Not Extended"
|
|
775
|
-
},
|
|
776
|
-
{
|
|
777
|
-
code: 511,
|
|
778
|
-
message: "Network Authentication Required"
|
|
779
|
-
}
|
|
780
|
-
];
|
|
781
|
-
var statusSchema = {
|
|
782
|
-
description: "Return status code or random status code if more than one are given ",
|
|
783
|
-
tags: [
|
|
784
|
-
"Status Codes"
|
|
785
|
-
],
|
|
786
|
-
params: {
|
|
787
|
-
type: "object",
|
|
788
|
-
properties: {
|
|
789
|
-
code: {
|
|
790
|
-
type: "string",
|
|
791
|
-
description: "HTTP status code to be returned"
|
|
792
|
-
}
|
|
793
|
-
}
|
|
601
|
+
response: {
|
|
602
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
603
|
+
204: {
|
|
604
|
+
type: "null"
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
var deleteCookieRoute = /* @__PURE__ */ __name((fastify) => {
|
|
609
|
+
fastify.delete("/cookies", {
|
|
610
|
+
schema: cookiesDeleteSchema
|
|
611
|
+
}, async (request, reply) => {
|
|
612
|
+
const { name } = request.query;
|
|
613
|
+
reply.clearCookie(name, {
|
|
614
|
+
path: "/"
|
|
615
|
+
});
|
|
616
|
+
return reply.status(204).send();
|
|
617
|
+
});
|
|
618
|
+
}, "deleteCookieRoute");
|
|
619
|
+
|
|
620
|
+
// src/routes/cookies/get.ts
|
|
621
|
+
var getCookiesRouteSchema = {
|
|
622
|
+
description: "Return cookies from the request",
|
|
623
|
+
tags: [
|
|
624
|
+
"Cookies"
|
|
625
|
+
],
|
|
626
|
+
response: {
|
|
627
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
628
|
+
200: {
|
|
629
|
+
type: "object",
|
|
630
|
+
properties: {
|
|
631
|
+
cookies: {
|
|
632
|
+
type: "object",
|
|
633
|
+
additionalProperties: {
|
|
634
|
+
type: "string"
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
},
|
|
638
|
+
required: [
|
|
639
|
+
"cookies"
|
|
640
|
+
]
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
var getCookiesRoute = /* @__PURE__ */ __name((fastify) => {
|
|
645
|
+
fastify.get("/cookies", {
|
|
646
|
+
schema: getCookiesRouteSchema
|
|
647
|
+
}, async (request, reply) => {
|
|
648
|
+
reply.type("application/json");
|
|
649
|
+
await reply.send({
|
|
650
|
+
cookies: request.cookies
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
}, "getCookiesRoute");
|
|
654
|
+
|
|
655
|
+
// src/routes/cookies/post.ts
|
|
656
|
+
var postCookieRouteSchema = {
|
|
657
|
+
description: "Set a cookie",
|
|
658
|
+
tags: [
|
|
659
|
+
"Cookies"
|
|
660
|
+
],
|
|
661
|
+
body: {
|
|
662
|
+
type: "object",
|
|
663
|
+
properties: {
|
|
664
|
+
name: {
|
|
665
|
+
type: "string"
|
|
666
|
+
},
|
|
667
|
+
value: {
|
|
668
|
+
type: "string"
|
|
669
|
+
},
|
|
670
|
+
expires: {
|
|
671
|
+
type: "string",
|
|
672
|
+
format: "date-time"
|
|
673
|
+
}
|
|
674
|
+
},
|
|
675
|
+
required: [
|
|
676
|
+
"name",
|
|
677
|
+
"value"
|
|
678
|
+
],
|
|
679
|
+
additionalProperties: false
|
|
794
680
|
},
|
|
795
681
|
response: {
|
|
796
682
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
797
683
|
200: {
|
|
798
684
|
type: "object",
|
|
799
685
|
properties: {
|
|
800
|
-
|
|
801
|
-
type: "
|
|
686
|
+
cookies: {
|
|
687
|
+
type: "object",
|
|
688
|
+
additionalProperties: {
|
|
689
|
+
type: "string"
|
|
690
|
+
}
|
|
802
691
|
}
|
|
803
|
-
}
|
|
692
|
+
},
|
|
693
|
+
required: [
|
|
694
|
+
"cookies"
|
|
695
|
+
]
|
|
804
696
|
}
|
|
805
697
|
}
|
|
806
698
|
};
|
|
807
|
-
var
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
699
|
+
var postCookieRoute = /* @__PURE__ */ __name((fastify) => {
|
|
700
|
+
fastify.post("/cookies", {
|
|
701
|
+
schema: postCookieRouteSchema
|
|
702
|
+
}, async (request, reply) => {
|
|
703
|
+
const { name, value, expires } = request.body;
|
|
704
|
+
if (expires) {
|
|
705
|
+
const date = new Date(expires);
|
|
706
|
+
reply.setCookie(name, value, {
|
|
707
|
+
path: "/",
|
|
708
|
+
expires: date
|
|
709
|
+
});
|
|
710
|
+
} else {
|
|
711
|
+
reply.setCookie(name, value, {
|
|
712
|
+
path: "/"
|
|
713
|
+
});
|
|
812
714
|
}
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
}, "handler");
|
|
817
|
-
fastify.get("/status/:code?", {
|
|
818
|
-
schema: statusSchema
|
|
819
|
-
}, handler);
|
|
820
|
-
fastify.post("/status/:code?", {
|
|
821
|
-
schema: statusSchema
|
|
822
|
-
}, handler);
|
|
823
|
-
fastify.put("/status/:code?", {
|
|
824
|
-
schema: statusSchema
|
|
825
|
-
}, handler);
|
|
826
|
-
fastify.patch("/status/:code?", {
|
|
827
|
-
schema: statusSchema
|
|
828
|
-
}, handler);
|
|
829
|
-
fastify.delete("/status/:code?", {
|
|
830
|
-
schema: statusSchema
|
|
831
|
-
}, handler);
|
|
832
|
-
}, "statusCodeRoute");
|
|
715
|
+
return reply.status(200).send();
|
|
716
|
+
});
|
|
717
|
+
}, "postCookieRoute");
|
|
833
718
|
|
|
834
|
-
// src/routes/
|
|
835
|
-
var
|
|
836
|
-
description: "
|
|
719
|
+
// src/routes/http-methods/delete.ts
|
|
720
|
+
var deleteSchema = {
|
|
721
|
+
description: "Handles a DELETE request and returns request information",
|
|
837
722
|
tags: [
|
|
838
|
-
"
|
|
723
|
+
"HTTP Methods"
|
|
839
724
|
],
|
|
725
|
+
body: {
|
|
726
|
+
type: "object",
|
|
727
|
+
additionalProperties: true,
|
|
728
|
+
description: "The body of the DELETE request"
|
|
729
|
+
},
|
|
840
730
|
response: {
|
|
841
731
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
842
732
|
200: {
|
|
843
733
|
type: "object",
|
|
844
734
|
properties: {
|
|
845
|
-
|
|
846
|
-
type: "string"
|
|
847
|
-
|
|
735
|
+
method: {
|
|
736
|
+
type: "string"
|
|
737
|
+
},
|
|
738
|
+
headers: {
|
|
739
|
+
type: "object",
|
|
740
|
+
additionalProperties: {
|
|
741
|
+
type: "string"
|
|
742
|
+
}
|
|
743
|
+
},
|
|
744
|
+
body: {
|
|
745
|
+
type: "object",
|
|
746
|
+
additionalProperties: true
|
|
848
747
|
}
|
|
849
748
|
},
|
|
850
749
|
required: [
|
|
851
|
-
"
|
|
750
|
+
"method",
|
|
751
|
+
"headers",
|
|
752
|
+
"body"
|
|
852
753
|
]
|
|
853
754
|
}
|
|
854
755
|
}
|
|
855
756
|
};
|
|
856
|
-
var
|
|
857
|
-
fastify.
|
|
858
|
-
schema:
|
|
859
|
-
}, async (request) => {
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
ip
|
|
866
|
-
};
|
|
867
|
-
});
|
|
868
|
-
}, "ipRoute");
|
|
757
|
+
var deleteRoute = /* @__PURE__ */ __name((fastify) => {
|
|
758
|
+
fastify.delete("/delete", {
|
|
759
|
+
schema: deleteSchema
|
|
760
|
+
}, async (request) => ({
|
|
761
|
+
method: "DELETE",
|
|
762
|
+
headers: request.headers,
|
|
763
|
+
body: request.body
|
|
764
|
+
}));
|
|
765
|
+
}, "deleteRoute");
|
|
869
766
|
|
|
870
|
-
// src/routes/
|
|
871
|
-
var
|
|
872
|
-
description: "Returns
|
|
767
|
+
// src/routes/http-methods/get.ts
|
|
768
|
+
var getSchema = {
|
|
769
|
+
description: "Returns request information for GET requests",
|
|
873
770
|
tags: [
|
|
874
|
-
"
|
|
771
|
+
"HTTP Methods"
|
|
875
772
|
],
|
|
773
|
+
querystring: {
|
|
774
|
+
type: "object",
|
|
775
|
+
additionalProperties: true,
|
|
776
|
+
properties: {},
|
|
777
|
+
description: "Query parameters sent in the request"
|
|
778
|
+
},
|
|
779
|
+
headers: {
|
|
780
|
+
type: "object",
|
|
781
|
+
additionalProperties: true,
|
|
782
|
+
properties: {},
|
|
783
|
+
description: "Headers sent in the request"
|
|
784
|
+
},
|
|
876
785
|
response: {
|
|
877
786
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
878
787
|
200: {
|
|
879
788
|
type: "object",
|
|
880
789
|
properties: {
|
|
790
|
+
method: {
|
|
791
|
+
type: "string"
|
|
792
|
+
},
|
|
881
793
|
headers: {
|
|
882
794
|
type: "object",
|
|
883
795
|
additionalProperties: {
|
|
884
796
|
type: "string"
|
|
885
|
-
}
|
|
886
|
-
|
|
797
|
+
}
|
|
798
|
+
},
|
|
799
|
+
queryParams: {
|
|
800
|
+
type: "object",
|
|
801
|
+
additionalProperties: {
|
|
802
|
+
type: "string"
|
|
803
|
+
}
|
|
887
804
|
}
|
|
888
805
|
},
|
|
889
806
|
required: [
|
|
890
|
-
"
|
|
807
|
+
"method",
|
|
808
|
+
"headers",
|
|
809
|
+
"queryParams"
|
|
891
810
|
]
|
|
892
811
|
}
|
|
893
812
|
}
|
|
894
813
|
};
|
|
895
|
-
var
|
|
896
|
-
fastify.get("/
|
|
897
|
-
schema:
|
|
898
|
-
}, async (request) => {
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
}, "headersRoute");
|
|
814
|
+
var getRoute = /* @__PURE__ */ __name((fastify) => {
|
|
815
|
+
fastify.get("/get", {
|
|
816
|
+
schema: getSchema
|
|
817
|
+
}, async (request, _reply) => ({
|
|
818
|
+
method: "GET",
|
|
819
|
+
headers: request.headers,
|
|
820
|
+
queryParams: request.query
|
|
821
|
+
}));
|
|
822
|
+
}, "getRoute");
|
|
905
823
|
|
|
906
|
-
// src/routes/
|
|
907
|
-
var
|
|
908
|
-
description: "
|
|
824
|
+
// src/routes/http-methods/patch.ts
|
|
825
|
+
var patchSchema = {
|
|
826
|
+
description: "Handles a PATCH request and returns request information",
|
|
909
827
|
tags: [
|
|
910
|
-
"
|
|
828
|
+
"HTTP Methods"
|
|
911
829
|
],
|
|
830
|
+
body: {
|
|
831
|
+
type: "object",
|
|
832
|
+
additionalProperties: true,
|
|
833
|
+
description: "The body of the PATCH request"
|
|
834
|
+
},
|
|
912
835
|
response: {
|
|
913
836
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
914
837
|
200: {
|
|
915
838
|
type: "object",
|
|
916
839
|
properties: {
|
|
917
|
-
|
|
918
|
-
type: "string"
|
|
919
|
-
|
|
840
|
+
method: {
|
|
841
|
+
type: "string"
|
|
842
|
+
},
|
|
843
|
+
headers: {
|
|
844
|
+
type: "object",
|
|
845
|
+
additionalProperties: {
|
|
846
|
+
type: "string"
|
|
847
|
+
}
|
|
848
|
+
},
|
|
849
|
+
body: {
|
|
850
|
+
type: "object",
|
|
851
|
+
additionalProperties: true
|
|
920
852
|
}
|
|
921
853
|
},
|
|
922
854
|
required: [
|
|
923
|
-
"
|
|
855
|
+
"method",
|
|
856
|
+
"headers",
|
|
857
|
+
"body"
|
|
924
858
|
]
|
|
925
859
|
}
|
|
926
860
|
}
|
|
927
861
|
};
|
|
928
|
-
var
|
|
929
|
-
fastify.
|
|
930
|
-
schema:
|
|
931
|
-
}, async (request) => {
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
}, "userAgentRoute");
|
|
862
|
+
var patchRoute = /* @__PURE__ */ __name((fastify) => {
|
|
863
|
+
fastify.patch("/patch", {
|
|
864
|
+
schema: patchSchema
|
|
865
|
+
}, async (request) => ({
|
|
866
|
+
method: "PATCH",
|
|
867
|
+
headers: request.headers,
|
|
868
|
+
body: request.body
|
|
869
|
+
}));
|
|
870
|
+
}, "patchRoute");
|
|
938
871
|
|
|
939
|
-
// src/routes/
|
|
940
|
-
var
|
|
941
|
-
description: "Handles
|
|
872
|
+
// src/routes/http-methods/post.ts
|
|
873
|
+
var postSchema = {
|
|
874
|
+
description: "Handles a POST request and returns request information",
|
|
942
875
|
tags: [
|
|
943
|
-
"
|
|
876
|
+
"HTTP Methods"
|
|
944
877
|
],
|
|
878
|
+
body: {
|
|
879
|
+
type: "object",
|
|
880
|
+
additionalProperties: true,
|
|
881
|
+
description: "The body of the POST request"
|
|
882
|
+
},
|
|
945
883
|
response: {
|
|
946
|
-
// eslint-disable-next-line
|
|
884
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
947
885
|
200: {
|
|
948
|
-
type: "
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
886
|
+
type: "object",
|
|
887
|
+
properties: {
|
|
888
|
+
method: {
|
|
889
|
+
type: "string"
|
|
890
|
+
},
|
|
891
|
+
headers: {
|
|
892
|
+
type: "object",
|
|
893
|
+
additionalProperties: {
|
|
894
|
+
type: "string"
|
|
895
|
+
}
|
|
896
|
+
},
|
|
897
|
+
body: {
|
|
898
|
+
type: "object",
|
|
899
|
+
additionalProperties: true
|
|
900
|
+
}
|
|
901
|
+
},
|
|
902
|
+
required: [
|
|
903
|
+
"method",
|
|
904
|
+
"headers",
|
|
905
|
+
"body"
|
|
906
|
+
]
|
|
955
907
|
}
|
|
956
908
|
}
|
|
957
909
|
};
|
|
958
|
-
var
|
|
959
|
-
|
|
910
|
+
var postRoute = /* @__PURE__ */ __name((fastify) => {
|
|
911
|
+
fastify.post("/post", {
|
|
912
|
+
schema: postSchema
|
|
913
|
+
}, async (request) => ({
|
|
914
|
+
method: "POST",
|
|
915
|
+
headers: request.headers,
|
|
916
|
+
body: request.body
|
|
917
|
+
}));
|
|
918
|
+
}, "postRoute");
|
|
919
|
+
|
|
920
|
+
// src/routes/http-methods/put.ts
|
|
921
|
+
var putSchema = {
|
|
922
|
+
description: "Handles a PUT request and returns request information",
|
|
960
923
|
tags: [
|
|
961
|
-
"
|
|
924
|
+
"HTTP Methods"
|
|
962
925
|
],
|
|
963
|
-
|
|
926
|
+
body: {
|
|
964
927
|
type: "object",
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
type: "integer",
|
|
968
|
-
description: "Number of seconds to cache the resource."
|
|
969
|
-
}
|
|
970
|
-
},
|
|
971
|
-
required: [
|
|
972
|
-
"value"
|
|
973
|
-
]
|
|
928
|
+
additionalProperties: true,
|
|
929
|
+
description: "The body of the PUT request"
|
|
974
930
|
},
|
|
975
931
|
response: {
|
|
976
|
-
// eslint-disable-next-line
|
|
977
|
-
200: {
|
|
978
|
-
type: "string",
|
|
979
|
-
description: "Confirmation message for setting cache."
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
};
|
|
983
|
-
var cacheRoutes = /* @__PURE__ */ __name((fastify) => {
|
|
984
|
-
fastify.get("/cache", {
|
|
985
|
-
schema: cacheGetSchema
|
|
986
|
-
}, async (request, reply) => {
|
|
987
|
-
const ifModifiedSince = request.headers["if-modified-since"];
|
|
988
|
-
const ifNoneMatch = request.headers["if-none-match"];
|
|
989
|
-
if (ifModifiedSince ?? ifNoneMatch) {
|
|
990
|
-
await reply.status(304).send();
|
|
991
|
-
return;
|
|
992
|
-
}
|
|
993
|
-
await reply.send("Cache content or resource response here.");
|
|
994
|
-
});
|
|
995
|
-
fastify.get("/cache/:value", {
|
|
996
|
-
schema: cacheSetSchema
|
|
997
|
-
}, async (request, reply) => {
|
|
998
|
-
const { value } = request.params;
|
|
999
|
-
await reply.header("Cache-Control", `max-age=${value}`).send(`Cache-Control set for ${value} seconds.`);
|
|
1000
|
-
});
|
|
1001
|
-
}, "cacheRoutes");
|
|
1002
|
-
|
|
1003
|
-
// src/routes/response-inspection/etag.ts
|
|
1004
|
-
var etagSchema = {
|
|
1005
|
-
description: "Handles ETag-based conditional requests.",
|
|
1006
|
-
tags: [
|
|
1007
|
-
"Response Inspection"
|
|
1008
|
-
],
|
|
1009
|
-
params: {
|
|
1010
|
-
type: "object",
|
|
1011
|
-
properties: {
|
|
1012
|
-
etag: {
|
|
1013
|
-
type: "string",
|
|
1014
|
-
description: "The ETag value for the resource."
|
|
1015
|
-
}
|
|
1016
|
-
},
|
|
1017
|
-
required: [
|
|
1018
|
-
"etag"
|
|
1019
|
-
]
|
|
1020
|
-
},
|
|
1021
|
-
response: {
|
|
1022
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1023
|
-
200: {
|
|
1024
|
-
type: "string",
|
|
1025
|
-
description: "Resource content for matching ETag."
|
|
1026
|
-
},
|
|
1027
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1028
|
-
304: {
|
|
1029
|
-
type: "null",
|
|
1030
|
-
description: "Indicates the resource has not been modified."
|
|
1031
|
-
},
|
|
1032
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1033
|
-
412: {
|
|
1034
|
-
type: "null",
|
|
1035
|
-
description: "Indicates a precondition failed due to mismatched ETag."
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
};
|
|
1039
|
-
var etagRoutes = /* @__PURE__ */ __name((fastify) => {
|
|
1040
|
-
fastify.get("/etag/:etag", {
|
|
1041
|
-
schema: etagSchema
|
|
1042
|
-
}, async (request, reply) => {
|
|
1043
|
-
const { etag } = request.params;
|
|
1044
|
-
const ifNoneMatch = request.headers["if-none-match"];
|
|
1045
|
-
const ifMatch = request.headers["if-match"];
|
|
1046
|
-
if (ifNoneMatch === etag) {
|
|
1047
|
-
await reply.status(304).send();
|
|
1048
|
-
return;
|
|
1049
|
-
}
|
|
1050
|
-
if (ifMatch && ifMatch !== etag) {
|
|
1051
|
-
await reply.status(412).send();
|
|
1052
|
-
return;
|
|
1053
|
-
}
|
|
1054
|
-
await reply.header("ETag", etag).send("Resource content for matching ETag.");
|
|
1055
|
-
});
|
|
1056
|
-
}, "etagRoutes");
|
|
1057
|
-
|
|
1058
|
-
// src/routes/response-inspection/response-headers.ts
|
|
1059
|
-
import { escape } from "html-escaper";
|
|
1060
|
-
var responseHeadersSchema = {
|
|
1061
|
-
description: "Returns a set of response headers based on the query string.",
|
|
1062
|
-
tags: [
|
|
1063
|
-
"Response Inspection"
|
|
1064
|
-
],
|
|
1065
|
-
querystring: {
|
|
1066
|
-
type: "object",
|
|
1067
|
-
additionalProperties: true,
|
|
1068
|
-
description: "Freeform query string parameters to be added as response headers."
|
|
1069
|
-
},
|
|
1070
|
-
response: {
|
|
1071
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
932
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1072
933
|
200: {
|
|
1073
934
|
type: "object",
|
|
1074
|
-
|
|
1075
|
-
|
|
935
|
+
properties: {
|
|
936
|
+
method: {
|
|
937
|
+
type: "string"
|
|
938
|
+
},
|
|
939
|
+
headers: {
|
|
940
|
+
type: "object",
|
|
941
|
+
additionalProperties: {
|
|
942
|
+
type: "string"
|
|
943
|
+
}
|
|
944
|
+
},
|
|
945
|
+
body: {
|
|
946
|
+
type: "object",
|
|
947
|
+
additionalProperties: true
|
|
948
|
+
}
|
|
949
|
+
},
|
|
950
|
+
required: [
|
|
951
|
+
"method",
|
|
952
|
+
"headers",
|
|
953
|
+
"body"
|
|
954
|
+
]
|
|
1076
955
|
}
|
|
1077
956
|
}
|
|
1078
957
|
};
|
|
1079
|
-
var
|
|
1080
|
-
fastify.
|
|
1081
|
-
schema:
|
|
1082
|
-
}, async (request
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
schema: responseHeadersSchema
|
|
1095
|
-
}, async (request, reply) => {
|
|
1096
|
-
const queryParameters = request.query ?? {};
|
|
1097
|
-
const body = request.body ?? {};
|
|
1098
|
-
for (const [key, value] of Object.entries(queryParameters)) {
|
|
1099
|
-
reply.header(key, escape(value));
|
|
1100
|
-
}
|
|
1101
|
-
for (const [key, value] of Object.entries(body)) {
|
|
1102
|
-
reply.header(key, escape(value));
|
|
958
|
+
var putRoute = /* @__PURE__ */ __name((fastify) => {
|
|
959
|
+
fastify.put("/put", {
|
|
960
|
+
schema: putSchema
|
|
961
|
+
}, async (request) => ({
|
|
962
|
+
method: "PUT",
|
|
963
|
+
headers: request.headers,
|
|
964
|
+
body: request.body
|
|
965
|
+
}));
|
|
966
|
+
}, "putRoute");
|
|
967
|
+
|
|
968
|
+
// src/routes/index.ts
|
|
969
|
+
var indexRoute = /* @__PURE__ */ __name((fastify) => {
|
|
970
|
+
fastify.get("/", {
|
|
971
|
+
schema: {
|
|
972
|
+
hide: true
|
|
1103
973
|
}
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
974
|
+
}, async (_request, reply) => {
|
|
975
|
+
const redocHtml = `
|
|
976
|
+
<!doctype html>
|
|
977
|
+
<html>
|
|
978
|
+
<head>
|
|
979
|
+
<title>{ mockhttp }</title>
|
|
980
|
+
<meta charset="utf-8" />
|
|
981
|
+
<meta
|
|
982
|
+
name="viewport"
|
|
983
|
+
content="width=device-width, initial-scale=1" />
|
|
984
|
+
<style>
|
|
985
|
+
:root {
|
|
986
|
+
--scalar-custom-header-height: 70px;
|
|
987
|
+
}
|
|
988
|
+
.custom-header {
|
|
989
|
+
height: var(--scalar-custom-header-height);
|
|
990
|
+
background-color: var(--scalar-background-1);
|
|
991
|
+
box-shadow: inset 0 -1px 0 var(--scalar-border-color);
|
|
992
|
+
color: var(--scalar-color-1);
|
|
993
|
+
font-size: var(--scalar-font-size-2);
|
|
994
|
+
padding: 0 18px;
|
|
995
|
+
position: sticky;
|
|
996
|
+
justify-content: space-between;
|
|
997
|
+
top: 0;
|
|
998
|
+
z-index: 100;
|
|
999
|
+
}
|
|
1000
|
+
.custom-header,
|
|
1001
|
+
.custom-header nav {
|
|
1002
|
+
display: flex;
|
|
1003
|
+
align-items: center;
|
|
1004
|
+
gap: 18px;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
.custom-header img {
|
|
1008
|
+
height: var(--scalar-custom-header-height);
|
|
1009
|
+
object-fit: contain;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
.custom-header a:hover {
|
|
1013
|
+
color: var(--scalar-color-2);
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
.github-corner {
|
|
1017
|
+
fill: var(--scalar-border-color) !important;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
</style>
|
|
1021
|
+
</head>
|
|
1022
|
+
<body>
|
|
1023
|
+
<header class="custom-header scalar-app">
|
|
1024
|
+
<a href="/"><img src="/logo.svg" alt="{ mockhttp }" /></a>
|
|
1025
|
+
<a href="https://github.com/jaredwray/mockhttp" class="github-corner" aria-label="View source on GitHub">
|
|
1026
|
+
<svg width="70" height="70" viewBox="0 0 250 250" style="color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true">
|
|
1027
|
+
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
|
|
1028
|
+
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
|
|
1029
|
+
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
|
|
1030
|
+
</header>
|
|
1031
|
+
|
|
1032
|
+
<script
|
|
1033
|
+
id="api-reference"
|
|
1034
|
+
data-url="/docs/json"></script>
|
|
1035
|
+
|
|
1036
|
+
<script src="/scalar/browser/standalone.js"></script>
|
|
1037
|
+
</body>
|
|
1038
|
+
</html>
|
|
1039
|
+
`;
|
|
1040
|
+
await reply.type("text/html").send(redocHtml);
|
|
1112
1041
|
});
|
|
1113
|
-
}, "
|
|
1042
|
+
}, "indexRoute");
|
|
1114
1043
|
|
|
1115
1044
|
// src/routes/redirects/absolute-redirect.ts
|
|
1116
1045
|
var absoluteRedirectSchema = {
|
|
@@ -1173,35 +1102,85 @@ var absoluteRedirectRoute = /* @__PURE__ */ __name((fastify) => {
|
|
|
1173
1102
|
});
|
|
1174
1103
|
}, "absoluteRedirectRoute");
|
|
1175
1104
|
|
|
1176
|
-
// src/routes/redirects/
|
|
1177
|
-
var
|
|
1105
|
+
// src/routes/redirects/redirect-to.ts
|
|
1106
|
+
var redirectToSchema = {
|
|
1178
1107
|
tags: [
|
|
1179
1108
|
"Redirects"
|
|
1180
1109
|
],
|
|
1181
|
-
description: "Redirects the request to the target URL
|
|
1182
|
-
|
|
1183
|
-
type: "object",
|
|
1184
|
-
properties: {
|
|
1185
|
-
value: {
|
|
1186
|
-
type: "integer",
|
|
1187
|
-
minimum: 1
|
|
1188
|
-
}
|
|
1189
|
-
},
|
|
1190
|
-
required: [
|
|
1191
|
-
"value"
|
|
1192
|
-
]
|
|
1193
|
-
},
|
|
1194
|
-
headers: {
|
|
1110
|
+
description: "Redirects the request to the target URL",
|
|
1111
|
+
querystring: {
|
|
1195
1112
|
type: "object",
|
|
1196
1113
|
properties: {
|
|
1197
|
-
|
|
1198
|
-
type: "string"
|
|
1114
|
+
url: {
|
|
1115
|
+
type: "string",
|
|
1116
|
+
format: "uri",
|
|
1117
|
+
description: "The URL to redirect to"
|
|
1118
|
+
},
|
|
1119
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1120
|
+
status_code: {
|
|
1121
|
+
type: "string",
|
|
1122
|
+
pattern: "^[1-5][0-9][0-9]$",
|
|
1123
|
+
description: "The HTTP status code to use for redirection"
|
|
1199
1124
|
}
|
|
1200
1125
|
},
|
|
1201
1126
|
required: [
|
|
1202
|
-
"
|
|
1203
|
-
]
|
|
1204
|
-
|
|
1127
|
+
"url"
|
|
1128
|
+
],
|
|
1129
|
+
additionalProperties: false
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
var redirectToRoute = /* @__PURE__ */ __name((fastify) => {
|
|
1133
|
+
const handler = /* @__PURE__ */ __name(async (request, reply) => {
|
|
1134
|
+
const { url, status_code } = request.query;
|
|
1135
|
+
const statusCode = status_code ? Number(status_code) : 302;
|
|
1136
|
+
await reply.redirect(url, statusCode);
|
|
1137
|
+
}, "handler");
|
|
1138
|
+
fastify.get("/redirect-to", {
|
|
1139
|
+
schema: redirectToSchema
|
|
1140
|
+
}, handler);
|
|
1141
|
+
fastify.post("/redirect-to", {
|
|
1142
|
+
schema: redirectToSchema
|
|
1143
|
+
}, handler);
|
|
1144
|
+
fastify.put("/redirect-to", {
|
|
1145
|
+
schema: redirectToSchema
|
|
1146
|
+
}, handler);
|
|
1147
|
+
fastify.patch("/redirect-to", {
|
|
1148
|
+
schema: redirectToSchema
|
|
1149
|
+
}, handler);
|
|
1150
|
+
fastify.delete("/redirect-to", {
|
|
1151
|
+
schema: redirectToSchema
|
|
1152
|
+
}, handler);
|
|
1153
|
+
}, "redirectToRoute");
|
|
1154
|
+
|
|
1155
|
+
// src/routes/redirects/relative-redirect.ts
|
|
1156
|
+
var relativeRedirectSchema = {
|
|
1157
|
+
tags: [
|
|
1158
|
+
"Redirects"
|
|
1159
|
+
],
|
|
1160
|
+
description: "Redirects the request to the target URL using an relative URL",
|
|
1161
|
+
params: {
|
|
1162
|
+
type: "object",
|
|
1163
|
+
properties: {
|
|
1164
|
+
value: {
|
|
1165
|
+
type: "integer",
|
|
1166
|
+
minimum: 1
|
|
1167
|
+
}
|
|
1168
|
+
},
|
|
1169
|
+
required: [
|
|
1170
|
+
"value"
|
|
1171
|
+
]
|
|
1172
|
+
},
|
|
1173
|
+
headers: {
|
|
1174
|
+
type: "object",
|
|
1175
|
+
properties: {
|
|
1176
|
+
host: {
|
|
1177
|
+
type: "string"
|
|
1178
|
+
}
|
|
1179
|
+
},
|
|
1180
|
+
required: [
|
|
1181
|
+
"host"
|
|
1182
|
+
]
|
|
1183
|
+
},
|
|
1205
1184
|
response: {
|
|
1206
1185
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1207
1186
|
302: {
|
|
@@ -1232,285 +1211,758 @@ var relativeRedirectRoute = /* @__PURE__ */ __name((fastify) => {
|
|
|
1232
1211
|
});
|
|
1233
1212
|
}, "relativeRedirectRoute");
|
|
1234
1213
|
|
|
1235
|
-
// src/routes/
|
|
1236
|
-
var
|
|
1237
|
-
|
|
1238
|
-
"Redirects"
|
|
1239
|
-
],
|
|
1240
|
-
description: "Redirects the request to the target URL",
|
|
1241
|
-
querystring: {
|
|
1242
|
-
type: "object",
|
|
1243
|
-
properties: {
|
|
1244
|
-
url: {
|
|
1245
|
-
type: "string",
|
|
1246
|
-
format: "uri",
|
|
1247
|
-
description: "The URL to redirect to"
|
|
1248
|
-
},
|
|
1249
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1250
|
-
status_code: {
|
|
1251
|
-
type: "string",
|
|
1252
|
-
pattern: "^[1-5][0-9][0-9]$",
|
|
1253
|
-
description: "The HTTP status code to use for redirection"
|
|
1254
|
-
}
|
|
1255
|
-
},
|
|
1256
|
-
required: [
|
|
1257
|
-
"url"
|
|
1258
|
-
],
|
|
1259
|
-
additionalProperties: false
|
|
1260
|
-
}
|
|
1261
|
-
};
|
|
1262
|
-
var redirectToRoute = /* @__PURE__ */ __name((fastify) => {
|
|
1263
|
-
const handler = /* @__PURE__ */ __name(async (request, reply) => {
|
|
1264
|
-
const { url, status_code } = request.query;
|
|
1265
|
-
const statusCode = status_code ? Number(status_code) : 302;
|
|
1266
|
-
await reply.redirect(url, statusCode);
|
|
1267
|
-
}, "handler");
|
|
1268
|
-
fastify.get("/redirect-to", {
|
|
1269
|
-
schema: redirectToSchema
|
|
1270
|
-
}, handler);
|
|
1271
|
-
fastify.post("/redirect-to", {
|
|
1272
|
-
schema: redirectToSchema
|
|
1273
|
-
}, handler);
|
|
1274
|
-
fastify.put("/redirect-to", {
|
|
1275
|
-
schema: redirectToSchema
|
|
1276
|
-
}, handler);
|
|
1277
|
-
fastify.patch("/redirect-to", {
|
|
1278
|
-
schema: redirectToSchema
|
|
1279
|
-
}, handler);
|
|
1280
|
-
fastify.delete("/redirect-to", {
|
|
1281
|
-
schema: redirectToSchema
|
|
1282
|
-
}, handler);
|
|
1283
|
-
}, "redirectToRoute");
|
|
1284
|
-
|
|
1285
|
-
// src/routes/cookies/get.ts
|
|
1286
|
-
var getCookiesRouteSchema = {
|
|
1287
|
-
description: "Return cookies from the request",
|
|
1214
|
+
// src/routes/request-inspection/headers.ts
|
|
1215
|
+
var headersSchema = {
|
|
1216
|
+
description: "Returns all headers of the client request",
|
|
1288
1217
|
tags: [
|
|
1289
|
-
"
|
|
1218
|
+
"Request Inspection"
|
|
1290
1219
|
],
|
|
1291
1220
|
response: {
|
|
1292
1221
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1293
1222
|
200: {
|
|
1294
1223
|
type: "object",
|
|
1295
1224
|
properties: {
|
|
1296
|
-
|
|
1225
|
+
headers: {
|
|
1297
1226
|
type: "object",
|
|
1298
1227
|
additionalProperties: {
|
|
1299
1228
|
type: "string"
|
|
1300
|
-
}
|
|
1229
|
+
},
|
|
1230
|
+
description: "All headers of the client request"
|
|
1301
1231
|
}
|
|
1302
1232
|
},
|
|
1303
1233
|
required: [
|
|
1304
|
-
"
|
|
1234
|
+
"headers"
|
|
1305
1235
|
]
|
|
1306
1236
|
}
|
|
1307
1237
|
}
|
|
1308
1238
|
};
|
|
1309
|
-
var
|
|
1310
|
-
fastify.get("/
|
|
1311
|
-
schema:
|
|
1312
|
-
}, async (request
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
}
|
|
1239
|
+
var headersRoute = /* @__PURE__ */ __name((fastify) => {
|
|
1240
|
+
fastify.get("/headers", {
|
|
1241
|
+
schema: headersSchema
|
|
1242
|
+
}, async (request) => {
|
|
1243
|
+
const { headers } = request;
|
|
1244
|
+
return {
|
|
1245
|
+
headers
|
|
1246
|
+
};
|
|
1317
1247
|
});
|
|
1318
|
-
}, "
|
|
1248
|
+
}, "headersRoute");
|
|
1319
1249
|
|
|
1320
|
-
// src/routes/
|
|
1321
|
-
var
|
|
1322
|
-
description: "
|
|
1250
|
+
// src/routes/request-inspection/ip.ts
|
|
1251
|
+
var ipSchema = {
|
|
1252
|
+
description: "Returns the IP address of the client",
|
|
1323
1253
|
tags: [
|
|
1324
|
-
"
|
|
1254
|
+
"Request Inspection"
|
|
1325
1255
|
],
|
|
1326
|
-
body: {
|
|
1327
|
-
type: "object",
|
|
1328
|
-
properties: {
|
|
1329
|
-
name: {
|
|
1330
|
-
type: "string"
|
|
1331
|
-
},
|
|
1332
|
-
value: {
|
|
1333
|
-
type: "string"
|
|
1334
|
-
},
|
|
1335
|
-
expires: {
|
|
1336
|
-
type: "string",
|
|
1337
|
-
format: "date-time"
|
|
1338
|
-
}
|
|
1339
|
-
},
|
|
1340
|
-
required: [
|
|
1341
|
-
"name",
|
|
1342
|
-
"value"
|
|
1343
|
-
],
|
|
1344
|
-
additionalProperties: false
|
|
1345
|
-
},
|
|
1346
1256
|
response: {
|
|
1347
1257
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1348
1258
|
200: {
|
|
1349
1259
|
type: "object",
|
|
1350
1260
|
properties: {
|
|
1351
|
-
|
|
1352
|
-
type: "
|
|
1353
|
-
|
|
1354
|
-
type: "string"
|
|
1355
|
-
}
|
|
1261
|
+
ip: {
|
|
1262
|
+
type: "string",
|
|
1263
|
+
description: "The IP address of the client"
|
|
1356
1264
|
}
|
|
1357
1265
|
},
|
|
1358
1266
|
required: [
|
|
1359
|
-
"
|
|
1267
|
+
"ip"
|
|
1360
1268
|
]
|
|
1361
1269
|
}
|
|
1362
1270
|
}
|
|
1363
1271
|
};
|
|
1364
|
-
var
|
|
1365
|
-
fastify.
|
|
1366
|
-
schema:
|
|
1367
|
-
}, async (request
|
|
1368
|
-
const
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
} else {
|
|
1376
|
-
reply.setCookie(name, value, {
|
|
1377
|
-
path: "/"
|
|
1378
|
-
});
|
|
1379
|
-
}
|
|
1380
|
-
return reply.status(200).send();
|
|
1272
|
+
var ipRoute = /* @__PURE__ */ __name((fastify) => {
|
|
1273
|
+
fastify.get("/ip", {
|
|
1274
|
+
schema: ipSchema
|
|
1275
|
+
}, async (request) => {
|
|
1276
|
+
const cloudflareIp = request.headers["cf-connecting-ip"];
|
|
1277
|
+
const forwardedIp = request.headers["x-forwarded-for"];
|
|
1278
|
+
const remoteIp = request.ip;
|
|
1279
|
+
const ip = cloudflareIp ?? (typeof forwardedIp === "string" ? forwardedIp.split(",")[0].trim() : remoteIp);
|
|
1280
|
+
return {
|
|
1281
|
+
ip
|
|
1282
|
+
};
|
|
1381
1283
|
});
|
|
1382
|
-
}, "
|
|
1284
|
+
}, "ipRoute");
|
|
1383
1285
|
|
|
1384
|
-
// src/routes/
|
|
1385
|
-
var
|
|
1386
|
-
description: "
|
|
1286
|
+
// src/routes/request-inspection/user-agent.ts
|
|
1287
|
+
var userAgentSchema = {
|
|
1288
|
+
description: "Returns the User-Agent header of the client request",
|
|
1387
1289
|
tags: [
|
|
1388
|
-
"
|
|
1290
|
+
"Request Inspection"
|
|
1389
1291
|
],
|
|
1390
|
-
querystring: {
|
|
1391
|
-
type: "object",
|
|
1392
|
-
properties: {
|
|
1393
|
-
name: {
|
|
1394
|
-
type: "string"
|
|
1395
|
-
}
|
|
1396
|
-
},
|
|
1397
|
-
required: [
|
|
1398
|
-
"name"
|
|
1399
|
-
],
|
|
1400
|
-
additionalProperties: false
|
|
1401
|
-
},
|
|
1402
1292
|
response: {
|
|
1403
1293
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1404
|
-
|
|
1405
|
-
type: "
|
|
1294
|
+
200: {
|
|
1295
|
+
type: "object",
|
|
1296
|
+
properties: {
|
|
1297
|
+
userAgent: {
|
|
1298
|
+
type: "string",
|
|
1299
|
+
description: "The User-Agent header of the client request"
|
|
1300
|
+
}
|
|
1301
|
+
},
|
|
1302
|
+
required: [
|
|
1303
|
+
"userAgent"
|
|
1304
|
+
]
|
|
1406
1305
|
}
|
|
1407
1306
|
}
|
|
1408
1307
|
};
|
|
1409
|
-
var
|
|
1410
|
-
fastify.
|
|
1411
|
-
schema:
|
|
1412
|
-
}, async (request
|
|
1413
|
-
const
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1308
|
+
var userAgentRoute = /* @__PURE__ */ __name((fastify) => {
|
|
1309
|
+
fastify.get("/user-agent", {
|
|
1310
|
+
schema: userAgentSchema
|
|
1311
|
+
}, async (request) => {
|
|
1312
|
+
const userAgent = request.headers["user-agent"];
|
|
1313
|
+
return {
|
|
1314
|
+
userAgent
|
|
1315
|
+
};
|
|
1316
|
+
});
|
|
1317
|
+
}, "userAgentRoute");
|
|
1318
|
+
|
|
1319
|
+
// src/routes/response-inspection/cache.ts
|
|
1320
|
+
var cacheGetSchema = {
|
|
1321
|
+
description: "Handles cache validation and retrieval.",
|
|
1322
|
+
tags: [
|
|
1323
|
+
"Response Inspection"
|
|
1324
|
+
],
|
|
1325
|
+
response: {
|
|
1326
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1327
|
+
200: {
|
|
1328
|
+
type: "string",
|
|
1329
|
+
description: "The cached content or resource."
|
|
1330
|
+
},
|
|
1331
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1332
|
+
304: {
|
|
1333
|
+
type: "null",
|
|
1334
|
+
description: "Indicates the resource has not been modified."
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
};
|
|
1338
|
+
var cacheSetSchema = {
|
|
1339
|
+
description: "Sets a Cache-Control header for n seconds.",
|
|
1340
|
+
tags: [
|
|
1341
|
+
"Response Inspection"
|
|
1342
|
+
],
|
|
1343
|
+
params: {
|
|
1344
|
+
type: "object",
|
|
1345
|
+
properties: {
|
|
1346
|
+
value: {
|
|
1347
|
+
type: "integer",
|
|
1348
|
+
description: "Number of seconds to cache the resource."
|
|
1349
|
+
}
|
|
1350
|
+
},
|
|
1351
|
+
required: [
|
|
1352
|
+
"value"
|
|
1353
|
+
]
|
|
1354
|
+
},
|
|
1355
|
+
response: {
|
|
1356
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1357
|
+
200: {
|
|
1358
|
+
type: "string",
|
|
1359
|
+
description: "Confirmation message for setting cache."
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
};
|
|
1363
|
+
var cacheRoutes = /* @__PURE__ */ __name((fastify) => {
|
|
1364
|
+
fastify.get("/cache", {
|
|
1365
|
+
schema: cacheGetSchema
|
|
1366
|
+
}, async (request, reply) => {
|
|
1367
|
+
const ifModifiedSince = request.headers["if-modified-since"];
|
|
1368
|
+
const ifNoneMatch = request.headers["if-none-match"];
|
|
1369
|
+
if (ifModifiedSince ?? ifNoneMatch) {
|
|
1370
|
+
await reply.status(304).send();
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
await reply.send("Cache content or resource response here.");
|
|
1374
|
+
});
|
|
1375
|
+
fastify.get("/cache/:value", {
|
|
1376
|
+
schema: cacheSetSchema
|
|
1377
|
+
}, async (request, reply) => {
|
|
1378
|
+
const { value } = request.params;
|
|
1379
|
+
await reply.header("Cache-Control", `max-age=${value}`).send(`Cache-Control set for ${value} seconds.`);
|
|
1380
|
+
});
|
|
1381
|
+
}, "cacheRoutes");
|
|
1382
|
+
|
|
1383
|
+
// src/routes/response-inspection/etag.ts
|
|
1384
|
+
var etagSchema = {
|
|
1385
|
+
description: "Handles ETag-based conditional requests.",
|
|
1386
|
+
tags: [
|
|
1387
|
+
"Response Inspection"
|
|
1388
|
+
],
|
|
1389
|
+
params: {
|
|
1390
|
+
type: "object",
|
|
1391
|
+
properties: {
|
|
1392
|
+
etag: {
|
|
1393
|
+
type: "string",
|
|
1394
|
+
description: "The ETag value for the resource."
|
|
1395
|
+
}
|
|
1396
|
+
},
|
|
1397
|
+
required: [
|
|
1398
|
+
"etag"
|
|
1399
|
+
]
|
|
1400
|
+
},
|
|
1401
|
+
response: {
|
|
1402
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1403
|
+
200: {
|
|
1404
|
+
type: "string",
|
|
1405
|
+
description: "Resource content for matching ETag."
|
|
1406
|
+
},
|
|
1407
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1408
|
+
304: {
|
|
1409
|
+
type: "null",
|
|
1410
|
+
description: "Indicates the resource has not been modified."
|
|
1411
|
+
},
|
|
1412
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1413
|
+
412: {
|
|
1414
|
+
type: "null",
|
|
1415
|
+
description: "Indicates a precondition failed due to mismatched ETag."
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
};
|
|
1419
|
+
var etagRoutes = /* @__PURE__ */ __name((fastify) => {
|
|
1420
|
+
fastify.get("/etag/:etag", {
|
|
1421
|
+
schema: etagSchema
|
|
1422
|
+
}, async (request, reply) => {
|
|
1423
|
+
const { etag } = request.params;
|
|
1424
|
+
const ifNoneMatch = request.headers["if-none-match"];
|
|
1425
|
+
const ifMatch = request.headers["if-match"];
|
|
1426
|
+
if (ifNoneMatch === etag) {
|
|
1427
|
+
await reply.status(304).send();
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
if (ifMatch && ifMatch !== etag) {
|
|
1431
|
+
await reply.status(412).send();
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
await reply.header("ETag", etag).send("Resource content for matching ETag.");
|
|
1435
|
+
});
|
|
1436
|
+
}, "etagRoutes");
|
|
1437
|
+
|
|
1438
|
+
// src/routes/response-inspection/response-headers.ts
|
|
1439
|
+
import { escape as escapeHtml } from "html-escaper";
|
|
1440
|
+
var responseHeadersSchema = {
|
|
1441
|
+
description: "Returns a set of response headers based on the query string.",
|
|
1442
|
+
tags: [
|
|
1443
|
+
"Response Inspection"
|
|
1444
|
+
],
|
|
1445
|
+
querystring: {
|
|
1446
|
+
type: "object",
|
|
1447
|
+
additionalProperties: true,
|
|
1448
|
+
description: "Freeform query string parameters to be added as response headers."
|
|
1449
|
+
},
|
|
1450
|
+
response: {
|
|
1451
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1452
|
+
200: {
|
|
1453
|
+
type: "object",
|
|
1454
|
+
additionalProperties: true,
|
|
1455
|
+
description: "Response headers as per query string parameters."
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
};
|
|
1459
|
+
var responseHeadersRoutes = /* @__PURE__ */ __name((fastify) => {
|
|
1460
|
+
fastify.get("/response-headers", {
|
|
1461
|
+
schema: responseHeadersSchema
|
|
1462
|
+
}, async (request, reply) => {
|
|
1463
|
+
const queryParameters = request.query;
|
|
1464
|
+
for (const [key, value] of Object.entries(queryParameters)) {
|
|
1465
|
+
reply.header(key, escapeHtml(value));
|
|
1466
|
+
}
|
|
1467
|
+
const cleanedResponse = Object.fromEntries(Object.entries(queryParameters).map(([key, value]) => [
|
|
1468
|
+
key,
|
|
1469
|
+
escapeHtml(value)
|
|
1470
|
+
]));
|
|
1471
|
+
await reply.send(cleanedResponse);
|
|
1472
|
+
});
|
|
1473
|
+
fastify.post("/response-headers", {
|
|
1474
|
+
schema: responseHeadersSchema
|
|
1475
|
+
}, async (request, reply) => {
|
|
1476
|
+
const queryParameters = request.query ?? {};
|
|
1477
|
+
const body = request.body ?? {};
|
|
1478
|
+
for (const [key, value] of Object.entries(queryParameters)) {
|
|
1479
|
+
reply.header(key, escapeHtml(value));
|
|
1480
|
+
}
|
|
1481
|
+
for (const [key, value] of Object.entries(body)) {
|
|
1482
|
+
reply.header(key, escapeHtml(value));
|
|
1483
|
+
}
|
|
1484
|
+
const cleanedResponse = Object.fromEntries(Object.entries({
|
|
1485
|
+
...queryParameters,
|
|
1486
|
+
...body
|
|
1487
|
+
}).map(([key, value]) => [
|
|
1488
|
+
key,
|
|
1489
|
+
escapeHtml(value)
|
|
1490
|
+
]));
|
|
1491
|
+
await reply.send(cleanedResponse);
|
|
1492
|
+
});
|
|
1493
|
+
}, "responseHeadersRoutes");
|
|
1494
|
+
|
|
1495
|
+
// src/routes/sitemap.ts
|
|
1496
|
+
var sitemapRoute = /* @__PURE__ */ __name((fastify) => {
|
|
1497
|
+
fastify.get("/sitemap.xml", {
|
|
1498
|
+
schema: {
|
|
1499
|
+
hide: true
|
|
1500
|
+
}
|
|
1501
|
+
}, async (_request, reply) => {
|
|
1502
|
+
try {
|
|
1503
|
+
const host = _request.headers.host ?? "mockhttp.org";
|
|
1504
|
+
const isSecure = _request.headers["x-forwarded-proto"]?.includes("https");
|
|
1505
|
+
const protocol = isSecure ? "https" : "http";
|
|
1506
|
+
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
1507
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
1508
|
+
<url>
|
|
1509
|
+
<loc>${protocol}://${host}/</loc>
|
|
1510
|
+
<changefreq>monthly</changefreq>
|
|
1511
|
+
<priority>1.0</priority>
|
|
1512
|
+
</url>
|
|
1513
|
+
</urlset>`;
|
|
1514
|
+
await reply.type("text/xml").send(xml);
|
|
1515
|
+
} catch (error) {
|
|
1516
|
+
fastify.log.error(error);
|
|
1517
|
+
await reply.status(500).send({
|
|
1518
|
+
error: "Internal Server Error"
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
});
|
|
1522
|
+
}, "sitemapRoute");
|
|
1523
|
+
|
|
1524
|
+
// src/routes/status-codes/index.ts
|
|
1525
|
+
import { randomInt } from "crypto";
|
|
1526
|
+
var statusCodes = [
|
|
1527
|
+
{
|
|
1528
|
+
code: 100,
|
|
1529
|
+
message: "Continue"
|
|
1530
|
+
},
|
|
1531
|
+
{
|
|
1532
|
+
code: 101,
|
|
1533
|
+
message: "Switching Protocols"
|
|
1534
|
+
},
|
|
1535
|
+
{
|
|
1536
|
+
code: 102,
|
|
1537
|
+
message: "Processing"
|
|
1538
|
+
},
|
|
1539
|
+
{
|
|
1540
|
+
code: 103,
|
|
1541
|
+
message: "Early Hints"
|
|
1542
|
+
},
|
|
1543
|
+
{
|
|
1544
|
+
code: 200,
|
|
1545
|
+
message: "OK"
|
|
1546
|
+
},
|
|
1547
|
+
{
|
|
1548
|
+
code: 201,
|
|
1549
|
+
message: "Created"
|
|
1550
|
+
},
|
|
1551
|
+
{
|
|
1552
|
+
code: 202,
|
|
1553
|
+
message: "Accepted"
|
|
1554
|
+
},
|
|
1555
|
+
{
|
|
1556
|
+
code: 203,
|
|
1557
|
+
message: "Non-Authoritative Information"
|
|
1558
|
+
},
|
|
1559
|
+
{
|
|
1560
|
+
code: 204,
|
|
1561
|
+
message: "No Content"
|
|
1562
|
+
},
|
|
1563
|
+
{
|
|
1564
|
+
code: 205,
|
|
1565
|
+
message: "Reset Content"
|
|
1566
|
+
},
|
|
1567
|
+
{
|
|
1568
|
+
code: 206,
|
|
1569
|
+
message: "Partial Content"
|
|
1570
|
+
},
|
|
1571
|
+
{
|
|
1572
|
+
code: 207,
|
|
1573
|
+
message: "Multi-Status"
|
|
1574
|
+
},
|
|
1575
|
+
{
|
|
1576
|
+
code: 208,
|
|
1577
|
+
message: "Already Reported"
|
|
1578
|
+
},
|
|
1579
|
+
{
|
|
1580
|
+
code: 226,
|
|
1581
|
+
message: "IM Used"
|
|
1582
|
+
},
|
|
1583
|
+
{
|
|
1584
|
+
code: 300,
|
|
1585
|
+
message: "Multiple Choices"
|
|
1586
|
+
},
|
|
1587
|
+
{
|
|
1588
|
+
code: 301,
|
|
1589
|
+
message: "Moved Permanently"
|
|
1590
|
+
},
|
|
1591
|
+
{
|
|
1592
|
+
code: 302,
|
|
1593
|
+
message: "Found"
|
|
1594
|
+
},
|
|
1595
|
+
{
|
|
1596
|
+
code: 303,
|
|
1597
|
+
message: "See Other"
|
|
1598
|
+
},
|
|
1599
|
+
{
|
|
1600
|
+
code: 304,
|
|
1601
|
+
message: "Not Modified"
|
|
1602
|
+
},
|
|
1603
|
+
{
|
|
1604
|
+
code: 305,
|
|
1605
|
+
message: "Use Proxy"
|
|
1606
|
+
},
|
|
1607
|
+
{
|
|
1608
|
+
code: 306,
|
|
1609
|
+
message: "Switch Proxy"
|
|
1610
|
+
},
|
|
1611
|
+
{
|
|
1612
|
+
code: 307,
|
|
1613
|
+
message: "Temporary Redirect"
|
|
1614
|
+
},
|
|
1615
|
+
{
|
|
1616
|
+
code: 308,
|
|
1617
|
+
message: "Permanent Redirect"
|
|
1618
|
+
},
|
|
1619
|
+
{
|
|
1620
|
+
code: 400,
|
|
1621
|
+
message: "Bad Request"
|
|
1622
|
+
},
|
|
1623
|
+
{
|
|
1624
|
+
code: 401,
|
|
1625
|
+
message: "Unauthorized"
|
|
1626
|
+
},
|
|
1627
|
+
{
|
|
1628
|
+
code: 402,
|
|
1629
|
+
message: "Payment Required"
|
|
1630
|
+
},
|
|
1631
|
+
{
|
|
1632
|
+
code: 403,
|
|
1633
|
+
message: "Forbidden"
|
|
1634
|
+
},
|
|
1635
|
+
{
|
|
1636
|
+
code: 404,
|
|
1637
|
+
message: "Not Found"
|
|
1638
|
+
},
|
|
1639
|
+
{
|
|
1640
|
+
code: 405,
|
|
1641
|
+
message: "Method Not Allowed"
|
|
1642
|
+
},
|
|
1643
|
+
{
|
|
1644
|
+
code: 406,
|
|
1645
|
+
message: "Not Acceptable"
|
|
1646
|
+
},
|
|
1647
|
+
{
|
|
1648
|
+
code: 407,
|
|
1649
|
+
message: "Proxy Authentication Required"
|
|
1650
|
+
},
|
|
1651
|
+
{
|
|
1652
|
+
code: 408,
|
|
1653
|
+
message: "Request Timeout"
|
|
1654
|
+
},
|
|
1655
|
+
{
|
|
1656
|
+
code: 409,
|
|
1657
|
+
message: "Conflict"
|
|
1658
|
+
},
|
|
1659
|
+
{
|
|
1660
|
+
code: 410,
|
|
1661
|
+
message: "Gone"
|
|
1662
|
+
},
|
|
1663
|
+
{
|
|
1664
|
+
code: 411,
|
|
1665
|
+
message: "Length Required"
|
|
1666
|
+
},
|
|
1667
|
+
{
|
|
1668
|
+
code: 412,
|
|
1669
|
+
message: "Precondition Failed"
|
|
1670
|
+
},
|
|
1671
|
+
{
|
|
1672
|
+
code: 413,
|
|
1673
|
+
message: "Payload Too Large"
|
|
1674
|
+
},
|
|
1675
|
+
{
|
|
1676
|
+
code: 414,
|
|
1677
|
+
message: "URI Too Long"
|
|
1678
|
+
},
|
|
1679
|
+
{
|
|
1680
|
+
code: 415,
|
|
1681
|
+
message: "Unsupported Media Type"
|
|
1682
|
+
},
|
|
1683
|
+
{
|
|
1684
|
+
code: 416,
|
|
1685
|
+
message: "Range Not Satisfiable"
|
|
1686
|
+
},
|
|
1687
|
+
{
|
|
1688
|
+
code: 417,
|
|
1689
|
+
message: "Expectation Failed"
|
|
1690
|
+
},
|
|
1691
|
+
{
|
|
1692
|
+
code: 418,
|
|
1693
|
+
message: "I'm a teapot"
|
|
1694
|
+
},
|
|
1695
|
+
{
|
|
1696
|
+
code: 421,
|
|
1697
|
+
message: "Misdirected Request"
|
|
1698
|
+
},
|
|
1699
|
+
{
|
|
1700
|
+
code: 422,
|
|
1701
|
+
message: "Unprocessable Entity"
|
|
1702
|
+
},
|
|
1703
|
+
{
|
|
1704
|
+
code: 423,
|
|
1705
|
+
message: "Locked"
|
|
1706
|
+
},
|
|
1707
|
+
{
|
|
1708
|
+
code: 424,
|
|
1709
|
+
message: "Failed Dependency"
|
|
1710
|
+
},
|
|
1711
|
+
{
|
|
1712
|
+
code: 425,
|
|
1713
|
+
message: "Too Early"
|
|
1714
|
+
},
|
|
1715
|
+
{
|
|
1716
|
+
code: 426,
|
|
1717
|
+
message: "Upgrade Required"
|
|
1718
|
+
},
|
|
1719
|
+
{
|
|
1720
|
+
code: 428,
|
|
1721
|
+
message: "Precondition Required"
|
|
1722
|
+
},
|
|
1723
|
+
{
|
|
1724
|
+
code: 429,
|
|
1725
|
+
message: "Too Many Requests"
|
|
1726
|
+
},
|
|
1727
|
+
{
|
|
1728
|
+
code: 431,
|
|
1729
|
+
message: "Request Header Fields Too Large"
|
|
1730
|
+
},
|
|
1731
|
+
{
|
|
1732
|
+
code: 451,
|
|
1733
|
+
message: "Unavailable For Legal Reasons"
|
|
1734
|
+
},
|
|
1735
|
+
{
|
|
1736
|
+
code: 500,
|
|
1737
|
+
message: "Internal Server Error"
|
|
1738
|
+
},
|
|
1739
|
+
{
|
|
1740
|
+
code: 501,
|
|
1741
|
+
message: "Not Implemented"
|
|
1742
|
+
},
|
|
1743
|
+
{
|
|
1744
|
+
code: 502,
|
|
1745
|
+
message: "Bad Gateway"
|
|
1746
|
+
},
|
|
1747
|
+
{
|
|
1748
|
+
code: 503,
|
|
1749
|
+
message: "Service Unavailable"
|
|
1750
|
+
},
|
|
1751
|
+
{
|
|
1752
|
+
code: 504,
|
|
1753
|
+
message: "Gateway Timeout"
|
|
1754
|
+
},
|
|
1755
|
+
{
|
|
1756
|
+
code: 505,
|
|
1757
|
+
message: "HTTP Version Not Supported"
|
|
1758
|
+
},
|
|
1759
|
+
{
|
|
1760
|
+
code: 506,
|
|
1761
|
+
message: "Variant Also Negotiates"
|
|
1762
|
+
},
|
|
1763
|
+
{
|
|
1764
|
+
code: 507,
|
|
1765
|
+
message: "Insufficient Storage"
|
|
1766
|
+
},
|
|
1767
|
+
{
|
|
1768
|
+
code: 508,
|
|
1769
|
+
message: "Loop Detected"
|
|
1770
|
+
},
|
|
1771
|
+
{
|
|
1772
|
+
code: 510,
|
|
1773
|
+
message: "Not Extended"
|
|
1774
|
+
},
|
|
1775
|
+
{
|
|
1776
|
+
code: 511,
|
|
1777
|
+
message: "Network Authentication Required"
|
|
1778
|
+
}
|
|
1779
|
+
];
|
|
1780
|
+
var statusSchema = {
|
|
1781
|
+
description: "Return status code or random status code if more than one are given ",
|
|
1424
1782
|
tags: [
|
|
1425
|
-
"
|
|
1783
|
+
"Status Codes"
|
|
1426
1784
|
],
|
|
1785
|
+
params: {
|
|
1786
|
+
type: "object",
|
|
1787
|
+
properties: {
|
|
1788
|
+
code: {
|
|
1789
|
+
type: "string",
|
|
1790
|
+
description: "HTTP status code to be returned"
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
},
|
|
1427
1794
|
response: {
|
|
1428
|
-
// eslint-disable-next-line
|
|
1795
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1429
1796
|
200: {
|
|
1430
1797
|
type: "object",
|
|
1431
1798
|
properties: {
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
type: "object",
|
|
1435
|
-
description: "Route parameters",
|
|
1436
|
-
additionalProperties: true
|
|
1437
|
-
},
|
|
1438
|
-
// Echo back whatever was in the request.body
|
|
1439
|
-
data: {
|
|
1440
|
-
type: "object",
|
|
1441
|
-
description: "Request body data",
|
|
1442
|
-
additionalProperties: true
|
|
1443
|
-
},
|
|
1444
|
-
// If you’re using fastify-multipart, you’ll get
|
|
1445
|
-
// files via request.files(); this just allows
|
|
1446
|
-
// you to mirror them back
|
|
1447
|
-
files: {
|
|
1448
|
-
type: "object",
|
|
1449
|
-
description: "Uploaded files",
|
|
1450
|
-
additionalProperties: true
|
|
1451
|
-
},
|
|
1452
|
-
// If you parse application/x-www-form-urlencoded
|
|
1453
|
-
// these fields land in request.body too, but you can
|
|
1454
|
-
// mirror them separately if you like
|
|
1455
|
-
form: {
|
|
1456
|
-
type: "object",
|
|
1457
|
-
description: "Form data",
|
|
1458
|
-
additionalProperties: true
|
|
1459
|
-
},
|
|
1460
|
-
headers: {
|
|
1461
|
-
type: "object",
|
|
1462
|
-
description: "Request headers",
|
|
1463
|
-
additionalProperties: true
|
|
1464
|
-
},
|
|
1465
|
-
json: {
|
|
1466
|
-
type: "object",
|
|
1467
|
-
description: "Parsed JSON body",
|
|
1468
|
-
additionalProperties: true
|
|
1469
|
-
},
|
|
1470
|
-
method: {
|
|
1471
|
-
type: "string",
|
|
1472
|
-
description: "HTTP method used"
|
|
1473
|
-
},
|
|
1474
|
-
origin: {
|
|
1475
|
-
type: "string",
|
|
1476
|
-
description: "Origin of the request"
|
|
1477
|
-
},
|
|
1478
|
-
url: {
|
|
1479
|
-
type: "string",
|
|
1480
|
-
description: "Request URL"
|
|
1799
|
+
status: {
|
|
1800
|
+
type: "string"
|
|
1481
1801
|
}
|
|
1482
|
-
}
|
|
1483
|
-
// Don’t allow any other top-level keys in your response
|
|
1484
|
-
additionalProperties: false
|
|
1802
|
+
}
|
|
1485
1803
|
}
|
|
1486
1804
|
}
|
|
1487
1805
|
};
|
|
1488
|
-
var
|
|
1806
|
+
var statusCodeRoute = /* @__PURE__ */ __name((fastify) => {
|
|
1489
1807
|
const handler = /* @__PURE__ */ __name(async (request, reply) => {
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
method: request.method,
|
|
1498
|
-
origin: request.headers.origin ?? request.headers.host ?? "",
|
|
1499
|
-
url: request.url
|
|
1500
|
-
};
|
|
1501
|
-
await reply.send(response);
|
|
1808
|
+
let { code } = request.params;
|
|
1809
|
+
if (!code || !statusCodes.map((status) => status.code).includes(Number(code))) {
|
|
1810
|
+
code = statusCodes[randomInt(0, statusCodes.length)].code.toString();
|
|
1811
|
+
}
|
|
1812
|
+
await reply.code(Number(code)).send({
|
|
1813
|
+
status: `Response with status code ${code}`
|
|
1814
|
+
});
|
|
1502
1815
|
}, "handler");
|
|
1503
|
-
|
|
1504
|
-
schema:
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
fastify.
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
fastify.
|
|
1513
|
-
|
|
1816
|
+
fastify.get("/status/:code?", {
|
|
1817
|
+
schema: statusSchema
|
|
1818
|
+
}, handler);
|
|
1819
|
+
fastify.post("/status/:code?", {
|
|
1820
|
+
schema: statusSchema
|
|
1821
|
+
}, handler);
|
|
1822
|
+
fastify.put("/status/:code?", {
|
|
1823
|
+
schema: statusSchema
|
|
1824
|
+
}, handler);
|
|
1825
|
+
fastify.patch("/status/:code?", {
|
|
1826
|
+
schema: statusSchema
|
|
1827
|
+
}, handler);
|
|
1828
|
+
fastify.delete("/status/:code?", {
|
|
1829
|
+
schema: statusSchema
|
|
1830
|
+
}, handler);
|
|
1831
|
+
}, "statusCodeRoute");
|
|
1832
|
+
|
|
1833
|
+
// src/swagger.ts
|
|
1834
|
+
import { fastifySwaggerUi } from "@fastify/swagger-ui";
|
|
1835
|
+
|
|
1836
|
+
// package.json
|
|
1837
|
+
var package_default = {
|
|
1838
|
+
name: "@jaredwray/mockhttp",
|
|
1839
|
+
version: "0.7.0",
|
|
1840
|
+
description: "Mock Http - Easy way to mock http with httpbin replacement",
|
|
1841
|
+
type: "module",
|
|
1842
|
+
main: "dist/index.js",
|
|
1843
|
+
directories: {
|
|
1844
|
+
test: "test"
|
|
1845
|
+
},
|
|
1846
|
+
scripts: {
|
|
1847
|
+
lint: "biome check --write --error-on-warnings",
|
|
1848
|
+
test: "pnpm lint && vitest run --coverage",
|
|
1849
|
+
"test:ci": "biome check --error-on-warnings && vitest run --coverage",
|
|
1850
|
+
dev: "pnpm lint && tsx src/index.ts --watch",
|
|
1851
|
+
build: "rimraf ./dist && tsup src/index.ts --format esm --dts --clean",
|
|
1852
|
+
"docker:build": "docker build -t jaredwray/mockhttp .",
|
|
1853
|
+
"docker:run": "docker run -p 3000:3000 jaredwray/mockhttp",
|
|
1854
|
+
"docker:push": "docker push jaredwray/mockhttp",
|
|
1855
|
+
"docker:stop": 'docker stop $(docker ps -a -q --filter ancestor=jaredwray/mockhttp --format="{{.ID}}")',
|
|
1856
|
+
"docker:readme": "tsx ./scripts/create-docker-readme.ts",
|
|
1857
|
+
"docker:compose:up": "docker-compose up -d",
|
|
1858
|
+
"docker:compose:down": "docker-compose down",
|
|
1859
|
+
prepare: "pnpm run build",
|
|
1860
|
+
start: "node dist/index.js"
|
|
1861
|
+
},
|
|
1862
|
+
repository: {
|
|
1863
|
+
type: "git",
|
|
1864
|
+
url: "git+https://github.com/jaredwray/mockhttp.git"
|
|
1865
|
+
},
|
|
1866
|
+
keywords: [
|
|
1867
|
+
"mock",
|
|
1868
|
+
"http",
|
|
1869
|
+
"httpbin",
|
|
1870
|
+
"test"
|
|
1871
|
+
],
|
|
1872
|
+
author: "Jared Wray <me@jaredwray.com>",
|
|
1873
|
+
license: "MIT",
|
|
1874
|
+
bugs: {
|
|
1875
|
+
url: "https://github.com/jaredwray/mockhttp/issues"
|
|
1876
|
+
},
|
|
1877
|
+
homepage: "https://github.com/jaredwray/mockhttp#readme",
|
|
1878
|
+
devDependencies: {
|
|
1879
|
+
"@biomejs/biome": "^2.2.2",
|
|
1880
|
+
"@swc/core": "^1.13.4",
|
|
1881
|
+
"@types/html-escaper": "^3.0.4",
|
|
1882
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
1883
|
+
rimraf: "^6.0.1",
|
|
1884
|
+
tsup: "^8.5.0",
|
|
1885
|
+
tsx: "^4.20.4",
|
|
1886
|
+
typescript: "^5.9.2",
|
|
1887
|
+
vitest: "^3.2.4"
|
|
1888
|
+
},
|
|
1889
|
+
dependencies: {
|
|
1890
|
+
"@fastify/cookie": "^11.0.2",
|
|
1891
|
+
"@fastify/helmet": "^13.0.1",
|
|
1892
|
+
"@fastify/static": "^8.2.0",
|
|
1893
|
+
"@fastify/swagger": "^9.5.1",
|
|
1894
|
+
"@fastify/swagger-ui": "^5.2.3",
|
|
1895
|
+
"@scalar/api-reference": "^1.34.4",
|
|
1896
|
+
"detect-port": "^2.1.0",
|
|
1897
|
+
fastify: "^5.5.0",
|
|
1898
|
+
hookified: "^1.12.0",
|
|
1899
|
+
"html-escaper": "^3.0.3",
|
|
1900
|
+
pino: "^9.9.0",
|
|
1901
|
+
"pino-pretty": "^13.1.1"
|
|
1902
|
+
},
|
|
1903
|
+
files: [
|
|
1904
|
+
"dist",
|
|
1905
|
+
"public",
|
|
1906
|
+
"LICENCE"
|
|
1907
|
+
],
|
|
1908
|
+
pnpm: {
|
|
1909
|
+
onlyBuiltDependencies: [
|
|
1910
|
+
"@swc/core",
|
|
1911
|
+
"unrs-resolver",
|
|
1912
|
+
"vue-demi"
|
|
1913
|
+
]
|
|
1914
|
+
}
|
|
1915
|
+
};
|
|
1916
|
+
|
|
1917
|
+
// src/swagger.ts
|
|
1918
|
+
var description = `
|
|
1919
|
+
A simple HTTP server that can be used to mock HTTP responses for testing purposes. Inspired by [httpbin](https://httpbin.org/) and built using \`nodejs\` and \`fastify\` with the idea of running it via https://mockhttp.org, via docker \`jaredwray/mockhttp\`, or nodejs \`npm install jaredwray/mockhttp\`.
|
|
1920
|
+
|
|
1921
|
+
* [GitHub Repository](https://github.com/jaredwray/mockhttp)
|
|
1922
|
+
* [Docker Image](https://hub.docker.com/r/jaredwray/mockhttp)
|
|
1923
|
+
* [NPM Package](https://www.npmjs.com/package/jaredwray/mockhttp)
|
|
1924
|
+
|
|
1925
|
+
# About mockhttp.org
|
|
1926
|
+
|
|
1927
|
+
[mockhttp.org](https://mockhttp.org) is a free service that runs this codebase and allows you to use it for testing purposes. It is a simple way to mock HTTP responses for testing purposes. Ran via [Cloudflare](https://cloudflare.com) and [Google Cloud Run](https://cloud.google.com/run/) across 7 regions globally and can do millions of requests per second.
|
|
1928
|
+
`;
|
|
1929
|
+
var fastifySwaggerConfig = {
|
|
1930
|
+
openapi: {
|
|
1931
|
+
info: {
|
|
1932
|
+
title: "Mock HTTP API",
|
|
1933
|
+
description,
|
|
1934
|
+
version: package_default.version
|
|
1935
|
+
},
|
|
1936
|
+
consumes: [
|
|
1937
|
+
"application/json"
|
|
1938
|
+
],
|
|
1939
|
+
produces: [
|
|
1940
|
+
"application/json"
|
|
1941
|
+
]
|
|
1942
|
+
}
|
|
1943
|
+
};
|
|
1944
|
+
var registerSwaggerUi = /* @__PURE__ */ __name(async (fastify) => {
|
|
1945
|
+
await fastify.register(fastifySwaggerUi, {
|
|
1946
|
+
routePrefix: "/docs",
|
|
1947
|
+
uiConfig: {
|
|
1948
|
+
docExpansion: "none",
|
|
1949
|
+
deepLinking: false
|
|
1950
|
+
},
|
|
1951
|
+
uiHooks: {
|
|
1952
|
+
/* c8 ignore next 6 */
|
|
1953
|
+
onRequest(_request, _reply, next) {
|
|
1954
|
+
next();
|
|
1955
|
+
},
|
|
1956
|
+
preHandler(_request, _reply, next) {
|
|
1957
|
+
next();
|
|
1958
|
+
}
|
|
1959
|
+
},
|
|
1960
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1961
|
+
staticCSP: true,
|
|
1962
|
+
transformSpecification: /* @__PURE__ */ __name((swaggerObject, _request, _reply) => swaggerObject, "transformSpecification"),
|
|
1963
|
+
transformSpecificationClone: true
|
|
1964
|
+
});
|
|
1965
|
+
}, "registerSwaggerUi");
|
|
1514
1966
|
|
|
1515
1967
|
// src/mock-http.ts
|
|
1516
1968
|
var MockHttp = class extends Hookified {
|
|
@@ -1529,7 +1981,8 @@ var MockHttp = class extends Hookified {
|
|
|
1529
1981
|
responseInspection: true,
|
|
1530
1982
|
statusCodes: true,
|
|
1531
1983
|
cookies: true,
|
|
1532
|
-
anything: true
|
|
1984
|
+
anything: true,
|
|
1985
|
+
auth: true
|
|
1533
1986
|
};
|
|
1534
1987
|
// eslint-disable-next-line new-cap
|
|
1535
1988
|
_server = Fastify();
|
|
@@ -1669,7 +2122,7 @@ var MockHttp = class extends Hookified {
|
|
|
1669
2122
|
if (this._apiDocs) {
|
|
1670
2123
|
await this.registerApiDocs();
|
|
1671
2124
|
}
|
|
1672
|
-
const { httpMethods, redirects, requestInspection, responseInspection, statusCodes: statusCodes2, cookies, anything } = this._httpBin;
|
|
2125
|
+
const { httpMethods, redirects, requestInspection, responseInspection, statusCodes: statusCodes2, cookies, anything, auth } = this._httpBin;
|
|
1673
2126
|
if (httpMethods) {
|
|
1674
2127
|
await this.registerHttpMethods();
|
|
1675
2128
|
}
|
|
@@ -1691,6 +2144,9 @@ var MockHttp = class extends Hookified {
|
|
|
1691
2144
|
if (anything) {
|
|
1692
2145
|
await this.registerAnythingRoutes();
|
|
1693
2146
|
}
|
|
2147
|
+
if (auth) {
|
|
2148
|
+
await this.registerAuthRoutes();
|
|
2149
|
+
}
|
|
1694
2150
|
if (this._autoDetectPort) {
|
|
1695
2151
|
const originalPort = this._port;
|
|
1696
2152
|
this._port = await this.detectPort();
|
|
@@ -1799,6 +2255,17 @@ var MockHttp = class extends Hookified {
|
|
|
1799
2255
|
const fastify = fastifyInstance ?? this._server;
|
|
1800
2256
|
await fastify.register(anythingRoute);
|
|
1801
2257
|
}
|
|
2258
|
+
/**
|
|
2259
|
+
* Register the auth routes.
|
|
2260
|
+
* @param fastifyInstance - the server instance to register the routes on.
|
|
2261
|
+
*/
|
|
2262
|
+
async registerAuthRoutes(fastifyInstance) {
|
|
2263
|
+
const fastify = fastifyInstance ?? this._server;
|
|
2264
|
+
await fastify.register(basicAuthRoute);
|
|
2265
|
+
await fastify.register(hiddenBasicAuthRoute);
|
|
2266
|
+
await fastify.register(bearerAuthRoute);
|
|
2267
|
+
await fastify.register(digestAuthRoute);
|
|
2268
|
+
}
|
|
1802
2269
|
};
|
|
1803
2270
|
|
|
1804
2271
|
// src/index.ts
|
|
@@ -1813,7 +2280,9 @@ var start = /* @__PURE__ */ __name(async () => {
|
|
|
1813
2280
|
await mockHttp.start();
|
|
1814
2281
|
return mockHttp;
|
|
1815
2282
|
}, "start");
|
|
1816
|
-
|
|
2283
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
2284
|
+
await start();
|
|
2285
|
+
}
|
|
1817
2286
|
export {
|
|
1818
2287
|
MockHttp as default,
|
|
1819
2288
|
MockHttp as mockhttp,
|