テスト用のHTTP Serverいろいろ

アプリの単体テストなどでHTTP serverを用意したい時があるので、色々な環境、言語でHTTP serverの起動をまとめてみた。

# 静的コンテンツ
## Amazon S3
[Amazon S3 での静的ウェブサイトのホスティング](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/WebsiteHosting.html)を使う

## Ruby
[WEBrick](https://docs.ruby-lang.org/ja/latest/library/webrick.html)を使う

```html:~/document_root/index.html
<!DOCTYPE html>
<html><body><p>hello, world.</p></body></html>
```

```ruby:httpserver.rb
# encoding: UTF-8
#!usr/bin/env ruby

require 'webrick'

include WEBrick

$server = HTTPServer.new(:Port => 8080, :DocumentRoot => "~/document_root")

trap("INT"){ $server.shutdown }
$server.start
```


```bash:起動
$ ruby httpserver.rb
```

```bash:確認
$ curl http://127.0.0.1:8081/
<!DOCTYPE html>
<html><body><p>hello, world.</p></body></html>
```

## Python 3

[HTTPServer](https://docs.python.jp/3/library/http.server.html#http.server.HTTPServer), [SimpleHTTPRequestHandler](https://docs.python.jp/3/library/http.server.html#http.server.SimpleHTTPRequestHandler)を使う

```html:~/document_root/index.html
<!DOCTYPE html>
<html><body><p>hello, world.</p></body></html>
```
```python:httpserver.py
#! /usr/bin/python
# -*- coding: utf-8 -*-
from http.server import HTTPServer, SimpleHTTPRequestHandler
import time
import os

hostname = '127.0.0.1'
port = 8081

document_root_dir = os.path.expanduser('~/document_root')
os.chdir(document_root_dir)

httpd = HTTPServer((hostname, port), SimpleHTTPRequestHandler)
print(time.asctime(), "Server Starts - %s:%s" % (hostname, port))

try:
    httpd.serve_forever()
except KeyboardInterrupt:
    pass

httpd.server_close()
print(time.asctime(), "Server Stops - %s:%s" % (hostname, port))
```

```bash:起動
$ python ~/httpserver.py
```
```bash:確認
$ curl http://127.0.0.1:8081/
<!DOCTYPE html>
<html><body><p>hello, world.</p></body></html>
```

## Node.js
[express](http://expressjs.com/)を使う

```html:document_root/index.html
<!DOCTYPE html>
<html><body><p>hello, world.</p></body></html>
```

```json:package.json
{
  "name": "httpserver",
  "version": "1.0.0",
  "description": "HTTP server",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "author": "Satoshi Iwaki",
  "license": "ISC",
  "devDependencies": {
    "express": "~4.16.3"
  }
}
```

```javascript:index.js
const express = require('express');
const app = express();
const port = 8081;

app.use('/', express.static(__dirname + '/document_root'));
app.listen(port, () => {
    console.log('Server Starts - port:' + port);
})
```

```bash:起動
$ npm install
$ npm start
```

```bash:確認
$ curl http://127.0.0.1:8081/
<!DOCTYPE html>
<html><body><p>hello, world.</p></body></html>
```

# 動的コンテンツ

## Ruby
[WEBrick](https://docs.ruby-lang.org/ja/latest/library/webrick.html)を使う

以下、POSTされたコンテンツのSHA256ハッシュを返すサンプル

```ruby:httpserver.rb
# encoding: UTF-8
#!usr/bin/env ruby

require 'webrick'

include WEBrick

$server = HTTPServer.new(:Port => 8080, :DocumentRoot => "~/document_root")

load ("./httpserver_servlet.rb")

trap("INT"){ $server.shutdown }
$server.start
```
```ruby:httpserver_servlet.rb
# encoding: UTF-8
#!usr/bin/env ruby

require 'webrick/https'

class HelloServlet < HTTPServlet::AbstractServlet
    @@mutex = Mutex.new

    def do_POST(req, res)
        logger = WEBrick::BasicLog.new()
        logger << "------------ Request  --------------"
        logger << req
        logger << "------------------------------------"

        digest = OpenSSL::Digest::SHA256.hexdigest(req.body)
        res.status = 200
        res.body = "{\"content\" : \"#{req.body}\", \"digest\" : \"#{digest}\"}"
        res['Content-Type'] = "application/json"
        logger << "------------ Response --------------"
        logger << res
        logger << "------------------------------------"
    end
end
$server.mount("/api/digest", HelloServlet)
```
```bash:起動
$ ruby httpserver.rb
```

```bash:確認
curl -H "Content-Type: text/plain" --data "hello, world." http://127.0.0.1:8081/api/digest
{"content" : "hello, world.", "digest" : "df4f2d5707fd85996a13aab607eb3f6b19d0e99d97281d5eae68a6c16317a329"}
```

## Python 3
[HTTPServer](https://docs.python.jp/3/library/http.server.html#http.server.HTTPServer), [BaseHTTPRequestHandler](https://docs.python.jp/3/library/http.server.html#http.server.BaseHTTPRequestHandler)を使う

以下、POSTされたコンテンツのSHA256ハッシュを返すサンプル

```python:httpserver.py
#! /usr/bin/python
# -*- coding: utf-8 -*-
from http.server import HTTPServer, BaseHTTPRequestHandler
import hashlib
import time

hostname = '127.0.0.1'
port = 8081

class HelloHandler(BaseHTTPRequestHandler):
	def do_POST(self):
		if self.path=="/api/digest":
			length = int(self.headers['Content-Length'])
			content = self.rfile.read(length)
			digest = hashlib.sha256(content).hexdigest();
			message = '{{"content" : "{}", "digest" : "{}"}}'.format(str(content, encoding='utf-8'), digest)

			self.send_response(200)
			self.send_header('Content-Type', 'application/json')
			self.send_header('Content-Length', str(len(message)))
			self.end_headers()
			self.wfile.write(bytes(message, 'utf-8'))
		else:
			self.send_error(404,'File Not Found: %s' % self.path)
		return

httpd = HTTPServer((hostname, port), HelloHandler)
print(time.asctime(), "Server Starts - %s:%s" % (hostname, port))

try:
    httpd.serve_forever()
except KeyboardInterrupt:
    pass

httpd.server_close()
print(time.asctime(), "Server Stops - %s:%s" % (hostname, port))
```

```bash:起動
$ python httpserver.py
```

```bash:確認
$ curl -H "Content-Type: text/plain" --data "hello, world." http://127.0.0.1:8081/api/digest
{"content" : "hello, world.", "digest" : "df4f2d5707fd85996a13aab607eb3f6b19d0e99d97281d5eae68a6c16317a329"}
```

## Node.js

[express](http://expressjs.com/)を使う

以下、POSTされたコンテンツのSHA256ハッシュを返すサンプル

```json:package.json
{
  "name": "httpserver",
  "version": "1.0.0",
  "description": "HTTP server",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "test": "test"
  },
  "author": "Satoshi Iwaki",
  "license": "ISC",
  "devDependencies": {
    "express": "~4.16.3",
    "body-parser": "~1.18.3"
  }
}
```

```javascript:index.js
const express = require('express');
const bodyParser = require('body-parser');
const crypto = require('crypto');
const app = express();
const port = 8081;
const shasum = crypto.createHash('sha256');

app.use(bodyParser.text());
app.post('/api/digest', (req, res) => {
	shasum.update(req.body);
	const digest = shasum.digest('hex');
	const body = JSON.stringify({
		content: req.body,
		digest: digest.toString()
	});
	res.append('Content-Type', 'application/json');
	res.send(body);
});
app.listen(port, () => {
    console.log('Server Starts - port:' + port);
});
```

```bash:起動
$ npm install
$ npm start
```

```bash:確認
$ curl -H "Content-Type: text/plain" --data "hello, world." http://127.0.0.1:8081/api/digest
{"content":"hello, world.","digest":"df4f2d5707fd85996a13aab607eb3f6b19d0e99d97281d5eae68a6c16317a329"}
```

## Objective-C
[GCDWebServer](https://github.com/swisspol/GCDWebServer)を使う