Skip to content

Commit 23779e9

Browse files
author
Joris Berthelot
committed
Project import
0 parents  commit 23779e9

File tree

9 files changed

+379
-0
lines changed

9 files changed

+379
-0
lines changed

.dockerignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.git
2+
.DS_Store
3+
node_modules

.editorconfig

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
; This file is for unifying the coding style for different editors and IDEs.
2+
; More information at http://editorconfig.org
3+
4+
root = true
5+
6+
[*]
7+
charset = utf-8
8+
end_of_line = lf
9+
indent_size = 4
10+
indent_style = space
11+
insert_final_newline = true
12+
trim_trailing_whitespace = true
13+
14+
; Works with some editors only
15+
quote_type = single
16+
max_line_length = 120
17+
spaces_around_brackets = true
18+
spaces_around_operators = true

.travis.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
sudo: required
2+
3+
env:
4+
global:
5+
- TAG_PATTERN="^[0-9]+(\.[0-9]+){2}(-(alpha|beta|rc))?$"
6+
- DOCKER_IMAGE=eexit/mirror-http-server:${TRAVIS_TAG:=$TRAVIS_BUILD_NUMBER}
7+
8+
services:
9+
- docker
10+
11+
before_install:
12+
- docker login --email=$DOCKER_EMAIL --username=$DOCKER_USER --password=$DOCKER_PASSWD
13+
14+
install:
15+
- docker build -t $DOCKER_IMAGE .
16+
17+
script:
18+
- docker run $DOCKER_IMAGE npm test
19+
20+
after_success:
21+
- if [[ "$TRAVIS_TAG" =~ $TAG_PATTERN ]]; then docker push $DOCKER_IMAGE; fi

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FROM node:4.2-onbuild
2+
MAINTAINER Joris Berthelot <[email protected]>

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2015 Joris Berthelot
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
![logo](logo.png)
2+
3+
# Mirror HTTP Server [![Build Status](https://travis-ci.org/eexit/mirror-http-server.svg)](https://travis-ci.org/eexit/mirror-http-server)
4+
5+
*A dummy HTTP server that responds whatever you told him to.*
6+
7+
Build to play with HTTP or test your API. Make a HTTP call to the dummy server with the specified headers you want the server responds with.
8+
9+
## Usage
10+
11+
Pull the [Docker](https://www.docker.com) container:
12+
13+
$ docker pull eexit/mirror-http-server
14+
15+
Start the container:
16+
17+
$ docker run -itp 80:80 eexit/mirror-http-server
18+
2015-11-05T20:59:57.353Z] INFO: mirror-http-server/17 on ccc867df5980: Listening on http://0.0.0.0:80
19+
20+
For this README examples, I use the great [HTTPie](https://github.com/jkbrzt/httpie) tool.
21+
22+
Send request againt it:
23+
24+
http $(docker-machine ip default)
25+
26+
```http
27+
HTTP/1.1 200 OK
28+
Connection: keep-alive
29+
Content-Length: 0
30+
Date: Thu, 05 Nov 2015 21:33:20 GMT
31+
X-Powered-By: Express
32+
```
33+
34+
You can use any [HTTP verbs](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods) with any path, any request body and any header.
35+
36+
### Behavioural request headers
37+
38+
You can change the server response code and body by setting specific `X-Mirror-*` headers to your request.
39+
40+
### `X-Mirror-Code`
41+
42+
Change the server response [status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes).
43+
Here, simulate a server error:
44+
45+
$ http $(docker-machine ip default) X-Mirror-Code:503
46+
47+
```http
48+
HTTP/1.1 503 Service Unavailable
49+
Connection: keep-alive
50+
Content-Length: 0
51+
Date: Thu, 05 Nov 2015 22:30:11 GMT
52+
X-Powered-By: Express
53+
```
54+
55+
Here, simulate a `301` redirection:
56+
57+
http $(docker-machine ip default) \
58+
X-Mirror-Code:301 \
59+
X-Mirror-Location:http://www.eexit.net \
60+
X-Mirror-Content-Type:"text/plain; charset=ISO-8859-1"
61+
62+
```http
63+
HTTP/1.1 301 Moved Permanently
64+
Connection: keep-alive
65+
Content-Length: 0
66+
Content-Type: text/plain; charset=ISO-8859-1
67+
Date: Thu, 05 Nov 2015 22:40:02 GMT
68+
Location: http://www.eexit.net
69+
X-Powered-By: Express
70+
```
71+
72+
If you add the `--follow` option, it will output my website HTML source.
73+
74+
If you check the container logs:
75+
76+
```json
77+
[2015-11-05T22:48:59.564Z] INFO: mirror-server/18 on 6cb74ed853b0:
78+
request: {
79+
"ip": "192.168.99.1",
80+
"ips": [],
81+
"method": "GET",
82+
"url": "/",
83+
"headers": {
84+
"host": "192.168.99.100",
85+
"x-mirror-code": "301",
86+
"accept-encoding": "gzip, deflate",
87+
"x-mirror-location": "http://www.eexit.net",
88+
"accept": "*/*",
89+
"user-agent": "HTTPie/0.9.2",
90+
"connection": "keep-alive",
91+
"x-mirror-content-type": "text/plain; charset=ISO-8859-1"
92+
},
93+
"body": {}
94+
}
95+
```
96+
97+
### `X-Mirror-Request`
98+
99+
If you access to the server logs or want to exploit the what's logged, set the `X-Mirror-Request` to receive what's logged in a JSON format:
100+
101+
$ http POST $(docker-machine ip default)/resource \
102+
X-Mirror-Code:201 \
103+
X-Mirror-Request:true \
104+
key1=value1 key2=value2
105+
106+
```http
107+
HTTP/1.1 201 Created
108+
Connection: keep-alive
109+
Content-Length: 373
110+
Content-Type: application/json; charset=utf-8
111+
Date: Thu, 05 Nov 2015 22:57:17 GMT
112+
ETag: W/"175-3rxm7gM5Zwu88cZOABP92A"
113+
X-Powered-By: Express
114+
115+
{
116+
"request": {
117+
"body": {
118+
"key1": "value1",
119+
"key2": "value2"
120+
},
121+
"headers": {
122+
"accept": "application/json",
123+
"accept-encoding": "gzip, deflate",
124+
"connection": "keep-alive",
125+
"content-length": "36",
126+
"content-type": "application/json",
127+
"host": "192.168.99.100",
128+
"user-agent": "HTTPie/0.9.2",
129+
"x-mirror-code": "201",
130+
"x-mirror-request": "true"
131+
},
132+
"ip": "192.168.99.1",
133+
"ips": [],
134+
"method": "POST",
135+
"url": "/resource"
136+
}
137+
}
138+
```
139+
140+
### `X-Mirror-Body`
141+
142+
Instead, if you with the dummy server to return you the same body you requested to it, set the `X-Mirror-Body` header.
143+
144+
Note: the `X-Mirror-Request` header will override `X-Mirror-Body` header.
145+
146+
$ http PUT $(docker-machine ip default)/resource \
147+
X-Mirror-Code:400 \
148+
X-Mirror-Body:true \
149+
key1=value1 key2=value2
150+
151+
```http
152+
HTTP/1.1 400 Bad Request
153+
Connection: keep-alive
154+
Content-Length: 33
155+
Content-Type: application/json; charset=utf-8
156+
Date: Thu, 05 Nov 2015 23:52:34 GMT
157+
ETag: W/"21-/0XMODUWUwfvQUwjyixvZw"
158+
X-Powered-By: Express
159+
160+
{
161+
"key1": "value1",
162+
"key2": "value2"
163+
}
164+
```
165+
166+
### Works for all headers
167+
168+
Aside to the previous three special headers, you can set your wanted response header by prepending your header name by `X-Mirror-`.
169+
170+
In the request:
171+
172+
```http
173+
Content-Type: application/json
174+
X-Mirror-Content-Type: text/html
175+
```
176+
177+
You'll get in your response:
178+
179+
```http
180+
Content-Type: text/html
181+
```
182+
183+
You can even override Express headers or any other default header:
184+
185+
```http
186+
X-Mirror-X-Powered-By: eexit-engine
187+
X-Mirror-Date: some date
188+
```
189+
190+
Will turn into:
191+
192+
```http
193+
X-Powered-By: eexit-engine
194+
Date: some date
195+
```

logo.png

17.9 KB
Loading

package.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "mirror-http-server",
3+
"version": "1.0.0",
4+
"description": "A dummy HTTP server that responds whatever you told him to",
5+
"scripts": {
6+
"start": "node server.js | npm run bunyan",
7+
"start:dev": "nodemon server.js | npm run bunyan",
8+
"test": "echo \"No test specified yet\"",
9+
"bunyan": "$(npm bin)/bunyan"
10+
},
11+
"keywords": [
12+
"node",
13+
"nodejs",
14+
"server",
15+
"http",
16+
"mirror",
17+
"dumb",
18+
"dump",
19+
"test",
20+
"development"
21+
],
22+
"author": "Joris Berthelot <[email protected]>",
23+
"license": "MIT",
24+
"dependencies": {
25+
"body-parser": "^1.14.1",
26+
"bunyan": "^1.5.1",
27+
"express": "^4.13.3",
28+
"lodash": "^3.10.1",
29+
"nodemon": "^1.8.1"
30+
}
31+
}

server.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
'use strict';
2+
3+
var host = process.env.HOST || '0.0.0.0';
4+
var port = process.env.PORT || 80;
5+
6+
var _ = require('lodash');
7+
var bunyan = require('bunyan');
8+
var bodyParser = require('body-parser');
9+
var pckg = require(__dirname + '/package.json');
10+
var logger = bunyan.createLogger({name: pckg.name});
11+
var express = require('express');
12+
var app = express();
13+
14+
app.enable('trust proxy');
15+
app.use(bodyParser.json());
16+
app.use(bodyParser.urlencoded({ extended: true }));
17+
18+
// Intercepts all HTTP verb requests
19+
app.all('*', function (req, res, next) {
20+
// Returned response headers
21+
var responseHeaders = {};
22+
23+
// Parses the wanted response code
24+
var mirrorCode = req.get('X-Mirror-Code') || 200;
25+
26+
// Finds out if the request should be returned as the response
27+
var mirrorRequest = (req.get('X-Mirror-Request')
28+
&& req.get('X-Mirror-Request').toLowerCase() == 'true')
29+
|| false;
30+
31+
// Finds out if the response should be returned
32+
var mirrorBody = (req.get('X-Mirror-Body')
33+
&& req.get('X-Mirror-Body').toLowerCase() == 'true')
34+
|| false;
35+
36+
// Parses X-Mirror-* headers, skips app specific headers
37+
var reqHeaders = _.without(
38+
_.filter(
39+
Object.keys(req.headers), function (name) {
40+
return _.startsWith(name, 'x-mirror-');
41+
}
42+
), 'x-mirror-code', 'x-mirror-request', 'x-mirror-body'
43+
);
44+
45+
// Injects X-Mirror-* headers to response headers
46+
reqHeaders.forEach(function (name) {
47+
var resHeader = _.startCase(_.trimLeft(name, 'x-mirror-')).replace(' ', '-');
48+
responseHeaders[resHeader] = req.headers[name];
49+
});
50+
51+
// Builds the request object
52+
var request = {
53+
request: {
54+
ip: req.ip,
55+
ips: req.ips,
56+
method: req.method,
57+
url: req.originalUrl,
58+
headers: req.headers,
59+
body: req.body
60+
}
61+
};
62+
63+
logger.info(request);
64+
65+
66+
// Prepares the response
67+
res.status(mirrorCode).set(responseHeaders);
68+
69+
// Appends the full request or only the request body if wanted
70+
if (mirrorRequest) {
71+
res.json(request);
72+
} else if (mirrorBody) {
73+
res.send(req.body);
74+
}
75+
76+
// Flushes!
77+
res.end();
78+
});
79+
80+
// Basic error handler
81+
app.use(function (err, req, res, next) {
82+
logger.fatal(err);
83+
res.status(500).json(err);
84+
});
85+
86+
app.listen(port, host, 511, function () {
87+
logger.info('Listening on http://%s:%s', host, port);
88+
});

0 commit comments

Comments
 (0)