webrick 1.4.0 → 1.5.1
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 +4 -4
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +63 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/webrick.rb +1 -1
- 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 +44 -32
- data/lib/webrick/httprequest.rb +53 -14
- data/lib/webrick/httpresponse.rb +95 -50
- data/lib/webrick/https.rb +2 -2
- data/lib/webrick/httpserver.rb +23 -9
- data/lib/webrick/httpservlet.rb +5 -5
- data/lib/webrick/httpservlet/abstract.rb +3 -3
- 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 -30
- data/lib/webrick/httpservlet/prochandler.rb +1 -1
- data/lib/webrick/httpstatus.rb +1 -1
- data/lib/webrick/httputils.rb +3 -0
- data/lib/webrick/log.rb +1 -1
- data/lib/webrick/server.rb +2 -2
- data/lib/webrick/ssl.rb +2 -2
- data/lib/webrick/version.rb +1 -1
- data/webrick.gemspec +76 -0
- metadata +17 -11
data/lib/webrick/httpproxy.rb
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
# $IPR: httpproxy.rb,v 1.18 2003/03/08 18:58:10 gotoyuzo Exp $
|
11
11
|
# $kNotwork: straw.rb,v 1.3 2002/02/12 15:13:07 gotoken Exp $
|
12
12
|
|
13
|
-
|
13
|
+
require_relative "httpserver"
|
14
14
|
require "net/http"
|
15
15
|
|
16
16
|
module WEBrick
|
@@ -211,21 +211,15 @@ module WEBrick
|
|
211
211
|
end
|
212
212
|
|
213
213
|
def do_GET(req, res)
|
214
|
-
perform_proxy_request(req, res
|
215
|
-
http.get(path, header)
|
216
|
-
end
|
214
|
+
perform_proxy_request(req, res, Net::HTTP::Get)
|
217
215
|
end
|
218
216
|
|
219
217
|
def do_HEAD(req, res)
|
220
|
-
perform_proxy_request(req, res
|
221
|
-
http.head(path, header)
|
222
|
-
end
|
218
|
+
perform_proxy_request(req, res, Net::HTTP::Head)
|
223
219
|
end
|
224
220
|
|
225
221
|
def do_POST(req, res)
|
226
|
-
perform_proxy_request(req, res
|
227
|
-
http.post(path, req.body || "", header)
|
228
|
-
end
|
222
|
+
perform_proxy_request(req, res, Net::HTTP::Post, req.body_reader)
|
229
223
|
end
|
230
224
|
|
231
225
|
def do_OPTIONS(req, res)
|
@@ -301,38 +295,56 @@ module WEBrick
|
|
301
295
|
return FakeProxyURI
|
302
296
|
end
|
303
297
|
|
304
|
-
def perform_proxy_request(req, res)
|
298
|
+
def perform_proxy_request(req, res, req_class, body_stream = nil)
|
305
299
|
uri = req.request_uri
|
306
300
|
path = uri.path.dup
|
307
301
|
path << "?" << uri.query if uri.query
|
308
302
|
header = setup_proxy_header(req, res)
|
309
303
|
upstream = setup_upstream_proxy_authentication(req, res, header)
|
310
|
-
response = nil
|
311
304
|
|
305
|
+
body_tmp = []
|
312
306
|
http = Net::HTTP.new(uri.host, uri.port, upstream.host, upstream.port)
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
307
|
+
req_fib = Fiber.new do
|
308
|
+
http.start do
|
309
|
+
if @config[:ProxyTimeout]
|
310
|
+
################################## these issues are
|
311
|
+
http.open_timeout = 30 # secs # necessary (maybe because
|
312
|
+
http.read_timeout = 60 # secs # Ruby's bug, but why?)
|
313
|
+
##################################
|
314
|
+
end
|
315
|
+
if body_stream && req['transfer-encoding'] =~ /\bchunked\b/i
|
316
|
+
header['Transfer-Encoding'] = 'chunked'
|
317
|
+
end
|
318
|
+
http_req = req_class.new(path, header)
|
319
|
+
http_req.body_stream = body_stream if body_stream
|
320
|
+
http.request(http_req) do |response|
|
321
|
+
# Persistent connection requirements are mysterious for me.
|
322
|
+
# So I will close the connection in every response.
|
323
|
+
res['proxy-connection'] = "close"
|
324
|
+
res['connection'] = "close"
|
325
|
+
|
326
|
+
# stream Net::HTTP::HTTPResponse to WEBrick::HTTPResponse
|
327
|
+
res.status = response.code.to_i
|
328
|
+
res.chunked = response.chunked?
|
329
|
+
choose_header(response, res)
|
330
|
+
set_cookie(response, res)
|
331
|
+
set_via(res)
|
332
|
+
response.read_body do |buf|
|
333
|
+
body_tmp << buf
|
334
|
+
Fiber.yield # wait for res.body Proc#call
|
335
|
+
end
|
336
|
+
end # http.request
|
337
|
+
end
|
338
|
+
end
|
339
|
+
req_fib.resume # read HTTP response headers and first chunk of the body
|
340
|
+
res.body = ->(socket) do
|
341
|
+
while buf = body_tmp.shift
|
342
|
+
socket.write(buf)
|
343
|
+
buf.clear
|
344
|
+
req_fib.resume # continue response.read_body
|
319
345
|
end
|
320
|
-
response = yield(http, path, header)
|
321
346
|
end
|
322
|
-
|
323
|
-
# Persistent connection requirements are mysterious for me.
|
324
|
-
# So I will close the connection in every response.
|
325
|
-
res['proxy-connection'] = "close"
|
326
|
-
res['connection'] = "close"
|
327
|
-
|
328
|
-
# Convert Net::HTTP::HTTPResponse to WEBrick::HTTPResponse
|
329
|
-
res.status = response.code.to_i
|
330
|
-
choose_header(response, res)
|
331
|
-
set_cookie(response, res)
|
332
|
-
set_via(res)
|
333
|
-
res.body = response.body
|
334
347
|
end
|
335
|
-
|
336
348
|
# :stopdoc:
|
337
349
|
end
|
338
350
|
end
|
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
|
@@ -110,13 +113,14 @@ module WEBrick
|
|
110
113
|
@chunked = false
|
111
114
|
@filename = nil
|
112
115
|
@sent_size = 0
|
116
|
+
@bodytempfile = nil
|
113
117
|
end
|
114
118
|
|
115
119
|
##
|
116
120
|
# The response's HTTP status line
|
117
121
|
|
118
122
|
def status_line
|
119
|
-
"HTTP/#@http_version #@status #@reason_phrase
|
123
|
+
"HTTP/#@http_version #@status #@reason_phrase".rstrip << CRLF
|
120
124
|
end
|
121
125
|
|
122
126
|
##
|
@@ -250,8 +254,11 @@ module WEBrick
|
|
250
254
|
elsif %r{^multipart/byteranges} =~ @header['content-type']
|
251
255
|
@header.delete('content-length')
|
252
256
|
elsif @header['content-length'].nil?
|
253
|
-
|
254
|
-
|
257
|
+
if @body.respond_to? :readpartial
|
258
|
+
elsif @body.respond_to? :call
|
259
|
+
make_body_tempfile
|
260
|
+
else
|
261
|
+
@header['content-length'] = (@body ? @body.bytesize : 0).to_s
|
255
262
|
end
|
256
263
|
end
|
257
264
|
|
@@ -274,11 +281,38 @@ module WEBrick
|
|
274
281
|
# Location is a single absoluteURI.
|
275
282
|
if location = @header['location']
|
276
283
|
if @request_uri
|
277
|
-
@header['location'] = @request_uri.merge(location)
|
284
|
+
@header['location'] = @request_uri.merge(location).to_s
|
278
285
|
end
|
279
286
|
end
|
280
287
|
end
|
281
288
|
|
289
|
+
def make_body_tempfile # :nodoc:
|
290
|
+
return if @bodytempfile
|
291
|
+
bodytempfile = Tempfile.create("webrick")
|
292
|
+
if @body.nil?
|
293
|
+
# nothing
|
294
|
+
elsif @body.respond_to? :readpartial
|
295
|
+
IO.copy_stream(@body, bodytempfile)
|
296
|
+
@body.close
|
297
|
+
elsif @body.respond_to? :call
|
298
|
+
@body.call(bodytempfile)
|
299
|
+
else
|
300
|
+
bodytempfile.write @body
|
301
|
+
end
|
302
|
+
bodytempfile.rewind
|
303
|
+
@body = @bodytempfile = bodytempfile
|
304
|
+
@header['content-length'] = bodytempfile.stat.size.to_s
|
305
|
+
end
|
306
|
+
|
307
|
+
def remove_body_tempfile # :nodoc:
|
308
|
+
if @bodytempfile
|
309
|
+
@bodytempfile.close
|
310
|
+
File.unlink @bodytempfile.path
|
311
|
+
@bodytempfile = nil
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
|
282
316
|
##
|
283
317
|
# Sends the headers on +socket+
|
284
318
|
|
@@ -287,14 +321,19 @@ module WEBrick
|
|
287
321
|
data = status_line()
|
288
322
|
@header.each{|key, value|
|
289
323
|
tmp = key.gsub(/\bwww|^te$|\b\w/){ $&.upcase }
|
290
|
-
data << "#{tmp}: #{value}" << CRLF
|
324
|
+
data << "#{tmp}: #{check_header(value)}" << CRLF
|
291
325
|
}
|
292
326
|
@cookies.each{|cookie|
|
293
|
-
data << "Set-Cookie: " << cookie.to_s << CRLF
|
327
|
+
data << "Set-Cookie: " << check_header(cookie.to_s) << CRLF
|
294
328
|
}
|
295
329
|
data << CRLF
|
296
|
-
|
330
|
+
socket.write(data)
|
297
331
|
end
|
332
|
+
rescue InvalidHeader => e
|
333
|
+
@header.clear
|
334
|
+
@cookies.clear
|
335
|
+
set_error e
|
336
|
+
retry
|
298
337
|
end
|
299
338
|
|
300
339
|
##
|
@@ -310,12 +349,6 @@ module WEBrick
|
|
310
349
|
end
|
311
350
|
end
|
312
351
|
|
313
|
-
def to_s # :nodoc:
|
314
|
-
ret = ""
|
315
|
-
send_response(ret)
|
316
|
-
ret
|
317
|
-
end
|
318
|
-
|
319
352
|
##
|
320
353
|
# Redirects to +url+ with a WEBrick::HTTPStatus::Redirect +status+.
|
321
354
|
#
|
@@ -324,8 +357,9 @@ module WEBrick
|
|
324
357
|
# res.set_redirect WEBrick::HTTPStatus::TemporaryRedirect
|
325
358
|
|
326
359
|
def set_redirect(status, url)
|
360
|
+
url = URI(url).to_s
|
327
361
|
@body = "<HTML><A HREF=\"#{url}\">#{url}</A>.</HTML>\n"
|
328
|
-
@header['location'] = url
|
362
|
+
@header['location'] = url
|
329
363
|
raise status
|
330
364
|
end
|
331
365
|
|
@@ -359,6 +393,15 @@ module WEBrick
|
|
359
393
|
|
360
394
|
private
|
361
395
|
|
396
|
+
def check_header(header_value)
|
397
|
+
header_value = header_value.to_s
|
398
|
+
if /[\r\n]/ =~ header_value
|
399
|
+
raise InvalidHeader
|
400
|
+
else
|
401
|
+
header_value
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
362
405
|
# :stopdoc:
|
363
406
|
|
364
407
|
def error_body(backtrace, ex, host, port)
|
@@ -401,22 +444,34 @@ module WEBrick
|
|
401
444
|
@body.readpartial(@buffer_size, buf)
|
402
445
|
size = buf.bytesize
|
403
446
|
data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
|
404
|
-
|
447
|
+
socket.write(data)
|
405
448
|
data.clear
|
406
449
|
@sent_size += size
|
407
450
|
rescue EOFError
|
408
451
|
break
|
409
452
|
end while true
|
410
453
|
buf.clear
|
411
|
-
|
454
|
+
socket.write("0#{CRLF}#{CRLF}")
|
412
455
|
else
|
413
|
-
|
414
|
-
|
415
|
-
|
456
|
+
if %r{\Abytes (\d+)-(\d+)/\d+\z} =~ @header['content-range']
|
457
|
+
offset = $1.to_i
|
458
|
+
size = $2.to_i - offset + 1
|
459
|
+
else
|
460
|
+
offset = nil
|
461
|
+
size = @header['content-length']
|
462
|
+
size = size.to_i if size
|
463
|
+
end
|
464
|
+
begin
|
465
|
+
@sent_size = IO.copy_stream(@body, socket, size, offset)
|
466
|
+
rescue NotImplementedError
|
467
|
+
@body.seek(offset, IO::SEEK_SET)
|
468
|
+
@sent_size = IO.copy_stream(@body, socket, size)
|
469
|
+
end
|
416
470
|
end
|
417
471
|
ensure
|
418
472
|
@body.close
|
419
473
|
end
|
474
|
+
remove_body_tempfile
|
420
475
|
end
|
421
476
|
|
422
477
|
def send_body_string(socket)
|
@@ -429,13 +484,13 @@ module WEBrick
|
|
429
484
|
size = buf.bytesize
|
430
485
|
data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
|
431
486
|
buf.clear
|
432
|
-
|
487
|
+
socket.write(data)
|
433
488
|
@sent_size += size
|
434
489
|
end
|
435
|
-
|
490
|
+
socket.write("0#{CRLF}#{CRLF}")
|
436
491
|
else
|
437
492
|
if @body && @body.bytesize > 0
|
438
|
-
|
493
|
+
socket.write(@body)
|
439
494
|
@sent_size = @body.bytesize
|
440
495
|
end
|
441
496
|
end
|
@@ -446,10 +501,15 @@ module WEBrick
|
|
446
501
|
# do nothing
|
447
502
|
elsif chunked?
|
448
503
|
@body.call(ChunkedWrapper.new(socket, self))
|
449
|
-
|
504
|
+
socket.write("0#{CRLF}#{CRLF}")
|
450
505
|
else
|
451
506
|
size = @header['content-length'].to_i
|
452
|
-
@
|
507
|
+
if @bodytempfile
|
508
|
+
@bodytempfile.rewind
|
509
|
+
IO.copy_stream(@bodytempfile, socket)
|
510
|
+
else
|
511
|
+
@body.call(socket)
|
512
|
+
end
|
453
513
|
@sent_size = size
|
454
514
|
end
|
455
515
|
end
|
@@ -461,40 +521,25 @@ module WEBrick
|
|
461
521
|
end
|
462
522
|
|
463
523
|
def write(buf)
|
464
|
-
return if buf.empty?
|
524
|
+
return 0 if buf.empty?
|
465
525
|
socket = @socket
|
466
526
|
@resp.instance_eval {
|
467
527
|
size = buf.bytesize
|
468
528
|
data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
|
469
|
-
|
529
|
+
socket.write(data)
|
470
530
|
data.clear
|
471
531
|
@sent_size += size
|
532
|
+
size
|
472
533
|
}
|
473
534
|
end
|
474
|
-
alias :<< :write
|
475
|
-
end
|
476
|
-
|
477
|
-
def _send_file(output, input, offset, size)
|
478
|
-
while offset > 0
|
479
|
-
sz = @buffer_size < size ? @buffer_size : size
|
480
|
-
buf = input.read(sz)
|
481
|
-
offset -= buf.bytesize
|
482
|
-
end
|
483
535
|
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
end
|
488
|
-
else
|
489
|
-
while size > 0
|
490
|
-
sz = @buffer_size < size ? @buffer_size : size
|
491
|
-
buf = input.read(sz)
|
492
|
-
_write_data(output, buf)
|
493
|
-
size -= buf.bytesize
|
494
|
-
end
|
536
|
+
def <<(*buf)
|
537
|
+
write(buf)
|
538
|
+
self
|
495
539
|
end
|
496
540
|
end
|
497
541
|
|
542
|
+
# preserved for compatibility with some 3rd-party handlers
|
498
543
|
def _write_data(socket, data)
|
499
544
|
socket << data
|
500
545
|
end
|