1- import re
1+ from collections . abc import Iterator
22from urllib .parse import urljoin , urlparse
33
44from httpx import Request , Response
1616from mcp .shared .inbound import MCP_PROTOCOL_VERSION_HEADER
1717
1818
19+ def _read_quoted_www_auth_value (header : str , value_start : int ) -> tuple [str | None , int ]:
20+ value_chars : list [str ] = []
21+ escaped = False
22+ index = value_start + 1
23+
24+ while index < len (header ):
25+ char = header [index ]
26+ if escaped :
27+ value_chars .append (char )
28+ escaped = False
29+ elif char == "\\ " :
30+ escaped = True
31+ elif char == '"' :
32+ value = "" .join (value_chars )
33+ return (value or None ), index + 1
34+ else :
35+ value_chars .append (char )
36+ index += 1
37+
38+ return None , len (header )
39+
40+
41+ def _read_www_auth_value (header : str , value_start : int ) -> tuple [str | None , int ]:
42+ while value_start < len (header ) and header [value_start ] in " \t " :
43+ value_start += 1
44+
45+ if value_start >= len (header ):
46+ return None , value_start
47+
48+ if header [value_start ] == '"' :
49+ return _read_quoted_www_auth_value (header , value_start )
50+
51+ value_end = value_start
52+ while value_end < len (header ) and header [value_end ] not in " \t ," :
53+ value_end += 1
54+
55+ if value_end == value_start :
56+ return None , value_end
57+
58+ return header [value_start :value_end ], value_end
59+
60+
61+ def _iter_www_auth_params (header : str ) -> Iterator [tuple [str , str ]]:
62+ index = 0
63+ while index < len (header ):
64+ while index < len (header ) and header [index ] in " \t ," :
65+ index += 1
66+
67+ name_start = index
68+ while index < len (header ) and header [index ] not in " \t =," :
69+ index += 1
70+
71+ if index == name_start :
72+ index += 1
73+ continue
74+
75+ name = header [name_start :index ]
76+ value_start = index
77+ while value_start < len (header ) and header [value_start ] in " \t " :
78+ value_start += 1
79+
80+ if value_start >= len (header ) or header [value_start ] != "=" :
81+ index = value_start
82+ continue
83+
84+ value , index = _read_www_auth_value (header , value_start + 1 )
85+ if value is not None :
86+ yield name , value
87+
88+
1989def extract_field_from_www_auth (response : Response , field_name : str ) -> str | None :
2090 """Extract field from WWW-Authenticate header.
2191
@@ -26,14 +96,9 @@ def extract_field_from_www_auth(response: Response, field_name: str) -> str | No
2696 if not www_auth_header :
2797 return None
2898
29- # Pattern matches complete auth-param names: field_name="value" or field_name=value (unquoted).
30- field_pattern = re .escape (field_name )
31- pattern = rf'(?:^|[\s,]){ field_pattern } =(?:"([^"]+)"|([^\s,]+))'
32- match = re .search (pattern , www_auth_header )
33-
34- if match :
35- # Return quoted value if present, otherwise unquoted value
36- return match .group (1 ) or match .group (2 )
99+ for name , value in _iter_www_auth_params (www_auth_header ):
100+ if name == field_name :
101+ return value
37102
38103 return None
39104
0 commit comments