webrick 1.4.0.beta1 → 1.4.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of webrick might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/lib/webrick/cgi.rb +7 -3
- data/lib/webrick/config.rb +5 -5
- data/lib/webrick/cookie.rb +1 -1
- data/lib/webrick/httpauth.rb +5 -5
- data/lib/webrick/httpauth/basicauth.rb +13 -5
- data/lib/webrick/httpauth/digestauth.rb +10 -23
- data/lib/webrick/httpauth/htdigest.rb +4 -4
- data/lib/webrick/httpauth/htgroup.rb +9 -6
- data/lib/webrick/httpauth/htpasswd.rb +39 -6
- data/lib/webrick/httpproxy.rb +48 -36
- data/lib/webrick/httprequest.rb +53 -14
- data/lib/webrick/httpresponse.rb +97 -49
- data/lib/webrick/https.rb +66 -1
- data/lib/webrick/httpserver.rb +25 -11
- data/lib/webrick/httpservlet.rb +5 -5
- data/lib/webrick/httpservlet/abstract.rb +3 -5
- data/lib/webrick/httpservlet/cgi_runner.rb +2 -2
- data/lib/webrick/httpservlet/cgihandler.rb +3 -5
- data/lib/webrick/httpservlet/erbhandler.rb +2 -2
- data/lib/webrick/httpservlet/filehandler.rb +49 -31
- data/lib/webrick/httpservlet/prochandler.rb +1 -1
- data/lib/webrick/httpstatus.rb +1 -5
- data/lib/webrick/httputils.rb +3 -0
- data/lib/webrick/log.rb +3 -3
- data/lib/webrick/server.rb +34 -17
- data/lib/webrick/ssl.rb +20 -7
- data/lib/webrick/utils.rb +1 -2
- data/lib/webrick/version.rb +1 -1
- metadata +17 -13
data/lib/webrick/httprequest.rb
CHANGED
@@ -10,10 +10,10 @@
|
|
10
10
|
# $IPR: httprequest.rb,v 1.64 2003/07/13 17:18:22 gotoyuzo Exp $
|
11
11
|
|
12
12
|
require 'uri'
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
require_relative 'httpversion'
|
14
|
+
require_relative 'httpstatus'
|
15
|
+
require_relative 'httputils'
|
16
|
+
require_relative 'cookie'
|
17
17
|
|
18
18
|
module WEBrick
|
19
19
|
|
@@ -226,9 +226,9 @@ module WEBrick
|
|
226
226
|
raise HTTPStatus::BadRequest, "bad URI `#{@unparsed_uri}'."
|
227
227
|
end
|
228
228
|
|
229
|
-
if /
|
229
|
+
if /\Aclose\z/io =~ self["connection"]
|
230
230
|
@keep_alive = false
|
231
|
-
elsif
|
231
|
+
elsif /\Akeep-alive\z/io =~ self["connection"]
|
232
232
|
@keep_alive = true
|
233
233
|
elsif @http_version < "1.1"
|
234
234
|
@keep_alive = false
|
@@ -257,6 +257,32 @@ module WEBrick
|
|
257
257
|
@body.empty? ? nil : @body
|
258
258
|
end
|
259
259
|
|
260
|
+
##
|
261
|
+
# Prepares the HTTPRequest object for use as the
|
262
|
+
# source for IO.copy_stream
|
263
|
+
|
264
|
+
def body_reader
|
265
|
+
@body_tmp = []
|
266
|
+
@body_rd = Fiber.new do
|
267
|
+
body do |buf|
|
268
|
+
@body_tmp << buf
|
269
|
+
Fiber.yield
|
270
|
+
end
|
271
|
+
end
|
272
|
+
@body_rd.resume # grab the first chunk and yield
|
273
|
+
self
|
274
|
+
end
|
275
|
+
|
276
|
+
# for IO.copy_stream. Note: we may return a larger string than +size+
|
277
|
+
# here; but IO.copy_stream does not care.
|
278
|
+
def readpartial(size, buf = ''.b) # :nodoc
|
279
|
+
res = @body_tmp.shift or raise EOFError, 'end of file reached'
|
280
|
+
buf.replace(res)
|
281
|
+
res.clear
|
282
|
+
@body_rd.resume # get more chunks
|
283
|
+
buf
|
284
|
+
end
|
285
|
+
|
260
286
|
##
|
261
287
|
# Request query as a Hash
|
262
288
|
|
@@ -414,13 +440,19 @@ module WEBrick
|
|
414
440
|
|
415
441
|
MAX_URI_LENGTH = 2083 # :nodoc:
|
416
442
|
|
443
|
+
# same as Mongrel, Thin and Puma
|
444
|
+
MAX_HEADER_LENGTH = (112 * 1024) # :nodoc:
|
445
|
+
|
417
446
|
def read_request_line(socket)
|
418
447
|
@request_line = read_line(socket, MAX_URI_LENGTH) if socket
|
419
|
-
|
448
|
+
raise HTTPStatus::EOFError unless @request_line
|
449
|
+
|
450
|
+
@request_bytes = @request_line.bytesize
|
451
|
+
if @request_bytes >= MAX_URI_LENGTH and @request_line[-1, 1] != LF
|
420
452
|
raise HTTPStatus::RequestURITooLarge
|
421
453
|
end
|
454
|
+
|
422
455
|
@request_time = Time.now
|
423
|
-
raise HTTPStatus::EOFError unless @request_line
|
424
456
|
if /^(\S+)\s+(\S++)(?:\s+HTTP\/(\d+\.\d+))?\r?\n/mo =~ @request_line
|
425
457
|
@request_method = $1
|
426
458
|
@unparsed_uri = $2
|
@@ -435,6 +467,9 @@ module WEBrick
|
|
435
467
|
if socket
|
436
468
|
while line = read_line(socket)
|
437
469
|
break if /\A(#{CRLF}|#{LF})\z/om =~ line
|
470
|
+
if (@request_bytes += line.bytesize) > MAX_HEADER_LENGTH
|
471
|
+
raise HTTPStatus::RequestEntityTooLarge, 'headers too large'
|
472
|
+
end
|
438
473
|
@raw_header << line
|
439
474
|
end
|
440
475
|
end
|
@@ -468,7 +503,7 @@ module WEBrick
|
|
468
503
|
return unless socket
|
469
504
|
if tc = self['transfer-encoding']
|
470
505
|
case tc
|
471
|
-
when /
|
506
|
+
when /\Achunked\z/io then read_chunked(socket, block)
|
472
507
|
else raise HTTPStatus::NotImplemented, "Transfer-Encoding: #{tc}."
|
473
508
|
end
|
474
509
|
elsif self['content-length'] || @remaining_size
|
@@ -502,12 +537,16 @@ module WEBrick
|
|
502
537
|
def read_chunked(socket, block)
|
503
538
|
chunk_size, = read_chunk_size(socket)
|
504
539
|
while chunk_size > 0
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
540
|
+
begin
|
541
|
+
sz = [ chunk_size, @buffer_size ].min
|
542
|
+
data = read_data(socket, sz) # read chunk-data
|
543
|
+
if data.nil? || data.bytesize != sz
|
544
|
+
raise HTTPStatus::BadRequest, "bad chunk data size."
|
545
|
+
end
|
546
|
+
block.call(data)
|
547
|
+
end while (chunk_size -= sz) > 0
|
548
|
+
|
509
549
|
read_line(socket) # skip CRLF
|
510
|
-
block.call(data)
|
511
550
|
chunk_size, = read_chunk_size(socket)
|
512
551
|
end
|
513
552
|
read_header(socket) # trailer + CRLF
|
data/lib/webrick/httpresponse.rb
CHANGED
@@ -10,10 +10,11 @@
|
|
10
10
|
# $IPR: httpresponse.rb,v 1.45 2003/07/11 11:02:25 gotoyuzo Exp $
|
11
11
|
|
12
12
|
require 'time'
|
13
|
-
require '
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
require 'uri'
|
14
|
+
require_relative 'httpversion'
|
15
|
+
require_relative 'htmlutils'
|
16
|
+
require_relative 'httputils'
|
17
|
+
require_relative 'httpstatus'
|
17
18
|
|
18
19
|
module WEBrick
|
19
20
|
##
|
@@ -21,6 +22,8 @@ module WEBrick
|
|
21
22
|
# WEBrick HTTP Servlet.
|
22
23
|
|
23
24
|
class HTTPResponse
|
25
|
+
class InvalidHeader < StandardError
|
26
|
+
end
|
24
27
|
|
25
28
|
##
|
26
29
|
# HTTP Response version
|
@@ -251,7 +254,7 @@ module WEBrick
|
|
251
254
|
@header.delete('content-length')
|
252
255
|
elsif @header['content-length'].nil?
|
253
256
|
unless @body.is_a?(IO)
|
254
|
-
@header['content-length'] = @body ? @body.bytesize : 0
|
257
|
+
@header['content-length'] = (@body ? @body.bytesize : 0).to_s
|
255
258
|
end
|
256
259
|
end
|
257
260
|
|
@@ -274,7 +277,7 @@ module WEBrick
|
|
274
277
|
# Location is a single absoluteURI.
|
275
278
|
if location = @header['location']
|
276
279
|
if @request_uri
|
277
|
-
@header['location'] = @request_uri.merge(location)
|
280
|
+
@header['location'] = @request_uri.merge(location).to_s
|
278
281
|
end
|
279
282
|
end
|
280
283
|
end
|
@@ -287,14 +290,19 @@ module WEBrick
|
|
287
290
|
data = status_line()
|
288
291
|
@header.each{|key, value|
|
289
292
|
tmp = key.gsub(/\bwww|^te$|\b\w/){ $&.upcase }
|
290
|
-
data << "#{tmp}: #{value}" << CRLF
|
293
|
+
data << "#{tmp}: #{check_header(value)}" << CRLF
|
291
294
|
}
|
292
295
|
@cookies.each{|cookie|
|
293
|
-
data << "Set-Cookie: " << cookie.to_s << CRLF
|
296
|
+
data << "Set-Cookie: " << check_header(cookie.to_s) << CRLF
|
294
297
|
}
|
295
298
|
data << CRLF
|
296
|
-
|
299
|
+
socket.write(data)
|
297
300
|
end
|
301
|
+
rescue InvalidHeader => e
|
302
|
+
@header.clear
|
303
|
+
@cookies.clear
|
304
|
+
set_error e
|
305
|
+
retry
|
298
306
|
end
|
299
307
|
|
300
308
|
##
|
@@ -303,6 +311,8 @@ module WEBrick
|
|
303
311
|
def send_body(socket) # :nodoc:
|
304
312
|
if @body.respond_to? :readpartial then
|
305
313
|
send_body_io(socket)
|
314
|
+
elsif @body.respond_to?(:call) then
|
315
|
+
send_body_proc(socket)
|
306
316
|
else
|
307
317
|
send_body_string(socket)
|
308
318
|
end
|
@@ -322,8 +332,9 @@ module WEBrick
|
|
322
332
|
# res.set_redirect WEBrick::HTTPStatus::TemporaryRedirect
|
323
333
|
|
324
334
|
def set_redirect(status, url)
|
335
|
+
url = URI(url).to_s
|
325
336
|
@body = "<HTML><A HREF=\"#{url}\">#{url}</A>.</HTML>\n"
|
326
|
-
@header['location'] = url
|
337
|
+
@header['location'] = url
|
327
338
|
raise status
|
328
339
|
end
|
329
340
|
|
@@ -357,6 +368,15 @@ module WEBrick
|
|
357
368
|
|
358
369
|
private
|
359
370
|
|
371
|
+
def check_header(header_value)
|
372
|
+
header_value = header_value.to_s
|
373
|
+
if /[\r\n]/ =~ header_value
|
374
|
+
raise InvalidHeader
|
375
|
+
else
|
376
|
+
header_value
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
360
380
|
# :stopdoc:
|
361
381
|
|
362
382
|
def error_body(backtrace, ex, host, port)
|
@@ -394,24 +414,34 @@ module WEBrick
|
|
394
414
|
if @request_method == "HEAD"
|
395
415
|
# do nothing
|
396
416
|
elsif chunked?
|
417
|
+
buf = ''
|
397
418
|
begin
|
398
|
-
buf
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
end
|
410
|
-
_write_data(socket, "0#{CRLF}#{CRLF}")
|
419
|
+
@body.readpartial(@buffer_size, buf)
|
420
|
+
size = buf.bytesize
|
421
|
+
data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
|
422
|
+
socket.write(data)
|
423
|
+
data.clear
|
424
|
+
@sent_size += size
|
425
|
+
rescue EOFError
|
426
|
+
break
|
427
|
+
end while true
|
428
|
+
buf.clear
|
429
|
+
socket.write("0#{CRLF}#{CRLF}")
|
411
430
|
else
|
412
|
-
|
413
|
-
|
414
|
-
|
431
|
+
if %r{\Abytes (\d+)-(\d+)/\d+\z} =~ @header['content-range']
|
432
|
+
offset = $1.to_i
|
433
|
+
size = $2.to_i - offset + 1
|
434
|
+
else
|
435
|
+
offset = nil
|
436
|
+
size = @header['content-length']
|
437
|
+
size = size.to_i if size
|
438
|
+
end
|
439
|
+
begin
|
440
|
+
@sent_size = IO.copy_stream(@body, socket, size, offset)
|
441
|
+
rescue NotImplementedError
|
442
|
+
@body.seek(offset, IO::SEEK_SET)
|
443
|
+
@sent_size = IO.copy_stream(@body, socket, size)
|
444
|
+
end
|
415
445
|
end
|
416
446
|
ensure
|
417
447
|
@body.close
|
@@ -425,42 +455,60 @@ module WEBrick
|
|
425
455
|
body ? @body.bytesize : 0
|
426
456
|
while buf = @body[@sent_size, @buffer_size]
|
427
457
|
break if buf.empty?
|
428
|
-
|
429
|
-
data
|
430
|
-
|
431
|
-
|
432
|
-
@sent_size +=
|
458
|
+
size = buf.bytesize
|
459
|
+
data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
|
460
|
+
buf.clear
|
461
|
+
socket.write(data)
|
462
|
+
@sent_size += size
|
433
463
|
end
|
434
|
-
|
464
|
+
socket.write("0#{CRLF}#{CRLF}")
|
435
465
|
else
|
436
466
|
if @body && @body.bytesize > 0
|
437
|
-
|
467
|
+
socket.write(@body)
|
438
468
|
@sent_size = @body.bytesize
|
439
469
|
end
|
440
470
|
end
|
441
471
|
end
|
442
472
|
|
443
|
-
def
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
473
|
+
def send_body_proc(socket)
|
474
|
+
if @request_method == "HEAD"
|
475
|
+
# do nothing
|
476
|
+
elsif chunked?
|
477
|
+
@body.call(ChunkedWrapper.new(socket, self))
|
478
|
+
socket.write("0#{CRLF}#{CRLF}")
|
479
|
+
else
|
480
|
+
size = @header['content-length'].to_i
|
481
|
+
@body.call(socket)
|
482
|
+
@sent_size = size
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
class ChunkedWrapper
|
487
|
+
def initialize(socket, resp)
|
488
|
+
@socket = socket
|
489
|
+
@resp = resp
|
448
490
|
end
|
449
491
|
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
size
|
460
|
-
|
492
|
+
def write(buf)
|
493
|
+
return 0 if buf.empty?
|
494
|
+
socket = @socket
|
495
|
+
@resp.instance_eval {
|
496
|
+
size = buf.bytesize
|
497
|
+
data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
|
498
|
+
socket.write(data)
|
499
|
+
data.clear
|
500
|
+
@sent_size += size
|
501
|
+
size
|
502
|
+
}
|
503
|
+
end
|
504
|
+
|
505
|
+
def <<(*buf)
|
506
|
+
write(buf)
|
507
|
+
self
|
461
508
|
end
|
462
509
|
end
|
463
510
|
|
511
|
+
# preserved for compatibility with some 3rd-party handlers
|
464
512
|
def _write_data(socket, data)
|
465
513
|
socket << data
|
466
514
|
end
|
data/lib/webrick/https.rb
CHANGED
@@ -9,7 +9,8 @@
|
|
9
9
|
#
|
10
10
|
# $IPR: https.rb,v 1.15 2003/07/22 19:20:42 gotoyuzo Exp $
|
11
11
|
|
12
|
-
|
12
|
+
require_relative 'ssl'
|
13
|
+
require_relative 'httpserver'
|
13
14
|
|
14
15
|
module WEBrick
|
15
16
|
module Config
|
@@ -84,4 +85,68 @@ module WEBrick
|
|
84
85
|
|
85
86
|
# :startdoc:
|
86
87
|
end
|
88
|
+
|
89
|
+
##
|
90
|
+
#--
|
91
|
+
# Fake WEBrick::HTTPRequest for lookup_server
|
92
|
+
|
93
|
+
class SNIRequest
|
94
|
+
|
95
|
+
##
|
96
|
+
# The SNI hostname
|
97
|
+
|
98
|
+
attr_reader :host
|
99
|
+
|
100
|
+
##
|
101
|
+
# The socket address of the server
|
102
|
+
|
103
|
+
attr_reader :addr
|
104
|
+
|
105
|
+
##
|
106
|
+
# The port this request is for
|
107
|
+
|
108
|
+
attr_reader :port
|
109
|
+
|
110
|
+
##
|
111
|
+
# Creates a new SNIRequest.
|
112
|
+
|
113
|
+
def initialize(sslsocket, hostname)
|
114
|
+
@host = hostname
|
115
|
+
@addr = sslsocket.addr
|
116
|
+
@port = @addr[1]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
##
|
122
|
+
#--
|
123
|
+
# Adds SSL functionality to WEBrick::HTTPServer
|
124
|
+
|
125
|
+
class HTTPServer < ::WEBrick::GenericServer
|
126
|
+
##
|
127
|
+
# ServerNameIndication callback
|
128
|
+
|
129
|
+
def ssl_servername_callback(sslsocket, hostname = nil)
|
130
|
+
req = SNIRequest.new(sslsocket, hostname)
|
131
|
+
server = lookup_server(req)
|
132
|
+
server ? server.ssl_context : nil
|
133
|
+
end
|
134
|
+
|
135
|
+
# :stopdoc:
|
136
|
+
|
137
|
+
##
|
138
|
+
# Check whether +server+ is also SSL server.
|
139
|
+
# Also +server+'s SSL context will be created.
|
140
|
+
|
141
|
+
alias orig_virtual_host virtual_host
|
142
|
+
|
143
|
+
def virtual_host(server)
|
144
|
+
if @config[:SSLEnable] && !server.ssl_context
|
145
|
+
raise ArgumentError, "virtual host must set SSLEnable to true"
|
146
|
+
end
|
147
|
+
orig_virtual_host(server)
|
148
|
+
end
|
149
|
+
|
150
|
+
# :startdoc:
|
151
|
+
end
|
87
152
|
end
|
data/lib/webrick/httpserver.rb
CHANGED
@@ -10,13 +10,13 @@
|
|
10
10
|
# $IPR: httpserver.rb,v 1.63 2002/10/01 17:16:32 gotoyuzo Exp $
|
11
11
|
|
12
12
|
require 'io/wait'
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
13
|
+
require_relative 'server'
|
14
|
+
require_relative 'httputils'
|
15
|
+
require_relative 'httpstatus'
|
16
|
+
require_relative 'httprequest'
|
17
|
+
require_relative 'httpresponse'
|
18
|
+
require_relative 'httpservlet'
|
19
|
+
require_relative 'accesslog'
|
20
20
|
|
21
21
|
module WEBrick
|
22
22
|
class HTTPServerError < ServerError; end
|
@@ -68,8 +68,8 @@ module WEBrick
|
|
68
68
|
|
69
69
|
def run(sock)
|
70
70
|
while true
|
71
|
-
|
72
|
-
|
71
|
+
req = create_request(@config)
|
72
|
+
res = create_response(@config)
|
73
73
|
server = self
|
74
74
|
begin
|
75
75
|
timeout = @config[:RequestTimeout]
|
@@ -224,6 +224,20 @@ module WEBrick
|
|
224
224
|
}
|
225
225
|
end
|
226
226
|
|
227
|
+
##
|
228
|
+
# Creates the HTTPRequest used when handling the HTTP
|
229
|
+
# request. Can be overridden by subclasses.
|
230
|
+
def create_request(with_webrick_config)
|
231
|
+
HTTPRequest.new(with_webrick_config)
|
232
|
+
end
|
233
|
+
|
234
|
+
##
|
235
|
+
# Creates the HTTPResponse used when handling the HTTP
|
236
|
+
# request. Can be overridden by subclasses.
|
237
|
+
def create_response(with_webrick_config)
|
238
|
+
HTTPResponse.new(with_webrick_config)
|
239
|
+
end
|
240
|
+
|
227
241
|
##
|
228
242
|
# Mount table for the path a servlet is mounted on in the directory space
|
229
243
|
# of the server. Users of WEBrick can only access this indirectly via
|
@@ -267,12 +281,12 @@ module WEBrick
|
|
267
281
|
k.sort!
|
268
282
|
k.reverse!
|
269
283
|
k.collect!{|path| Regexp.escape(path) }
|
270
|
-
@scanner = Regexp.new("
|
284
|
+
@scanner = Regexp.new("\\A(" + k.join("|") +")(?=/|\\z)")
|
271
285
|
end
|
272
286
|
|
273
287
|
def normalize(dir)
|
274
288
|
ret = dir ? dir.dup : ""
|
275
|
-
ret.sub!(%r
|
289
|
+
ret.sub!(%r|/+\z|, "")
|
276
290
|
ret
|
277
291
|
end
|
278
292
|
end
|