A nimble http and echo server to test arbitrary http requests and REST API responses.
1.8K
A nimble http and echo server to test arbitrary http requests and REST API responses.
erised [options]
Parameters:
-cert string
path to a valid X.509 certificate file
-https
use HTTPS instead of HTTP. A valid X.509 certificate and private key are required
-idle int
maximum time in seconds to wait for the next request when keep-alive is enabled (default 120)
-json
use JSON log format
-key string
path to a valid private key file
-level string
one of debug/info/warn/error/off (default "info")
-path string
path to search recursively for X-Erised-Response-File
-port int
port to listen. Default is 8080 for HTTP and 8443 for HTTPS
-profile string
profile this session. A valid file name is required
-read int
maximum duration in seconds for reading the entire request (default 5)
-write int
maximum duration in seconds before timing out response writes (default 10)
For help type erised -h
When executing erised with no parameters, the server will listen on port 8080 for incoming http requests.
If you're using the -path option, please EXERCISE GREAT CAUTION when setting the path to search. See Known Issues for more information. For security reasons, path is restricted to the directory or subdirectories where the program was invoked.
The latest version is also available as a Docker image at edaddario/erised.
To start the server in a docker container, with defaults values, execute the following command:
docker run --rm -p 8080:8080 --name erised edaddario/erised
If you would like to return file based responses (X-Erised-Response-File set) when using the docker image, you'll need to map the directory containing your local files and set the -path option accordingly.
The following example maps the /local_directory/response_files directory in your local machine to /files in the docker image, and then sets the -path option:
docker run --rm -p 8080:8080 --name erised -v /local_directory/response_files:/files edaddario/erised -path ./files
URL routes, HTTP methods (e.g. GET, POST, PATCH, etc.), query strings and body are ignored, except for:
| Name | Method | Purpose |
|---|---|---|
| erised/headers | GET | Returns request headers |
| erised/info | GET | Returns miscellaneous information |
| erised/ip | GET | Returns the client IP |
| erised/shutdown | POST | Shutdowns the server |
The erised/echoserver path will ignore any additional segments after /echoserver, including HTTP methods, query strings and body, and it will return a webpage displaying server information and the request's parameters.
| Name | Method | Purpose |
|---|---|---|
| erised/echoserver/* | any | Returns a webpage displaying server information and the request's parameters |
Erised's response behaviour is controlled via custom headers in the http request:
| Name | Purpose |
|---|---|
| X-Erised-Content-Type | Sets the response Content-Type. Valid values are text (default) for text/plain, json for application/json, xml for application/xml and gzip for application/octet-stream. When using gzip, Content-Encoding is also set to gzip and the response body is compressed accordingly. |
| X-Erised-Data | Returns the same value in the response body |
| X-Erised-Headers | Returns the value(s) in the response header. Values must be in a JSON key/value list |
| X-Erised-Location | Sets the response Location to the new (redirected) URL or path, when 300 ≤ X-Erised-Status-Code < 310 |
| X-Erised-Response-Delay | Number of milliseconds to wait before sending response back to client |
| X-Erised-Response-File | Returns the contents of file in the response body. If present, X-Erised-Data is ignored |
| X-Erised-Status-Code | Sets the HTTP Status Code |
No validation is performed on X-Erised-Data or X-Erised-Location.
Valid X-Erised-Status-Code values are:
OK or 200 (default)
MultipleChoices or 300
MovedPermanently or 301
Found or 302
SeeOther or 303
UseProxy or 305
TemporaryRedirect or 307
PermanentRedirect or 308
BadRequest or 400
Unauthorized or 401
PaymentRequired or 402
Forbidden or 403
NotFound or 404
MethodNotAllowed or 405
RequestTimeout or 408
Conflict or 409
Gone or 410
Teapot or 418
TooManyRequests or 429
InternalServerError or 500
NotImplemented or 501
BadGateway or 502
ServiceUnavailable or 503
GatewayTimeout or 504
HTTPVersionNotSupported or 505
InsufficientStorage or 507
LoopDetected or 508
NotExtended or 510
NetworkAuthenticationRequired or 511
Any other value will resolve to 200 (OK)
In order to enable HTTPS support, erised requires a valid X.509 certificate signed by a trusted Certification Authority (CA) like IdenTrust, DigiCert or Let's Encrypt.
If you don't have one, or would prefer to use a local version, you'll find the necessary certificate (localCA.pem) to setup a Root CA in the /certs folder, which will allow your computer to verify the included test certificate and key. The process to install the CA certificate is beyond the objective of this README, but Google or your favourite AI can help. Once the certificate is installed, it will show as Erised Test CA. Please ensure it is marked as trusted.
You should now be able to run erised in HTTPS mode by executing erised -https -cert erised.crt -key erised.key where erised.crt is the "site's" (your computer) X.509 certificate and erised.key is the private key.
As mentioned before, covering the intricacies of establishing cryptographically secure digital identities and documenting the process to generate the relevant keys and certificates is well beyond the scope of this README, but it is important to at least call out some of the risks incurred when trusting a digital certificate because, in addition to validate identity and secure the communication between parties, they are also used to "sign" code (programs and libraries) that can run with privileged permissions.
I hope the message is clear: when dealing with anything security related it always pays to be very careful and when in doubt, the best option is just not to.
The Secure Sockets Layer (SSL) certificates included with erised are linked to localhost and can only be used to enable TLS/SSL communication within your own computer.
For transparency, the (high level) steps to create them are:
Please note that neither of the private keys are password protected. This is definitely not something that you would normally do, but decided to simplify the process in case you'd want to tinker with the provided certs.
erised may be full of bugs. Poeple "... have wasted away before it, not knowing if what they have seen is real, or even possible..." so, use it with caution for it gives no knowledge or truth.
Of all of its deficiencies, the most notable is:
I may or may not address this or any other issues in a future release. Caveat Emptor
When developing and testing REST API clients, sooner or later I'd come across situations where I needed a quick and easy way to dynamically test endpoint's responses under different scenarios. Although there are many excellent frameworks and mock servers available, the time and effort required to configure them is sometimes not justified, specially if the application under test exposes many routes, so after some brief and unsuccessful googling I decided to create my own.
erised was partially inspired by Kenneth Reitz's HTTP Request & Response Service httpbin.io and later on by Marchandise Rudy's Echo-Server.
The typical use case is to get a response to an arbitrary http request when your ability to control the server's behaviour is limited or non-existent.
Imagine you're developing some client for api.chucknorris.io and want to test the /jokes/random path. You could certainly make live calls against the server:
curl -w '\n' -v -k https://api.chucknorris.io/jokes/random
(response edited for clarity)
* Trying 104.31.94.71...
* TCP_NODELAY set
* Connected to api.chucknorris.io (104.31.94.71) port 443 (#0)
> GET /jokes/random HTTP/2
> Host: api.chucknorris.io
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/2 200
< date: Wed, 30 Dec 2020 00:21:14 GMT
< content-type: application/json;charset=UTF-8
<
* Connection #0 to host api.chucknorris.io left intact
{"categories":[],"created_at":"2020-01-05 13:42:18.823766","icon_url":"https://assets.chucknorris.host/img/avatar/chuck-norris.png","id":"CfW0ccNFTpeq_v1r13IjTQ","updated_at":"2020-01-05 13:42:18.823766","url":"https://api.chucknorris.io/jokes/CfW0ccNFTpeq_v1r13IjTQ","value":"The lord giveth and Chuck Norris taketh away"}
* Closing connection 0
Or, even better yet, you could use erised like this:
curl -w '\n' -v \
-H "X-Erised-Status-Code:OK" \
-H "X-Erised-Content-Type:json" \
-H "X-Erised-Data:{\"categories\":[],\"created_at\":\"2020-01-05 13:42:26.766831\",\"icon_url\":\"https://assets.chucknorris.host/img/avatar/chuck-norris.png\",\"id\":\"CfW0ccNFTpeq_v1r13IjTQ\",\"updated_at\":\"2020-01-05 13:42:26.766831\",\"url\":\"https://api.chucknorris.io/jokes/CfW0ccNFTpeq_v1r13IjTQ\",\"value\":\"The lord giveth and Chuck Norris taketh away\"}" \
http://localhost:8080/jokes/random
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /jokes/random HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
> X-Erised-Status-Code:OK
> X-Erised-Content-Type:json
> X-Erised-Data:{"categories":[],"created_at":"2020-01-05 13:42:26.766831","icon_url":"https://assets.chucknorris.host/img/avatar/chuck-norris.png","id":"CfW0ccNFTpeq_v1r13IjTQ","updated_at":"2020-01-05 13:42:26.766831","url":"https://api.chucknorris.io/jokes/CfW0ccNFTpeq_v1r13IjTQ","value":"The lord giveth and Chuck Norris taketh away"}
>
< HTTP/1.1 200 OK
< Content-Encoding: identity
< Content-Type: application/json
< Date: Wed, 30 Dec 2020 01:13:54 GMT
< Content-Length: 323
<
* Connection #0 to host localhost left intact
{"categories":[],"created_at":"2020-01-05 13:42:26.766831","icon_url":"https://assets.chucknorris.host/img/avatar/chuck-norris.png","id":"CfW0ccNFTpeq_v1r13IjTQ","updated_at":"2020-01-05 13:42:26.766831","url":"https://api.chucknorris.io/jokes/CfW0ccNFTpeq_v1r13IjTQ","value":"The lord giveth and Chuck Norris taketh away"}
* Closing connection 0
and even simulate common failures like,
curl -w '\n' -v \
-H "X-Erised-Status-Code:NotFound" \
-H "X-Erised-Content-Type:json" \
-H "X-Erised-Data:{\"timestamp\":\"2020-12-30T11:21:32.793Z\",\"status\":404,\"error\":\"Not Found\",\"message\":\"Chuck Norris knows everything there is to know - Except where this page is.\",\"path\":\"/jokes/random\"}" \
http://localhost:8080/jokes/random
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /jokes/random HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
> X-Erised-Status-Code:NotFound
> X-Erised-Content-Type:json
> X-Erised-Data:{"timestamp":"2020-12-30T11:21:32.793Z","status":404,"error":"Not Found","message":"Chuck Norris knows everything there is to know - Except where this page is.","path":"/jokes/random"}
>
< HTTP/1.1 404 Not Found
< Content-Encoding: identity
< Content-Type: application/json
< Date: Wed, 30 Dec 2020 11:25:21 GMT
< Content-Length: 184
<
* Connection #0 to host localhost left intact
{"timestamp":"2020-12-30T11:21:32.793Z","status":404,"error":"Not Found","message":"Chuck Norris knows everything there is to know - Except where this page is.","path":"/jokes/random"}
* Closing connection 0
curl -w '\n' -v http://localhost:8080
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Encoding: identity
< Content-Type: text/plain
< Date: Tue, 29 Dec 2020 18:35:48 GMT
< Content-Length: 0
<
* Connection #0 to host localhost left intact
* Closing connection 0
curl -w '\n' -I -H "X-Erised-Headers:{\"My-Header\":\"Hello World\",\"Another-Header\":\"Goodbye World\"}" http://localhost:8080
HTTP/1.1 200 OK
Another-Header: Goodbye World
Content-Encoding: identity
Content-Type: text/plain
My-Header: Hello World
Date: Sat, 13 Mar 2021 22:56:09 GMT
curl -w '\n' -v -H "X-Erised-Data:Hello World" http://localhost:8080
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
> X-Erised-Data:Hello World
>
< HTTP/1.1 200 OK
< Content-Encoding: identity
< Content-Type: text/plain
< Date: Tue, 29 Dec 2020 18:38:10 GMT
< Content-Length: 11
<
* Connection #0 to host localhost left intact
Hello World
* Closing connection 0
curl -w '\n' -v -H "X-Erised-Content-Type:json" -H "X-Erised-Data:[{\"Hello\":\"World\"}]" http://localhost:8080
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
> X-Erised-Content-Type:json
> X-Erised-Data:[{Hello:World}]
>
< HTTP/1.1 200 OK
< Content-Encoding: identity
< Content-Type: application/json
< Date: Tue, 29 Dec 2020 18:43:55 GMT
< Content-Length: 15
<
* Connection #0 to host localhost left intact
[{"Hello":"World"}]
* Closing connection 0
curl -w '\n' -v -H "X-Erised-Status-Code:Teapot" -H "X-Erised-Data:Server refuses to brew coffee because it is, permanently, a teapot." http://localhost:8080
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
> X-Erised-Status-Code:Teapot
> X-Erised-Data:Server refuses to brew coffee because it is, permanently, a teapot.
>
< HTTP/1.1 418 I'm a teapot
< Content-Encoding: identity
< Content-Type: text/plain
< Date: Tue, 29 Dec 2020 18:54:46 GMT
< Content-Length: 67
<
* Connection #0 to host localhost left intact
Server refuses to brew coffee because it is, permanently, a teapot.
* Closing connection 0
Content type
Image
Digest
sha256:53e6f5bc3…
Size
11.3 MB
Last updated
about 2 years ago
docker pull edaddario/erised