36
36
_KEY_VALUE_FORMAT = rf"{ _OWS } { _KEY_FORMAT } { _OWS } ={ _OWS } { _VALUE_FORMAT } { _OWS } "
37
37
38
38
_HEADER_PATTERN = compile (_KEY_VALUE_FORMAT )
39
+ _LIBERAL_HEADER_PATTERN = compile (
40
+ rf"{ _OWS } { _KEY_FORMAT } { _OWS } ={ _OWS } [\w ]*{ _OWS } "
41
+ )
39
42
_DELIMITER_PATTERN = compile (r"[ \t]*,[ \t]*" )
40
43
41
44
_BAGGAGE_PROPERTY_FORMAT = rf"{ _KEY_VALUE_FORMAT } |{ _OWS } { _KEY_FORMAT } { _OWS } "
42
45
46
+ _INVALID_HEADER_ERROR_MESSAGE_STRICT_TEMPLATE = (
47
+ "Header format invalid! Header values in environment variables must be "
48
+ "URL encoded per the OpenTelemetry Protocol Exporter specification: %s"
49
+ )
50
+
51
+ _INVALID_HEADER_ERROR_MESSAGE_LIBERAL_TEMPLATE = (
52
+ "Header format invalid! Header values in environment variables must be "
53
+ "URL encoded per the OpenTelemetry Protocol Exporter specification or "
54
+ "a comma separated list of name=value occurrences: %s"
55
+ )
43
56
44
57
# pylint: disable=invalid-name
45
58
@@ -49,30 +62,51 @@ def parse_headers(s: str) -> Mapping[str, str]:
49
62
return parse_env_headers (s )
50
63
51
64
52
- def parse_env_headers (s : str ) -> Mapping [str , str ]:
65
+ def parse_env_headers (s : str , liberal : bool = False ) -> Mapping [str , str ]:
53
66
"""
54
67
Parse ``s``, which is a ``str`` instance containing HTTP headers encoded
55
68
for use in ENV variables per the W3C Baggage HTTP header format at
56
69
https://www.w3.org/TR/baggage/#baggage-http-header-format, except that
57
70
additional semi-colon delimited metadata is not supported.
71
+ If ``liberal`` is True we try to parse ``s`` anyway to be more compatible
72
+ with other languages SDKs that accept non URL-encoded headers by default.
58
73
"""
59
74
headers : Dict [str , str ] = {}
60
75
headers_list : List [str ] = split (_DELIMITER_PATTERN , s )
61
76
for header in headers_list :
62
77
if not header : # empty string
63
78
continue
64
- match = _HEADER_PATTERN .fullmatch (header .strip ())
65
- if not match :
79
+ header_match = _HEADER_PATTERN .fullmatch (header .strip ())
80
+ if not header_match and not liberal :
66
81
_logger .warning (
67
- "Header format invalid! Header values in environment variables must be "
68
- "URL encoded per the OpenTelemetry Protocol Exporter specification: %s" ,
69
- header ,
82
+ _INVALID_HEADER_ERROR_MESSAGE_STRICT_TEMPLATE , header
70
83
)
71
84
continue
72
- # value may contain any number of `=`
73
- name , value = match .string .split ("=" , 1 )
74
- name = unquote (name ).strip ().lower ()
75
- value = unquote (value ).strip ()
76
- headers [name ] = value
85
+
86
+ if header_match :
87
+ match_string : str = header_match .string
88
+ # value may contain any number of `=`
89
+ name , value = match_string .split ("=" , 1 )
90
+ name = unquote (name ).strip ().lower ()
91
+ value = unquote (value ).strip ()
92
+ headers [name ] = value
93
+ else :
94
+ # this is not url-encoded and does not match the spec but we decided to be
95
+ # liberal in what we accept to match other languages SDKs behaviour
96
+ liberal_header_match = _LIBERAL_HEADER_PATTERN .fullmatch (
97
+ header .strip ()
98
+ )
99
+ if not liberal_header_match :
100
+ _logger .warning (
101
+ _INVALID_HEADER_ERROR_MESSAGE_LIBERAL_TEMPLATE , header
102
+ )
103
+ continue
104
+
105
+ liberal_match_string : str = liberal_header_match .string
106
+ # value may contain any number of `=`
107
+ name , value = liberal_match_string .split ("=" , 1 )
108
+ name = name .strip ().lower ()
109
+ value = value .strip ()
110
+ headers [name ] = value
77
111
78
112
return headers
0 commit comments