AWK HTTPサーバ

AWK Users JPの中で紹介されている簡易HTTPサーバをベースに少しだけ機能追加してみた。例では固定の文字列しか扱っていなかったので最低限リクエストしたパスに従いそのファイルを表示できるようにした。

ソースコード – httpd.awk

http://github.com/yokawasa/any/tree/master/awk_httpd/
http://github.com/yokawasa/any/blob/master/awk_httpd/httpd.awk

#! /usr/bin/gawk -f

BEGIN {
    port = "8080";
    docroot = "./";
    http_service = "/inet/tcp/" port "/0/0";
    RS = ORS = "\r\n";
    for (;;) {
       if ((http_service |& getline reqline) > 0) {
            request_handler(http_service, reqline, docroot);
        }
       close(http_service);
    }
}

function request_handler(http_service,reqline, docroot) {
    # parse request line
    if ( split(reqline, t, " ") !=3 ) {
        show_error(http_service, 400, "Bad Request");
        return 1;
    }
    req_method = t[1];
    req_uri = (index(t[2], "/")==1) ? substr(t[2],2) : t[2];
    if (req_method != "GET" ) {
        show_error(http_service, 405, "Method Not Allowed");
       return 1;
    }
    # set path and query string
    path = docroot req_uri;
    n = split(path,tt,"?");
    if (n >=2 ) {
        path = tt[1];
        ENVIRON["QUERY_STRING"] = tt[2];
    }
    # path should end with "/" if it's directory.
    if(dir_exists(path)) {
        if (substr(path,length(path)) != "/") {
            path = path "/";
        }
        # set default file
        path = path "index.html";
    }

    # check if file exists
    if (!file_exists(path)) {
        show_error(http_service, 404, "Not Found");
       return 1;
    }
    show_page(http_service, path);
    return 0;
}

function file_exists(path) {
    cmd ="if [ -f " path " ]; then echo OK; fi";
    exist = 0;
    if ( (cmd |getline res) > 0) {
        gsub("\n","", res);
        if (res == "OK") {
            exist = 1;
        }
    }
    close(cmd);
    return exist;
}

function dir_exists(path) {
    cmd ="if [ -d " path " ]; then echo OK; fi";
    exist = 0;
    if ( (cmd |getline res) > 0) {
        gsub("\n","", res);
        if (res == "OK") {
            exist = 1;
        }
    }
    close(cmd);
    return exist;
}

function file_read(file) {
    buf = "";
    while (getline < file > 0) {
         buf = buf $0;
    }
    close(file);
    return buf;
}

function find_mime_type(path) {
    mime_type = "application/ocet-stream"; # default mime type
    n = split(path, t, "/");
    if (n >=2 ) {  
        filename = t[n];
        m = split(filename, tt, ".");
        if (m >=2 ) {
            ext = tolower(tt[m]);
            if (ext == "html" || ext == "htm") {
                mime_type = "text/html";
            }else if (ext == "css" ) {
                mime_type = "text/css";
            }else if (ext == "txt" || ext == "text" ) {
                mime_type = "text/plain";
            }
        }
    }
    return mime_type;
}

function show_page( http_service,path) {
    outbuf = file_read(path);
    mime_type = find_mime_type(path);
    content_len = length(buf);
    print_output(http_service,200,"OK",outbuf,mime_type,content_len);
}

function show_error( http_service, errcode, reason) {
    outbuf = "<h1>" reason "</h1>";
    mime_type = "text/html";
    content_len = length(buf);
    print_output(http_service,errcode,reason,outbuf,mime_type,content_len);
}

function print_output( http_service,code,reason,outbuf,mime_type,content_len) {
    print "HTTP/1.x " code " " reason |& http_service;
    print "Content-type: " mime_type |& http_service;
    print "Content-Length: " content_len |& http_service;
    print ""                        |& http_service;
    print outbuf |& http_service;
}

このプログラムの肝はgawkネットワーク接続用ファイル表記/inet/~でTCP/IPネットワーク接続の利用を宣言しhttp_service変数に格納、 ‘|&’ オペレータでINPUTとOUTPUTの2方向のネットワーク接続パイプを作成し、 ネットワーク接続パイプラインからのINPUT内容は” http_service |& getline’ で受けとり、 OUTPUT内容は ‘ print “書き込む内容” |& http_service’ で書き込む。 これに尽きる。

あと、残念なことにAWKはバイナリーデータが扱えない。よってHTTPサーバとしては致命的ではあるが画像やその他メディアファイルの表示をすることができない。 このHTTPサーバでは .htm、.html、.css、.txtファイルに対してはそれぞれMIMEタイプを’text/html’、’text/css’、’text/plain’としているがそれ以外のファイルタイプに対しては一律’application/ocet-stream’としている。

サーバ起動、サンプルページの表示

git clone でソースコードを取得する。リポジトリanyのサブディレクトリ any/awk_httpdが今回のサンプルにあたる。そして取得したhttpd.awkをgawkのファイル指定で実行する。gawkのパスが/usr/bin/gawkな場合は直接 ./httpd.awkで実行すればよい。 httpd.awkはデフォルトでポートが8080、ドキュメントルートが”./”となっている。ちなみに同一ディレクトリにポートとドキュメントルートのオプション指定ができるhttpd.igawkを用意している。 これを使用するには実行環境にigawkシェルスクリプトがインストールされている必要がある。

$ git clone git@github.com:yokawasa/any.git  
Initialized empty Git repository in /home/m/dev/github/t/any/.git/
remote: Counting objects: 45, done.
remote: Compressing objects: 100% (44/44), done.
remote: Total 45 (delta 11), reused 0 (delta 0)
Receiving objects: 100% (45/45), 13.99 KiB, done.
Resolving deltas: 100% (11/11), done

$ cd any/awk_httpd

$ ls -1
httpd.awk
httpd.igawk
pages

$ gawk -f ./httpd.awk

ページ表示テスト用にany/awk_httpd/pages下にhtml、cssファイルを用意してあるのでこれらを/home/awk_httpd/pages配下に配置して実際にブラウザでindex.htmlを表示させてみる。成功すると以下のようなページが表示される。

AWK HTTP Server Page

おわり。

Related posts:

  1. LibeventとAPRでイベント駆動型HTTPサーバを作成してみた

Posted in: Programming / プログラミング

Tags: , , , ,



DeliciousFacebookRedditTwitterGoogle

1 Comment

rssComments RSS transmitTrackBack Identifier URI


[...] This post was mentioned on Twitter by itakada17, ussy. ussy said: ほえー AWK HTTPサーバ http://yk55.com/blog/2010/06/29/awk_http/ [...]

Pingback by Tweets that mention AWK HTTPサーバ – Yoichi Kawasaki blog -- Topsy.com on July 24, 2010 3:26 am

addLeave a comment