diff --git a/lib/evil-proxy/agentproxy.rb b/lib/evil-proxy/agentproxy.rb index 2652194..fe1369c 100644 --- a/lib/evil-proxy/agentproxy.rb +++ b/lib/evil-proxy/agentproxy.rb @@ -13,38 +13,57 @@ def fire key, *args @mitm_server.fire key, *args, self end - def perform_proxy_request(req, res) + def perform_proxy_request(req, res, req_class, body_stream = nil) uri = req.request_uri path = uri.path.dup path << "?" << uri.query if uri.query - header = Hash.new - choose_header(req, header) - response = nil + header = setup_proxy_header(req, res) + upstream = setup_upstream_proxy_authentication(req, res, header) - http = Net::HTTP.new(uri.host, uri.port) + body_tmp = [] + http = Net::HTTP.new(uri.host, uri.port, upstream.host, upstream.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE - http.start do - if @config[:ProxyTimeout] - ################################## these issues are - http.open_timeout = 30 # secs # necessary (maybe because - http.read_timeout = 60 # secs # Ruby's bug, but why?) - ################################## - end + req_fib = Fiber.new do + http.start do + if @config[:ProxyTimeout] + ################################## these issues are + http.open_timeout = 30 # secs # necessary (maybe because + http.read_timeout = 60 # secs # Ruby's bug, but why?) + ################################## + end + if body_stream && req['transfer-encoding'] =~ /\bchunked\b/i + header['Transfer-Encoding'] = 'chunked' + end + http_req = req_class.new(path, header) + http_req.body_stream = body_stream if body_stream + http.request(http_req) do |response| + # Persistent connection requirements are mysterious for me. + # So I will close the connection in every response. + res['proxy-connection'] = "close" + res['connection'] = "close" - response = yield(http, path, header) + # stream Net::HTTP::HTTPResponse to WEBrick::HTTPResponse + res.status = response.code.to_i + res.chunked = response.chunked? + choose_header(response, res) + set_cookie(response, res) + set_via(res) + response.read_body do |buf| + body_tmp << buf + Fiber.yield # wait for res.body Proc#call + end + end # http.request + end + end + req_fib.resume # read HTTP response headers and first chunk of the body + res.body = ->(socket) do + while buf = body_tmp.shift + socket.write(buf) + buf.clear + req_fib.resume # continue response.read_body + end end - - # Persistent connection requirements are mysterious for me. - # So I will close the connection in every response. - res['proxy-connection'] = "close" - res['connection'] = "close" - - # Convert Net::HTTP::HTTPResponse to WEBrick::HTTPResponse - res.status = response.code.to_i - choose_header(response, res) - set_cookie(response, res) - res.body = response.body end def service req, res diff --git a/lib/evil-proxy/httpproxy.rb b/lib/evil-proxy/httpproxy.rb index b099907..fe18e4a 100644 --- a/lib/evil-proxy/httpproxy.rb +++ b/lib/evil-proxy/httpproxy.rb @@ -69,25 +69,25 @@ def self.define_callback_methods callback end def do_PUT(req, res) - perform_proxy_request(req, res) do |http, path, header| + perform_proxy_request(req, res, req.class) do |http, path, header| http.put(path, req.body || '', header) end end def do_DELETE(req, res) - perform_proxy_request(req, res) do |http, path, header| + perform_proxy_request(req, res, req.class) do |http, path, header| http.delete(path, header) end end def do_PATCH(req, res) - perform_proxy_request(req, res) do |http, path, header| + perform_proxy_request(req, res, req.class) do |http, path, header| http.patch(path, req.body || '', header) end end def do_OPTIONS(req, res) - perform_proxy_request(req, res) do |http, path, header| + perform_proxy_request(req, res, req.class) do |http, path, header| http.options(path, header) end end