rubysl-webrick 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +14 -6
  2. data/.travis.yml +5 -6
  3. data/lib/rubysl/webrick/version.rb +1 -1
  4. data/lib/rubysl/webrick/webrick.rb +199 -2
  5. data/lib/webrick/accesslog.rb +96 -5
  6. data/lib/webrick/cgi.rb +80 -29
  7. data/lib/webrick/compat.rb +20 -0
  8. data/lib/webrick/config.rb +59 -5
  9. data/lib/webrick/cookie.rb +66 -5
  10. data/lib/webrick/htmlutils.rb +4 -1
  11. data/lib/webrick/httpauth.rb +53 -3
  12. data/lib/webrick/httpauth/authenticator.rb +53 -16
  13. data/lib/webrick/httpauth/basicauth.rb +45 -2
  14. data/lib/webrick/httpauth/digestauth.rb +82 -17
  15. data/lib/webrick/httpauth/htdigest.rb +38 -1
  16. data/lib/webrick/httpauth/htgroup.rb +32 -0
  17. data/lib/webrick/httpauth/htpasswd.rb +40 -2
  18. data/lib/webrick/httpauth/userdb.rb +27 -4
  19. data/lib/webrick/httpproxy.rb +197 -112
  20. data/lib/webrick/httprequest.rb +268 -50
  21. data/lib/webrick/httpresponse.rb +170 -33
  22. data/lib/webrick/https.rb +26 -3
  23. data/lib/webrick/httpserver.rb +75 -7
  24. data/lib/webrick/httpservlet/abstract.rb +88 -6
  25. data/lib/webrick/httpservlet/cgi_runner.rb +5 -4
  26. data/lib/webrick/httpservlet/cgihandler.rb +37 -18
  27. data/lib/webrick/httpservlet/erbhandler.rb +40 -7
  28. data/lib/webrick/httpservlet/filehandler.rb +116 -28
  29. data/lib/webrick/httpservlet/prochandler.rb +17 -4
  30. data/lib/webrick/httpstatus.rb +86 -18
  31. data/lib/webrick/httputils.rb +131 -23
  32. data/lib/webrick/httpversion.rb +28 -2
  33. data/lib/webrick/log.rb +72 -5
  34. data/lib/webrick/server.rb +158 -33
  35. data/lib/webrick/ssl.rb +78 -9
  36. data/lib/webrick/utils.rb +151 -5
  37. data/lib/webrick/version.rb +5 -1
  38. data/rubysl-webrick.gemspec +0 -1
  39. metadata +12 -24
@@ -1,4 +1,4 @@
1
- #
1
+ #--
2
2
  # httpauth/userdb.rb -- UserDB mix-in module.
3
3
  #
4
4
  # Author: IPR -- Internet Programming with Ruby -- writers
@@ -9,19 +9,42 @@
9
9
 
10
10
  module WEBrick
11
11
  module HTTPAuth
12
+
13
+ ##
14
+ # User database mixin for HTTPAuth. This mixin dispatches user record
15
+ # access to the underlying auth_type for this database.
16
+
12
17
  module UserDB
13
- attr_accessor :auth_type # BasicAuth or DigestAuth
18
+
19
+ ##
20
+ # The authentication type.
21
+ #
22
+ # WEBrick::HTTPAuth::BasicAuth or WEBrick::HTTPAuth::DigestAuth are
23
+ # built-in.
24
+
25
+ attr_accessor :auth_type
26
+
27
+ ##
28
+ # Creates an obscured password in +realm+ with +user+ and +password+
29
+ # using the auth_type of this database.
14
30
 
15
31
  def make_passwd(realm, user, pass)
16
32
  @auth_type::make_passwd(realm, user, pass)
17
33
  end
18
34
 
35
+ ##
36
+ # Sets a password in +realm+ with +user+ and +password+ for the
37
+ # auth_type of this database.
38
+
19
39
  def set_passwd(realm, user, pass)
20
40
  self[user] = pass
21
- end
41
+ end
42
+
43
+ ##
44
+ # Retrieves a password in +realm+ for +user+ for the auth_type of this
45
+ # database. +reload_db+ is a dummy value.
22
46
 
23
47
  def get_passwd(realm, user, reload_db=false)
24
- # reload_db is dummy
25
48
  make_passwd(realm, user, self[user])
26
49
  end
27
50
  end
@@ -15,24 +15,83 @@ require "net/http"
15
15
  Net::HTTP::version_1_2 if RUBY_VERSION < "1.7"
16
16
 
17
17
  module WEBrick
18
- NullReader = Object.new
19
- class << NullReader
18
+
19
+ NullReader = Object.new # :nodoc:
20
+ class << NullReader # :nodoc:
20
21
  def read(*args)
21
22
  nil
22
23
  end
23
24
  alias gets read
24
25
  end
25
26
 
26
- class HTTPProxyServer < HTTPServer
27
- def initialize(config)
27
+ FakeProxyURI = Object.new # :nodoc:
28
+ class << FakeProxyURI # :nodoc:
29
+ def method_missing(meth, *args)
30
+ if %w(scheme host port path query userinfo).member?(meth.to_s)
31
+ return nil
32
+ end
28
33
  super
34
+ end
35
+ end
36
+
37
+ # :startdoc:
38
+
39
+ ##
40
+ # An HTTP Proxy server which proxies GET, HEAD and POST requests.
41
+ #
42
+ # To create a simple proxy server:
43
+ #
44
+ # require 'webrick'
45
+ # require 'webrick/httpproxy'
46
+ #
47
+ # proxy = WEBrick::HTTPProxyServer.new Port: 8000
48
+ #
49
+ # trap 'INT' do proxy.shutdown end
50
+ # trap 'TERM' do proxy.shutdown end
51
+ #
52
+ # proxy.start
53
+ #
54
+ # See ::new for proxy-specific configuration items.
55
+ #
56
+ # == Modifying proxied responses
57
+ #
58
+ # To modify content the proxy server returns use the +:ProxyContentHandler+
59
+ # option:
60
+ #
61
+ # handler = proc do |req, res|
62
+ # if res['content-type'] == 'text/plain' then
63
+ # res.body << "\nThis content was proxied!\n"
64
+ # end
65
+ # end
66
+ #
67
+ # proxy =
68
+ # WEBrick::HTTPProxyServer.new Port: 8000, ProxyContentHandler: handler
69
+
70
+ class HTTPProxyServer < HTTPServer
71
+
72
+ ##
73
+ # Proxy server configurations. The proxy server handles the following
74
+ # configuration items in addition to those supported by HTTPServer:
75
+ #
76
+ # :ProxyAuthProc:: Called with a request and response to authorize a
77
+ # request
78
+ # :ProxyVia:: Appended to the via header
79
+ # :ProxyURI:: The proxy server's URI
80
+ # :ProxyContentHandler:: Called with a request and response and allows
81
+ # modification of the response
82
+ # :ProxyTimeout:: Sets the proxy timeouts to 30 seconds for open and 60
83
+ # seconds for read operations
84
+
85
+ def initialize(config={}, default=Config::HTTP)
86
+ super(config, default)
29
87
  c = @config
30
88
  @via = "#{c[:HTTPVersion]} #{c[:ServerName]}:#{c[:Port]}"
31
89
  end
32
90
 
91
+ # :stopdoc:
33
92
  def service(req, res)
34
93
  if req.request_method == "CONNECT"
35
- proxy_connect(req, res)
94
+ do_CONNECT(req, res)
36
95
  elsif req.unparsed_uri =~ %r!^http://!
37
96
  proxy_service(req, res)
38
97
  else
@@ -47,118 +106,24 @@ module WEBrick
47
106
  req.header.delete("proxy-authorization")
48
107
  end
49
108
 
50
- # Some header fields should not be transferred.
51
- HopByHop = %w( connection keep-alive proxy-authenticate upgrade
52
- proxy-authorization te trailers transfer-encoding )
53
- ShouldNotTransfer = %w( set-cookie proxy-connection )
54
- def split_field(f) f ? f.split(/,\s+/).collect{|i| i.downcase } : [] end
55
-
56
- def choose_header(src, dst)
57
- connections = split_field(src['connection'])
58
- src.each{|key, value|
59
- key = key.downcase
60
- if HopByHop.member?(key) || # RFC2616: 13.5.1
61
- connections.member?(key) || # RFC2616: 14.10
62
- ShouldNotTransfer.member?(key) # pragmatics
63
- @logger.debug("choose_header: `#{key}: #{value}'")
64
- next
65
- end
66
- dst[key] = value
67
- }
68
- end
69
-
70
- # Net::HTTP is stupid about the multiple header fields.
71
- # Here is workaround:
72
- def set_cookie(src, dst)
73
- if str = src['set-cookie']
74
- cookies = []
75
- str.split(/,\s*/).each{|token|
76
- if /^[^=]+;/o =~ token
77
- cookies[-1] << ", " << token
78
- elsif /=/o =~ token
79
- cookies << token
80
- else
81
- cookies[-1] << ", " << token
82
- end
83
- }
84
- dst.cookies.replace(cookies)
85
- end
86
- end
87
-
88
- def set_via(h)
89
- if @config[:ProxyVia]
90
- if h['via']
91
- h['via'] << ", " << @via
92
- else
93
- h['via'] = @via
94
- end
95
- end
96
- end
97
-
98
109
  def proxy_uri(req, res)
99
- @config[:ProxyURI]
110
+ # should return upstream proxy server's URI
111
+ return @config[:ProxyURI]
100
112
  end
101
113
 
102
114
  def proxy_service(req, res)
103
115
  # Proxy Authentication
104
- proxy_auth(req, res)
105
-
106
- # Create Request-URI to send to the origin server
107
- uri = req.request_uri
108
- path = uri.path.dup
109
- path << "?" << uri.query if uri.query
110
-
111
- # Choose header fields to transfer
112
- header = Hash.new
113
- choose_header(req, header)
114
- set_via(header)
115
-
116
- # select upstream proxy server
117
- if proxy = proxy_uri(req, res)
118
- proxy_host = proxy.host
119
- proxy_port = proxy.port
120
- if proxy.userinfo
121
- credentials = "Basic " + [proxy.userinfo].pack("m*")
122
- credentials.chomp!
123
- header['proxy-authorization'] = credentials
124
- end
125
- end
116
+ proxy_auth(req, res)
126
117
 
127
- response = nil
128
118
  begin
129
- http = Net::HTTP.new(uri.host, uri.port, proxy_host, proxy_port)
130
- http.start{
131
- if @config[:ProxyTimeout]
132
- ################################## these issues are
133
- http.open_timeout = 30 # secs # necessary (maybe bacause
134
- http.read_timeout = 60 # secs # Ruby's bug, but why?)
135
- ##################################
136
- end
137
- case req.request_method
138
- when "GET" then response = http.get(path, header)
139
- when "POST" then response = http.post(path, req.body || "", header)
140
- when "HEAD" then response = http.head(path, header)
141
- else
142
- raise HTTPStatus::MethodNotAllowed,
143
- "unsupported method `#{req.request_method}'."
144
- end
145
- }
119
+ self.send("do_#{req.request_method}", req, res)
120
+ rescue NoMethodError
121
+ raise HTTPStatus::MethodNotAllowed,
122
+ "unsupported method `#{req.request_method}'."
146
123
  rescue => err
147
124
  logger.debug("#{err.class}: #{err.message}")
148
125
  raise HTTPStatus::ServiceUnavailable, err.message
149
126
  end
150
-
151
- # Persistent connction requirements are mysterious for me.
152
- # So I will close the connection in every response.
153
- res['proxy-connection'] = "close"
154
- res['connection'] = "close"
155
-
156
- # Convert Net::HTTP::HTTPResponse to WEBrick::HTTPProxy
157
- res.status = response.code.to_i
158
- choose_header(response, res)
159
- set_cookie(response, res)
160
- set_via(res)
161
- res.body = response.body
162
127
 
163
128
  # Process contents
164
129
  if handler = @config[:ProxyContentHandler]
@@ -166,7 +131,7 @@ module WEBrick
166
131
  end
167
132
  end
168
133
 
169
- def proxy_connect(req, res)
134
+ def do_CONNECT(req, res)
170
135
  # Proxy Authentication
171
136
  proxy_auth(req, res)
172
137
 
@@ -179,8 +144,7 @@ module WEBrick
179
144
  if proxy = proxy_uri(req, res)
180
145
  proxy_request_line = "CONNECT #{host}:#{port} HTTP/1.0"
181
146
  if proxy.userinfo
182
- credentials = "Basic " + [proxy.userinfo].pack("m*")
183
- credentials.chomp!
147
+ credentials = "Basic " + [proxy.userinfo].pack("m").delete("\n")
184
148
  end
185
149
  host, port = proxy.host, proxy.port
186
150
  end
@@ -222,7 +186,7 @@ module WEBrick
222
186
  res.send_response(ua)
223
187
  access_log(@config, req, res)
224
188
 
225
- # Should clear request-line not to send the sesponse twice.
189
+ # Should clear request-line not to send the response twice.
226
190
  # see: HTTPServer#run
227
191
  req.parse(NullReader) rescue nil
228
192
  end
@@ -231,11 +195,11 @@ module WEBrick
231
195
  while fds = IO::select([ua, os])
232
196
  if fds[0].member?(ua)
233
197
  buf = ua.sysread(1024);
234
- @logger.debug("CONNECT: #{buf.size} byte from User-Agent")
198
+ @logger.debug("CONNECT: #{buf.bytesize} byte from User-Agent")
235
199
  os.syswrite(buf)
236
200
  elsif fds[0].member?(os)
237
201
  buf = os.sysread(1024);
238
- @logger.debug("CONNECT: #{buf.size} byte from #{host}:#{port}")
202
+ @logger.debug("CONNECT: #{buf.bytesize} byte from #{host}:#{port}")
239
203
  ua.syswrite(buf)
240
204
  end
241
205
  end
@@ -247,8 +211,129 @@ module WEBrick
247
211
  raise HTTPStatus::EOFError
248
212
  end
249
213
 
214
+ def do_GET(req, res)
215
+ perform_proxy_request(req, res) do |http, path, header|
216
+ http.get(path, header)
217
+ end
218
+ end
219
+
220
+ def do_HEAD(req, res)
221
+ perform_proxy_request(req, res) do |http, path, header|
222
+ http.head(path, header)
223
+ end
224
+ end
225
+
226
+ def do_POST(req, res)
227
+ perform_proxy_request(req, res) do |http, path, header|
228
+ http.post(path, req.body || "", header)
229
+ end
230
+ end
231
+
250
232
  def do_OPTIONS(req, res)
251
233
  res['allow'] = "GET,HEAD,POST,OPTIONS,CONNECT"
252
234
  end
235
+
236
+ private
237
+
238
+ # Some header fields should not be transferred.
239
+ HopByHop = %w( connection keep-alive proxy-authenticate upgrade
240
+ proxy-authorization te trailers transfer-encoding )
241
+ ShouldNotTransfer = %w( set-cookie proxy-connection )
242
+ def split_field(f) f ? f.split(/,\s+/).collect{|i| i.downcase } : [] end
243
+
244
+ def choose_header(src, dst)
245
+ connections = split_field(src['connection'])
246
+ src.each{|key, value|
247
+ key = key.downcase
248
+ if HopByHop.member?(key) || # RFC2616: 13.5.1
249
+ connections.member?(key) || # RFC2616: 14.10
250
+ ShouldNotTransfer.member?(key) # pragmatics
251
+ @logger.debug("choose_header: `#{key}: #{value}'")
252
+ next
253
+ end
254
+ dst[key] = value
255
+ }
256
+ end
257
+
258
+ # Net::HTTP is stupid about the multiple header fields.
259
+ # Here is workaround:
260
+ def set_cookie(src, dst)
261
+ if str = src['set-cookie']
262
+ cookies = []
263
+ str.split(/,\s*/).each{|token|
264
+ if /^[^=]+;/o =~ token
265
+ cookies[-1] << ", " << token
266
+ elsif /=/o =~ token
267
+ cookies << token
268
+ else
269
+ cookies[-1] << ", " << token
270
+ end
271
+ }
272
+ dst.cookies.replace(cookies)
273
+ end
274
+ end
275
+
276
+ def set_via(h)
277
+ if @config[:ProxyVia]
278
+ if h['via']
279
+ h['via'] << ", " << @via
280
+ else
281
+ h['via'] = @via
282
+ end
283
+ end
284
+ end
285
+
286
+ def setup_proxy_header(req, res)
287
+ # Choose header fields to transfer
288
+ header = Hash.new
289
+ choose_header(req, header)
290
+ set_via(header)
291
+ return header
292
+ end
293
+
294
+ def setup_upstream_proxy_authentication(req, res, header)
295
+ if upstream = proxy_uri(req, res)
296
+ if upstream.userinfo
297
+ header['proxy-authorization'] =
298
+ "Basic " + [upstream.userinfo].pack("m").delete("\n")
299
+ end
300
+ return upstream
301
+ end
302
+ return FakeProxyURI
303
+ end
304
+
305
+ def perform_proxy_request(req, res)
306
+ uri = req.request_uri
307
+ path = uri.path.dup
308
+ path << "?" << uri.query if uri.query
309
+ header = setup_proxy_header(req, res)
310
+ upstream = setup_upstream_proxy_authentication(req, res, header)
311
+ response = nil
312
+
313
+ http = Net::HTTP.new(uri.host, uri.port, upstream.host, upstream.port)
314
+ http.start do
315
+ if @config[:ProxyTimeout]
316
+ ################################## these issues are
317
+ http.open_timeout = 30 # secs # necessary (maybe bacause
318
+ http.read_timeout = 60 # secs # Ruby's bug, but why?)
319
+ ##################################
320
+ end
321
+ response = yield(http, path, header)
322
+ end
323
+
324
+ # Persistent connection requirements are mysterious for me.
325
+ # So I will close the connection in every response.
326
+ res['proxy-connection'] = "close"
327
+ res['connection'] = "close"
328
+
329
+ # Convert Net::HTTP::HTTPResponse to WEBrick::HTTPResponse
330
+ res.status = response.code.to_i
331
+ choose_header(response, res)
332
+ set_cookie(response, res)
333
+ set_via(res)
334
+ res.body = response.body
335
+ end
336
+
337
+ # :stopdoc:
253
338
  end
254
339
  end
@@ -8,9 +8,7 @@
8
8
  #
9
9
  # $IPR: httprequest.rb,v 1.64 2003/07/13 17:18:22 gotoyuzo Exp $
10
10
 
11
- require 'timeout'
12
11
  require 'uri'
13
-
14
12
  require 'webrick/httpversion'
15
13
  require 'webrick/httpstatus'
16
14
  require 'webrick/httputils'
@@ -18,32 +16,141 @@ require 'webrick/cookie'
18
16
 
19
17
  module WEBrick
20
18
 
19
+ ##
20
+ # An HTTP request. This is consumed by service and do_* methods in
21
+ # WEBrick servlets
22
+
21
23
  class HTTPRequest
22
- BODY_CONTAINABLE_METHODS = [ "POST", "PUT" ]
23
- BUFSIZE = 1024*4
24
24
 
25
- # Request line
25
+ BODY_CONTAINABLE_METHODS = [ "POST", "PUT" ] # :nodoc:
26
+
27
+ # :section: Request line
28
+
29
+ ##
30
+ # The complete request line such as:
31
+ #
32
+ # GET / HTTP/1.1
33
+
26
34
  attr_reader :request_line
27
- attr_reader :request_method, :unparsed_uri, :http_version
28
35
 
29
- # Request-URI
30
- attr_reader :request_uri, :host, :port, :path
31
- attr_accessor :script_name, :path_info, :query_string
36
+ ##
37
+ # The request method, GET, POST, PUT, etc.
38
+
39
+ attr_reader :request_method
40
+
41
+ ##
42
+ # The unparsed URI of the request
43
+
44
+ attr_reader :unparsed_uri
45
+
46
+ ##
47
+ # The HTTP version of the request
48
+
49
+ attr_reader :http_version
50
+
51
+ # :section: Request-URI
52
+
53
+ ##
54
+ # The parsed URI of the request
55
+
56
+ attr_reader :request_uri
57
+
58
+ ##
59
+ # The request path
60
+
61
+ attr_reader :path
62
+
63
+ ##
64
+ # The script name (CGI variable)
65
+
66
+ attr_accessor :script_name
67
+
68
+ ##
69
+ # The path info (CGI variable)
70
+
71
+ attr_accessor :path_info
72
+
73
+ ##
74
+ # The query from the URI of the request
75
+
76
+ attr_accessor :query_string
77
+
78
+ # :section: Header and entity body
79
+
80
+ ##
81
+ # The raw header of the request
82
+
83
+ attr_reader :raw_header
32
84
 
33
- # Header and entity body
34
- attr_reader :raw_header, :header, :cookies
35
- attr_reader :accept, :accept_charset
36
- attr_reader :accept_encoding, :accept_language
85
+ ##
86
+ # The parsed header of the request
87
+
88
+ attr_reader :header
89
+
90
+ ##
91
+ # The parsed request cookies
92
+
93
+ attr_reader :cookies
94
+
95
+ ##
96
+ # The Accept header value
97
+
98
+ attr_reader :accept
99
+
100
+ ##
101
+ # The Accept-Charset header value
102
+
103
+ attr_reader :accept_charset
104
+
105
+ ##
106
+ # The Accept-Encoding header value
107
+
108
+ attr_reader :accept_encoding
109
+
110
+ ##
111
+ # The Accept-Language header value
112
+
113
+ attr_reader :accept_language
114
+
115
+ # :section:
116
+
117
+ ##
118
+ # The remote user (CGI variable)
37
119
 
38
- # Misc
39
120
  attr_accessor :user
40
- attr_reader :addr, :peeraddr
121
+
122
+ ##
123
+ # The socket address of the server
124
+
125
+ attr_reader :addr
126
+
127
+ ##
128
+ # The socket address of the client
129
+
130
+ attr_reader :peeraddr
131
+
132
+ ##
133
+ # Hash of request attributes
134
+
41
135
  attr_reader :attributes
136
+
137
+ ##
138
+ # Is this a keep-alive connection?
139
+
42
140
  attr_reader :keep_alive
141
+
142
+ ##
143
+ # The local time this request was received
144
+
43
145
  attr_reader :request_time
44
146
 
147
+ ##
148
+ # Creates a new HTTP request. WEBrick::Config::HTTP is the default
149
+ # configuration.
150
+
45
151
  def initialize(config)
46
152
  @config = config
153
+ @buffer_size = @config[:InputBufferSize]
47
154
  @logger = config[:Logger]
48
155
 
49
156
  @request_line = @request_method =
@@ -72,8 +179,15 @@ module WEBrick
72
179
 
73
180
  @remaining_size = nil
74
181
  @socket = nil
182
+
183
+ @forwarded_proto = @forwarded_host = @forwarded_port =
184
+ @forwarded_server = @forwarded_for = nil
75
185
  end
76
186
 
187
+ ##
188
+ # Parses a request from +socket+. This is called internally by
189
+ # WEBrick::HTTPServer.
190
+
77
191
  def parse(socket=nil)
78
192
  @socket = socket
79
193
  begin
@@ -98,6 +212,7 @@ module WEBrick
98
212
  return if @unparsed_uri == "*"
99
213
 
100
214
  begin
215
+ setup_forwarded_info
101
216
  @request_uri = parse_uri(@unparsed_uri)
102
217
  @path = HTTPUtils::unescape(@request_uri.path)
103
218
  @path = HTTPUtils::normalize_path(@path)
@@ -121,12 +236,29 @@ module WEBrick
121
236
  end
122
237
  end
123
238
 
124
- def body(&block)
239
+ ##
240
+ # Generate HTTP/1.1 100 continue response if the client expects it,
241
+ # otherwise does nothing.
242
+
243
+ def continue # :nodoc:
244
+ if self['expect'] == '100-continue' && @config[:HTTPVersion] >= "1.1"
245
+ @socket << "HTTP/#{@config[:HTTPVersion]} 100 continue#{CRLF}#{CRLF}"
246
+ @header.delete('expect')
247
+ end
248
+ end
249
+
250
+ ##
251
+ # Returns the request body.
252
+
253
+ def body(&block) # :yields: body_chunk
125
254
  block ||= Proc.new{|chunk| @body << chunk }
126
255
  read_body(@socket, block)
127
256
  @body.empty? ? nil : @body
128
257
  end
129
258
 
259
+ ##
260
+ # Request query as a Hash
261
+
130
262
  def query
131
263
  unless @query
132
264
  parse_query()
@@ -134,14 +266,23 @@ module WEBrick
134
266
  @query
135
267
  end
136
268
 
269
+ ##
270
+ # The content-length header
271
+
137
272
  def content_length
138
273
  return Integer(self['content-length'])
139
274
  end
140
275
 
276
+ ##
277
+ # The content-type header
278
+
141
279
  def content_type
142
280
  return self['content-type']
143
281
  end
144
282
 
283
+ ##
284
+ # Retrieves +header_name+
285
+
145
286
  def [](header_name)
146
287
  if @header
147
288
  value = @header[header_name.downcase]
@@ -149,18 +290,61 @@ module WEBrick
149
290
  end
150
291
  end
151
292
 
293
+ ##
294
+ # Iterates over the request headers
295
+
152
296
  def each
153
- @header.each{|k, v|
154
- value = @header[k]
155
- yield(k, value.empty? ? nil : value.join(", "))
156
- }
297
+ if @header
298
+ @header.each{|k, v|
299
+ value = @header[k]
300
+ yield(k, value.empty? ? nil : value.join(", "))
301
+ }
302
+ end
303
+ end
304
+
305
+ ##
306
+ # The host this request is for
307
+
308
+ def host
309
+ return @forwarded_host || @host
310
+ end
311
+
312
+ ##
313
+ # The port this request is for
314
+
315
+ def port
316
+ return @forwarded_port || @port
317
+ end
318
+
319
+ ##
320
+ # The server name this request is for
321
+
322
+ def server_name
323
+ return @forwarded_server || @config[:ServerName]
324
+ end
325
+
326
+ ##
327
+ # The client's IP address
328
+
329
+ def remote_ip
330
+ return self["client-ip"] || @forwarded_for || @peeraddr[3]
331
+ end
332
+
333
+ ##
334
+ # Is this an SSL request?
335
+
336
+ def ssl?
337
+ return @request_uri.scheme == "https"
157
338
  end
158
339
 
340
+ ##
341
+ # Should the connection this request was made on be kept alive?
342
+
159
343
  def keep_alive?
160
344
  @keep_alive
161
345
  end
162
346
 
163
- def to_s
347
+ def to_s # :nodoc:
164
348
  ret = @request_line.dup
165
349
  @raw_header.each{|line| ret << line }
166
350
  ret << CRLF
@@ -168,7 +352,10 @@ module WEBrick
168
352
  ret
169
353
  end
170
354
 
171
- def fixup()
355
+ ##
356
+ # Consumes any remaining body and updates keep-alive status
357
+
358
+ def fixup() # :nodoc:
172
359
  begin
173
360
  body{|chunk| } # read remaining body
174
361
  rescue HTTPStatus::Error => ex
@@ -180,11 +367,11 @@ module WEBrick
180
367
  end
181
368
  end
182
369
 
183
- def meta_vars
184
- # This method provides the metavariables defined by the revision 3
185
- # of ``The WWW Common Gateway Interface Version 1.1''.
186
- # (http://Web.Golux.Com/coar/cgi/)
370
+ # This method provides the metavariables defined by the revision 3
371
+ # of "The WWW Common Gateway Interface Version 1.1"
372
+ # http://Web.Golux.Com/coar/cgi/
187
373
 
374
+ def meta_vars
188
375
  meta = Hash.new
189
376
 
190
377
  cl = self["Content-Length"]
@@ -221,11 +408,18 @@ module WEBrick
221
408
 
222
409
  private
223
410
 
411
+ # :stopdoc:
412
+
413
+ MAX_URI_LENGTH = 2083 # :nodoc:
414
+
224
415
  def read_request_line(socket)
225
- @request_line = read_line(socket) if socket
416
+ @request_line = read_line(socket, MAX_URI_LENGTH) if socket
417
+ if @request_line.bytesize >= MAX_URI_LENGTH and @request_line[-1, 1] != LF
418
+ raise HTTPStatus::RequestURITooLarge
419
+ end
226
420
  @request_time = Time.now
227
421
  raise HTTPStatus::EOFError unless @request_line
228
- if /^(\S+)\s+(\S+?)(?:\s+HTTP\/(\d+\.\d+))?\r?\n/mo =~ @request_line
422
+ if /^(\S+)\s+(\S++)(?:\s+HTTP\/(\d+\.\d+))?\r?\n/mo =~ @request_line
229
423
  @request_method = $1
230
424
  @unparsed_uri = $2
231
425
  @http_version = HTTPVersion.new($3 ? $3 : "0.9")
@@ -242,20 +436,19 @@ module WEBrick
242
436
  @raw_header << line
243
437
  end
244
438
  end
245
- begin
246
- @header = HTTPUtils::parse_header(@raw_header)
247
- rescue => ex
248
- raise HTTPStatus::BadRequest, ex.message
249
- end
439
+ @header = HTTPUtils::parse_header(@raw_header.join)
250
440
  end
251
441
 
252
442
  def parse_uri(str, scheme="http")
253
443
  if @config[:Escape8bitURI]
254
444
  str = HTTPUtils::escape8bit(str)
255
445
  end
446
+ str.sub!(%r{\A/+}o, '/')
256
447
  uri = URI::parse(str)
257
448
  return uri if uri.absolute?
258
- if self["host"]
449
+ if @forwarded_host
450
+ host, port = @forwarded_host, @forwarded_port
451
+ elsif self["host"]
259
452
  pattern = /\A(#{URI::REGEXP::PATTERN::HOST})(?::(\d+))?\z/n
260
453
  host, port = *self['host'].scan(pattern)[0]
261
454
  elsif @addr.size > 0
@@ -263,7 +456,7 @@ module WEBrick
263
456
  else
264
457
  host, port = @config[:ServerName], @config[:Port]
265
458
  end
266
- uri.scheme = scheme
459
+ uri.scheme = @forwarded_proto || scheme
267
460
  uri.host = host
268
461
  uri.port = port ? port.to_i : nil
269
462
  return URI::parse(uri.to_s)
@@ -278,10 +471,10 @@ module WEBrick
278
471
  end
279
472
  elsif self['content-length'] || @remaining_size
280
473
  @remaining_size ||= self['content-length'].to_i
281
- while @remaining_size > 0
282
- sz = BUFSIZE < @remaining_size ? BUFSIZE : @remaining_size
474
+ while @remaining_size > 0
475
+ sz = [@buffer_size, @remaining_size].min
283
476
  break unless buf = read_data(socket, sz)
284
- @remaining_size -= buf.size
477
+ @remaining_size -= buf.bytesize
285
478
  block.call(buf)
286
479
  end
287
480
  if @remaining_size > 0 && @socket.eof?
@@ -307,13 +500,8 @@ module WEBrick
307
500
  def read_chunked(socket, block)
308
501
  chunk_size, = read_chunk_size(socket)
309
502
  while chunk_size > 0
310
- data = ""
311
- while data.size < chunk_size
312
- tmp = read_data(socket, chunk_size-data.size) # read chunk-data
313
- break unless tmp
314
- data << tmp
315
- end
316
- if data.nil? || data.size != chunk_size
503
+ data = read_data(socket, chunk_size) # read chunk-data
504
+ if data.nil? || data.bytesize != chunk_size
317
505
  raise BadRequest, "bad chunk data size."
318
506
  end
319
507
  read_line(socket) # skip CRLF
@@ -325,10 +513,10 @@ module WEBrick
325
513
  @remaining_size = 0
326
514
  end
327
515
 
328
- def _read_data(io, method, arg)
516
+ def _read_data(io, method, *arg)
329
517
  begin
330
- timeout(@config[:RequestTimeout]){
331
- return io.__send__(method, arg)
518
+ WEBrick::Utils.timeout(@config[:RequestTimeout]){
519
+ return io.__send__(method, *arg)
332
520
  }
333
521
  rescue Errno::ECONNRESET
334
522
  return nil
@@ -337,8 +525,8 @@ module WEBrick
337
525
  end
338
526
  end
339
527
 
340
- def read_line(io)
341
- _read_data(io, :gets, LF)
528
+ def read_line(io, size=4096)
529
+ _read_data(io, :gets, LF, size)
342
530
  end
343
531
 
344
532
  def read_data(io, size)
@@ -361,5 +549,35 @@ module WEBrick
361
549
  raise HTTPStatus::BadRequest, ex.message
362
550
  end
363
551
  end
552
+
553
+ PrivateNetworkRegexp = /
554
+ ^unknown$|
555
+ ^((::ffff:)?127.0.0.1|::1)$|
556
+ ^(::ffff:)?(10|172\.(1[6-9]|2[0-9]|3[01])|192\.168)\.
557
+ /ixo
558
+
559
+ # It's said that all X-Forwarded-* headers will contain more than one
560
+ # (comma-separated) value if the original request already contained one of
561
+ # these headers. Since we could use these values as Host header, we choose
562
+ # the initial(first) value. (apr_table_mergen() adds new value after the
563
+ # existing value with ", " prefix)
564
+ def setup_forwarded_info
565
+ if @forwarded_server = self["x-forwarded-server"]
566
+ @forwarded_server = @forwarded_server.split(",", 2).first
567
+ end
568
+ @forwarded_proto = self["x-forwarded-proto"]
569
+ if host_port = self["x-forwarded-host"]
570
+ host_port = host_port.split(",", 2).first
571
+ @forwarded_host, tmp = host_port.split(":", 2)
572
+ @forwarded_port = (tmp || (@forwarded_proto == "https" ? 443 : 80)).to_i
573
+ end
574
+ if addrs = self["x-forwarded-for"]
575
+ addrs = addrs.split(",").collect(&:strip)
576
+ addrs.reject!{|ip| PrivateNetworkRegexp =~ ip }
577
+ @forwarded_for = addrs.first
578
+ end
579
+ end
580
+
581
+ # :startdoc:
364
582
  end
365
583
  end