File: webrick/httpserver.rb

Overview
Module Structure
Class Hierarchy
Code

Overview

Module Structure

  module: <Toplevel Module>
  module: WEBrick#19
  class: HTTPServerError#20
inherits from
  ServerError ( WEBrick )
  class: HTTPServer#22
inherits from
  GenericServer ( WEBrick )
has properties
method: initialize / 2 #23
method: run / 1 #43
method: service / 2 #89
method: do_OPTIONS / 2 #107
method: mount / 3 #111
method: mount_proc / 3 #116
method: unmount / 1 #122
alias: umount unmount #126
method: search_servlet / 1 #128
method: virtual_host / 1 #136
method: lookup_server / 1 #147
method: access_log / 3 #156
  class: MountTable#163
inherits from
  Object ( Builtin-Module )
has properties
method: initialize #164
method: [] / 1 #169
method: []= / 2 #174
method: delete / 1 #181
method: scan / 1 #188
method: compile #195
method: normalize / 1 #203

Class Hierarchy

Code

   1  #
   2  # httpserver.rb -- HTTPServer Class
   3  #
   4  # Author: IPR -- Internet Programming with Ruby -- writers
   5  # Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
   6  # Copyright (c) 2002 Internet Programming with Ruby writers. All rights
   7  # reserved.
   8  #
   9  # $IPR: httpserver.rb,v 1.63 2002/10/01 17:16:32 gotoyuzo Exp $
  10 
  11  require 'webrick/server'
  12  require 'webrick/httputils'
  13  require 'webrick/httpstatus'
  14  require 'webrick/httprequest'
  15  require 'webrick/httpresponse'
  16  require 'webrick/httpservlet'
  17  require 'webrick/accesslog'
  18 
  19  module WEBrick
  20    class HTTPServerError < ServerError; end
  21 
  22    class HTTPServer < ::WEBrick::GenericServer
  23      def initialize(config={}, default=Config::HTTP)
  24        super
  25        @http_version = HTTPVersion::convert(@config[:HTTPVersion])
  26 
  27        @mount_tab = MountTable.new
  28        if @config[:DocumentRoot]
  29          mount("/", HTTPServlet::FileHandler, @config[:DocumentRoot],
  30                @config[:DocumentRootOptions])
  31        end
  32 
  33        unless @config[:AccessLog]
  34          @config[:AccessLog] = [
  35            [ $stderr, AccessLog::COMMON_LOG_FORMAT ],
  36            [ $stderr, AccessLog::REFERER_LOG_FORMAT ]
  37          ]
  38        end
  39   
  40        @virtual_hosts = Array.new
  41      end
  42 
  43      def run(sock)
  44        while true 
  45          res = HTTPResponse.new(@config)
  46          req = HTTPRequest.new(@config)
  47          server = self
  48          begin
  49            timeout = @config[:RequestTimeout]
  50            while timeout > 0
  51              break if IO.select([sock], nil, nil, 0.5)
  52              timeout = 0 if @status != :Running
  53              timeout -= 0.5
  54            end
  55            raise HTTPStatus::EOFError if timeout <= 0 || sock.eof?
  56            req.parse(sock)
  57            res.request_method = req.request_method
  58            res.request_uri = req.request_uri
  59            res.request_http_version = req.http_version
  60            res.keep_alive = req.keep_alive?
  61            server = lookup_server(req) || self
  62            if callback = server[:RequestCallback] || server[:RequestHandler]
  63              callback.call(req, res)
  64            end
  65            server.service(req, res)
  66          rescue HTTPStatus::EOFError, HTTPStatus::RequestTimeout => ex
  67            res.set_error(ex)
  68          rescue HTTPStatus::Error => ex
  69            @logger.error(ex.message)
  70            res.set_error(ex)
  71          rescue HTTPStatus::Status => ex
  72            res.status = ex.code
  73          rescue StandardError => ex
  74            @logger.error(ex)
  75            res.set_error(ex, true)
  76          ensure
  77            if req.request_line
  78              req.fixup()
  79              res.send_response(sock)
  80              server.access_log(@config, req, res)
  81            end
  82          end
  83          break if @http_version < "1.1"
  84          break unless req.keep_alive?
  85          break unless res.keep_alive?
  86        end
  87      end
  88 
  89      def service(req, res)
  90        if req.unparsed_uri == "*"
  91          if req.request_method == "OPTIONS"
  92            do_OPTIONS(req, res)
  93            raise HTTPStatus::OK
  94          end
  95          raise HTTPStatus::NotFound, "`#{req.unparsed_uri}' not found."
  96        end
  97 
  98        servlet, options, script_name, path_info = search_servlet(req.path)
  99        raise HTTPStatus::NotFound, "`#{req.path}' not found." unless servlet
 100        req.script_name = script_name
 101        req.path_info = path_info
 102        si = servlet.get_instance(self, *options)
 103        @logger.debug(format("%s is invoked.", si.class.name))
 104        si.service(req, res)
 105      end
 106 
 107      def do_OPTIONS(req, res)
 108        res["allow"] = "GET,HEAD,POST,OPTIONS"
 109      end
 110 
 111      def mount(dir, servlet, *options)
 112        @logger.debug(sprintf("%s is mounted on %s.", servlet.inspect, dir))
 113        @mount_tab[dir] = [ servlet, options ]
 114      end
 115 
 116      def mount_proc(dir, proc=nil, &block)
 117        proc ||= block
 118        raise HTTPServerError, "must pass a proc or block" unless proc
 119        mount(dir, HTTPServlet::ProcHandler.new(proc))
 120      end
 121 
 122      def unmount(dir)
 123        @logger.debug(sprintf("unmount %s.", dir))
 124        @mount_tab.delete(dir)
 125      end
 126      alias umount unmount
 127 
 128      def search_servlet(path)
 129        script_name, path_info = @mount_tab.scan(path)
 130        servlet, options = @mount_tab[script_name]
 131        if servlet
 132          [ servlet, options, script_name, path_info ]
 133        end
 134      end
 135 
 136      def virtual_host(server)
 137        @virtual_hosts << server
 138        @virtual_hosts = @virtual_hosts.sort_by{|s|
 139          num = 0
 140          num -= 4 if s[:BindAddress]
 141          num -= 2 if s[:Port]
 142          num -= 1 if s[:ServerName]
 143          num
 144        }
 145      end
 146 
 147      def lookup_server(req)
 148        @virtual_hosts.find{|s|
 149          (s[:BindAddress].nil? || req.addr[3] == s[:BindAddress]) &&
 150          (s[:Port].nil?        || req.port == s[:Port])           &&
 151          ((s[:ServerName].nil?  || req.host == s[:ServerName]) ||
 152           (!s[:ServerAlias].nil? && s[:ServerAlias].find{|h| h === req.host}))
 153        }
 154      end
 155 
 156      def access_log(config, req, res)
 157        param = AccessLog::setup_params(config, req, res)
 158        @config[:AccessLog].each{|logger, fmt|
 159          logger << AccessLog::format(fmt+"\n", param)
 160        }
 161      end
 162 
 163      class MountTable
 164        def initialize
 165          @tab = Hash.new
 166          compile
 167        end
 168 
 169        def [](dir)
 170          dir = normalize(dir)
 171          @tab[dir]
 172        end
 173 
 174        def []=(dir, val)
 175          dir = normalize(dir)
 176          @tab[dir] = val
 177          compile
 178          val
 179        end
 180 
 181        def delete(dir)
 182          dir = normalize(dir)
 183          res = @tab.delete(dir)
 184          compile
 185          res
 186        end
 187 
 188        def scan(path)
 189          @scanner =~ path
 190          [ $&, $' ]
 191        end
 192 
 193        private
 194 
 195        def compile
 196          k = @tab.keys
 197          k.sort!
 198          k.reverse!
 199          k.collect!{|path| Regexp.escape(path) }
 200          @scanner = Regexp.new("^(" + k.join("|") +")(?=/|$)")
 201        end
 202 
 203        def normalize(dir)
 204          ret = dir ? dir.dup : ""
 205          ret.sub!(%r|/+$|, "")
 206          ret
 207        end
 208      end
 209    end
 210  end