зеркало из https://github.com/github/ruby.git
WEBrick::HTTPResponse create tempfile if required.
WEBrick::HTTPProxyServer implementes HTTP proxy using WEBrick and Net::HTTP. WEBrick accepts HTTP/1.0 clients and Net::HTTP uses always HTTP/1.1. However HTTP/1.1 supports chunked transfer coding HTTP/1.0 doesn't. Chunked transfer coding doesn't require that content-length before the content is sent. But non-chunked transfer coding require content-length before the content is sent. So, when HTTP/1.0 clients connects WEBrick::HTTPProxyServer and origin server returns chunked response, WEBrick::HTTPProxyServer needs to store whole content to know the length of it. This patch do it using tempfile.
This commit is contained in:
Родитель
d57ce99b7d
Коммит
50d85436f8
|
@ -113,6 +113,7 @@ module WEBrick
|
|||
@chunked = false
|
||||
@filename = nil
|
||||
@sent_size = 0
|
||||
@bodytempfile = nil
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -253,7 +254,10 @@ module WEBrick
|
|||
elsif %r{^multipart/byteranges} =~ @header['content-type']
|
||||
@header.delete('content-length')
|
||||
elsif @header['content-length'].nil?
|
||||
unless @body.is_a?(IO)
|
||||
if @body.respond_to? :readpartial
|
||||
elsif @body.respond_to? :call
|
||||
make_body_tempfile
|
||||
else
|
||||
@header['content-length'] = (@body ? @body.bytesize : 0).to_s
|
||||
end
|
||||
end
|
||||
|
@ -282,6 +286,33 @@ module WEBrick
|
|||
end
|
||||
end
|
||||
|
||||
def make_body_tempfile # :nodoc:
|
||||
return if @bodytempfile
|
||||
bodytempfile = Tempfile.create("webrick")
|
||||
if @body.nil?
|
||||
# nothing
|
||||
elsif @body.respond_to? :readpartial
|
||||
IO.copy_stream(@body, bodytempfile)
|
||||
@body.close
|
||||
elsif @body.respond_to? :call
|
||||
@body.call(bodytempfile)
|
||||
else
|
||||
bodytempfile.write @body
|
||||
end
|
||||
bodytempfile.rewind
|
||||
@body = @bodytempfile = bodytempfile
|
||||
@header['content-length'] = bodytempfile.stat.size.to_s
|
||||
end
|
||||
|
||||
def remove_body_tempfile # :nodoc:
|
||||
if @bodytempfile
|
||||
@bodytempfile.close
|
||||
File.unlink @bodytempfile.path
|
||||
@bodytempfile = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
##
|
||||
# Sends the headers on +socket+
|
||||
|
||||
|
@ -445,6 +476,7 @@ module WEBrick
|
|||
ensure
|
||||
@body.close
|
||||
end
|
||||
remove_body_tempfile
|
||||
end
|
||||
|
||||
def send_body_string(socket)
|
||||
|
@ -477,7 +509,12 @@ module WEBrick
|
|||
socket.write("0#{CRLF}#{CRLF}")
|
||||
else
|
||||
size = @header['content-length'].to_i
|
||||
@body.call(socket)
|
||||
if @bodytempfile
|
||||
@bodytempfile.rewind
|
||||
IO.copy_stream(@bodytempfile, socket)
|
||||
else
|
||||
@body.call(socket)
|
||||
end
|
||||
@sent_size = size
|
||||
end
|
||||
end
|
||||
|
|
|
@ -215,6 +215,46 @@ class TestWEBrickHTTPProxy < Test::Unit::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_http10_proxy_chunked
|
||||
# Testing HTTP/1.0 client request and HTTP/1.1 chunked response
|
||||
# from origin server.
|
||||
# +------+
|
||||
# V |
|
||||
# client -------> proxy ---+
|
||||
# GET GET
|
||||
# HTTP/1.0 HTTP/1.1
|
||||
# non-chunked chunked
|
||||
#
|
||||
proxy_handler_called = request_handler_called = 0
|
||||
config = {
|
||||
:ServerName => "localhost.localdomain",
|
||||
:ProxyContentHandler => Proc.new{|req, res| proxy_handler_called += 1 },
|
||||
:RequestCallback => Proc.new{|req, res| request_handler_called += 1 }
|
||||
}
|
||||
log_tester = lambda {|log, access_log|
|
||||
log.reject! {|str|
|
||||
%r{WARN chunked is set for an HTTP/1\.0 request\. \(ignored\)} =~ str
|
||||
}
|
||||
assert_equal([], log)
|
||||
}
|
||||
TestWEBrick.start_httpproxy(config, log_tester){|server, addr, port, log|
|
||||
body = nil
|
||||
server.mount_proc("/"){|req, res|
|
||||
body = "#{req.request_method} #{req.path} #{req.body}"
|
||||
res.chunked = true
|
||||
res.body = -> (socket) { body.each_char {|c| socket.write c } }
|
||||
}
|
||||
http = Net::HTTP.new(addr, port, addr, port)
|
||||
|
||||
# Don't use Net::HTTP because it uses HTTP/1.1.
|
||||
TCPSocket.open(addr, port) {|s|
|
||||
s.write "GET / HTTP/1.0\r\nHost: localhost.localdomain\r\n\r\n"
|
||||
response = s.read
|
||||
assert_equal(body, response[/.*\z/])
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def make_certificate(key, cn)
|
||||
subject = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=#{cn}")
|
||||
exts = [
|
||||
|
|
Загрузка…
Ссылка в новой задаче