はじめに
私は組み込みメインの開発ばかりで、Web周りはインフラ側、触ったとしてもAjaxのXMLHttpRequestでレスポンスパースするような画面とは関わらない処理を書くくらいでした。
なのでちょっと視界を広げようと思い、Webフロントエンド側の開発もちょっとやってみようかと思い立ちました。
今回はまずnode.jsを利用して、cgiが利用可能な簡易webサーバーを作成します。
Node.jsのインストール
Node.jsは、サーバーサイドで動作させることが出来るツールです。
パッケージはNode.jsのパッケージ管理ツールであるnpmをインストールすれば一緒にインストールされます。
sudo apt install npm
or
sudo yum install npm
作成webサーバーの機能仕様
以下のような構成で動作するwebサーバーとなっています。topdir
はコード内で/usr/local/www/にべた書き。
topdir --- html => index.html達の格納する場所
|
`-- cgi-bin => cgiコマンドを格納する場所
機能
- 8080番ポートでlistenするwebサーバー
- /index.html(or /)アクセス or /cgi-binアクセスをサポート
- /index.html(or /)アクセス時は、
topdir/html/index.html
が開かれる。cssやjsもファイルがあれば取得可能です。 - /cgi-binアクセス時は、/cgi-bin/
コマンド名で指定したコマンドと対応する
topdir/cgi-bin`内の同コマンドを実行し、標準出力の結果がそのままHTTPレスポンスとなるよう結果をパースして処理します。
- /index.html(or /)アクセス時は、
備考
- index.htmlが読み込むファイル達は、ファイルの種類に応じてmime typeを変更する必要があります。(コード内
mime
変数でべた書き) - htmlファイル以外でサポートしたいuriは、
ResArray
に定義してます。追加する場合は、var ResArray = ['cgi-bin'];
の中にurlを追加し、Responseオブジェクトに処理を記載してあげます。 - このコードでは、cgiはpython3.6で動作するもの限定としています。
execSync
のpython3.6
部分を適宜変更してください。
動作確認環境
Ubuntu 18.04 Desktop
コード
こちらの記事をベースに、このような実装にしました。
// 必要なファイルを読み込み
var http = require('http');
var url = require('url');
var fs = require('fs');
var path = require('path');
//server定義
var server = http.createServer();
//serverのtop directory定義
var topdir = "/usr/local/www/"
//mime type定義
var mime = {
".html": "text/html",
".css": "text/css; charset=utf-8",
".js": "application/javascript",
".png": "image/png"
//必要に応じてmime typeを追加
};
// http.createServerがrequestを受け取った場合の処理
server.on('request', function (req, res) {
// ファイル読み込みじゃないURIはここに記載
var ResArray = ['cgi-bin'];
// Responseオブジェクトを作成し、その中に必要な処理を書いていき、条件によって対応させる
var Response = {
// ファイル読み込みURI
"other": function (req_path) {
try {
//html内のファイルなら読み込み
var fpath = topdir + 'html' + req_path.split("?")[0]
if ( fpath === topdir + 'html/' ) {
fpath += "index.html"
}
fs.statSync(fpath);
data = fs.readFileSync(fpath)
// HTTPレスポンスヘッダを出力する
res.writeHead(200, {
'content-type': mime[path.extname(fpath)] || "text/plain",
'content-length': data.length
});
// HTTPレスポンスボディを出力する
res.write(data);
res.end()
} catch(err) {
//ファイルが無いならNot Foundにする
res.writeHead(404, {"content-type": "text/plain"});
res.write("404 Not Found\n");
res.end()
}
},
//cgi実処理
"cgi-bin": function (req_path) {
//コマンド、query設定
var command = req_path.split("/")[2].split("?")[0]
var query_string = req_path.split("/")[2].split("?")[1]
const execSync = require('child_process').execSync;
//これはpython3.6で実行するcommand限定。queryは環境変数QUERY_STRINGに設定
var result = execSync("cd " + topdir + "cgi-bin; QUERY_STRING=\""+ query_string + "\" python3.6 " + command).toString();
separate_head_body = result.split("\n\n")
if ( separate_head_body.length === 1 ) {
head={'content-Type': 'text/plain'}
body=separate_head_body[0]
} else {
head = ParseHeader(separate_head_body[0])
body=separate_head_body[1]
}
console.log("head:" + head.toString())
res.writeHead(200, head)
// HTTPレスポンスボディを出力する
console.log("body:" + body)
res.write(body);
res.end()
}
}
// cgiのレスポンスパース処理
function ParseHeader(header_string) {
// HTTPヘッダーは連想配列で表現する。
var head_hash={}
// 各HTTPヘッダーは改行で分割
heads=header_string.split("\n")
for(let i = 0; i < heads.length; i++) {
// HTTPヘッダーをさらに:で分ける。
part = heads[i].split(":")
//execSyncでの実行結果で改行コードが変わるケースがあったので、Content-Lengthは指定せずchunkedにする
if ( part[0].toLowerCase() !== "content-length" ) {
//HTTPヘッダーをhashに追加
head_hash[part[0].toLowerCase()] = part[1]
}
}
return head_hash
}
// URI取得、
function GetURI (uri) {
uri_part = uri.split("/")
if ( ResArray.indexOf(uri_part[1]) === -1 ) {
return "other"
} else {
return uri_part[1]
}
}
// server.onのメイン処理
var uri = url.parse(req.url).pathname;
var req_path = url.parse(req.url).path;
// urlと対応するResponseオブジェクト内関数を実行
Response[GetURI(uri)](req_path);
});
// 指定されたポート(8080)でコネクションの受け入れを開始する
server.listen(8080)
実行
html、cgiを所定の位置に格納して以下を実行するだけです。
node webServer.js
http://ip:8080/index.html
等該当のurlに対する応答が帰ってくればOK
cgiはこちらのcgi-binを/usr/local/wwwにコピーして動作確認。
index.htmlはDoxygenで生成したファイル(自分はここのdocsフォルダ内)を/usr/local/www/htmlに配置して動作確認をしました。
cgiの実行はこちらを参考に、require('child_process').execSync
を利用してコマンド実行しています。
参考
コードの参考: ExpressなしでNode.jsで簡単なWebサーバを作ってみる、HTTPを学ぶ
HTTPリクエストパス取得: Node.jsでURLをパースする
mime : Node.jsでhttpサーバを立てた際にCSSが読み取れない場合の対処法について
cgiの実行: Node.jsからシェルコマンドを実行する