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
|
---|