khttpsvは、オブジェクト指向スクリプト言語Rubyによって実装された HTTPサーバ(Webサーバ)です。 現在のところCGIは実装されていますが、SSIやユーザ認証機能は未実装です。
差分プログラミング(*1)によって各種設定を行いますので、 種々の設定ファイルが不要になっています。 これは、サーバを作るほうにとっても 設定ファイルの解析が不要になるというすばらしい副作用があります^^;;
(*1) 機能を追加する際、従来からの変更部分だけプログラミングすれば 実装が可能になってしまうという手法
とはいえ、スクリプト言語の常として、実行速度はあまり速くはありません。 高いパフォーマンスが要求されない個人的な学習用・実験用・組み込み型 Webサーバとしてお使いいただければ幸いです。
RubyがインストールされているUNIX環境なら、たぶん動きます。
# いいかげんですいません ^^;;
なお、 Ruby1.4.6 / Linux 2.2.16(Debian GNU/Linux 2.1) で動作確認をしました。
配付は、 http://www.geocities.co.jp/Technopolis/6855/khttpsv.tar.gz にて行っています。GPL2に従ってなんなりと。
# なんなりとナニするんだ…
さきにも述べたとおり、設定ファイルは実行スクリプトでもあります。 まずは、以下の内容を持ったsample_template.rbというファイルを 適当な名前にコピーしてください。拡張子は.rbにしてください。
require 'khttpsv_simple.rb' class Sample_server < Khttpsv_simple end Sample_server.start(8080)
もし8080番以外のポートで動かしたい場合は、 Sample_server.start(8080)の数値を変更してください。 また、コンテンツは/usr/local/www/htdocs/、 または~user/.public_html/以下に置いてください。
これらの準備がすんだら、あとは
% ruby ファイル名.rbで実行するだけです。 ファイルに実行属性を付け、先頭行に#!/usr/local/bin/rubyなどの行を 付けても良いでしょう。
このように、基本的にはsample_template.rbをコピーして "class 〜"から"end"までの間を書きたしていくことによって設定を行います。
Ruby(やその他のオブジェクト指向言語)をご存知の方なら、 これが「クラスの継承」によってなされていることにお気づきかと思います。 ですから、設定はRubyの文法が許す限りどんな処理でも書くことができます(*2)。
(*2) Rubyの詳細な文法はhttp://www.ruby-lang.org/から得ることができます。
例
def initialize(s)
super
@server_name = 'MyPrivateServer/2.2.16'
(以下略)
end
デフォルトでは、アクセスを受け付けたIPアドレスを返すように定義されています。 バーチャルホスティングを実施する場合は、 このメソッドを必ず再定義する必要があります。
例: バーチャルホスティングなしの設定
def get_server_addr
return 'www.tomoeda-es.ac.jp'
end
例: 名前ベースのバーチャルホスティング
def get_server_addr
# name-based virtual hosting
if @req_headers['Host'] then
if @req_headers['Host'][0] =~ /^www.domain1.co.jp:/i then
return 'www.domain1.co.jp'
elsif @req_headers['Host'][0] =~ /^www.domain2.co.jp:/i then
return 'www.domain2.co.jp'
end
end
return 'www.default.co.jp'
end
ホスト名ベースのバーチャルドメインを作る場合、
ネットワークインターフェースに複数のIPアドレスを割り当てる必要はありません。
ただし、古いHTTPクライアントを使っている場合は
これらのバーチャルドメインを区別して扱うことができません。
例: アドレスベースのバーチャルホスティング
def get_server_addr
# IP-based virtual hosting
if @sock.addr[3] == '192.168.2.1' then
return 'www.domain1.co.jp'
elsif @sock.addr[3] == '192.168.2.2' then
return 'www.domain2.co.jp'
else
return 'www.default.co.jp'
end
end
コンピュータが複数のIPを持っている場合、
アドレスベースのバーチャルホスティングを実施することができます。
この場合、複数のネットワークインターフェース(NIC)を持つか、
ひとつのNICに複数のIPアドレスを割り当てることになります。
例
def initialize(s)
super
# like an NCSA httpd
@index_file = 'welcome.html'
# http://server/~foo/bar/baz.html seeks for ~foo/.www/bar/baz.html
@user_document_dir = '.www'
...
end
例: バーチャルドメインを使わない場合
def document_root
return '/var/www'
end
常に/var/wwwディレクトリをドキュメントルートとします。
例: バーチャルホスティングを使う場合
def document_root
case @server_addr
when 'www.domain1.co.jp'
return '/var/www/dir1'
when 'www.domain2.co.jp'
return '/var/www/dir2'
else
return '/var/www'
end
end
この例の場合、
クライアントが"www.domain1.co.jp"にアクセスしていると思っているなら/var/www/dir1を、
クライアントが"www.domain2.co.jp"にアクセスしていると思っているなら/var/www/dir2を
それぞれドキュメントルートとします。
例
def hook_translate_path(s)
if s.sub!(%r{^/cgi-bin/}, '/usr/lib/cgi-bin/') then
# translate uri into path, for exapmle
# http://server/cgi-bin/foo/bar.cgi -> /usr/lib/cgi-bin/foo/bar.cgi
return s
end
if s.sub!(%r{^/icons/}, '/usr/X11/lib/X11/pixmap/') then
return s
end
return super
end
この例では、
http://server/cgi-bin/...というURLが要求されると/usr/lib/cgi-bin/...というパスに、
http://server/icons/...というURLが要求されると/usr/X11/lib/X11/pixmap/...というパスに、
それぞれ置き換えます。
なお、Rubyではメソッド名に'?'を含めることができます。
例
def allow?(a)
return true if a == 'cgi_exec'
return super
end
この例では、CGIの実行('cgi_exec')を明示的に許可し、
その他はデフォルトの動作に従うようになっています。
例
def cgi?
if @translated_path[-4,4] == '.asp' && FileTest.executable?(@translated_path) then
return true
elsif @translated_path[-4,4] == '.cgi' && FileTest.executable?(@translated_path) then
return true
end
return false
end
aspまたはcgiという拡張子をもつ実行ファイルをCGIであるとしています。
ちなみに、Rubyでは文字列クラスに[-4,4]という演算子を適用すると、
「うしろから数えて4バイト目から4バイトを取り出した文字列」という意味になります。
MIME_mapはmime_type.rbというファイルで定義されているので、 メソッドを再定義しなくてもこちらを変更すればよい場合があります。
例
def get_mime_type
if @translated_path[-4,4].downcase == '.doc'
return 'text/plain'
else
return super
end
end
この例では、拡張子がdocの場合(MS-Wordではなく)plain textであるとし、
それ以外の場合はデフォルトの動作を引き継ぎます。
デフォルトでは、LOG_INFOより重要なメッセージを、 スレッドIDと一緒に標準出力に表示します (たぶん、再定義したいでしょう)。
例
def Khttpsv_template.logger(s, lev = LOG_INFO)
pri = nil
case lev
when LOG_NOTICE
pri = 'local0.notice'
when LOG_WARN
pri = 'local0.warning'
when LOG_ERR
pri = 'local0.error'
end
if pri then
system('logger', '-t', 'khttpsv', '-p', pri, "#{Thread.current.id}: #{s}")
end
STDERR.print "#{Thread.current.id}: #{s}\n"
end
この例では、LOG_INFOより重要なメッセージをsyslogに出力します。
また、すべてのメッセージを標準エラーに出力します。