WEBRick を使ってプラグインを公開する方法
WEBRick の問題点
プラグインを自動的にインストールできる plugin コマンドにはいつもお世話になっている。このコマンドの実体は、(Rails gem home directory)/lib/commands/plugin.rb にある。ソースコードをつらつらと眺めていると、
% ruby script/plugin install http://www.example.com/plugins/foobar/
なとどした場合、普通に http でファイルを取ってくるだけのようだ。しかし、plugin コマンドはなかなか賢くて、ウェブサーバが返すディレクトリのファイルリストを元にサブフォルダもろともごっそりと、RAILS/vendor/plugin/foobar/ 以下にダウンロードしてくれる。
WEBRick を使ってプラグインを公開することもできるのだが、その場合、1つ問題点がある。WEBrick が表示するディレクトリのファイルリストは、ファイル名とか項目ごとにソートができるようになっていて、その部分ハイパーリンクが plugin コマンドを誤作動させるのだ。それを避ける方法を考えてみた。
要点は、いらないハイパーリンクを消すだけ。
# WEBrick::HTTPServlet::FileHandler#set_dir_list ... #res.body << " <A HREF=\"?N=#{d1}\">Name</A> " res.body << " Name " #res.body << "<A HREF=\"?M=#{d1}\">Last modified</A> " res.body << "Last modified " #res.body << "<A HREF=\"?S=#{d1}\">Size</A>\n" res.body << "Size\n" ...
ごくシンプルなウェブサーバを作ってみたので、参考にしてほしい。
サンプルコード
#!/usr/local/bin/ruby require 'webrick' module WEBrick module HTTPServlet class FileHandler def set_dir_list(req, res) redirect_to_directory_uri(req, res) unless @options[:FancyIndexing] raise HTTPStatus::Forbidden, "no access permission to `#{req.path}'" end local_path = res.filename list = Dir::entries(local_path).collect{|name| next if name == "." || name == ".." next if nondisclosure_name?(name) st = (File::stat(local_path + name) rescue nil) if st.nil? [ name, nil, -1 ] elsif st.directory? [ name + "/", st.mtime, -1 ] else [ name, st.mtime, st.size ] end } list.compact! if d0 = req.query["N"]; idx = 0 elsif d0 = req.query["M"]; idx = 1 elsif d0 = req.query["S"]; idx = 2 else d0 = "A" ; idx = 0 end d1 = (d0 == "A") ? "D" : "A" if d0 == "A" list.sort!{|a,b| a[idx] <=> b[idx] } else list.sort!{|a,b| b[idx] <=> a[idx] } end res['content-type'] = "text/html" res.body = <<-_end_of_html_ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> <HTML> <HEAD><TITLE>Index of #{HTMLUtils::escape(req.path)}</TITLE></HEAD> <BODY> <H1>Index of #{HTMLUtils::escape(req.path)}</H1> _end_of_html_ res.body << "<PRE>\n" #res.body << " <A HREF=\"?N=#{d1}\">Name</A> " res.body << " Name " #res.body << "<A HREF=\"?M=#{d1}\">Last modified</A> " res.body << "Last modified " #res.body << "<A HREF=\"?S=#{d1}\">Size</A>\n" res.body << "Size\n" res.body << "<HR>\n" list.unshift [ "..", File::mtime(local_path+".."), -1 ] list.each{ |name, time, size| if name == ".." dname = "Parent Directory" elsif name.size > 25 dname = name.sub(/^(.{23})(.*)/){ $1 + ".." } else dname = name end s = " <A HREF=\"#{HTTPUtils::escape(name)}\">#{dname}</A>" s << " " * (30 - dname.size) s << (time ? time.strftime("%Y/%m/%d %H:%M ") : " " * 22) s << (size >= 0 ? size.to_s : "-") << "\n" res.body << s } res.body << "</PRE><HR>" res.body << <<-_end_of_html_ <ADDRESS> #{HTMLUtils::escape(@config[:ServerSoftware])}<BR> at #{req.host}:#{req.port} </ADDRESS> </BODY> </HTML> _end_of_html_ end end end end include WEBrick mime_types = HTTPUtils::DefaultMimeTypes mime_types['rhtml'] = 'text/html' s = HTTPServer.new( :Port => 2000, :MimeTypes => mime_types, :DocumentRoot => File.expand_path(File.dirname(".")) + "/htdocs" ) trap("INT"){ s.shutdown } s.start