概要

なんとなく作り始めてみました。
セキュリティホールがいっぱいあると思われるので、ローカルホストの外に出しちゃダメね。GItHubのリポジトリはこちら → https://github.com/kunst1080/shwaf/

ざっくりとした仕様など

動作確認はFreeBSD 8.3でのみ実施。
最近Play Frameworkを使ってるので、だいたいそんなかんじ。

  • httpサーバはNetCatにおまかせ
  • ルーティングは「router.sh」に書く
  • modelはひとまずなし。
  • viewはhtml。バッククォートでシェルスクリプトが書ける
    • htmlの内容がヒアドキュメントとしてcatコマンドに埋め込まれます
  • controllerは普通のシェルスクリプト

最低限必要なports

/usr/local/bin/bash

設計

  • bin/start が bin/listen を無限ループで呼び出す
  • bin/listen は、1回のリクエスト毎にリクエストヘッダを解析し、メソッド名とurlを引数にrouter.shを呼び出す
  • router.sh はシェル内の記述に則り、controller(app/*.sh)を呼び出す
  • controller(app/*.sh)はいろいろ処理した後、renderコマンドでhtml(app/html/*.html)を出力する
    • renderコマンドにはレスポンスコード(OK|BAD_REQUEST)・コンテキストタイプ・出力するhtmlファイルのファイル名を渡す

ソースコード一部抜粋

<View>

app/html/index.html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<html>
    <head>
        <title>テスト</title>
    </head>
    <body>
    <p class="hoge">hello world</p>
    <p>`date`</p>
    </body>
</html>

※出力はこうなります

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<html>
    <head>
        <title>テスト</title>
    </head>
    <body>
    <p class="hoge">hello world</p>
    <p>2014年 1月16日 木曜日 03時22分14秒 JST</p>
    </body>
</html> 

<Controller>

app/index.sh
1
2
3
#!/bin/sh
render OK HTML index.html

lib/render

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#!/bin/sh

status=$1
content=$2
html=$3

cat lib/status/${status}
cat lib/contenttype/${content}
echo ""
printf "cat <<++EOF\n`cat app/html/${html}` \n++EOF\n" | sh

<基盤>

router.sh
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#!/bin/sh
PATH=$PATH:lib

method=$1
url=$2

case "${url}" in
    "/" | "/index.html")
        app/index.sh
        return $?
        ;;
    *)
        render BAD_REQUEST PLAIN error.txt
        return 9
        ;;
esac

bin/listen
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/local/bin/bash

LISTEN_PORT=8080

function server() {
    # request
    IFS=' '
    read -u ${FD} "method" "path" "protocol"
    # header
    #  TODO:後で実装。awk使ったほうがいいかも。
    declare -A HEADERS
    HEADERS[Host]="ServerName"
    
    ./router.sh "${method}" "${path}"

    # status
    if [ $? -eq 0 ] ; then
        echo "${HEADERS[Host]};`date`; ${method}; ${path}" >&2
    else
        echo "process failed : ${HEADERS[Host]};`date`; ${method}; ${path}" >&2
    fi
    unset HEADERS
}

PIPE_FILE=tmp/server.$$
if [ ! -e "${PIPE_FILE}" ];
then
    mkfifo "${PIPE_FILE}"
fi
exec {FD}<>"${PIPE_FILE}"
trap "test -e ${PIPE_FILE} && rm -f ${PIPE_FILE};exit" 0 HUP INT QUIT

server | nc -l ${LISTEN_PORT} >&${FD}

参考URL