--[[
https://tools.ietf.org/html/rfc7230
https://tools.ietf.org/html/rfc7231
]]
local lpeg = require "lpeg"
local core = require "lpeg_patterns.core"
local email = require "lpeg_patterns.email"
local language = require "lpeg_patterns.language"
local uri = require "lpeg_patterns.uri"
local util = require "lpeg_patterns.util"
local C = lpeg.C
local Cc = lpeg.Cc
local Cf = lpeg.Cf
local Cg = lpeg.Cg
local Cs = lpeg.Cs
local Ct = lpeg.Ct
local Cmt = lpeg.Cmt
local P = lpeg.P
local R = lpeg.R
local S = lpeg.S
local V = lpeg.V
local _M = {}
local T_F = S"Tt" * Cc(true) + S"Ff" * Cc(false)
local function no_rich_capture(patt)
return C(patt) / function(a) return a end
end
local function case_insensitive(str)
local patt = P(true)
for i=1, #str do
local c = str:sub(i, i)
patt = patt * S(c:upper() .. c:lower())
end
return patt
end
-- RFC 7230 Section 3.2.3
_M.OWS = (core.SP + core.HTAB)^0
_M.RWS = (core.SP + core.HTAB)^1
_M.BWS = _M.OWS
-- RFC 5023
local slugtext = _M.RWS / " "
+ P"%" * (core.HEXDIG * core.HEXDIG / util.read_hex) / string.char
+ R"\32\126"
_M.SLUG = Cs(slugtext^0)
-- RFC 6454
-- discard captures from scheme, host, port and just get whole string
local serialized_origin = C(uri.scheme * P"://" * uri.host * (P":" * uri.port)^-1/function() end)
local origin_list = serialized_origin * (core.SP * serialized_origin)^0
local origin_list_or_null = P"null" + origin_list
_M.Origin = _M.OWS * origin_list_or_null * _M.OWS
-- Analogue to RFC 7230 Section 7's ABNF extension of '#'
-- Also documented as `#rule` under RFC 2616 Section 2.1
local comma_sep, comma_sep_trim do
local sep = _M.OWS * lpeg.P "," * _M.OWS
local optional_sep = (lpeg.P"," + core.SP + core.HTAB)^0
comma_sep = function(element, min, max)
local extra = sep * optional_sep * element
local patt = element
if min then
for _=2, min do
patt = patt * extra
end
else
min = 0
patt = patt^-1
end
if max then
local more = max-min-1
patt = patt * extra^-more
else
patt = patt * extra^0
end
return patt
end
-- allows leading + trailing
comma_sep_trim = function (...)
return optional_sep * comma_sep(...) * optional_sep
end
end
-- RFC 7034
_M.X_Frame_Options = case_insensitive "deny" * Cc("deny")
+ case_insensitive "sameorigin" * Cc("sameorigin")
+ case_insensitive "allow-from" * _M.RWS * serialized_origin
-- RFC 7230 Section 2.6
local HTTP_name = P"HTTP"
local HTTP_version = HTTP_name * P"/" * (core.DIGIT * P"." * core.DIGIT / util.safe_tonumber)
-- RFC 7230 Section 2.7
local absolute_path = (P"/" * uri.segment )^1
local partial_uri = Ct(uri.relative_part * (P"?" * uri.query)^-1)
-- RFC 7230 Section 3.2.6
local tchar = S "!#$%&'*+-.^_`|~" + core.DIGIT + core.ALPHA
_M.token = C(tchar^1)
local obs_text = R("\128\255")
_M.qdtext = core.HTAB + core.SP + P"\33" + R("\35\91", "\93\126") + obs_text
local quoted_pair = Cs(P"\\" * C(core.HTAB + core.SP + core.VCHAR + obs_text) / "%1")
_M.quoted_string = core.DQUOTE * Cs((_M.qdtext + quoted_pair)^0) * core.DQUOTE
local ctext = core.HTAB + core.SP + R("\33\39", "\42\91", "\93\126") + obs_text
_M.comment = P { P"(" * ( ctext + quoted_pair + V(1) )^0 * P")" }
-- RFC 7230 Section 3.2
_M.field_name = _M.token / string.lower -- case insensitive
local field_vchar = core.VCHAR + obs_text
local field_content = field_vchar * (( core.SP + core.HTAB )^1 * field_vchar)^-1
local obs_fold = ( core.SP + core.HTAB )^0 * core.CRLF * ( core.SP + core.HTAB )^1 / " "
-- field_value is not correct, see Errata: https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4189
_M.field_value = Cs((field_content + obs_fold)^0)
_M.header_field = _M.field_name * P":" * _M.OWS * _M.field_value * _M.OWS
-- RFC 7230 Section 3.3.2
_M.Content_Length = core.DIGIT^1
-- RFC 7230 Section 4
-- See https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4683
local transfer_parameter = (_M.token - S"qQ" * _M.BWS * P"=") * _M.BWS * P"=" * _M.BWS * ( _M.token + _M.quoted_string )
local transfer_extension = Cf(Ct(_M.token / string.lower) -- case insensitive
* ( _M.OWS * P";" * _M.OWS * Cg(transfer_parameter) )^0, rawset)
local transfer_coding = transfer_extension
-- RFC 7230 Section 3.3.1
_M.Transfer_Encoding = comma_sep_trim(transfer_coding, 1)
-- RFC 7230 Section 4.1.1
local chunk_ext_name = _M.token
local chunk_ext_val = _M.token + _M.quoted_string
-- See https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667
_M.chunk_ext = ( P";" * chunk_ext_name * ( P"=" * chunk_ext_val)^-1 )^0
-- RFC 7230 Section 4.3
local rank = (P"0" * ((P"." * core.DIGIT^-3) / util.safe_tonumber + Cc(0)) + P"1" * ("." * (P"0")^-3)^-1) * Cc(1)
local t_ranking = _M.OWS * P";" * _M.OWS * S"qQ" * P"=" * rank -- q is case insensitive
local t_codings = (transfer_coding * t_ranking^-1) / function(t, q)
if q then
t["q"] = q
end
return t
end
_M.TE = comma_sep_trim(t_codings)
-- RFC 7230 Section 4.4
_M.Trailer = comma_sep_trim(_M.field_name, 1)
-- RFC 7230 Section 5.3
local origin_form = Cs(absolute_path * (P"?" * uri.query)^-1)
local absolute_form = no_rich_capture(uri.absolute_uri)
local authority_form = no_rich_capture(uri.authority)
local asterisk_form = C"*"
_M.request_target = asterisk_form + origin_form + absolute_form + authority_form
-- RFC 7230 Section 3.1.1
local method = _M.token
_M.request_line = method * core.SP * _M.request_target * core.SP * HTTP_version * core.CRLF
-- RFC 7230 Section 5.4
_M.Host = uri.host * (P":" * uri.port)^-1
-- RFC 7230 Section 6.7
local protocol_name = _M.token
local protocol_version = _M.token
local protocol = protocol_name * (P"/" * protocol_version)^-1 / "%0"
_M.Upgrade = comma_sep_trim(protocol)
-- RFC 7230 Section 5.7.1
local received_protocol = (protocol_name * P"/" + Cc("HTTP")) * protocol_version / "%1/%2"
local pseudonym = _M.token
-- workaround for https://lists.w3.org/Archives/Public/ietf-http-wg/2016OctDec/0527.html
local received_by = uri.host * ((P":" * uri.port) + -lpeg.B(",")) / "%0" + pseudonym
_M.Via = comma_sep_trim(Ct(Cg(received_protocol, "protocol") * _M.RWS * Cg(received_by, "by") * (_M.RWS * Cg(_M.comment, "comment"))^-1), 1)
-- RFC 7230 Section 6.1
local connection_option = _M.token / string.lower -- case insensitive
_M.Connection = comma_sep_trim(connection_option)
-- RFC 7231 Section 3.1.1
local content_coding = _M.token / string.lower -- case insensitive
_M.Content_Encoding = comma_sep_trim(content_coding, 1)
-- RFC 7231 Section 3.1.2
local type = _M.token / string.lower -- case insensitive
local subtype = _M.token / string.lower -- case insensitive
local parameter = _M.token / string.lower -- case insensitive
* P"=" * (_M.token + _M.quoted_string)
local media_type = Cg(type, "type") * P"/" * Cg(subtype, "subtype")
* Cg(Cf(Ct(true) * (_M.OWS * P";" * _M.OWS * Cg(parameter))^0, rawset), "parameters")
local charset = _M.token / string.lower -- case insensitive
_M.Content_Type = Ct(media_type)
-- RFC 7231 Section 3.1.3
_M.Content_Language = comma_sep_trim(language.Language_Tag, 1)
-- RFC 7231 Section 3.1.4.2
_M.Content_Location = uri.absolute_uri + partial_uri
-- RFC 7231 Section 5.1.1
_M.Expect = P"100-"*S"cC"*S"oO"*S"nN"*S"tT"*S"iI"*S"nN"*S"uU"*S"eE" * Cc("100-continue")
-- RFC 7231 Section 5.1.2
_M.Max_Forwards = core.DIGIT^1 / tonumber
-- RFC 7231 Section 5.3.1
local qvalue = rank -- luacheck: ignore 211
local weight = t_ranking
-- RFC 7231 Section 5.3.2
local media_range = (P"*/*"
+ (Cg(type, "type") * P"/*")
+ (Cg(type, "type") * P"/" * Cg(subtype, "subtype"))
) * Cg(Cf(Ct(true) * (_M.OWS * ";" * _M.OWS * Cg(parameter) - weight)^0, rawset), "parameters")
local accept_ext = _M.OWS * P";" * _M.OWS * _M.token * (P"=" * (_M.token + _M.quoted_string))^-1
local accept_params = Cg(weight, "q") * Cg(Cf(Ct(true) * Cg(accept_ext)^0, rawset), "extensions")
_M.Accept = comma_sep_trim(Ct(media_range * (accept_params+Cg(Ct(true), "extensions"))))
-- RFC 7231 Section 5.3.3
_M.Accept_Charset = comma_sep_trim((charset + P"*") * weight^-1, 1)
-- RFC 7231 Section 5.3.4
local codings = content_coding + "*"
_M.Accept_Encoding = comma_sep_trim(codings * weight^-1)
-- RFC 4647 Section 2.1
local alphanum = core.ALPHA + core.DIGIT
local language_range = (core.ALPHA * core.ALPHA^-7 * (P"-" * alphanum * alphanum^-7)^0) + P"*"
-- RFC 7231 Section 5.3.5
_M.Accept_Language = comma_sep_trim(language_range * weight^-1, 1)
-- RFC 7231 Section 5.5.1
_M.From = email.mailbox
-- RFC 7231 Section 5.5.2
_M.Referer = uri.absolute_uri + partial_uri
-- RFC 7231 Section 5.5.3
local product_version = _M.token
local product = _M.token * (P"/" * product_version)^-1
_M.User_Agent = product * (_M.RWS * (product + _M.comment))^0
-- RFC 7231 Section 7.1.1.1
-- Uses os.date field names
local day_name = Cg(P"Mon"*Cc(2)
+ P"Tue"*Cc(3)
+ P"Wed"*Cc(4)
+ P"Thu"*Cc(5)
+ P"Fri"*Cc(6)
+ P"Sat"*Cc(7)
+ P"Sun"*Cc(1), "wday")
local day = Cg(core.DIGIT * core.DIGIT / tonumber, "day")
local month = Cg(P"Jan"*Cc(1)
+ P"Feb"*Cc(2)
+ P"Mar"*Cc(3)
+ P"Apr"*Cc(4)
+ P"May"*Cc(5)
+ P"Jun"*Cc(6)
+ P"Jul"*Cc(7)
+ P"Aug"*Cc(8)
+ P"Sep"*Cc(9)
+ P"Oct"*Cc(10)
+ P"Nov"*Cc(11)
+ P"Dec"*Cc(12), "month")
local year = Cg(core.DIGIT * core.DIGIT * core.DIGIT * core.DIGIT / tonumber, "year")
local date1 = day * core.SP * month * core.SP * year
local GMT = P"GMT"
local minute = Cg(core.DIGIT * core.DIGIT / tonumber, "min")
local second = Cg(core.DIGIT * core.DIGIT / tonumber, "sec")
local hour = Cg(core.DIGIT * core.DIGIT / tonumber, "hour")
-- XXX only match 00:00:00 - 23:59:60 (leap second)?
local time_of_day = hour * P":" * minute * P":" * second
_M.IMF_fixdate = Ct(day_name * P"," * core.SP * date1 * core.SP * time_of_day * core.SP * GMT)
local date2 do
local year_barrier = 70
local twodayyear = Cg(core.DIGIT * core.DIGIT / function(y)
y = tonumber(y, 10)
if y < year_barrier then
return 2000+y
else
return 1900+y
end
end, "year")
date2 = day * P"-" * month * P"-" * twodayyear
end
local day_name_l = Cg(P"Monday"*Cc(2)
+ P"Tuesday"*Cc(3)
+ P"Wednesday"*Cc(4)
+ P"Thursday"*Cc(5)
+ P"Friday"*Cc(6)
+ P"Saturday"*Cc(7)
+ P"Sunday"*Cc(1), "wday")
local rfc850_date = Ct(day_name_l * P"," * core.SP * date2 * core.SP * time_of_day * core.SP * GMT)
local date3 = month * core.SP * (day + Cg(core.SP * core.DIGIT / tonumber, "day"))
local asctime_date = Ct(day_name * core.SP * date3 * core.SP * time_of_day * core.SP * year)
local obs_date = rfc850_date + asctime_date
local HTTP_date = _M.IMF_fixdate + obs_date
_M.Date = HTTP_date
-- RFC 7231 Section 7.1.2
_M.Location = uri.uri_reference
-- RFC 7231 Section 7.1.3
local delay_seconds = core.DIGIT^1 / tonumber
_M.Retry_After = HTTP_date + delay_seconds
-- RFC 7231 Section 7.1.4
_M.Vary = P"*" + comma_sep(_M.field_name, 1)
-- RFC 7231 Section 7.4.1
_M.Allow = comma_sep_trim(method)
-- RFC 7231 Section 7.4.2
_M.Server = product * (_M.RWS * (product + _M.comment))^0
-- RFC 5789
_M.Accept_Patch = comma_sep_trim(media_type, 1)
-- RFC 5987
local attr_char = core.ALPHA + core.DIGIT + S"!#$&+-.^_`|~"
-- can't use uri.pct_encoded, as it doesn't decode all characters
local pct_encoded = P"%" * (core.HEXDIG * core.HEXDIG / util.read_hex) / string.char
local value_chars = Cs((pct_encoded + attr_char)^0)
local parmname = C(attr_char^1)
local ext_value do
-- ext-value uses charset from RFC 5987 instead
local mime_charsetc = core.ALPHA + core.DIGIT + S"!#$%&+-^_`{}~"
local mime_charset = C(mime_charsetc^1)
ext_value = Cg(mime_charset, "charset") * P"'" * Cg(language.Language_Tag, "language")^-1 * P"'" * value_chars
end
do -- RFC 5988
local ptokenchar = S"!#$%&'()*+-./:<=>?@[]^_`{|}~" + core.DIGIT + core.ALPHA
local ptoken = ptokenchar^1
local ext_name_star = parmname * P"*"
local link_extension = ext_name_star * P"=" * ext_value
+ parmname * (P"=" * (ptoken + _M.quoted_string))^-1
-- See https://www.rfc-editor.org/errata_search.php?rfc=5988&eid=3158
local link_param = link_extension
local link_value = Cf(Ct(P"<" * uri.uri_reference * P">") * (_M.OWS * P";" * _M.OWS * Cg(link_param))^0, rawset)
-- TODO: handle multiple ext_value variants...
-- e.g. server might provide one title in english, one in chinese, client should be able to pick which one to display
_M.Link = comma_sep_trim(link_value)
end
do -- RFC 6265
local cookie_name = _M.token
local cookie_octet = S"!" + R("\35\43", "\45\58", "\60\91", "\93\126")
local cookie_value = core.DQUOTE * C(cookie_octet^0) * core.DQUOTE + C(cookie_octet^0)
local cookie_pair = cookie_name * _M.BWS * P"=" * _M.BWS * cookie_value * _M.BWS
local ext_char = core.CHAR - core.CTL - S";"
ext_char = ext_char - core.WSP + core.WSP * #(core.WSP^0 * ext_char) -- No trailing whitespace
-- Complexity is to make sure whitespace before an `=` isn't captured
local extension_av = ((ext_char - S"=" - core.WSP) + core.WSP^1 * #(1-S"="))^0 / string.lower
* _M.BWS * P"=" * _M.BWS * C(ext_char^0)
+ (ext_char)^0 / string.lower * Cc(true)
local cookie_av = extension_av
local set_cookie_string = cookie_pair * Cf(Ct(true) * (P";" * _M.OWS * Cg(cookie_av))^0, rawset)
_M.Set_Cookie = set_cookie_string
local cookie_string = Cf(Ct(true) * Cg(cookie_pair) * (P";" * _M.OWS * Cg(cookie_pair))^0, rawset)
_M.Cookie = cookie_string
end
do -- RFC 6266
local disp_ext_type = _M.token / string.lower
local disposition_type = disp_ext_type
local ext_token = C((tchar-P"*"*(-tchar))^1) * P"*" -- can't use 'token' here as we need to not include the "*" at the end
local value = _M.token + _M.quoted_string
local disp_ext_parm = ext_token * _M.OWS * P"=" * _M.OWS * ext_value
+ _M.token * _M.OWS * P"=" * _M.OWS * value
local disposition_parm = disp_ext_parm
_M.Content_Disposition = disposition_type * Cf(Ct(true) * (_M.OWS * P";" * _M.OWS * Cg(disposition_parm))^0, rawset)
end
-- RFC 6455
local base64_character = core.ALPHA + core.DIGIT + S"+/"
local base64_data = base64_character * base64_character * base64_character * base64_character
local base64_padding = base64_character * base64_character * P"=="
+ base64_character * base64_character * base64_character * P"="
local base64_value_non_empty = (base64_data^1 * base64_padding^-1) + base64_padding
_M.Sec_WebSocket_Accept = base64_value_non_empty
_M.Sec_WebSocket_Key = base64_value_non_empty
local registered_token = _M.token
local extension_token = registered_token
local extension_param do
local EOF = P(-1)
local token_then_EOF = Cc(true) * _M.token * EOF
-- the quoted-string must be a valid token
local quoted_token = Cmt(_M.quoted_string, function(_, _, q)
return token_then_EOF:match(q)
end)
extension_param = _M.token * ((P"=" * (_M.token + quoted_token)) + Cc(true))
end
local extension = extension_token * Cg(Cf(Ct(true) * (P";" * Cg(extension_param))^0, rawset), "parameters")
local extension_list = comma_sep_trim(Ct(extension))
_M.Sec_WebSocket_Extensions = extension_list
_M.Sec_WebSocket_Protocol_Client = comma_sep_trim(_M.token)
_M.Sec_WebSocket_Protocol_Server = _M.token
local NZDIGIT = S"123456789"
-- Limited to 0-255 range, with no leading zeros
local version = (
P"2" * (S"01234" * core.DIGIT + P"5" * S"012345")
+ (P"1") * core.DIGIT * core.DIGIT
+ NZDIGIT * core.DIGIT^-1
) / tonumber
_M.Sec_WebSocket_Version_Client = version
_M.Sec_WebSocket_Version_Server = comma_sep_trim(version)
-- RFC 6797
local directive_name = _M.token / string.lower
local directive_value = _M.token + _M.quoted_string
local directive = Cg(directive_name * ((_M.OWS * P"=" * _M.OWS * directive_value) + Cc(true)))
_M.Strict_Transport_Security = directive^-1 * (_M.OWS * P";" * _M.OWS * directive^-1)^0
-- RFC 7089
_M.Accept_Datetime = _M.IMF_fixdate
_M.Memento_Datetime = _M.IMF_fixdate
-- RFC 7232 Section 2.2
_M.Last_Modified = HTTP_date
-- RFC 7232 Section 2.3
local weak = P"W/" -- case sensitive
local etagc = P"\33" + R"\35\115" + obs_text
local opaque_tag = core.DQUOTE * etagc^0 * core.DQUOTE
local entity_tag = Cg(weak*Cc(true) + Cc(false), "weak") * C(opaque_tag)
_M.ETag = entity_tag
-- RFC 7232 Section 3.1
_M.If_Match = P"*" + comma_sep(entity_tag, 1)
-- RFC 7232 Section 3.2
_M.If_None_Match = P"*" + comma_sep(entity_tag, 1)
-- RFC 7232 Section 3.3
_M.If_Modified_Since = HTTP_date
-- RFC 7232 Section 3.4
_M.If_Unmodified_Since = HTTP_date
-- RFC 4918
local Coded_URL = P"<" * uri.absolute_uri * P">"
local extend = Coded_URL + _M.token
local compliance_class = P"1" + P"2" + P"3" + extend
_M.DAV = comma_sep_trim(compliance_class)
_M.Depth = P"0" * Cc(0)
+ P"1" * Cc(1)
+ case_insensitive "infinity" * Cc(math.huge)
local Simple_ref = uri.absolute_uri + partial_uri
_M.Destination = Simple_ref
local State_token = Coded_URL
local Condition = (case_insensitive("not") * Cc("not"))^-1
* _M.OWS * (State_token + P"[" * entity_tag * P"]")
local List = P"(" * _M.OWS * (Condition * _M.OWS)^1 * P")"
local No_tag_list = List
local Resource_Tag = P"<" * Simple_ref * P">"
local Tagged_list = Resource_Tag * _M.OWS * (List * _M.OWS)^1
_M.If = (Tagged_list * _M.OWS)^1 + (No_tag_list * _M.OWS)^1
_M.Lock_Token = Coded_URL
_M.Overwrite = T_F
local DAVTimeOutVal = core.DIGIT^1 / tonumber
local TimeType = case_insensitive "Second-" * DAVTimeOutVal
+ case_insensitive "Infinite" * Cc(math.huge)
_M.TimeOut = comma_sep_trim(TimeType)
-- RFC 5323
_M.DASL = comma_sep_trim(Coded_URL, 1)
-- RFC 6638
_M.Schedule_Reply = T_F
_M.Schedule_Tag = opaque_tag
_M.If_Schedule_Tag_Match = opaque_tag
-- RFC 7233
local bytes_unit = P"bytes"
local other_range_unit = _M.token
local range_unit = C(bytes_unit) + other_range_unit
local first_byte_pos = core.DIGIT^1 / tonumber
local last_byte_pos = core.DIGIT^1 / tonumber
local byte_range_spec = first_byte_pos * P"-" * last_byte_pos^-1
local suffix_length = core.DIGIT^1 / tonumber
local suffix_byte_range_spec = Cc(nil) * P"-" * suffix_length
local byte_range_set = comma_sep(byte_range_spec + suffix_byte_range_spec, 1)
local byte_ranges_specifier = bytes_unit * P"=" * byte_range_set
-- RFC 7233 Section 2.3
local acceptable_ranges = comma_sep_trim(range_unit, 1) + P"none"
_M.Accept_Ranges = acceptable_ranges
-- RFC 7233 Section 3.1
local other_range_set = core.VCHAR^1
local other_ranges_specifier = other_range_unit * P"=" * other_range_set
_M.Range = byte_ranges_specifier + other_ranges_specifier
-- RFC 7233 Section 3.2
_M.If_Range = entity_tag + HTTP_date
-- RFC 7233 Section 4.2
local complete_length = core.DIGIT^1 / tonumber
local unsatisfied_range = P"*/" * complete_length
local byte_range = first_byte_pos * P"-" * last_byte_pos
local byte_range_resp = byte_range * P"/" * (complete_length + P"*")
local byte_content_range = bytes_unit * core.SP * (byte_range_resp + unsatisfied_range)
local other_range_resp = core.CHAR^0
local other_content_range = other_range_unit * core.SP * other_range_resp
_M.Content_Range = byte_content_range + other_content_range
-- RFC 7234 Section 1.2.1
local delta_seconds = core.DIGIT^1 / tonumber
-- RFC 7234 Section 5.1
_M.Age = delta_seconds
-- RFC 7234 Section 5.2
local cache_directive = _M.token * (P"=" * (_M.token + _M.quoted_string))^-1
_M.Cache_Control = comma_sep_trim(cache_directive, 1)
-- RFC 7234 Section 5.3
_M.Expires = HTTP_date
-- RFC 7234 Section 5.4
local extension_pragma = _M.token * (P"=" * (_M.token + _M.quoted_string))^-1
local pragma_directive = "no_cache" + extension_pragma
_M.Pragma = comma_sep_trim(pragma_directive, 1)
-- RFC 7234 Section 5.5
local warn_code = core.DIGIT * core.DIGIT * core.DIGIT
local warn_agent = (uri.host * (P":" * uri.port)^-1) + pseudonym
local warn_text = _M.quoted_string
local warn_date = core.DQUOTE * HTTP_date * core.DQUOTE
local warning_value = warn_code * core.SP * warn_agent * core.SP * warn_text * (core.SP * warn_date)^-1
_M.Warning = comma_sep_trim(warning_value, 1)
-- RFC 7235 Section 2
local auth_scheme = _M.token
local auth_param = Cg(_M.token / string.lower * _M.BWS * P"=" * _M.BWS * (_M.token + _M.quoted_string))
local token68 = C((core.ALPHA + core.DIGIT + P"-" + P"." + P"_" + P"~" + P"+" + P"/" )^1 * (P"=")^0)
-- TODO: each parameter name MUST only occur once per challenge
local challenge = auth_scheme * (core.SP^1 * (Cf(Ct(true) * comma_sep(auth_param), rawset) + token68))^-1
local credentials = challenge
-- RFC 7235 Section 4
_M.WWW_Authenticate = comma_sep_trim(Ct(challenge), 1)
_M.Authorization = credentials
_M.Proxy_Authenticate = _M.WWW_Authenticate
_M.Proxy_Authorization = _M.Proxy_Authorization
-- RFC 7239 Section 4
local value = _M.token + _M.quoted_string
local forwarded_pair = _M.token * P"=" * value
local forwarded_element = forwarded_pair^-1 * (P";" * forwarded_pair^-1)^0
_M.Forwarded = comma_sep_trim(forwarded_element)
-- RFC 7469
local Public_Key_Directives = directive * (_M.OWS * P";" * _M.OWS * directive)^0
_M.Public_Key_Pins = Public_Key_Directives
_M.Public_Key_Pins_Report_Only = Public_Key_Directives
-- RFC 7486
_M.Hobareg = C"regok" + C"reginwork"
-- RFC 7615
_M.Authentication_Info = comma_sep_trim(auth_param)
_M.Proxy_Authentication_Info = comma_sep_trim(auth_param)
-- RFC 7639
local protocol_id = _M.token
_M.ALPN = comma_sep_trim(protocol_id, 1)
-- RFC 7809
_M.CalDAV_Timezones = T_F
-- RFC 7838
local clear = C"clear" -- case-sensitive
local alt_authority = _M.quoted_string -- containing [ uri_host ] ":" port
local alternative = protocol_id * P"=" * alt_authority
local alt_value = alternative * (_M.OWS * P";" * _M.OWS * parameter)^0
_M.Alt_Svc = clear + comma_sep_trim(alt_value, 1)
_M.Alt_Used = uri.host * (P":" * uri.port)^-1
return _M