@@ -5,6 +5,7 @@ local ce = require "cqueues.errno"
5
5
local new_fifo = require " fifo"
6
6
local lpeg = require " lpeg"
7
7
local http_patts = require " lpeg_patterns.http"
8
+ local uri_patts = require " lpeg_patterns.uri"
8
9
local new_headers = require " http.headers" .new
9
10
local reason_phrases = require " http.h1_reason_phrases"
10
11
local stream_common = require " http.stream_common"
@@ -22,6 +23,8 @@ local Connection = lpeg.Ct(http_patts.Connection) * EOF
22
23
local Content_Encoding = lpeg .Ct (http_patts .Content_Encoding ) * EOF
23
24
local Transfer_Encoding = lpeg .Ct (http_patts .Transfer_Encoding ) * EOF
24
25
local TE = lpeg .Ct (http_patts .TE ) * EOF
26
+ local absolute_form = uri_patts .absolute_uri * EOF
27
+ local authority_form = uri_patts .authority * EOF
25
28
26
29
local function has (list , val )
27
30
if list then
@@ -249,6 +252,41 @@ function stream_methods:step(timeout)
249
252
return true
250
253
end
251
254
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
+
252
290
-- read_headers may be called more than once for a stream
253
291
-- e.g. for 100 Continue
254
292
-- this function *should never throw* under normal operation
@@ -281,12 +319,43 @@ function stream_methods:read_headers(timeout)
281
319
self .peer_version = httpversion
282
320
headers = new_headers ()
283
321
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" , " *" )
288
357
end
289
- headers :append (" :scheme" , self : checktls () and " https " or " http " )
358
+ headers :append (" :scheme" , scheme )
290
359
self :set_state (" open" )
291
360
else -- client
292
361
-- Make sure we're at front of connection pipeline
@@ -342,9 +411,17 @@ function stream_methods:read_headers(timeout)
342
411
end
343
412
k = k :lower () -- normalise to lower case
344
413
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 )
346
424
end
347
- headers :append (k , v )
348
425
end
349
426
350
427
do
@@ -742,13 +819,9 @@ function stream_methods:write_headers(headers, end_stream, timeout)
742
819
return nil , err , errno
743
820
end
744
821
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
752
825
end
753
826
end
754
827
end
0 commit comments