@@ -5,6 +5,7 @@ local ce = require "cqueues.errno"
55local new_fifo = require " fifo"
66local lpeg = require " lpeg"
77local http_patts = require " lpeg_patterns.http"
8+ local uri_patts = require " lpeg_patterns.uri"
89local new_headers = require " http.headers" .new
910local reason_phrases = require " http.h1_reason_phrases"
1011local stream_common = require " http.stream_common"
@@ -22,6 +23,8 @@ local Connection = lpeg.Ct(http_patts.Connection) * EOF
2223local Content_Encoding = lpeg .Ct (http_patts .Content_Encoding ) * EOF
2324local Transfer_Encoding = lpeg .Ct (http_patts .Transfer_Encoding ) * EOF
2425local TE = lpeg .Ct (http_patts .TE ) * EOF
26+ local absolute_form = uri_patts .absolute_uri * EOF
27+ local authority_form = uri_patts .authority * EOF
2528
2629local function has (list , val )
2730 if list then
@@ -249,6 +252,41 @@ function stream_methods:step(timeout)
249252 return true
250253end
251254
255+ -- should return scheme, authority, path
256+ local function parse_target (path )
257+ if path :sub (1 , 1 ) == " /" or path == " *" then
258+ -- 'origin-form' or 'asterisk-form'
259+ -- early exit for common case
260+ return nil , nil , path
261+ end
262+
263+ local absolute_uri = absolute_form :match (path )
264+ if absolute_uri then
265+ -- don't want normalised form of authority or path
266+ local authority
267+ if absolute_uri .host then
268+ authority , path = path :match (" ://([^/]*)(.*)" )
269+ if path == " " then
270+ path = nil
271+ end
272+ else
273+ -- authority is nil
274+ -- path should be nil if there are no characters.
275+ path = path :match (" :(.+)" )
276+ end
277+ return absolute_uri .scheme , authority , path
278+ end
279+
280+ if authority_form :match (path ) then
281+ -- don't want normalised form of authority
282+ -- `path` *is* the authority
283+ return nil , path , nil
284+ end
285+
286+ -- other...
287+ return nil , nil , path
288+ end
289+
252290-- read_headers may be called more than once for a stream
253291-- e.g. for 100 Continue
254292-- this function *should never throw* under normal operation
@@ -281,12 +319,43 @@ function stream_methods:read_headers(timeout)
281319 self .peer_version = httpversion
282320 headers = new_headers ()
283321 headers :append (" :method" , method )
284- if method == " CONNECT" then
285- headers :append (" :authority" , target )
286- else
287- headers :append (" :path" , target )
322+ local scheme , authority , path = parse_target (target )
323+ if authority then
324+ -- RFC 7230 Section 5.4
325+ -- When a proxy receives a request with an absolute-form of
326+ -- request-target, the proxy MUST ignore the received Host header field
327+ -- (if any) and instead replace it with the host information of the
328+ -- request-target.
329+ headers :append (" :authority" , authority )
330+ end
331+ -- RFC 7230 Section 5.5
332+ -- If the request-target is in absolute-form, the effective request URI
333+ -- is the same as the request-target. Otherwise, the effective request
334+ -- URI is constructed as follows:
335+ if not scheme then
336+ -- If the server's configuration (or outbound gateway) provides a
337+ -- fixed URI scheme, that scheme is used for the effective request
338+ -- URI. Otherwise, if the request is received over a TLS-secured TCP
339+ -- connection, the effective request URI's scheme is "https"; if not,
340+ -- the scheme is "http".
341+ if self :checktls () then
342+ scheme = " https"
343+ else
344+ scheme = " http"
345+ end
346+ end
347+ if path then
348+ headers :append (" :path" , path )
349+ elseif method == " OPTIONS" then
350+ -- RFC 7230 Section 5.3.4
351+ -- If a proxy receives an OPTIONS request with an absolute-form of
352+ -- request-target in which the URI has an empty path and no query
353+ -- component, then the last proxy on the request chain MUST send a
354+ -- request-target of "*" when it forwards the request to the indicated
355+ -- origin server.
356+ headers :append (" :path" , " *" )
288357 end
289- headers :append (" :scheme" , self : checktls () and " https " or " http " )
358+ headers :append (" :scheme" , scheme )
290359 self :set_state (" open" )
291360 else -- client
292361 -- Make sure we're at front of connection pipeline
@@ -342,9 +411,17 @@ function stream_methods:read_headers(timeout)
342411 end
343412 k = k :lower () -- normalise to lower case
344413 if k == " host" and not is_trailers then
345- k = " :authority"
414+ -- RFC 7230 Section 5.4
415+ -- When a proxy receives a request with an absolute-form of
416+ -- request-target, the proxy MUST ignore the received Host header field
417+ -- (if any) and instead replace it with the host information of the
418+ -- request-target.
419+ if not headers :has (" :authority" ) then
420+ headers :append (" :authority" , v )
421+ end
422+ else
423+ headers :append (k , v )
346424 end
347- headers :append (k , v )
348425 end
349426
350427 do
@@ -742,13 +819,9 @@ function stream_methods:write_headers(headers, end_stream, timeout)
742819 return nil , err , errno
743820 end
744821 elseif name == " :authority" then
745- -- for CONNECT requests, :authority is the path
746- if self .req_method ~= " CONNECT" then
747- -- otherwise it's the Host header
748- local ok , err , errno = self .connection :write_header (" host" , value , 0 )
749- if not ok then
750- return nil , err , errno
751- end
822+ local ok , err , errno = self .connection :write_header (" host" , value , 0 )
823+ if not ok then
824+ return nil , err , errno
752825 end
753826 end
754827 end
0 commit comments