読者です 読者をやめる 読者になる 読者になる

webrickでリダイレクトするときの注意

久々にはてなダイアリーの「日記を書く」を押すとエディタが変わってました。
どうもお久しぶりです。
最近は社内や派遣先でrubyの勉強会を定期的に開いたりしてruby漬けの毎日です。
その勉強会のネタにしようと以前書いた「IronRubyでWindowsのサービスをコントロールする」を見ながらコードを検証していたのですが、FirefoxChromeでうまく動作しないことが分かりました。現象としてはページ上のリンクをクリックしても内部の処理が行われず、結果表示ページにリダイレクトされてしまうというものです。コードを最小限にして検証してみたのですがこれはIronRubyだけの問題ではなくRubyでも同様の問題が発生するようです。
301 リダイレクトをchromeがキャッシュする」という記事を読むと、リダイレクトを一度行うとURLに対してリダイレクト先をブラウザが記憶してしまうようです。確かにリダイレクト時に設定する「HTTPStatus::MovedPermanently」は恒久的なページの移動を表すものなのでWebブラウザが記憶してしまうのも分かります。
回避方法としてヘッダーにPragma: no-cacheを付けることにしました。
以下が修正したコードです。

web.rb

require 'webrick'
require 'erb'
require 'System.ServiceProcess'
require 'uri'

include System::ServiceProcess

include WEBrick
server = HTTPServer.new({
  :Port => 8080,
  :BindAddress => '127.0.0.1',
  :MimeTypes => HTTPUtils::DefaultMimeTypes.merge({"rhtml"=>"text/html"}),
  :DocumentRoot => Dir.pwd})

class ListServlet < HTTPServlet::AbstractServlet
  def do_GET(req, res)
    @services = ServiceController.GetServices.sort_by{|x| x.display_name}
    open('list.rhtml') do |f|
      e = ERB.new(f.read)
      res.body = e.result(binding)
    end
    res['Content-Type'] = "text/html"
  end
end

server.mount("/list", ListServlet)
server.mount_proc("/start") { |req, res|
  service = ServiceController.new(File.basename(URI.parse(req.path).path))
  service.start
  service.WaitForStatus(ServiceControllerStatus.Running)
  res['Pragma'] = 'no-cache' #追加
  res.set_redirect(HTTPStatus::MovedPermanently, '/list')
}
server.mount_proc("/stop") { |req, res|
  service = ServiceController.new(File.basename(URI.parse(req.path).path))
  service.stop
  service.WaitForStatus(ServiceControllerStatus.Stopped)
  res['Pragma'] = 'no-cache' #追加
  res.set_redirect(HTTPStatus::MovedPermanently, '/list')
}

trap('INT') { server.shutdown }
server.start

ブラウザの履歴を削除してから動かすとちゃんと動作しました。
Railsではリダイレクトは日常的に使ってるからブラウザにキャッシュしない仕組みがすでに入っているんでしょうね。