Skip to content

Commit

Permalink
Project import
Browse files Browse the repository at this point in the history
  • Loading branch information
Joris Berthelot committed Nov 6, 2015
0 parents commit 23779e9
Show file tree
Hide file tree
Showing 9 changed files with 379 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.git
.DS_Store
node_modules
18 changes: 18 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
; This file is for unifying the coding style for different editors and IDEs.
; More information at http://editorconfig.org

root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

; Works with some editors only
quote_type = single
max_line_length = 120
spaces_around_brackets = true
spaces_around_operators = true
21 changes: 21 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
sudo: required

env:
global:
- TAG_PATTERN="^[0-9]+(\.[0-9]+){2}(-(alpha|beta|rc))?$"
- DOCKER_IMAGE=eexit/mirror-http-server:${TRAVIS_TAG:=$TRAVIS_BUILD_NUMBER}

services:
- docker

before_install:
- docker login --email=$DOCKER_EMAIL --username=$DOCKER_USER --password=$DOCKER_PASSWD

install:
- docker build -t $DOCKER_IMAGE .

script:
- docker run $DOCKER_IMAGE npm test

after_success:
- if [[ "$TRAVIS_TAG" =~ $TAG_PATTERN ]]; then docker push $DOCKER_IMAGE; fi
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FROM node:4.2-onbuild
MAINTAINER Joris Berthelot <[email protected]>
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2015 Joris Berthelot

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
195 changes: 195 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
![logo](logo.png)

# Mirror HTTP Server [![Build Status](https://travis-ci.org/eexit/mirror-http-server.svg)](https://travis-ci.org/eexit/mirror-http-server)

*A dummy HTTP server that responds whatever you told him to.*

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.

## Usage

Pull the [Docker](https://www.docker.com) container:

$ docker pull eexit/mirror-http-server

Start the container:

$ docker run -itp 80:80 eexit/mirror-http-server
2015-11-05T20:59:57.353Z] INFO: mirror-http-server/17 on ccc867df5980: Listening on http://0.0.0.0:80

For this README examples, I use the great [HTTPie](https://github.com/jkbrzt/httpie) tool.

Send request againt it:

http $(docker-machine ip default)

```http
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 0
Date: Thu, 05 Nov 2015 21:33:20 GMT
X-Powered-By: Express
```

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.

### Behavioural request headers

You can change the server response code and body by setting specific `X-Mirror-*` headers to your request.

### `X-Mirror-Code`

Change the server response [status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes).
Here, simulate a server error:

$ http $(docker-machine ip default) X-Mirror-Code:503

```http
HTTP/1.1 503 Service Unavailable
Connection: keep-alive
Content-Length: 0
Date: Thu, 05 Nov 2015 22:30:11 GMT
X-Powered-By: Express
```

Here, simulate a `301` redirection:

http $(docker-machine ip default) \
X-Mirror-Code:301 \
X-Mirror-Location:http://www.eexit.net \
X-Mirror-Content-Type:"text/plain; charset=ISO-8859-1"

```http
HTTP/1.1 301 Moved Permanently
Connection: keep-alive
Content-Length: 0
Content-Type: text/plain; charset=ISO-8859-1
Date: Thu, 05 Nov 2015 22:40:02 GMT
Location: http://www.eexit.net
X-Powered-By: Express
```

If you add the `--follow` option, it will output my website HTML source.

If you check the container logs:

```json
[2015-11-05T22:48:59.564Z] INFO: mirror-server/18 on 6cb74ed853b0:
request: {
"ip": "192.168.99.1",
"ips": [],
"method": "GET",
"url": "/",
"headers": {
"host": "192.168.99.100",
"x-mirror-code": "301",
"accept-encoding": "gzip, deflate",
"x-mirror-location": "http://www.eexit.net",
"accept": "*/*",
"user-agent": "HTTPie/0.9.2",
"connection": "keep-alive",
"x-mirror-content-type": "text/plain; charset=ISO-8859-1"
},
"body": {}
}
```

### `X-Mirror-Request`

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:

$ http POST $(docker-machine ip default)/resource \
X-Mirror-Code:201 \
X-Mirror-Request:true \
key1=value1 key2=value2

```http
HTTP/1.1 201 Created
Connection: keep-alive
Content-Length: 373
Content-Type: application/json; charset=utf-8
Date: Thu, 05 Nov 2015 22:57:17 GMT
ETag: W/"175-3rxm7gM5Zwu88cZOABP92A"
X-Powered-By: Express
{
"request": {
"body": {
"key1": "value1",
"key2": "value2"
},
"headers": {
"accept": "application/json",
"accept-encoding": "gzip, deflate",
"connection": "keep-alive",
"content-length": "36",
"content-type": "application/json",
"host": "192.168.99.100",
"user-agent": "HTTPie/0.9.2",
"x-mirror-code": "201",
"x-mirror-request": "true"
},
"ip": "192.168.99.1",
"ips": [],
"method": "POST",
"url": "/resource"
}
}
```

### `X-Mirror-Body`

Instead, if you with the dummy server to return you the same body you requested to it, set the `X-Mirror-Body` header.

Note: the `X-Mirror-Request` header will override `X-Mirror-Body` header.

$ http PUT $(docker-machine ip default)/resource \
X-Mirror-Code:400 \
X-Mirror-Body:true \
key1=value1 key2=value2

```http
HTTP/1.1 400 Bad Request
Connection: keep-alive
Content-Length: 33
Content-Type: application/json; charset=utf-8
Date: Thu, 05 Nov 2015 23:52:34 GMT
ETag: W/"21-/0XMODUWUwfvQUwjyixvZw"
X-Powered-By: Express
{
"key1": "value1",
"key2": "value2"
}
```

### Works for all headers

Aside to the previous three special headers, you can set your wanted response header by prepending your header name by `X-Mirror-`.

In the request:

```http
Content-Type: application/json
X-Mirror-Content-Type: text/html
```

You'll get in your response:

```http
Content-Type: text/html
```

You can even override Express headers or any other default header:

```http
X-Mirror-X-Powered-By: eexit-engine
X-Mirror-Date: some date
```

Will turn into:

```http
X-Powered-By: eexit-engine
Date: some date
```
Binary file added logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "mirror-http-server",
"version": "1.0.0",
"description": "A dummy HTTP server that responds whatever you told him to",
"scripts": {
"start": "node server.js | npm run bunyan",
"start:dev": "nodemon server.js | npm run bunyan",
"test": "echo \"No test specified yet\"",
"bunyan": "$(npm bin)/bunyan"
},
"keywords": [
"node",
"nodejs",
"server",
"http",
"mirror",
"dumb",
"dump",
"test",
"development"
],
"author": "Joris Berthelot <[email protected]>",
"license": "MIT",
"dependencies": {
"body-parser": "^1.14.1",
"bunyan": "^1.5.1",
"express": "^4.13.3",
"lodash": "^3.10.1",
"nodemon": "^1.8.1"
}
}
88 changes: 88 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
'use strict';

var host = process.env.HOST || '0.0.0.0';
var port = process.env.PORT || 80;

var _ = require('lodash');
var bunyan = require('bunyan');
var bodyParser = require('body-parser');
var pckg = require(__dirname + '/package.json');
var logger = bunyan.createLogger({name: pckg.name});
var express = require('express');
var app = express();

app.enable('trust proxy');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// Intercepts all HTTP verb requests
app.all('*', function (req, res, next) {
// Returned response headers
var responseHeaders = {};

// Parses the wanted response code
var mirrorCode = req.get('X-Mirror-Code') || 200;

// Finds out if the request should be returned as the response
var mirrorRequest = (req.get('X-Mirror-Request')
&& req.get('X-Mirror-Request').toLowerCase() == 'true')
|| false;

// Finds out if the response should be returned
var mirrorBody = (req.get('X-Mirror-Body')
&& req.get('X-Mirror-Body').toLowerCase() == 'true')
|| false;

// Parses X-Mirror-* headers, skips app specific headers
var reqHeaders = _.without(
_.filter(
Object.keys(req.headers), function (name) {
return _.startsWith(name, 'x-mirror-');
}
), 'x-mirror-code', 'x-mirror-request', 'x-mirror-body'
);

// Injects X-Mirror-* headers to response headers
reqHeaders.forEach(function (name) {
var resHeader = _.startCase(_.trimLeft(name, 'x-mirror-')).replace(' ', '-');
responseHeaders[resHeader] = req.headers[name];
});

// Builds the request object
var request = {
request: {
ip: req.ip,
ips: req.ips,
method: req.method,
url: req.originalUrl,
headers: req.headers,
body: req.body
}
};

logger.info(request);


// Prepares the response
res.status(mirrorCode).set(responseHeaders);

// Appends the full request or only the request body if wanted
if (mirrorRequest) {
res.json(request);
} else if (mirrorBody) {
res.send(req.body);
}

// Flushes!
res.end();
});

// Basic error handler
app.use(function (err, req, res, next) {
logger.fatal(err);
res.status(500).json(err);
});

app.listen(port, host, 511, function () {
logger.info('Listening on http://%s:%s', host, port);
});

0 comments on commit 23779e9

Please sign in to comment.