diff --git a/http/h1_stream.lua b/http/h1_stream.lua index 67c899b1..fc5731dc 100644 --- a/http/h1_stream.lua +++ b/http/h1_stream.lua @@ -77,6 +77,7 @@ local function new_stream(connection) req_method = nil; -- string peer_version = nil; -- 1.0 or 1.1 + use_absolute_target = nil; -- tristate boolean has_main_headers = false; headers_in_progress = nil; headers_fifo = new_fifo(); @@ -648,8 +649,32 @@ function stream_methods:write_headers(headers, end_stream, timeout) assert(not headers:has(":path"), "CONNECT requests should not have a path") else -- RFC 7230 Section 5.4: A client MUST send a Host header field in all HTTP/1.1 request messages. - assert(self.connection.version < 1.1 or headers:has(":authority"), "missing authority") + local has_authority = headers:has(":authority") + assert(has_authority or self.connection.version < 1.1, "missing authority") target = assert(headers:get(":path"), "missing path") + + local use_absolute_target = self.use_absolute_target + if use_absolute_target then + assert(has_authority, "request-target absolute-form requires an authority") + elseif use_absolute_target == nil then + use_absolute_target = has_authority + end + if use_absolute_target then + -- RFC 7230 Section 5.3.2 + -- When making a request to a proxy, other than a CONNECT or server-wide + -- OPTIONS request (as detailed below), a client MUST send the target + -- URI in absolute-form as the request-target. + -- ... + -- To allow for transition to the absolute-form for all requests in some + -- future version of HTTP, a server MUST accept the absolute-form in + -- requests, even though HTTP/1.1 clients will only send them in + -- requests to proxies. + if target == "*" then + target = headers:get(":scheme") .. "://" .. headers:get(":authority") + else + target = headers:get(":scheme") .. "://" .. headers:get(":authority") .. target + end + end end if self.connection.req_locked then -- Wait until previous request has been fully written diff --git a/http/request.lua b/http/request.lua index f9b65d67..85bbf237 100644 --- a/http/request.lua +++ b/http/request.lua @@ -358,6 +358,7 @@ function request_methods:go(timeout) local port = self.port local tls = self.tls local version = self.version + local use_absolute_target -- RFC 6797 Section 8.3 if not tls and self.hsts and self.hsts:check(host) then @@ -456,17 +457,14 @@ function request_methods:go(timeout) if request_headers:get(":method") == "CONNECT" then error("cannot use HTTP Proxy with CONNECT method") end - -- TODO: Check if :path already has authority? - local old_url = self:to_uri(false) host = assert(proxy.host, "proxy is missing host") port = proxy.port or http_util.scheme_to_port[proxy.scheme] - -- proxy requests get a uri that includes host as their path - if not cloned_headers then - request_headers = request_headers:clone() - cloned_headers = true -- luacheck: ignore 311 - end - request_headers:upsert(":path", old_url) + use_absolute_target = true if proxy.userinfo then + if not cloned_headers then + request_headers = request_headers:clone() + cloned_headers = true -- luacheck: ignore 311 + end request_headers:upsert("proxy-authorization", "basic " .. basexx.to_base64(proxy.userinfo), true) end end @@ -520,6 +518,10 @@ function request_methods:go(timeout) end end + if use_absolute_target and connection.version < 2 then + stream.use_absolute_target = use_absolute_target + end + local body = self.body do -- Write outgoing headers local ok, err, errno = stream:write_headers(request_headers, body == nil, deadline and deadline-monotime())