@@ -227,6 +227,10 @@ def __eq__(self, other: typing.Any) -> bool:
227227
228228
229229def is_ipv4_hostname (hostname : str ) -> bool :
230+ """
231+ Check if the given hostname is a valid IPv4 address.
232+ Supports CIDR notation by checking only the address part.
233+ """
230234 try :
231235 ipaddress .IPv4Address (hostname .split ("/" )[0 ])
232236 except Exception :
@@ -235,8 +239,53 @@ def is_ipv4_hostname(hostname: str) -> bool:
235239
236240
237241def is_ipv6_hostname (hostname : str ) -> bool :
242+ """
243+ Check if the given hostname is a valid IPv6 address.
244+ Supports CIDR notation by checking only the address part.
245+ """
238246 try :
239247 ipaddress .IPv6Address (hostname .split ("/" )[0 ])
240248 except Exception :
241249 return False
242250 return True
251+
252+
253+ def is_ip_address (hostname : str ) -> bool :
254+ """
255+ Check if the given hostname is a valid IP address (either IPv4 or IPv6).
256+ Supports CIDR notation by checking only the address part.
257+ """
258+ return is_ipv4_hostname (hostname ) or is_ipv6_hostname (hostname )
259+
260+
261+ def normalize_header_key (key : str , * , preserve_case : bool = False ) -> str :
262+ """
263+ Normalize HTTP header keys for consistent comparison and storage.
264+
265+ By default, converts header keys to lowercase following HTTP/2 conventions.
266+ Can optionally preserve the original case for HTTP/1.1 compatibility.
267+
268+ Args:
269+ key: The header key to normalize
270+ preserve_case: If True, preserve the original case. If False (default),
271+ convert to lowercase.
272+
273+ Returns:
274+ The normalized header key as a string
275+
276+ Examples:
277+ >>> normalize_header_key("Content-Type")
278+ 'content-type'
279+ >>> normalize_header_key("Content-Type", preserve_case=True)
280+ 'Content-Type'
281+ >>> normalize_header_key("X-Custom-Header")
282+ 'x-custom-header'
283+
284+ Note:
285+ This function is useful when working with HTTP headers across different
286+ protocol versions. HTTP/2 requires lowercase header names, while HTTP/1.1
287+ traditionally uses title-case headers (though comparison is case-insensitive).
288+ """
289+ if preserve_case :
290+ return key .strip ()
291+ return key .strip ().lower ()
0 commit comments