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
@@ -15,21 +15,85 @@ require 'webrick/httputils'
15
15
  require 'webrick/httpstatus'
16
16
 
17
17
  module WEBrick
18
+ ##
19
+ # An HTTP response. This is filled in by the service or do_* methods of a
20
+ # WEBrick HTTP Servlet.
21
+
18
22
  class HTTPResponse
19
- BUFSIZE = 1024*4
20
23
 
21
- attr_reader :http_version, :status, :header
24
+ ##
25
+ # HTTP Response version
26
+
27
+ attr_reader :http_version
28
+
29
+ ##
30
+ # Response status code (200)
31
+
32
+ attr_reader :status
33
+
34
+ ##
35
+ # Response header
36
+
37
+ attr_reader :header
38
+
39
+ ##
40
+ # Response cookies
41
+
22
42
  attr_reader :cookies
43
+
44
+ ##
45
+ # Response reason phrase ("OK")
46
+
23
47
  attr_accessor :reason_phrase
48
+
49
+ ##
50
+ # Body may be a String or IO subclass.
51
+
24
52
  attr_accessor :body
25
53
 
26
- attr_accessor :request_method, :request_uri, :request_http_version
54
+ ##
55
+ # Request method for this response
56
+
57
+ attr_accessor :request_method
58
+
59
+ ##
60
+ # Request URI for this response
61
+
62
+ attr_accessor :request_uri
63
+
64
+ ##
65
+ # Request HTTP version for this response
66
+
67
+ attr_accessor :request_http_version
68
+
69
+ ##
70
+ # Filename of the static file in this response. Only used by the
71
+ # FileHandler servlet.
72
+
27
73
  attr_accessor :filename
74
+
75
+ ##
76
+ # Is this a keep-alive response?
77
+
28
78
  attr_accessor :keep_alive
29
- attr_reader :config, :sent_size
79
+
80
+ ##
81
+ # Configuration for this response
82
+
83
+ attr_reader :config
84
+
85
+ ##
86
+ # Bytes sent in this response
87
+
88
+ attr_reader :sent_size
89
+
90
+ ##
91
+ # Creates a new HTTP response object. WEBrick::Config::HTTP is the
92
+ # default configuration.
30
93
 
31
94
  def initialize(config)
32
95
  @config = config
96
+ @buffer_size = config[:OutputBufferSize]
33
97
  @logger = config[:Logger]
34
98
  @header = Hash.new
35
99
  @status = HTTPStatus::RC_OK
@@ -46,58 +110,97 @@ module WEBrick
46
110
  @sent_size = 0
47
111
  end
48
112
 
113
+ ##
114
+ # The response's HTTP status line
115
+
49
116
  def status_line
50
117
  "HTTP/#@http_version #@status #@reason_phrase #{CRLF}"
51
118
  end
52
119
 
120
+ ##
121
+ # Sets the response's status to the +status+ code
122
+
53
123
  def status=(status)
54
124
  @status = status
55
125
  @reason_phrase = HTTPStatus::reason_phrase(status)
56
126
  end
57
127
 
128
+ ##
129
+ # Retrieves the response header +field+
130
+
58
131
  def [](field)
59
132
  @header[field.downcase]
60
133
  end
61
134
 
135
+ ##
136
+ # Sets the response header +field+ to +value+
137
+
62
138
  def []=(field, value)
63
139
  @header[field.downcase] = value.to_s
64
140
  end
65
141
 
142
+ ##
143
+ # The content-length header
144
+
66
145
  def content_length
67
146
  if len = self['content-length']
68
147
  return Integer(len)
69
148
  end
70
149
  end
71
150
 
151
+ ##
152
+ # Sets the content-length header to +len+
153
+
72
154
  def content_length=(len)
73
155
  self['content-length'] = len.to_s
74
156
  end
75
157
 
158
+ ##
159
+ # The content-type header
160
+
76
161
  def content_type
77
162
  self['content-type']
78
163
  end
79
164
 
165
+ ##
166
+ # Sets the content-type header to +type+
167
+
80
168
  def content_type=(type)
81
169
  self['content-type'] = type
82
170
  end
83
171
 
172
+ ##
173
+ # Iterates over each header in the resopnse
174
+
84
175
  def each
85
- @header.each{|k, v| yield(k, v) }
176
+ @header.each{|field, value| yield(field, value) }
86
177
  end
87
178
 
179
+ ##
180
+ # Will this response body be returned using chunked transfer-encoding?
181
+
88
182
  def chunked?
89
183
  @chunked
90
184
  end
91
185
 
186
+ ##
187
+ # Enables chunked transfer encoding.
188
+
92
189
  def chunked=(val)
93
190
  @chunked = val ? true : false
94
191
  end
95
192
 
193
+ ##
194
+ # Will this response's connection be kept alive?
195
+
96
196
  def keep_alive?
97
197
  @keep_alive
98
198
  end
99
199
 
100
- def send_response(socket)
200
+ ##
201
+ # Sends the response on +socket+
202
+
203
+ def send_response(socket) # :nodoc:
101
204
  begin
102
205
  setup_header()
103
206
  send_header(socket)
@@ -111,7 +214,10 @@ module WEBrick
111
214
  end
112
215
  end
113
216
 
114
- def setup_header()
217
+ ##
218
+ # Sets up the headers for sending
219
+
220
+ def setup_header() # :nodoc:
115
221
  @reason_phrase ||= HTTPStatus::reason_phrase(@status)
116
222
  @header['server'] ||= @config[:ServerSoftware]
117
223
  @header['date'] ||= Time.now.httpdate
@@ -143,7 +249,7 @@ module WEBrick
143
249
  @header.delete('content-length')
144
250
  elsif @header['content-length'].nil?
145
251
  unless @body.is_a?(IO)
146
- @header['content-length'] = @body ? @body.size : 0
252
+ @header['content-length'] = @body ? @body.bytesize : 0
147
253
  end
148
254
  end
149
255
 
@@ -151,8 +257,13 @@ module WEBrick
151
257
  if @header['connection'] == "close"
152
258
  @keep_alive = false
153
259
  elsif keep_alive?
154
- if chunked? || @header['content-length']
260
+ if chunked? || @header['content-length'] || @status == 304 || @status == 204 || HTTPStatus.info?(@status)
155
261
  @header['connection'] = "Keep-Alive"
262
+ else
263
+ msg = "Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true"
264
+ @logger.warn(msg)
265
+ @header['connection'] = "close"
266
+ @keep_alive = false
156
267
  end
157
268
  else
158
269
  @header['connection'] = "close"
@@ -166,11 +277,14 @@ module WEBrick
166
277
  end
167
278
  end
168
279
 
169
- def send_header(socket)
280
+ ##
281
+ # Sends the headers on +socket+
282
+
283
+ def send_header(socket) # :nodoc:
170
284
  if @http_version.major > 0
171
285
  data = status_line()
172
286
  @header.each{|key, value|
173
- tmp = key.gsub(/\bwww|^te$|\b\w/){|s| s.upcase }
287
+ tmp = key.gsub(/\bwww|^te$|\b\w/){ $&.upcase }
174
288
  data << "#{tmp}: #{value}" << CRLF
175
289
  }
176
290
  @cookies.each{|cookie|
@@ -181,31 +295,44 @@ module WEBrick
181
295
  end
182
296
  end
183
297
 
184
- def send_body(socket)
298
+ ##
299
+ # Sends the body on +socket+
300
+
301
+ def send_body(socket) # :nodoc:
185
302
  case @body
186
303
  when IO then send_body_io(socket)
187
304
  else send_body_string(socket)
188
305
  end
189
306
  end
190
307
 
191
- def to_s
308
+ def to_s # :nodoc:
192
309
  ret = ""
193
310
  send_response(ret)
194
311
  ret
195
312
  end
196
313
 
314
+ ##
315
+ # Redirects to +url+ with a WEBrick::HTTPStatus::Redirect +status+.
316
+ #
317
+ # Example:
318
+ #
319
+ # res.set_redirect WEBrick::HTTPStatus::TemporaryRedirect
320
+
197
321
  def set_redirect(status, url)
198
322
  @body = "<HTML><A HREF=\"#{url.to_s}\">#{url.to_s}</A>.</HTML>\n"
199
323
  @header['location'] = url.to_s
200
324
  raise status
201
325
  end
202
326
 
327
+ ##
328
+ # Creates an error page for exception +ex+ with an optional +backtrace+
329
+
203
330
  def set_error(ex, backtrace=false)
204
331
  case ex
205
- when HTTPStatus::Status
332
+ when HTTPStatus::Status
206
333
  @keep_alive = false if HTTPStatus::error?(ex.code)
207
334
  self.status = ex.code
208
- else
335
+ else
209
336
  @keep_alive = false
210
337
  self.status = HTTPStatus::RC_INTERNAL_SERVER_ERROR
211
338
  end
@@ -253,18 +380,25 @@ module WEBrick
253
380
 
254
381
  private
255
382
 
383
+ # :stopdoc:
384
+
256
385
  def send_body_io(socket)
257
386
  begin
258
387
  if @request_method == "HEAD"
259
388
  # do nothing
260
389
  elsif chunked?
261
- while buf = @body.read(BUFSIZE)
262
- next if buf.empty?
263
- data = ""
264
- data << format("%x", buf.size) << CRLF
265
- data << buf << CRLF
266
- _write_data(socket, data)
267
- @sent_size += buf.size
390
+ begin
391
+ buf = ''
392
+ data = ''
393
+ while true
394
+ @body.readpartial( @buffer_size, buf ) # there is no need to clear buf?
395
+ data << format("%x", buf.bytesize) << CRLF
396
+ data << buf << CRLF
397
+ _write_data(socket, data)
398
+ data.clear
399
+ @sent_size += buf.bytesize
400
+ end
401
+ rescue EOFError # do nothing
268
402
  end
269
403
  _write_data(socket, "0#{CRLF}#{CRLF}")
270
404
  else
@@ -281,41 +415,41 @@ module WEBrick
281
415
  if @request_method == "HEAD"
282
416
  # do nothing
283
417
  elsif chunked?
284
- remain = body ? @body.size : 0
285
- while buf = @body[@sent_size, BUFSIZE]
418
+ body ? @body.bytesize : 0
419
+ while buf = @body[@sent_size, @buffer_size]
286
420
  break if buf.empty?
287
421
  data = ""
288
- data << format("%x", buf.size) << CRLF
422
+ data << format("%x", buf.bytesize) << CRLF
289
423
  data << buf << CRLF
290
424
  _write_data(socket, data)
291
- @sent_size += buf.size
425
+ @sent_size += buf.bytesize
292
426
  end
293
427
  _write_data(socket, "0#{CRLF}#{CRLF}")
294
428
  else
295
- if @body && @body.size > 0
429
+ if @body && @body.bytesize > 0
296
430
  _write_data(socket, @body)
297
- @sent_size = @body.size
431
+ @sent_size = @body.bytesize
298
432
  end
299
433
  end
300
434
  end
301
435
 
302
436
  def _send_file(output, input, offset, size)
303
437
  while offset > 0
304
- sz = BUFSIZE < offset ? BUFSIZE : offset
438
+ sz = @buffer_size < size ? @buffer_size : size
305
439
  buf = input.read(sz)
306
- offset -= buf.size
440
+ offset -= buf.bytesize
307
441
  end
308
442
 
309
443
  if size == 0
310
- while buf = input.read(BUFSIZE)
444
+ while buf = input.read(@buffer_size)
311
445
  _write_data(output, buf)
312
446
  end
313
447
  else
314
448
  while size > 0
315
- sz = BUFSIZE < size ? BUFSIZE : size
449
+ sz = @buffer_size < size ? @buffer_size : size
316
450
  buf = input.read(sz)
317
451
  _write_data(output, buf)
318
- size -= buf.size
452
+ size -= buf.bytesize
319
453
  end
320
454
  end
321
455
  end
@@ -323,5 +457,8 @@ module WEBrick
323
457
  def _write_data(socket, data)
324
458
  socket << data
325
459
  end
460
+
461
+ # :startdoc:
326
462
  end
463
+
327
464
  end
@@ -15,8 +15,28 @@ module WEBrick
15
15
  HTTP.update(SSL)
16
16
  end
17
17
 
18
+ ##
19
+ #--
20
+ # Adds SSL functionality to WEBrick::HTTPRequest
21
+
18
22
  class HTTPRequest
19
- attr_reader :cipher, :server_cert, :client_cert
23
+
24
+ ##
25
+ # HTTP request SSL cipher
26
+
27
+ attr_reader :cipher
28
+
29
+ ##
30
+ # HTTP request server certificate
31
+
32
+ attr_reader :server_cert
33
+
34
+ ##
35
+ # HTTP request client certificate
36
+
37
+ attr_reader :client_cert
38
+
39
+ # :stopdoc:
20
40
 
21
41
  alias orig_parse parse
22
42
 
@@ -33,17 +53,18 @@ module WEBrick
33
53
  alias orig_parse_uri parse_uri
34
54
 
35
55
  def parse_uri(str, scheme="https")
36
- if @server_cert
56
+ if server_cert
37
57
  return orig_parse_uri(str, scheme)
38
58
  end
39
59
  return orig_parse_uri(str)
40
60
  end
61
+ private :parse_uri
41
62
 
42
63
  alias orig_meta_vars meta_vars
43
64
 
44
65
  def meta_vars
45
66
  meta = orig_meta_vars
46
- if @server_cert
67
+ if server_cert
47
68
  meta["HTTPS"] = "on"
48
69
  meta["SSL_SERVER_CERT"] = @server_cert.to_pem
49
70
  meta["SSL_CLIENT_CERT"] = @client_cert ? @client_cert.to_pem : ""
@@ -59,5 +80,7 @@ module WEBrick
59
80
  end
60
81
  meta
61
82
  end
83
+
84
+ # :startdoc:
62
85
  end
63
86
  end
@@ -19,9 +19,30 @@ require 'webrick/accesslog'
19
19
  module WEBrick
20
20
  class HTTPServerError < ServerError; end
21
21
 
22
+ ##
23
+ # An HTTP Server
24
+
22
25
  class HTTPServer < ::WEBrick::GenericServer
26
+ ##
27
+ # Creates a new HTTP server according to +config+
28
+ #
29
+ # An HTTP server uses the following attributes:
30
+ #
31
+ # :AccessLog:: An array of access logs. See WEBrick::AccessLog
32
+ # :BindAddress:: Local address for the server to bind to
33
+ # :DocumentRoot:: Root path to serve files from
34
+ # :DocumentRootOptions:: Options for the default HTTPServlet::FileHandler
35
+ # :HTTPVersion:: The HTTP version of this server
36
+ # :Port:: Port to listen on
37
+ # :RequestCallback:: Called with a request and response before each
38
+ # request is serviced.
39
+ # :RequestTimeout:: Maximum time to wait between requests
40
+ # :ServerAlias:: Array of alternate names for this server for virtual
41
+ # hosting
42
+ # :ServerName:: Name for this server for virtual hosting
43
+
23
44
  def initialize(config={}, default=Config::HTTP)
24
- super
45
+ super(config, default)
25
46
  @http_version = HTTPVersion::convert(@config[:HTTPVersion])
26
47
 
27
48
  @mount_tab = MountTable.new
@@ -36,12 +57,15 @@ module WEBrick
36
57
  [ $stderr, AccessLog::REFERER_LOG_FORMAT ]
37
58
  ]
38
59
  end
39
-
60
+
40
61
  @virtual_hosts = Array.new
41
62
  end
42
63
 
64
+ ##
65
+ # Processes requests on +sock+
66
+
43
67
  def run(sock)
44
- while true
68
+ while true
45
69
  res = HTTPResponse.new(@config)
46
70
  req = HTTPRequest.new(@config)
47
71
  server = self
@@ -52,14 +76,19 @@ module WEBrick
52
76
  timeout = 0 if @status != :Running
53
77
  timeout -= 0.5
54
78
  end
55
- raise HTTPStatus::EOFError if timeout <= 0 || sock.eof?
79
+ raise HTTPStatus::EOFError if timeout <= 0
80
+ raise HTTPStatus::EOFError if sock.eof?
56
81
  req.parse(sock)
57
82
  res.request_method = req.request_method
58
83
  res.request_uri = req.request_uri
59
84
  res.request_http_version = req.http_version
60
85
  res.keep_alive = req.keep_alive?
61
86
  server = lookup_server(req) || self
62
- if callback = server[:RequestCallback] || server[:RequestHandler]
87
+ if callback = server[:RequestCallback]
88
+ callback.call(req, res)
89
+ elsif callback = server[:RequestHandler]
90
+ msg = ":RequestHandler is deprecated, please use :RequestCallback"
91
+ @logger.warn(msg)
63
92
  callback.call(req, res)
64
93
  end
65
94
  server.service(req, res)
@@ -75,7 +104,9 @@ module WEBrick
75
104
  res.set_error(ex, true)
76
105
  ensure
77
106
  if req.request_line
78
- req.fixup()
107
+ if req.keep_alive? && res.keep_alive?
108
+ req.fixup()
109
+ end
79
110
  res.send_response(sock)
80
111
  server.access_log(@config, req, res)
81
112
  end
@@ -86,6 +117,9 @@ module WEBrick
86
117
  end
87
118
  end
88
119
 
120
+ ##
121
+ # Services +req+ and fills in +res+
122
+
89
123
  def service(req, res)
90
124
  if req.unparsed_uri == "*"
91
125
  if req.request_method == "OPTIONS"
@@ -104,27 +138,45 @@ module WEBrick
104
138
  si.service(req, res)
105
139
  end
106
140
 
141
+ ##
142
+ # The default OPTIONS request handler says GET, HEAD, POST and OPTIONS
143
+ # requests are allowed.
144
+
107
145
  def do_OPTIONS(req, res)
108
146
  res["allow"] = "GET,HEAD,POST,OPTIONS"
109
147
  end
110
148
 
149
+ ##
150
+ # Mounts +servlet+ on +dir+ passing +options+ to the servlet at creation
151
+ # time
152
+
111
153
  def mount(dir, servlet, *options)
112
154
  @logger.debug(sprintf("%s is mounted on %s.", servlet.inspect, dir))
113
155
  @mount_tab[dir] = [ servlet, options ]
114
156
  end
115
157
 
158
+ ##
159
+ # Mounts +proc+ or +block+ on +dir+ and calls it with a
160
+ # WEBrick::HTTPRequest and WEBrick::HTTPResponse
161
+
116
162
  def mount_proc(dir, proc=nil, &block)
117
163
  proc ||= block
118
164
  raise HTTPServerError, "must pass a proc or block" unless proc
119
165
  mount(dir, HTTPServlet::ProcHandler.new(proc))
120
166
  end
121
167
 
168
+ ##
169
+ # Unmounts +dir+
170
+
122
171
  def unmount(dir)
123
172
  @logger.debug(sprintf("unmount %s.", dir))
124
173
  @mount_tab.delete(dir)
125
174
  end
126
175
  alias umount unmount
127
176
 
177
+ ##
178
+ # Finds a servlet for +path+
179
+
128
180
  def search_servlet(path)
129
181
  script_name, path_info = @mount_tab.scan(path)
130
182
  servlet, options = @mount_tab[script_name]
@@ -133,6 +185,9 @@ module WEBrick
133
185
  end
134
186
  end
135
187
 
188
+ ##
189
+ # Adds +server+ as a virtual host.
190
+
136
191
  def virtual_host(server)
137
192
  @virtual_hosts << server
138
193
  @virtual_hosts = @virtual_hosts.sort_by{|s|
@@ -144,6 +199,9 @@ module WEBrick
144
199
  }
145
200
  end
146
201
 
202
+ ##
203
+ # Finds the appropriate virtual host to handle +req+
204
+
147
205
  def lookup_server(req)
148
206
  @virtual_hosts.find{|s|
149
207
  (s[:BindAddress].nil? || req.addr[3] == s[:BindAddress]) &&
@@ -153,6 +211,10 @@ module WEBrick
153
211
  }
154
212
  end
155
213
 
214
+ ##
215
+ # Logs +req+ and +res+ in the access logs. +config+ is used for the
216
+ # server name.
217
+
156
218
  def access_log(config, req, res)
157
219
  param = AccessLog::setup_params(config, req, res)
158
220
  @config[:AccessLog].each{|logger, fmt|
@@ -160,7 +222,13 @@ module WEBrick
160
222
  }
161
223
  end
162
224
 
163
- class MountTable
225
+ ##
226
+ # Mount table for the path a servlet is mounted on in the directory space
227
+ # of the server. Users of WEBrick can only access this indirectly via
228
+ # WEBrick::HTTPServer#mount, WEBrick::HTTPServer#unmount and
229
+ # WEBrick::HTTPServer#search_servlet
230
+
231
+ class MountTable # :nodoc:
164
232
  def initialize
165
233
  @tab = Hash.new
166
234
  compile