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.

@@ -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
- require "webrick/httpserver"
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) do |http, path, header|
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) do |http, path, header|
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) do |http, path, header|
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
- http.start do
314
- if @config[:ProxyTimeout]
315
- ################################## these issues are
316
- http.open_timeout = 30 # secs # necessary (maybe because
317
- http.read_timeout = 60 # secs # Ruby's bug, but why?)
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
@@ -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
- require 'webrick/httpversion'
14
- require 'webrick/httpstatus'
15
- require 'webrick/httputils'
16
- require 'webrick/cookie'
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 /close/io =~ self["connection"]
229
+ if /\Aclose\z/io =~ self["connection"]
230
230
  @keep_alive = false
231
- elsif /keep-alive/io =~ self["connection"]
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
- if @request_line.bytesize >= MAX_URI_LENGTH and @request_line[-1, 1] != LF
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 /chunked/io then read_chunked(socket, block)
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
- data = read_data(socket, chunk_size) # read chunk-data
506
- if data.nil? || data.bytesize != chunk_size
507
- raise BadRequest, "bad chunk data size."
508
- end
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
@@ -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 'webrick/httpversion'
14
- require 'webrick/htmlutils'
15
- require 'webrick/httputils'
16
- require 'webrick/httpstatus'
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 #{CRLF}"
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
- unless @body.is_a?(IO)
254
- @header['content-length'] = @body ? @body.bytesize : 0
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
- _write_data(socket, data)
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.to_s
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
- _write_data(socket, data)
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
- _write_data(socket, "0#{CRLF}#{CRLF}")
454
+ socket.write("0#{CRLF}#{CRLF}")
412
455
  else
413
- size = @header['content-length'].to_i
414
- _send_file(socket, @body, 0, size)
415
- @sent_size = size
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
- _write_data(socket, data)
487
+ socket.write(data)
433
488
  @sent_size += size
434
489
  end
435
- _write_data(socket, "0#{CRLF}#{CRLF}")
490
+ socket.write("0#{CRLF}#{CRLF}")
436
491
  else
437
492
  if @body && @body.bytesize > 0
438
- _write_data(socket, @body)
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
- _write_data(socket, "0#{CRLF}#{CRLF}")
504
+ socket.write("0#{CRLF}#{CRLF}")
450
505
  else
451
506
  size = @header['content-length'].to_i
452
- @body.call(socket)
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
- _write_data(socket, data)
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
- if size == 0
485
- while buf = input.read(@buffer_size)
486
- _write_data(output, buf)
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