1616import base64
1717import json
1818import mimetypes
19+ from collections import defaultdict
1920from pathlib import Path
2021from types import SimpleNamespace
2122from typing import TYPE_CHECKING , Any , Callable , Dict , List , Optional , Union , cast
2223from urllib import parse
2324
2425from playwright ._impl ._api_structures import (
26+ Headers ,
27+ HeadersArray ,
2528 RemoteAddr ,
2629 RequestSizes ,
2730 ResourceTiming ,
3437 from_nullable_channel ,
3538)
3639from playwright ._impl ._event_context_manager import EventContextManagerImpl
37- from playwright ._impl ._helper import ContinueParameters , Header , locals_to_params
40+ from playwright ._impl ._helper import ContinueParameters , locals_to_params
3841from playwright ._impl ._wait_helper import WaitHelper
3942
4043if TYPE_CHECKING : # pragma: no cover
@@ -64,8 +67,8 @@ def __init__(
6467 "responseStart" : - 1 ,
6568 "responseEnd" : - 1 ,
6669 }
67- self ._headers : List [ Header ] = self ._initializer ["headers" ]
68- self ._all_headers_future : Optional [asyncio .Future [List [ Header ] ]] = None
70+ self ._provisional_headers = RawHeaders ( self ._initializer ["headers" ])
71+ self ._all_headers_future : Optional [asyncio .Future [RawHeaders ]] = None
6972
7073 def __repr__ (self ) -> str :
7174 return f"<Request url={ self .url !r} method={ self .method !r} >"
@@ -115,10 +118,6 @@ def post_data_buffer(self) -> Optional[bytes]:
115118 return None
116119 return base64 .b64decode (b64_content )
117120
118- @property
119- def headers (self ) -> Dict [str , str ]:
120- return headers_array_to_object (self ._headers , True )
121-
122121 async def response (self ) -> Optional ["Response" ]:
123122 return from_nullable_channel (await self ._channel .send ("response" ))
124123
@@ -145,25 +144,27 @@ def failure(self) -> Optional[str]:
145144 def timing (self ) -> ResourceTiming :
146145 return self ._timing
147146
148- async def all_headers (self ) -> Dict [str , str ]:
149- return headers_array_to_object (await self ._get_headers_if_needed (), True )
147+ @property
148+ def headers (self ) -> Headers :
149+ return self ._provisional_headers .headers ()
150150
151- async def headers_array (self ) -> List [List [str ]]:
152- return list (
153- map (
154- lambda header : [header ["name" ], header ["value" ]],
155- await self ._get_headers_if_needed (),
156- )
157- )
151+ async def all_headers (self ) -> Headers :
152+ return (await self ._actual_headers ()).headers ()
153+
154+ async def headers_array (self ) -> HeadersArray :
155+ return (await self ._actual_headers ()).headers_array ()
156+
157+ async def header_value (self , name : str ) -> Optional [str ]:
158+ return (await self ._actual_headers ()).get (name )
158159
159- async def _get_headers_if_needed (self ) -> List [ Header ] :
160+ async def _actual_headers (self ) -> "RawHeaders" :
160161 if not self ._all_headers_future :
161162 self ._all_headers_future = asyncio .Future ()
162163 response = await self .response ()
163164 if not response :
164- return self ._headers
165+ return self ._provisional_headers
165166 headers = await response ._channel .send ("rawRequestHeaders" )
166- self ._all_headers_future .set_result (headers )
167+ self ._all_headers_future .set_result (RawHeaders ( headers ) )
167168 return await self ._all_headers_future
168169
169170
@@ -256,10 +257,10 @@ def __init__(
256257 self ._request ._timing ["connectEnd" ] = timing ["connectEnd" ]
257258 self ._request ._timing ["requestStart" ] = timing ["requestStart" ]
258259 self ._request ._timing ["responseStart" ] = timing ["responseStart" ]
259- self ._headers = headers_array_to_object (
260- cast (List [ Header ] , self ._initializer ["headers" ]), True
260+ self ._provisional_headers = RawHeaders (
261+ cast (HeadersArray , self ._initializer ["headers" ])
261262 )
262- self ._raw_headers_future : Optional [asyncio .Future [List [ Header ] ]] = None
263+ self ._raw_headers_future : Optional [asyncio .Future [RawHeaders ]] = None
263264 self ._finished_future : asyncio .Future [bool ] = asyncio .Future ()
264265
265266 def __repr__ (self ) -> str :
@@ -284,25 +285,26 @@ def status_text(self) -> str:
284285 return self ._initializer ["statusText" ]
285286
286287 @property
287- def headers (self ) -> Dict [ str , str ] :
288- return self ._headers . copy ()
288+ def headers (self ) -> Headers :
289+ return self ._provisional_headers . headers ()
289290
290- async def all_headers (self ) -> Dict [ str , str ] :
291- return headers_array_to_object (await self ._get_headers_if_needed (), True )
291+ async def all_headers (self ) -> Headers :
292+ return (await self ._actual_headers ()). headers ( )
292293
293- async def headers_array (self ) -> List [List [str ]]:
294- return list (
295- map (
296- lambda header : [header ["name" ], header ["value" ]],
297- await self ._get_headers_if_needed (),
298- )
299- )
294+ async def headers_array (self ) -> HeadersArray :
295+ return (await self ._actual_headers ()).headers_array ()
296+
297+ async def header_value (self , name : str ) -> Optional [str ]:
298+ return (await self ._actual_headers ()).get (name )
299+
300+ async def header_values (self , name : str ) -> List [str ]:
301+ return (await self ._actual_headers ()).get_all (name )
300302
301- async def _get_headers_if_needed (self ) -> List [ Header ] :
303+ async def _actual_headers (self ) -> "RawHeaders" :
302304 if not self ._raw_headers_future :
303305 self ._raw_headers_future = asyncio .Future ()
304- headers = cast (List [ Header ] , await self ._channel .send ("rawResponseHeaders" ))
305- self ._raw_headers_future .set_result (headers )
306+ headers = cast (HeadersArray , await self ._channel .send ("rawResponseHeaders" ))
307+ self ._raw_headers_future .set_result (RawHeaders ( headers ) )
306308 return await self ._raw_headers_future
307309
308310 async def server_addr (self ) -> Optional [RemoteAddr ]:
@@ -420,12 +422,32 @@ def _on_close(self) -> None:
420422 self .emit (WebSocket .Events .Close , self )
421423
422424
423- def serialize_headers (headers : Dict [str , str ]) -> List [ Header ] :
425+ def serialize_headers (headers : Dict [str , str ]) -> HeadersArray :
424426 return [{"name" : name , "value" : value } for name , value in headers .items ()]
425427
426428
427- def headers_array_to_object (headers : List [Header ], lower_case : bool ) -> Dict [str , str ]:
428- return {
429- (header ["name" ].lower () if lower_case else header ["name" ]): header ["value" ]
430- for header in headers
431- }
429+ class RawHeaders :
430+ def __init__ (self , headers : HeadersArray ) -> None :
431+ self ._headers_array = headers
432+ self ._headers_map : Dict [str , Dict [str , bool ]] = defaultdict (dict )
433+ for header in headers :
434+ self ._headers_map [header ["name" ].lower ()][header ["value" ]] = True
435+
436+ def get (self , name : str ) -> Optional [str ]:
437+ values = self .get_all (name )
438+ if not values :
439+ return None
440+ separator = "\n " if name .lower () == "set-cookie" else ", "
441+ return separator .join (values )
442+
443+ def get_all (self , name : str ) -> List [str ]:
444+ return list (self ._headers_map [name .lower ()].keys ())
445+
446+ def headers (self ) -> Dict [str , str ]:
447+ result = {}
448+ for name in self ._headers_map .keys ():
449+ result [name ] = cast (str , self .get (name ))
450+ return result
451+
452+ def headers_array (self ) -> HeadersArray :
453+ return self ._headers_array
0 commit comments