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,11 +1,11 @@
1
- #
1
+ #
2
2
  # prochandler.rb -- ProcHandler Class
3
- #
3
+ #
4
4
  # Author: IPR -- Internet Programming with Ruby -- writers
5
5
  # Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
6
6
  # Copyright (c) 2002 Internet Programming with Ruby writers. All rights
7
7
  # reserved.
8
- #
8
+ #
9
9
  # $IPR: prochandler.rb,v 1.7 2002/09/21 12:23:42 gotoyuzo Exp $
10
10
 
11
11
  require 'webrick/httpservlet/abstract.rb'
@@ -13,10 +13,22 @@ require 'webrick/httpservlet/abstract.rb'
13
13
  module WEBrick
14
14
  module HTTPServlet
15
15
 
16
+ ##
17
+ # Mounts a proc at a path that accepts a request and response.
18
+ #
19
+ # Instead of mounting this servlet with WEBrick::HTTPServer#mount use
20
+ # WEBrick::HTTPServer#mount_proc:
21
+ #
22
+ # server.mount_proc '/' do |req, res|
23
+ # res.body = 'it worked!'
24
+ # res.status = 200
25
+ # end
26
+
16
27
  class ProcHandler < AbstractServlet
28
+ # :stopdoc:
17
29
  def get_instance(server, *options)
18
30
  self
19
- end
31
+ end
20
32
 
21
33
  def initialize(proc)
22
34
  @proc = proc
@@ -27,6 +39,7 @@ module WEBrick
27
39
  end
28
40
 
29
41
  alias do_POST do_GET
42
+ # :startdoc:
30
43
  end
31
44
 
32
45
  end
@@ -1,4 +1,4 @@
1
- #
1
+ #--
2
2
  # httpstatus.rb -- HTTPStatus Class
3
3
  #
4
4
  # Author: IPR -- Internet Programming with Ruby -- writers
@@ -10,19 +10,50 @@
10
10
 
11
11
  module WEBrick
12
12
 
13
+ ##
14
+ # This module is used to manager HTTP status codes.
15
+ #
16
+ # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html for more
17
+ # information.
13
18
  module HTTPStatus
14
19
 
15
- class Status < StandardError; end
20
+ ##
21
+ # Root of the HTTP status class hierarchy
22
+ class Status < StandardError
23
+ def initialize(*args) # :nodoc:
24
+ args[0] = AccessLog.escape(args[0]) unless args.empty?
25
+ super(*args)
26
+ end
27
+ class << self
28
+ attr_reader :code, :reason_phrase # :nodoc:
29
+ end
30
+
31
+ # Returns the HTTP status code
32
+ def code() self::class::code end
33
+
34
+ # Returns the HTTP status description
35
+ def reason_phrase() self::class::reason_phrase end
36
+
37
+ alias to_i code # :nodoc:
38
+ end
39
+
40
+ # Root of the HTTP info statuses
16
41
  class Info < Status; end
42
+ # Root of the HTTP sucess statuses
17
43
  class Success < Status; end
44
+ # Root of the HTTP redirect statuses
18
45
  class Redirect < Status; end
46
+ # Root of the HTTP error statuses
19
47
  class Error < Status; end
48
+ # Root of the HTTP client error statuses
20
49
  class ClientError < Error; end
50
+ # Root of the HTTP server error statuses
21
51
  class ServerError < Error; end
22
-
52
+
23
53
  class EOFError < StandardError; end
24
54
 
25
- StatusMessage = {
55
+ # HTTP status codes and descriptions
56
+ StatusMessage = { # :nodoc:
26
57
  100 => 'Continue',
27
58
  101 => 'Switching Protocols',
28
59
  200 => 'OK',
@@ -32,6 +63,7 @@ module WEBrick
32
63
  204 => 'No Content',
33
64
  205 => 'Reset Content',
34
65
  206 => 'Partial Content',
66
+ 207 => 'Multi-Status',
35
67
  300 => 'Multiple Choices',
36
68
  301 => 'Moved Permanently',
37
69
  302 => 'Found',
@@ -57,17 +89,30 @@ module WEBrick
57
89
  415 => 'Unsupported Media Type',
58
90
  416 => 'Request Range Not Satisfiable',
59
91
  417 => 'Expectation Failed',
92
+ 422 => 'Unprocessable Entity',
93
+ 423 => 'Locked',
94
+ 424 => 'Failed Dependency',
95
+ 426 => 'Upgrade Required',
96
+ 428 => 'Precondition Required',
97
+ 429 => 'Too Many Requests',
98
+ 431 => 'Request Header Fields Too Large',
60
99
  500 => 'Internal Server Error',
61
100
  501 => 'Not Implemented',
62
101
  502 => 'Bad Gateway',
63
102
  503 => 'Service Unavailable',
64
103
  504 => 'Gateway Timeout',
65
- 505 => 'HTTP Version Not Supported'
104
+ 505 => 'HTTP Version Not Supported',
105
+ 507 => 'Insufficient Storage',
106
+ 511 => 'Network Authentication Required',
66
107
  }
67
108
 
68
- CodeToError = {}
109
+ # Maps a status code to the corresponding Status class
110
+ CodeToError = {} # :nodoc:
69
111
 
112
+ # Creates a status or error class for each status code and
113
+ # populates the CodeToError map.
70
114
  StatusMessage.each{|code, message|
115
+ message.freeze
71
116
  var_name = message.gsub(/[ \-]/,'_').upcase
72
117
  err_name = message.gsub(/[ \-]/,'')
73
118
 
@@ -79,42 +124,65 @@ module WEBrick
79
124
  when 500...600; parent = ServerError
80
125
  end
81
126
 
82
- eval %-
83
- RC_#{var_name} = #{code}
84
- class #{err_name} < #{parent}
85
- def self.code() RC_#{var_name} end
86
- def self.reason_phrase() StatusMessage[code] end
87
- def code() self::class::code end
88
- def reason_phrase() self::class::reason_phrase end
89
- alias to_i code
90
- end
91
- -
92
-
93
- CodeToError[code] = const_get(err_name)
127
+ const_set("RC_#{var_name}", code)
128
+ err_class = Class.new(parent)
129
+ err_class.instance_variable_set(:@code, code)
130
+ err_class.instance_variable_set(:@reason_phrase, message)
131
+ const_set(err_name, err_class)
132
+ CodeToError[code] = err_class
94
133
  }
95
134
 
135
+ ##
136
+ # Returns the description corresponding to the HTTP status +code+
137
+ #
138
+ # WEBrick::HTTPStatus.reason_phrase 404
139
+ # => "Not Found"
96
140
  def reason_phrase(code)
97
141
  StatusMessage[code.to_i]
98
142
  end
143
+
144
+ ##
145
+ # Is +code+ an informational status?
99
146
  def info?(code)
100
147
  code.to_i >= 100 and code.to_i < 200
101
148
  end
149
+
150
+ ##
151
+ # Is +code+ a successful status?
102
152
  def success?(code)
103
153
  code.to_i >= 200 and code.to_i < 300
104
154
  end
155
+
156
+ ##
157
+ # Is +code+ a redirection status?
105
158
  def redirect?(code)
106
159
  code.to_i >= 300 and code.to_i < 400
107
160
  end
161
+
162
+ ##
163
+ # Is +code+ an error status?
108
164
  def error?(code)
109
165
  code.to_i >= 400 and code.to_i < 600
110
166
  end
167
+
168
+ ##
169
+ # Is +code+ a client error status?
111
170
  def client_error?(code)
112
171
  code.to_i >= 400 and code.to_i < 500
113
172
  end
173
+
174
+ ##
175
+ # Is +code+ a server error status?
114
176
  def server_error?(code)
115
177
  code.to_i >= 500 and code.to_i < 600
116
178
  end
117
179
 
180
+ ##
181
+ # Returns the status class corresponding to +code+
182
+ #
183
+ # WEBrick::HTTPStatus[302]
184
+ # => WEBrick::HTTPStatus::NotFound
185
+ #
118
186
  def self.[](code)
119
187
  CodeToError[code]
120
188
  end
@@ -12,12 +12,21 @@ require 'socket'
12
12
  require 'tempfile'
13
13
 
14
14
  module WEBrick
15
- CR = "\x0d"
16
- LF = "\x0a"
17
- CRLF = "\x0d\x0a"
15
+ CR = "\x0d" # :nodoc:
16
+ LF = "\x0a" # :nodoc:
17
+ CRLF = "\x0d\x0a" # :nodoc:
18
+
19
+ ##
20
+ # HTTPUtils provides utility methods for working with the HTTP protocol.
21
+ #
22
+ # This module is generally used internally by WEBrick
18
23
 
19
24
  module HTTPUtils
20
25
 
26
+ ##
27
+ # Normalizes a request path. Raises an exception if the path cannot be
28
+ # normalized.
29
+
21
30
  def normalize_path(path)
22
31
  raise "abnormal path `#{path}'" if path[0] != ?/
23
32
  ret = path.dup
@@ -31,7 +40,8 @@ module WEBrick
31
40
  end
32
41
  module_function :normalize_path
33
42
 
34
- #####
43
+ ##
44
+ # Default mime types
35
45
 
36
46
  DefaultMimeTypes = {
37
47
  "ai" => "application/postscript",
@@ -57,6 +67,7 @@ module WEBrick
57
67
  "jpe" => "image/jpeg",
58
68
  "jpeg" => "image/jpeg",
59
69
  "jpg" => "image/jpeg",
70
+ "js" => "application/javascript",
60
71
  "lha" => "application/octet-stream",
61
72
  "lzh" => "application/octet-stream",
62
73
  "mov" => "video/quicktime",
@@ -78,10 +89,12 @@ module WEBrick
78
89
  "rtf" => "application/rtf",
79
90
  "sgm" => "text/sgml",
80
91
  "sgml" => "text/sgml",
92
+ "svg" => "image/svg+xml",
81
93
  "tif" => "image/tiff",
82
94
  "tiff" => "image/tiff",
83
95
  "txt" => "text/plain",
84
96
  "xbm" => "image/x-xbitmap",
97
+ "xhtml" => "text/html",
85
98
  "xls" => "application/vnd.ms-excel",
86
99
  "xml" => "text/xml",
87
100
  "xpm" => "image/x-xpixmap",
@@ -89,7 +102,9 @@ module WEBrick
89
102
  "zip" => "application/zip",
90
103
  }
91
104
 
92
- # Load Apache compatible mime.types file.
105
+ ##
106
+ # Loads Apache-compatible mime.types in +file+.
107
+
93
108
  def load_mime_types(file)
94
109
  open(file){ |io|
95
110
  hash = Hash.new
@@ -97,7 +112,7 @@ module WEBrick
97
112
  next if /^#/ =~ line
98
113
  line.chomp!
99
114
  mimetype, ext0 = line.split(/\s+/, 2)
100
- next unless ext0
115
+ next unless ext0
101
116
  next if ext0.empty?
102
117
  ext0.split(/\s+/).each{ |ext| hash[ext] = mimetype }
103
118
  }
@@ -106,6 +121,10 @@ module WEBrick
106
121
  end
107
122
  module_function :load_mime_types
108
123
 
124
+ ##
125
+ # Returns the mime type of +filename+ from the list in +mime_tab+. If no
126
+ # mime type was found application/octet-stream is returned.
127
+
109
128
  def mime_type(filename, mime_tab)
110
129
  suffix1 = (/\.(\w+)$/ =~ filename && $1.downcase)
111
130
  suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ filename && $1.downcase)
@@ -113,12 +132,14 @@ module WEBrick
113
132
  end
114
133
  module_function :mime_type
115
134
 
116
- #####
135
+ ##
136
+ # Parses an HTTP header +raw+ into a hash of header fields with an Array
137
+ # of values.
117
138
 
118
139
  def parse_header(raw)
119
140
  header = Hash.new([].freeze)
120
141
  field = nil
121
- raw.each{|line|
142
+ raw.each_line{|line|
122
143
  case line
123
144
  when /^([A-Za-z0-9!\#$%&'*+\-.^_`|~]+):\s*(.*?)\s*\z/om
124
145
  field, value = $1, $2
@@ -128,11 +149,11 @@ module WEBrick
128
149
  when /^\s+(.*?)\s*\z/om
129
150
  value = $1
130
151
  unless field
131
- raise "bad header '#{line.inspect}'."
152
+ raise HTTPStatus::BadRequest, "bad header '#{line}'."
132
153
  end
133
154
  header[field][-1] << " " << value
134
155
  else
135
- raise "bad header '#{line.inspect}'."
156
+ raise HTTPStatus::BadRequest, "bad header '#{line}'."
136
157
  end
137
158
  }
138
159
  header.each{|key, values|
@@ -145,12 +166,18 @@ module WEBrick
145
166
  end
146
167
  module_function :parse_header
147
168
 
169
+ ##
170
+ # Splits a header value +str+ according to HTTP specification.
171
+
148
172
  def split_header_value(str)
149
173
  str.scan(%r'\G((?:"(?:\\.|[^"])+?"|[^",]+)+)
150
174
  (?:,\s*|\Z)'xn).flatten
151
175
  end
152
176
  module_function :split_header_value
153
177
 
178
+ ##
179
+ # Parses a Range header value +ranges_specifier+
180
+
154
181
  def parse_range_header(ranges_specifier)
155
182
  if /^bytes=(.*)/ =~ ranges_specifier
156
183
  byte_range_set = split_header_value($1)
@@ -166,6 +193,9 @@ module WEBrick
166
193
  end
167
194
  module_function :parse_range_header
168
195
 
196
+ ##
197
+ # Parses q values in +value+ as used in Accept headers.
198
+
169
199
  def parse_qvalues(value)
170
200
  tmp = []
171
201
  if value
@@ -184,7 +214,8 @@ module WEBrick
184
214
  end
185
215
  module_function :parse_qvalues
186
216
 
187
- #####
217
+ ##
218
+ # Removes quotes and escapes from +str+
188
219
 
189
220
  def dequote(str)
190
221
  ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup
@@ -193,20 +224,43 @@ module WEBrick
193
224
  end
194
225
  module_function :dequote
195
226
 
227
+ ##
228
+ # Quotes and escapes quotes in +str+
229
+
196
230
  def quote(str)
197
231
  '"' << str.gsub(/[\\\"]/o, "\\\1") << '"'
198
232
  end
199
233
  module_function :quote
200
234
 
201
- #####
235
+ ##
236
+ # Stores multipart form data. FormData objects are created when
237
+ # WEBrick::HTTPUtils.parse_form_data is called.
202
238
 
203
239
  class FormData < String
204
- EmptyRawHeader = [].freeze
205
- EmptyHeader = {}.freeze
240
+ EmptyRawHeader = [].freeze # :nodoc:
241
+ EmptyHeader = {}.freeze # :nodoc:
242
+
243
+ ##
244
+ # The name of the form data part
206
245
 
207
- attr_accessor :name, :filename, :next_data
246
+ attr_accessor :name
247
+
248
+ ##
249
+ # The filename of the form data part
250
+
251
+ attr_accessor :filename
252
+
253
+ attr_accessor :next_data # :nodoc:
208
254
  protected :next_data
209
255
 
256
+ ##
257
+ # Creates a new FormData object.
258
+ #
259
+ # +args+ is an Array of form data entries. One FormData will be created
260
+ # for each entry.
261
+ #
262
+ # This is called by WEBrick::HTTPUtils.parse_form_data for you
263
+
210
264
  def initialize(*args)
211
265
  @name = @filename = @next_data = nil
212
266
  if args.empty?
@@ -215,7 +269,7 @@ module WEBrick
215
269
  super("")
216
270
  else
217
271
  @raw_header = EmptyRawHeader
218
- @header = EmptyHeader
272
+ @header = EmptyHeader
219
273
  super(args.shift)
220
274
  unless args.empty?
221
275
  @next_data = self.class.new(*args)
@@ -223,6 +277,9 @@ module WEBrick
223
277
  end
224
278
  end
225
279
 
280
+ ##
281
+ # Retrieves the header at the first entry in +key+
282
+
226
283
  def [](*key)
227
284
  begin
228
285
  @header[key[0].downcase].join(", ")
@@ -231,11 +288,17 @@ module WEBrick
231
288
  end
232
289
  end
233
290
 
291
+ ##
292
+ # Adds +str+ to this FormData which may be the body, a header or a
293
+ # header entry.
294
+ #
295
+ # This is called by WEBrick::HTTPUtils.parse_form_data for you
296
+
234
297
  def <<(str)
235
298
  if @header
236
299
  super
237
300
  elsif str == CRLF
238
- @header = HTTPUtils::parse_header(@raw_header)
301
+ @header = HTTPUtils::parse_header(@raw_header.join)
239
302
  if cd = self['content-disposition']
240
303
  if /\s+name="(.*?)"/ =~ cd then @name = $1 end
241
304
  if /\s+filename="(.*?)"/ =~ cd then @filename = $1 end
@@ -246,10 +309,15 @@ module WEBrick
246
309
  self
247
310
  end
248
311
 
312
+ ##
313
+ # Adds +data+ at the end of the chain of entries
314
+ #
315
+ # This is called by WEBrick::HTTPUtils.parse_form_data for you.
316
+
249
317
  def append_data(data)
250
318
  tmp = self
251
319
  while tmp
252
- unless tmp.next_data
320
+ unless tmp.next_data
253
321
  tmp.next_data = data
254
322
  break
255
323
  end
@@ -258,6 +326,9 @@ module WEBrick
258
326
  self
259
327
  end
260
328
 
329
+ ##
330
+ # Yields each entry in this FormData
331
+
261
332
  def each_data
262
333
  tmp = self
263
334
  while tmp
@@ -267,6 +338,9 @@ module WEBrick
267
338
  end
268
339
  end
269
340
 
341
+ ##
342
+ # Returns all the FormData as an Array
343
+
270
344
  def list
271
345
  ret = []
272
346
  each_data{|data|
@@ -275,18 +349,27 @@ module WEBrick
275
349
  ret
276
350
  end
277
351
 
352
+ ##
353
+ # A FormData will behave like an Array
354
+
278
355
  alias :to_ary :list
279
356
 
357
+ ##
358
+ # This FormData's body
359
+
280
360
  def to_s
281
361
  String.new(self)
282
362
  end
283
363
  end
284
364
 
365
+ ##
366
+ # Parses the query component of a URI in +str+
367
+
285
368
  def parse_query(str)
286
369
  query = Hash.new
287
370
  if str
288
371
  str.split(/[&;]/).each{|x|
289
- next if x.empty?
372
+ next if x.empty?
290
373
  key, val = x.split(/=/,2)
291
374
  key = unescape_form(key)
292
375
  val = unescape_form(val.to_s)
@@ -303,12 +386,15 @@ module WEBrick
303
386
  end
304
387
  module_function :parse_query
305
388
 
389
+ ##
390
+ # Parses form data in +io+ with the given +boundary+
391
+
306
392
  def parse_form_data(io, boundary)
307
- boundary_regexp = /\A--#{boundary}(--)?#{CRLF}\z/
393
+ boundary_regexp = /\A--#{Regexp.quote(boundary)}(--)?#{CRLF}\z/
308
394
  form_data = Hash.new
309
395
  return form_data unless io
310
396
  data = nil
311
- io.each{|line|
397
+ io.each_line{|line|
312
398
  if boundary_regexp =~ line
313
399
  if data
314
400
  data.chop!
@@ -316,7 +402,7 @@ module WEBrick
316
402
  if form_data.has_key?(key)
317
403
  form_data[key].append_data(data)
318
404
  else
319
- form_data[key] = data
405
+ form_data[key] = data
320
406
  end
321
407
  end
322
408
  data = FormData.new
@@ -347,9 +433,11 @@ module WEBrick
347
433
 
348
434
  module_function
349
435
 
436
+ # :stopdoc:
437
+
350
438
  def _make_regex(str) /([#{Regexp.escape(str)}])/n end
351
439
  def _make_regex!(str) /([^#{Regexp.escape(str)}])/n end
352
- def _escape(str, regex) str.gsub(regex){ "%%%02X" % $1[0] } end
440
+ def _escape(str, regex) str.gsub(regex){ "%%%02X" % $1.ord } end
353
441
  def _unescape(str, regex) str.gsub(regex){ $1.hex.chr } end
354
442
 
355
443
  UNESCAPED = _make_regex(control+space+delims+unwise+nonascii)
@@ -358,24 +446,41 @@ module WEBrick
358
446
  ESCAPED = /%([0-9a-fA-F]{2})/
359
447
  UNESCAPED_PCHAR = _make_regex!(unreserved+":@&=+$,")
360
448
 
449
+ # :startdoc:
450
+
451
+ ##
452
+ # Escapes HTTP reserved and unwise characters in +str+
453
+
361
454
  def escape(str)
362
455
  _escape(str, UNESCAPED)
363
456
  end
364
457
 
458
+ ##
459
+ # Unescapes HTTP reserved and unwise characters in +str+
460
+
365
461
  def unescape(str)
366
462
  _unescape(str, ESCAPED)
367
463
  end
368
464
 
465
+ ##
466
+ # Escapes form reserved characters in +str+
467
+
369
468
  def escape_form(str)
370
469
  ret = _escape(str, UNESCAPED_FORM)
371
470
  ret.gsub!(/ /, "+")
372
471
  ret
373
472
  end
374
473
 
474
+ ##
475
+ # Unescapes form reserved characters in +str+
476
+
375
477
  def unescape_form(str)
376
478
  _unescape(str.gsub(/\+/, " "), ESCAPED)
377
479
  end
378
480
 
481
+ ##
482
+ # Escapes path +str+
483
+
379
484
  def escape_path(str)
380
485
  result = ""
381
486
  str.scan(%r{/([^/]*)}).each{|i|
@@ -384,6 +489,9 @@ module WEBrick
384
489
  return result
385
490
  end
386
491
 
492
+ ##
493
+ # Escapes 8 bit characters in +str+
494
+
387
495
  def escape8bit(str)
388
496
  _escape(str, NONASCII)
389
497
  end