[unix] netcatでhttpサーバ その1

2018年8月20日月曜日

awk HTTP unix web

webサーバからの応答によって、処理を変えたいときにテスト用のwebサーバが欲しくなるよね。
そんな時はnetcatを使おう。

まずは

まずは応答用のHTMLファイルを用意する。
normal.html 改行コードはCRLFにしておく。
HTTP/1.1 200 OK
Content-Type: text/html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
normal
</body>
</html>
テスト用のサーバ
server1.sh
#!/bin/sh
while :
do
  nc -l 8123 <normal.html
done

curlでlocalhost:8123にアクセスすると、応答が返ってくる。
$ curl http://localhost:8123/
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
normal
</body>
</html>

netcatを実行している側ではcurlからのリクエストが標準出力に出ているのがわかる。
$ ./server1.sh
GET /normal.html HTTP/1.1
Host: localhost:8123
User-Agent: curl/7.54.0
Accept: */*


URLのパスによって、応答ファイルを変える。

上記のやり方で、1回目の要求はエラーを返す、2回目の要求はエラーを返すといったことができる。
じゃあ、http://localhost:8123/normal.html のように指定されたファイルを返したい場合はどうするか。

先ほどの例ではリクエストを標準出力にそのまま出していたけど、これを fifo に入れて、netcat の入力に戻してみよう。
server2.sh
#!/bin/sh
trap 'rm server2_fifo' EXIT

mkfifo server2_fifo
while :
do
  nc -l 8123 <server2_fifo | awk 'NR==1 { print $2; exit; }' | xargs -I{} cat '.'{} >server2_fifo
done

まず、4行目の mkfifo で fifo を作る。
netcatは入力をfifoから取り出しつつ、リクエストの内容をパイプでawkに渡す。
リクエストヘッダの1行目の2つ目の要素が、ファイルパスなので、それをawkで取り出す。exitですぐに終了してあげるのがポイント。

GET /normal.html HTTP/1.1

次に取り出したファイルパスの先頭に'.'を付加して、catする。
catした結果はfifoにリダイレクトする。

これで、再度 curl でアクセスしてみよう。
$ curl http://localhost:8123/normal.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
normal
</body>
</html>

normal.htmlの内容が取得できていることがわかる。
ディレクトリや、存在しないファイルを指定すると、サーバ側がエラーになって何も返ってこない。

$ curl http://localhost:8123/
curl: (52) Empty reply from server
$ curl http://localhost:8123/nothing.html
curl: (52) Empty reply from server

$ ./server2.sh 
cat: ./: Is a directory
cat: ./nothing.html: No such file or directory