diff --git a/http/request.lua b/http/request.lua index 3f12a8f0..aa806482 100644 --- a/http/request.lua +++ b/http/request.lua @@ -378,8 +378,16 @@ function request_methods:go(timeout) if proxy then if type(proxy) == "string" then proxy = assert(uri_patt:match(proxy), "invalid proxy URI") + proxy.path = nil -- ignore proxy.path component else assert(type(proxy) == "table" and getmetatable(proxy) == nil and proxy.scheme, "invalid proxy URI") + proxy = { + scheme = proxy.scheme; + userinfo = proxy.userinfo; + host = proxy.host; + port = proxy.port; + -- ignore proxy.path component + } end if proxy.scheme == "http" or proxy.scheme == "https" then if tls then @@ -423,9 +431,6 @@ function request_methods:go(timeout) if request_headers:get(":method") == "CONNECT" then error("cannot use HTTP Proxy with CONNECT method") end - if proxy.path ~= nil and proxy.path ~= "" then - error("an HTTP proxy cannot have a path component") - end -- TODO: Check if :path already has authority? local old_url = self:to_uri(false) host = assert(proxy.host, "proxy is missing host") diff --git a/spec/request_spec.lua b/spec/request_spec.lua index d718916d..e0b1e057 100644 --- a/spec/request_spec.lua +++ b/spec/request_spec.lua @@ -629,6 +629,30 @@ describe("http.request module", function() stream:shutdown() end) end) + it("works with a proxy server with a path component", function() + test(function(stream) + local h = assert(stream:get_headers()) + local _, host, port = stream:localname() + local authority = http_util.to_authority(host, port, "http") + assert.same(authority, h:get ":authority") + assert.same("http://" .. authority .. "/", h:get(":path")) + local resp_headers = new_headers() + resp_headers:append(":status", "200") + assert(stream:write_headers(resp_headers, false)) + assert(stream:write_chunk("hello world", true)) + end, function(req) + req.proxy = { + scheme = "http"; + host = req.host; + port = req.port; + path = "/path"; + } + local headers, stream = assert(req:go()) + assert.same("200", headers:get(":status")) + assert.same("hello world", assert(stream:get_body_as_string())) + stream:shutdown() + end) + end) it("works with http proxies on OPTIONS requests", function() test(function(stream) local h = assert(stream:get_headers()) @@ -716,6 +740,34 @@ describe("http.request module", function() stream:shutdown() end) end) + it("CONNECT proxy with path component", function() + test(function(stream, s) + local h = assert(stream:get_headers()) + local resp_headers = new_headers() + resp_headers:append(":status", "200") + assert(stream:write_headers(resp_headers, false)) + if h:get(":method") == "CONNECT" then + assert(stream.connection.version < 2) + local sock = assert(stream.connection:take_socket()) + s:add_socket(sock) + return true + else + assert(stream:write_chunk("hello world", true)) + end + end, function(req) + req.tls = true + req.proxy = { + scheme = "http"; + host = req.host; + port = req.port; + path = "/path"; + } + local headers, stream = assert(req:go()) + assert.same("200", headers:get(":status")) + assert.same("hello world", assert(stream:get_body_as_string())) + stream:shutdown() + end) + end) it("fails correctly on non CONNECT proxy", function() test(function(stream) local h = assert(stream:get_headers())