@jaredwray/mockhttp 0.6.1 → 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 +1652 -1183
- 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.1",
|
|
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.13.0",
|
|
62
|
-
"@types/html-escaper": "^3.0.4",
|
|
63
|
-
"@vitest/coverage-v8": "^3.2.4",
|
|
64
|
-
rimraf: "^6.0.1",
|
|
65
|
-
tsup: "^8.5.0",
|
|
66
|
-
tsx: "^4.20.3",
|
|
67
|
-
typescript: "^5.8.3",
|
|
68
|
-
vitest: "^3.2.4",
|
|
69
|
-
xo: "^1.2.1"
|
|
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.32.9",
|
|
78
|
-
"detect-port": "^2.1.0",
|
|
79
|
-
fastify: "^5.4.0",
|
|
80
|
-
hookified: "^1.10.0",
|
|
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
|
-
|
|
43
|
+
// Echo back all your params under `args`
|
|
44
|
+
args: {
|
|
45
|
+
type: "object",
|
|
46
|
+
description: "Route parameters",
|
|
47
|
+
additionalProperties: true
|
|
301
48
|
},
|
|
302
|
-
|
|
49
|
+
// Echo back whatever was in the request.body
|
|
50
|
+
data: {
|
|
303
51
|
type: "object",
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
52
|
+
description: "Request body data",
|
|
53
|
+
additionalProperties: true
|
|
307
54
|
},
|
|
308
|
-
|
|
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: {
|
|
309
59
|
type: "object",
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
};
|
|
323
|
-
var getRoute = /* @__PURE__ */ __name((fastify) => {
|
|
324
|
-
fastify.get("/get", {
|
|
325
|
-
schema: getSchema
|
|
326
|
-
}, async (request, _reply) => ({
|
|
327
|
-
method: "GET",
|
|
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"
|
|
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: "
|
|
399
|
-
},
|
|
400
|
-
headers: {
|
|
401
|
-
type: "object",
|
|
402
|
-
additionalProperties: {
|
|
403
|
-
type: "string"
|
|
404
|
-
}
|
|
197
|
+
authenticated: {
|
|
198
|
+
type: "boolean"
|
|
405
199
|
},
|
|
406
|
-
|
|
407
|
-
type: "
|
|
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
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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
|
|
575
600
|
},
|
|
576
|
-
{
|
|
577
|
-
|
|
578
|
-
|
|
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
|
|
579
680
|
},
|
|
580
|
-
{
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
{
|
|
597
|
-
code: 303,
|
|
598
|
-
message: "See Other"
|
|
599
|
-
},
|
|
600
|
-
{
|
|
601
|
-
code: 304,
|
|
602
|
-
message: "Not Modified"
|
|
603
|
-
},
|
|
604
|
-
{
|
|
605
|
-
code: 305,
|
|
606
|
-
message: "Use Proxy"
|
|
607
|
-
},
|
|
608
|
-
{
|
|
609
|
-
code: 306,
|
|
610
|
-
message: "Switch Proxy"
|
|
611
|
-
},
|
|
612
|
-
{
|
|
613
|
-
code: 307,
|
|
614
|
-
message: "Temporary Redirect"
|
|
615
|
-
},
|
|
616
|
-
{
|
|
617
|
-
code: 308,
|
|
618
|
-
message: "Permanent Redirect"
|
|
619
|
-
},
|
|
620
|
-
{
|
|
621
|
-
code: 400,
|
|
622
|
-
message: "Bad Request"
|
|
623
|
-
},
|
|
624
|
-
{
|
|
625
|
-
code: 401,
|
|
626
|
-
message: "Unauthorized"
|
|
627
|
-
},
|
|
628
|
-
{
|
|
629
|
-
code: 402,
|
|
630
|
-
message: "Payment Required"
|
|
631
|
-
},
|
|
632
|
-
{
|
|
633
|
-
code: 403,
|
|
634
|
-
message: "Forbidden"
|
|
635
|
-
},
|
|
636
|
-
{
|
|
637
|
-
code: 404,
|
|
638
|
-
message: "Not Found"
|
|
639
|
-
},
|
|
640
|
-
{
|
|
641
|
-
code: 405,
|
|
642
|
-
message: "Method Not Allowed"
|
|
643
|
-
},
|
|
644
|
-
{
|
|
645
|
-
code: 406,
|
|
646
|
-
message: "Not Acceptable"
|
|
647
|
-
},
|
|
648
|
-
{
|
|
649
|
-
code: 407,
|
|
650
|
-
message: "Proxy Authentication Required"
|
|
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"
|
|
681
|
+
response: {
|
|
682
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
683
|
+
200: {
|
|
684
|
+
type: "object",
|
|
685
|
+
properties: {
|
|
686
|
+
cookies: {
|
|
687
|
+
type: "object",
|
|
688
|
+
additionalProperties: {
|
|
689
|
+
type: "string"
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
},
|
|
693
|
+
required: [
|
|
694
|
+
"cookies"
|
|
695
|
+
]
|
|
696
|
+
}
|
|
779
697
|
}
|
|
780
|
-
|
|
781
|
-
var
|
|
782
|
-
|
|
698
|
+
};
|
|
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
|
+
});
|
|
714
|
+
}
|
|
715
|
+
return reply.status(200).send();
|
|
716
|
+
});
|
|
717
|
+
}, "postCookieRoute");
|
|
718
|
+
|
|
719
|
+
// src/routes/http-methods/delete.ts
|
|
720
|
+
var deleteSchema = {
|
|
721
|
+
description: "Handles a DELETE request and returns request information",
|
|
783
722
|
tags: [
|
|
784
|
-
"
|
|
723
|
+
"HTTP Methods"
|
|
785
724
|
],
|
|
786
|
-
|
|
725
|
+
body: {
|
|
787
726
|
type: "object",
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
type: "string",
|
|
791
|
-
description: "HTTP status code to be returned"
|
|
792
|
-
}
|
|
793
|
-
}
|
|
727
|
+
additionalProperties: true,
|
|
728
|
+
description: "The body of the DELETE request"
|
|
794
729
|
},
|
|
795
730
|
response: {
|
|
796
731
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
797
732
|
200: {
|
|
798
733
|
type: "object",
|
|
799
734
|
properties: {
|
|
800
|
-
|
|
735
|
+
method: {
|
|
801
736
|
type: "string"
|
|
737
|
+
},
|
|
738
|
+
headers: {
|
|
739
|
+
type: "object",
|
|
740
|
+
additionalProperties: {
|
|
741
|
+
type: "string"
|
|
742
|
+
}
|
|
743
|
+
},
|
|
744
|
+
body: {
|
|
745
|
+
type: "object",
|
|
746
|
+
additionalProperties: true
|
|
802
747
|
}
|
|
803
|
-
}
|
|
748
|
+
},
|
|
749
|
+
required: [
|
|
750
|
+
"method",
|
|
751
|
+
"headers",
|
|
752
|
+
"body"
|
|
753
|
+
]
|
|
804
754
|
}
|
|
805
755
|
}
|
|
806
756
|
};
|
|
807
|
-
var
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
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");
|
|
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");
|
|
833
766
|
|
|
834
|
-
// src/routes/
|
|
835
|
-
var
|
|
836
|
-
description: "Returns
|
|
767
|
+
// src/routes/http-methods/get.ts
|
|
768
|
+
var getSchema = {
|
|
769
|
+
description: "Returns request information for GET requests",
|
|
837
770
|
tags: [
|
|
838
|
-
"
|
|
771
|
+
"HTTP Methods"
|
|
839
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
|
+
},
|
|
840
785
|
response: {
|
|
841
786
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
842
787
|
200: {
|
|
843
788
|
type: "object",
|
|
844
789
|
properties: {
|
|
845
|
-
|
|
846
|
-
type: "string"
|
|
847
|
-
|
|
790
|
+
method: {
|
|
791
|
+
type: "string"
|
|
792
|
+
},
|
|
793
|
+
headers: {
|
|
794
|
+
type: "object",
|
|
795
|
+
additionalProperties: {
|
|
796
|
+
type: "string"
|
|
797
|
+
}
|
|
798
|
+
},
|
|
799
|
+
queryParams: {
|
|
800
|
+
type: "object",
|
|
801
|
+
additionalProperties: {
|
|
802
|
+
type: "string"
|
|
803
|
+
}
|
|
848
804
|
}
|
|
849
805
|
},
|
|
850
806
|
required: [
|
|
851
|
-
"
|
|
807
|
+
"method",
|
|
808
|
+
"headers",
|
|
809
|
+
"queryParams"
|
|
852
810
|
]
|
|
853
811
|
}
|
|
854
812
|
}
|
|
855
813
|
};
|
|
856
|
-
var
|
|
857
|
-
fastify.get("/
|
|
858
|
-
schema:
|
|
859
|
-
}, async (request) => {
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
ip
|
|
866
|
-
};
|
|
867
|
-
});
|
|
868
|
-
}, "ipRoute");
|
|
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");
|
|
869
823
|
|
|
870
|
-
// src/routes/
|
|
871
|
-
var
|
|
872
|
-
description: "
|
|
824
|
+
// src/routes/http-methods/patch.ts
|
|
825
|
+
var patchSchema = {
|
|
826
|
+
description: "Handles a PATCH request and returns request information",
|
|
873
827
|
tags: [
|
|
874
|
-
"
|
|
828
|
+
"HTTP Methods"
|
|
875
829
|
],
|
|
830
|
+
body: {
|
|
831
|
+
type: "object",
|
|
832
|
+
additionalProperties: true,
|
|
833
|
+
description: "The body of the PATCH request"
|
|
834
|
+
},
|
|
876
835
|
response: {
|
|
877
836
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
878
837
|
200: {
|
|
879
838
|
type: "object",
|
|
880
839
|
properties: {
|
|
840
|
+
method: {
|
|
841
|
+
type: "string"
|
|
842
|
+
},
|
|
881
843
|
headers: {
|
|
882
844
|
type: "object",
|
|
883
845
|
additionalProperties: {
|
|
884
846
|
type: "string"
|
|
885
|
-
}
|
|
886
|
-
|
|
847
|
+
}
|
|
848
|
+
},
|
|
849
|
+
body: {
|
|
850
|
+
type: "object",
|
|
851
|
+
additionalProperties: true
|
|
887
852
|
}
|
|
888
853
|
},
|
|
889
854
|
required: [
|
|
890
|
-
"
|
|
855
|
+
"method",
|
|
856
|
+
"headers",
|
|
857
|
+
"body"
|
|
891
858
|
]
|
|
892
859
|
}
|
|
893
860
|
}
|
|
894
861
|
};
|
|
895
|
-
var
|
|
896
|
-
fastify.
|
|
897
|
-
schema:
|
|
898
|
-
}, async (request) => {
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
}, "headersRoute");
|
|
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");
|
|
905
871
|
|
|
906
|
-
// src/routes/
|
|
907
|
-
var
|
|
908
|
-
description: "
|
|
872
|
+
// src/routes/http-methods/post.ts
|
|
873
|
+
var postSchema = {
|
|
874
|
+
description: "Handles a POST request and returns request information",
|
|
909
875
|
tags: [
|
|
910
|
-
"
|
|
876
|
+
"HTTP Methods"
|
|
911
877
|
],
|
|
878
|
+
body: {
|
|
879
|
+
type: "object",
|
|
880
|
+
additionalProperties: true,
|
|
881
|
+
description: "The body of the POST request"
|
|
882
|
+
},
|
|
912
883
|
response: {
|
|
913
884
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
914
885
|
200: {
|
|
915
886
|
type: "object",
|
|
916
887
|
properties: {
|
|
917
|
-
|
|
918
|
-
type: "string"
|
|
919
|
-
|
|
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
|
|
920
900
|
}
|
|
921
901
|
},
|
|
922
902
|
required: [
|
|
923
|
-
"
|
|
903
|
+
"method",
|
|
904
|
+
"headers",
|
|
905
|
+
"body"
|
|
924
906
|
]
|
|
925
907
|
}
|
|
926
908
|
}
|
|
927
909
|
};
|
|
928
|
-
var
|
|
929
|
-
fastify.
|
|
930
|
-
schema:
|
|
931
|
-
}, async (request) => {
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
}, "userAgentRoute");
|
|
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");
|
|
938
919
|
|
|
939
|
-
// src/routes/
|
|
940
|
-
var
|
|
941
|
-
description: "Handles
|
|
920
|
+
// src/routes/http-methods/put.ts
|
|
921
|
+
var putSchema = {
|
|
922
|
+
description: "Handles a PUT request and returns request information",
|
|
942
923
|
tags: [
|
|
943
|
-
"
|
|
924
|
+
"HTTP Methods"
|
|
944
925
|
],
|
|
926
|
+
body: {
|
|
927
|
+
type: "object",
|
|
928
|
+
additionalProperties: true,
|
|
929
|
+
description: "The body of the PUT request"
|
|
930
|
+
},
|
|
945
931
|
response: {
|
|
946
|
-
// eslint-disable-next-line
|
|
932
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
947
933
|
200: {
|
|
948
|
-
type: "
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
934
|
+
type: "object",
|
|
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
|
+
]
|
|
955
955
|
}
|
|
956
956
|
}
|
|
957
957
|
};
|
|
958
|
-
var
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
type: "integer",
|
|
968
|
-
description: "Number of seconds to cache the resource."
|
|
969
|
-
}
|
|
970
|
-
},
|
|
971
|
-
required: [
|
|
972
|
-
"value"
|
|
973
|
-
]
|
|
974
|
-
},
|
|
975
|
-
response: {
|
|
976
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
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");
|
|
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");
|
|
1002
967
|
|
|
1003
|
-
// src/routes/
|
|
1004
|
-
var
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
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;
|
|
968
|
+
// src/routes/index.ts
|
|
969
|
+
var indexRoute = /* @__PURE__ */ __name((fastify) => {
|
|
970
|
+
fastify.get("/", {
|
|
971
|
+
schema: {
|
|
972
|
+
hide: true
|
|
1053
973
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
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
|
+
}
|
|
1057
1006
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
await reply.send(
|
|
1092
|
-
});
|
|
1093
|
-
fastify.post("/response-headers", {
|
|
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));
|
|
1103
|
-
}
|
|
1104
|
-
const cleanedResponse = Object.fromEntries(Object.entries({
|
|
1105
|
-
...queryParameters,
|
|
1106
|
-
...body
|
|
1107
|
-
}).map(([key, value]) => [
|
|
1108
|
-
key,
|
|
1109
|
-
escape(value)
|
|
1110
|
-
]));
|
|
1111
|
-
await reply.send(cleanedResponse);
|
|
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,30 +1102,80 @@ 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
|
-
|
|
1110
|
+
description: "Redirects the request to the target URL",
|
|
1111
|
+
querystring: {
|
|
1183
1112
|
type: "object",
|
|
1184
1113
|
properties: {
|
|
1185
|
-
|
|
1186
|
-
type: "
|
|
1187
|
-
|
|
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"
|
|
1188
1124
|
}
|
|
1189
1125
|
},
|
|
1190
1126
|
required: [
|
|
1191
|
-
"
|
|
1192
|
-
]
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
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
|
+
}
|
|
1200
1179
|
},
|
|
1201
1180
|
required: [
|
|
1202
1181
|
"host"
|
|
@@ -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
|
-
return reply.status(204).send();
|
|
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
|
+
};
|
|
1418
1316
|
});
|
|
1419
|
-
}, "
|
|
1317
|
+
}, "userAgentRoute");
|
|
1420
1318
|
|
|
1421
|
-
// src/routes/
|
|
1422
|
-
var
|
|
1423
|
-
description: "
|
|
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,
|