We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Port the postData fix
https://github.com/microsoft/playwright/pull/5736

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `playwright/_impl/_network.py`
Content:
```
1 # Copyright (c) Microsoft Corporation.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 import base64
16 import json
17 import mimetypes
18 from pathlib import Path
19 from types import SimpleNamespace
20 from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union, cast
21 from urllib import parse
22 
23 from playwright._impl._api_structures import ResourceTiming
24 from playwright._impl._api_types import Error
25 from playwright._impl._connection import (
26     ChannelOwner,
27     from_channel,
28     from_nullable_channel,
29 )
30 from playwright._impl._event_context_manager import EventContextManagerImpl
31 from playwright._impl._helper import ContinueParameters, Header, locals_to_params
32 from playwright._impl._wait_helper import WaitHelper
33 
34 if TYPE_CHECKING:  # pragma: no cover
35     from playwright._impl._frame import Frame
36 
37 
38 class Request(ChannelOwner):
39     def __init__(
40         self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
41     ) -> None:
42         super().__init__(parent, type, guid, initializer)
43         self._redirected_from: Optional["Request"] = from_nullable_channel(
44             initializer.get("redirectedFrom")
45         )
46         self._redirected_to: Optional["Request"] = None
47         if self._redirected_from:
48             self._redirected_from._redirected_to = self
49         self._failure_text: Optional[str] = None
50         self._timing: ResourceTiming = {
51             "startTime": 0,
52             "domainLookupStart": -1,
53             "domainLookupEnd": -1,
54             "connectStart": -1,
55             "secureConnectionStart": -1,
56             "connectEnd": -1,
57             "requestStart": -1,
58             "responseStart": -1,
59             "responseEnd": -1,
60         }
61         self._headers: Dict[str, str] = parse_headers(self._initializer["headers"])
62 
63     @property
64     def url(self) -> str:
65         return self._initializer["url"]
66 
67     @property
68     def resource_type(self) -> str:
69         return self._initializer["resourceType"]
70 
71     @property
72     def method(self) -> str:
73         return self._initializer["method"]
74 
75     @property
76     def post_data(self) -> Optional[str]:
77         data = self.post_data_buffer
78         if not data:
79             return None
80         return data.decode()
81 
82     @property
83     def post_data_json(self) -> Optional[Any]:
84         post_data = self.post_data
85         if not post_data:
86             return None
87         content_type = self.headers["content-type"]
88         if not content_type:
89             return None
90         if content_type == "application/x-www-form-urlencoded":
91             return dict(parse.parse_qsl(post_data))
92         return json.loads(post_data)
93 
94     @property
95     def post_data_buffer(self) -> Optional[bytes]:
96         b64_content = self._initializer.get("postData")
97         if not b64_content:
98             return None
99         return base64.b64decode(b64_content)
100 
101     @property
102     def headers(self) -> Dict[str, str]:
103         return self._headers
104 
105     async def response(self) -> Optional["Response"]:
106         return from_nullable_channel(await self._channel.send("response"))
107 
108     @property
109     def frame(self) -> "Frame":
110         return from_channel(self._initializer["frame"])
111 
112     def is_navigation_request(self) -> bool:
113         return self._initializer["isNavigationRequest"]
114 
115     @property
116     def redirected_from(self) -> Optional["Request"]:
117         return self._redirected_from
118 
119     @property
120     def redirected_to(self) -> Optional["Request"]:
121         return self._redirected_to
122 
123     @property
124     def failure(self) -> Optional[str]:
125         return self._failure_text
126 
127     @property
128     def timing(self) -> ResourceTiming:
129         return self._timing
130 
131 
132 class Route(ChannelOwner):
133     def __init__(
134         self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
135     ) -> None:
136         super().__init__(parent, type, guid, initializer)
137 
138     @property
139     def request(self) -> Request:
140         return from_channel(self._initializer["request"])
141 
142     async def abort(self, errorCode: str = None) -> None:
143         await self._channel.send("abort", locals_to_params(locals()))
144 
145     async def fulfill(
146         self,
147         status: int = None,
148         headers: Dict[str, str] = None,
149         body: Union[str, bytes] = None,
150         path: Union[str, Path] = None,
151         contentType: str = None,
152     ) -> None:
153         params = locals_to_params(locals())
154         length = 0
155         if isinstance(body, str):
156             params["body"] = body
157             params["isBase64"] = False
158             length = len(body.encode())
159         elif isinstance(body, bytes):
160             params["body"] = base64.b64encode(body).decode()
161             params["isBase64"] = True
162             length = len(body)
163         elif path:
164             del params["path"]
165             file_content = Path(path).read_bytes()
166             params["body"] = base64.b64encode(file_content).decode()
167             params["isBase64"] = True
168             length = len(file_content)
169 
170         headers = {k.lower(): str(v) for k, v in params.get("headers", {}).items()}
171         if params.get("contentType"):
172             headers["content-type"] = params["contentType"]
173         elif path:
174             headers["content-type"] = (
175                 mimetypes.guess_type(str(Path(path)))[0] or "application/octet-stream"
176             )
177         if length and "content-length" not in headers:
178             headers["content-length"] = str(length)
179         params["headers"] = serialize_headers(headers)
180         await self._channel.send("fulfill", params)
181 
182     async def continue_(
183         self,
184         url: str = None,
185         method: str = None,
186         headers: Dict[str, str] = None,
187         postData: Union[str, bytes] = None,
188     ) -> None:
189         overrides: ContinueParameters = {}
190         if url:
191             overrides["url"] = url
192         if method:
193             overrides["method"] = method
194         if headers:
195             overrides["headers"] = serialize_headers(headers)
196         if isinstance(postData, str):
197             overrides["postData"] = base64.b64encode(postData.encode()).decode()
198         elif isinstance(postData, bytes):
199             overrides["postData"] = base64.b64encode(postData).decode()
200         await self._channel.send("continue", cast(Any, overrides))
201 
202 
203 class Response(ChannelOwner):
204     def __init__(
205         self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
206     ) -> None:
207         super().__init__(parent, type, guid, initializer)
208         self._request: Request = from_channel(self._initializer["request"])
209         timing = self._initializer["timing"]
210         self._request._timing["startTime"] = timing["startTime"]
211         self._request._timing["domainLookupStart"] = timing["domainLookupStart"]
212         self._request._timing["domainLookupEnd"] = timing["domainLookupEnd"]
213         self._request._timing["connectStart"] = timing["connectStart"]
214         self._request._timing["secureConnectionStart"] = timing["secureConnectionStart"]
215         self._request._timing["connectEnd"] = timing["connectEnd"]
216         self._request._timing["requestStart"] = timing["requestStart"]
217         self._request._timing["responseStart"] = timing["responseStart"]
218         self._request._headers = parse_headers(self._initializer["requestHeaders"])
219 
220     @property
221     def url(self) -> str:
222         return self._initializer["url"]
223 
224     @property
225     def ok(self) -> bool:
226         return self._initializer["status"] == 0 or (
227             self._initializer["status"] >= 200 and self._initializer["status"] <= 299
228         )
229 
230     @property
231     def status(self) -> int:
232         return self._initializer["status"]
233 
234     @property
235     def status_text(self) -> str:
236         return self._initializer["statusText"]
237 
238     @property
239     def headers(self) -> Dict[str, str]:
240         return parse_headers(self._initializer["headers"])
241 
242     async def finished(self) -> Optional[str]:
243         return await self._channel.send("finished")
244 
245     async def body(self) -> bytes:
246         binary = await self._channel.send("body")
247         return base64.b64decode(binary)
248 
249     async def text(self) -> str:
250         content = await self.body()
251         return content.decode()
252 
253     async def json(self) -> Union[Any]:
254         return json.loads(await self.text())
255 
256     @property
257     def request(self) -> Request:
258         return self._request
259 
260     @property
261     def frame(self) -> "Frame":
262         return self._request.frame
263 
264 
265 class WebSocket(ChannelOwner):
266 
267     Events = SimpleNamespace(
268         Close="close",
269         FrameReceived="framereceived",
270         FrameSent="framesent",
271         Error="socketerror",
272     )
273 
274     def __init__(
275         self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
276     ) -> None:
277         super().__init__(parent, type, guid, initializer)
278         self._is_closed = False
279         self._channel.on(
280             "frameSent",
281             lambda params: self._on_frame_sent(params["opcode"], params["data"]),
282         )
283         self._channel.on(
284             "frameReceived",
285             lambda params: self._on_frame_received(params["opcode"], params["data"]),
286         )
287         self._channel.on(
288             "error", lambda params: self.emit(WebSocket.Events.Error, params["error"])
289         )
290         self._channel.on("close", lambda params: self._on_close())
291 
292     @property
293     def url(self) -> str:
294         return self._initializer["url"]
295 
296     def expect_event(
297         self,
298         event: str,
299         predicate: Callable = None,
300         timeout: float = None,
301     ) -> EventContextManagerImpl:
302         if timeout is None:
303             timeout = cast(Any, self._parent)._timeout_settings.timeout()
304         wait_helper = WaitHelper(self, f"web_socket.expect_event({event})")
305         wait_helper.reject_on_timeout(
306             timeout, f'Timeout while waiting for event "{event}"'
307         )
308         if event != WebSocket.Events.Close:
309             wait_helper.reject_on_event(
310                 self, WebSocket.Events.Close, Error("Socket closed")
311             )
312         if event != WebSocket.Events.Error:
313             wait_helper.reject_on_event(
314                 self, WebSocket.Events.Error, Error("Socket error")
315             )
316         wait_helper.reject_on_event(self._parent, "close", Error("Page closed"))
317         wait_helper.wait_for_event(self, event, predicate)
318         return EventContextManagerImpl(wait_helper.result())
319 
320     async def wait_for_event(
321         self, event: str, predicate: Callable = None, timeout: float = None
322     ) -> Any:
323         async with self.expect_event(event, predicate, timeout) as event_info:
324             pass
325         return await event_info
326 
327     def _on_frame_sent(self, opcode: int, data: str) -> None:
328         if opcode == 2:
329             self.emit(WebSocket.Events.FrameSent, base64.b64decode(data))
330         else:
331             self.emit(WebSocket.Events.FrameSent, data)
332 
333     def _on_frame_received(self, opcode: int, data: str) -> None:
334         if opcode == 2:
335             self.emit(WebSocket.Events.FrameReceived, base64.b64decode(data))
336         else:
337             self.emit(WebSocket.Events.FrameReceived, data)
338 
339     def is_closed(self) -> bool:
340         return self._is_closed
341 
342     def _on_close(self) -> None:
343         self._is_closed = True
344         self.emit(WebSocket.Events.Close)
345 
346 
347 def serialize_headers(headers: Dict[str, str]) -> List[Header]:
348     return [{"name": name, "value": value} for name, value in headers.items()]
349 
350 
351 def parse_headers(headers: List[Header]) -> Dict[str, str]:
352     return {header["name"].lower(): header["value"] for header in headers}
353
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/playwright/_impl/_network.py b/playwright/_impl/_network.py
--- a/playwright/_impl/_network.py
+++ b/playwright/_impl/_network.py
@@ -85,11 +85,12 @@
         if not post_data:
             return None
         content_type = self.headers["content-type"]
-        if not content_type:
-            return None
         if content_type == "application/x-www-form-urlencoded":
             return dict(parse.parse_qsl(post_data))
-        return json.loads(post_data)
+        try:
+            return json.loads(post_data)
+        except Exception:
+            raise Error(f"POST data is not a valid JSON object: {post_data}")
 
     @property
     def post_data_buffer(self) -> Optional[bytes]:

{"golden_diff": "diff --git a/playwright/_impl/_network.py b/playwright/_impl/_network.py\n--- a/playwright/_impl/_network.py\n+++ b/playwright/_impl/_network.py\n@@ -85,11 +85,12 @@\n         if not post_data:\n             return None\n         content_type = self.headers[\"content-type\"]\n-        if not content_type:\n-            return None\n         if content_type == \"application/x-www-form-urlencoded\":\n             return dict(parse.parse_qsl(post_data))\n-        return json.loads(post_data)\n+        try:\n+            return json.loads(post_data)\n+        except Exception:\n+            raise Error(f\"POST data is not a valid JSON object: {post_data}\")\n \n     @property\n     def post_data_buffer(self) -> Optional[bytes]:\n", "issue": "Port the postData fix\nhttps://github.com/microsoft/playwright/pull/5736\n", "before_files": [{"content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport base64\nimport json\nimport mimetypes\nfrom pathlib import Path\nfrom types import SimpleNamespace\nfrom typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union, cast\nfrom urllib import parse\n\nfrom playwright._impl._api_structures import ResourceTiming\nfrom playwright._impl._api_types import Error\nfrom playwright._impl._connection import (\n    ChannelOwner,\n    from_channel,\n    from_nullable_channel,\n)\nfrom playwright._impl._event_context_manager import EventContextManagerImpl\nfrom playwright._impl._helper import ContinueParameters, Header, locals_to_params\nfrom playwright._impl._wait_helper import WaitHelper\n\nif TYPE_CHECKING:  # pragma: no cover\n    from playwright._impl._frame import Frame\n\n\nclass Request(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._redirected_from: Optional[\"Request\"] = from_nullable_channel(\n            initializer.get(\"redirectedFrom\")\n        )\n        self._redirected_to: Optional[\"Request\"] = None\n        if self._redirected_from:\n            self._redirected_from._redirected_to = self\n        self._failure_text: Optional[str] = None\n        self._timing: ResourceTiming = {\n            \"startTime\": 0,\n            \"domainLookupStart\": -1,\n            \"domainLookupEnd\": -1,\n            \"connectStart\": -1,\n            \"secureConnectionStart\": -1,\n            \"connectEnd\": -1,\n            \"requestStart\": -1,\n            \"responseStart\": -1,\n            \"responseEnd\": -1,\n        }\n        self._headers: Dict[str, str] = parse_headers(self._initializer[\"headers\"])\n\n    @property\n    def url(self) -> str:\n        return self._initializer[\"url\"]\n\n    @property\n    def resource_type(self) -> str:\n        return self._initializer[\"resourceType\"]\n\n    @property\n    def method(self) -> str:\n        return self._initializer[\"method\"]\n\n    @property\n    def post_data(self) -> Optional[str]:\n        data = self.post_data_buffer\n        if not data:\n            return None\n        return data.decode()\n\n    @property\n    def post_data_json(self) -> Optional[Any]:\n        post_data = self.post_data\n        if not post_data:\n            return None\n        content_type = self.headers[\"content-type\"]\n        if not content_type:\n            return None\n        if content_type == \"application/x-www-form-urlencoded\":\n            return dict(parse.parse_qsl(post_data))\n        return json.loads(post_data)\n\n    @property\n    def post_data_buffer(self) -> Optional[bytes]:\n        b64_content = self._initializer.get(\"postData\")\n        if not b64_content:\n            return None\n        return base64.b64decode(b64_content)\n\n    @property\n    def headers(self) -> Dict[str, str]:\n        return self._headers\n\n    async def response(self) -> Optional[\"Response\"]:\n        return from_nullable_channel(await self._channel.send(\"response\"))\n\n    @property\n    def frame(self) -> \"Frame\":\n        return from_channel(self._initializer[\"frame\"])\n\n    def is_navigation_request(self) -> bool:\n        return self._initializer[\"isNavigationRequest\"]\n\n    @property\n    def redirected_from(self) -> Optional[\"Request\"]:\n        return self._redirected_from\n\n    @property\n    def redirected_to(self) -> Optional[\"Request\"]:\n        return self._redirected_to\n\n    @property\n    def failure(self) -> Optional[str]:\n        return self._failure_text\n\n    @property\n    def timing(self) -> ResourceTiming:\n        return self._timing\n\n\nclass Route(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n\n    @property\n    def request(self) -> Request:\n        return from_channel(self._initializer[\"request\"])\n\n    async def abort(self, errorCode: str = None) -> None:\n        await self._channel.send(\"abort\", locals_to_params(locals()))\n\n    async def fulfill(\n        self,\n        status: int = None,\n        headers: Dict[str, str] = None,\n        body: Union[str, bytes] = None,\n        path: Union[str, Path] = None,\n        contentType: str = None,\n    ) -> None:\n        params = locals_to_params(locals())\n        length = 0\n        if isinstance(body, str):\n            params[\"body\"] = body\n            params[\"isBase64\"] = False\n            length = len(body.encode())\n        elif isinstance(body, bytes):\n            params[\"body\"] = base64.b64encode(body).decode()\n            params[\"isBase64\"] = True\n            length = len(body)\n        elif path:\n            del params[\"path\"]\n            file_content = Path(path).read_bytes()\n            params[\"body\"] = base64.b64encode(file_content).decode()\n            params[\"isBase64\"] = True\n            length = len(file_content)\n\n        headers = {k.lower(): str(v) for k, v in params.get(\"headers\", {}).items()}\n        if params.get(\"contentType\"):\n            headers[\"content-type\"] = params[\"contentType\"]\n        elif path:\n            headers[\"content-type\"] = (\n                mimetypes.guess_type(str(Path(path)))[0] or \"application/octet-stream\"\n            )\n        if length and \"content-length\" not in headers:\n            headers[\"content-length\"] = str(length)\n        params[\"headers\"] = serialize_headers(headers)\n        await self._channel.send(\"fulfill\", params)\n\n    async def continue_(\n        self,\n        url: str = None,\n        method: str = None,\n        headers: Dict[str, str] = None,\n        postData: Union[str, bytes] = None,\n    ) -> None:\n        overrides: ContinueParameters = {}\n        if url:\n            overrides[\"url\"] = url\n        if method:\n            overrides[\"method\"] = method\n        if headers:\n            overrides[\"headers\"] = serialize_headers(headers)\n        if isinstance(postData, str):\n            overrides[\"postData\"] = base64.b64encode(postData.encode()).decode()\n        elif isinstance(postData, bytes):\n            overrides[\"postData\"] = base64.b64encode(postData).decode()\n        await self._channel.send(\"continue\", cast(Any, overrides))\n\n\nclass Response(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._request: Request = from_channel(self._initializer[\"request\"])\n        timing = self._initializer[\"timing\"]\n        self._request._timing[\"startTime\"] = timing[\"startTime\"]\n        self._request._timing[\"domainLookupStart\"] = timing[\"domainLookupStart\"]\n        self._request._timing[\"domainLookupEnd\"] = timing[\"domainLookupEnd\"]\n        self._request._timing[\"connectStart\"] = timing[\"connectStart\"]\n        self._request._timing[\"secureConnectionStart\"] = timing[\"secureConnectionStart\"]\n        self._request._timing[\"connectEnd\"] = timing[\"connectEnd\"]\n        self._request._timing[\"requestStart\"] = timing[\"requestStart\"]\n        self._request._timing[\"responseStart\"] = timing[\"responseStart\"]\n        self._request._headers = parse_headers(self._initializer[\"requestHeaders\"])\n\n    @property\n    def url(self) -> str:\n        return self._initializer[\"url\"]\n\n    @property\n    def ok(self) -> bool:\n        return self._initializer[\"status\"] == 0 or (\n            self._initializer[\"status\"] >= 200 and self._initializer[\"status\"] <= 299\n        )\n\n    @property\n    def status(self) -> int:\n        return self._initializer[\"status\"]\n\n    @property\n    def status_text(self) -> str:\n        return self._initializer[\"statusText\"]\n\n    @property\n    def headers(self) -> Dict[str, str]:\n        return parse_headers(self._initializer[\"headers\"])\n\n    async def finished(self) -> Optional[str]:\n        return await self._channel.send(\"finished\")\n\n    async def body(self) -> bytes:\n        binary = await self._channel.send(\"body\")\n        return base64.b64decode(binary)\n\n    async def text(self) -> str:\n        content = await self.body()\n        return content.decode()\n\n    async def json(self) -> Union[Any]:\n        return json.loads(await self.text())\n\n    @property\n    def request(self) -> Request:\n        return self._request\n\n    @property\n    def frame(self) -> \"Frame\":\n        return self._request.frame\n\n\nclass WebSocket(ChannelOwner):\n\n    Events = SimpleNamespace(\n        Close=\"close\",\n        FrameReceived=\"framereceived\",\n        FrameSent=\"framesent\",\n        Error=\"socketerror\",\n    )\n\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._is_closed = False\n        self._channel.on(\n            \"frameSent\",\n            lambda params: self._on_frame_sent(params[\"opcode\"], params[\"data\"]),\n        )\n        self._channel.on(\n            \"frameReceived\",\n            lambda params: self._on_frame_received(params[\"opcode\"], params[\"data\"]),\n        )\n        self._channel.on(\n            \"error\", lambda params: self.emit(WebSocket.Events.Error, params[\"error\"])\n        )\n        self._channel.on(\"close\", lambda params: self._on_close())\n\n    @property\n    def url(self) -> str:\n        return self._initializer[\"url\"]\n\n    def expect_event(\n        self,\n        event: str,\n        predicate: Callable = None,\n        timeout: float = None,\n    ) -> EventContextManagerImpl:\n        if timeout is None:\n            timeout = cast(Any, self._parent)._timeout_settings.timeout()\n        wait_helper = WaitHelper(self, f\"web_socket.expect_event({event})\")\n        wait_helper.reject_on_timeout(\n            timeout, f'Timeout while waiting for event \"{event}\"'\n        )\n        if event != WebSocket.Events.Close:\n            wait_helper.reject_on_event(\n                self, WebSocket.Events.Close, Error(\"Socket closed\")\n            )\n        if event != WebSocket.Events.Error:\n            wait_helper.reject_on_event(\n                self, WebSocket.Events.Error, Error(\"Socket error\")\n            )\n        wait_helper.reject_on_event(self._parent, \"close\", Error(\"Page closed\"))\n        wait_helper.wait_for_event(self, event, predicate)\n        return EventContextManagerImpl(wait_helper.result())\n\n    async def wait_for_event(\n        self, event: str, predicate: Callable = None, timeout: float = None\n    ) -> Any:\n        async with self.expect_event(event, predicate, timeout) as event_info:\n            pass\n        return await event_info\n\n    def _on_frame_sent(self, opcode: int, data: str) -> None:\n        if opcode == 2:\n            self.emit(WebSocket.Events.FrameSent, base64.b64decode(data))\n        else:\n            self.emit(WebSocket.Events.FrameSent, data)\n\n    def _on_frame_received(self, opcode: int, data: str) -> None:\n        if opcode == 2:\n            self.emit(WebSocket.Events.FrameReceived, base64.b64decode(data))\n        else:\n            self.emit(WebSocket.Events.FrameReceived, data)\n\n    def is_closed(self) -> bool:\n        return self._is_closed\n\n    def _on_close(self) -> None:\n        self._is_closed = True\n        self.emit(WebSocket.Events.Close)\n\n\ndef serialize_headers(headers: Dict[str, str]) -> List[Header]:\n    return [{\"name\": name, \"value\": value} for name, value in headers.items()]\n\n\ndef parse_headers(headers: List[Header]) -> Dict[str, str]:\n    return {header[\"name\"].lower(): header[\"value\"] for header in headers}\n", "path": "playwright/_impl/_network.py"}], "after_files": [{"content": "# Copyright (c) Microsoft Corporation.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport base64\nimport json\nimport mimetypes\nfrom pathlib import Path\nfrom types import SimpleNamespace\nfrom typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union, cast\nfrom urllib import parse\n\nfrom playwright._impl._api_structures import ResourceTiming\nfrom playwright._impl._api_types import Error\nfrom playwright._impl._connection import (\n    ChannelOwner,\n    from_channel,\n    from_nullable_channel,\n)\nfrom playwright._impl._event_context_manager import EventContextManagerImpl\nfrom playwright._impl._helper import ContinueParameters, Header, locals_to_params\nfrom playwright._impl._wait_helper import WaitHelper\n\nif TYPE_CHECKING:  # pragma: no cover\n    from playwright._impl._frame import Frame\n\n\nclass Request(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._redirected_from: Optional[\"Request\"] = from_nullable_channel(\n            initializer.get(\"redirectedFrom\")\n        )\n        self._redirected_to: Optional[\"Request\"] = None\n        if self._redirected_from:\n            self._redirected_from._redirected_to = self\n        self._failure_text: Optional[str] = None\n        self._timing: ResourceTiming = {\n            \"startTime\": 0,\n            \"domainLookupStart\": -1,\n            \"domainLookupEnd\": -1,\n            \"connectStart\": -1,\n            \"secureConnectionStart\": -1,\n            \"connectEnd\": -1,\n            \"requestStart\": -1,\n            \"responseStart\": -1,\n            \"responseEnd\": -1,\n        }\n        self._headers: Dict[str, str] = parse_headers(self._initializer[\"headers\"])\n\n    @property\n    def url(self) -> str:\n        return self._initializer[\"url\"]\n\n    @property\n    def resource_type(self) -> str:\n        return self._initializer[\"resourceType\"]\n\n    @property\n    def method(self) -> str:\n        return self._initializer[\"method\"]\n\n    @property\n    def post_data(self) -> Optional[str]:\n        data = self.post_data_buffer\n        if not data:\n            return None\n        return data.decode()\n\n    @property\n    def post_data_json(self) -> Optional[Any]:\n        post_data = self.post_data\n        if not post_data:\n            return None\n        content_type = self.headers[\"content-type\"]\n        if content_type == \"application/x-www-form-urlencoded\":\n            return dict(parse.parse_qsl(post_data))\n        try:\n            return json.loads(post_data)\n        except Exception:\n            raise Error(f\"POST data is not a valid JSON object: {post_data}\")\n\n    @property\n    def post_data_buffer(self) -> Optional[bytes]:\n        b64_content = self._initializer.get(\"postData\")\n        if not b64_content:\n            return None\n        return base64.b64decode(b64_content)\n\n    @property\n    def headers(self) -> Dict[str, str]:\n        return self._headers\n\n    async def response(self) -> Optional[\"Response\"]:\n        return from_nullable_channel(await self._channel.send(\"response\"))\n\n    @property\n    def frame(self) -> \"Frame\":\n        return from_channel(self._initializer[\"frame\"])\n\n    def is_navigation_request(self) -> bool:\n        return self._initializer[\"isNavigationRequest\"]\n\n    @property\n    def redirected_from(self) -> Optional[\"Request\"]:\n        return self._redirected_from\n\n    @property\n    def redirected_to(self) -> Optional[\"Request\"]:\n        return self._redirected_to\n\n    @property\n    def failure(self) -> Optional[str]:\n        return self._failure_text\n\n    @property\n    def timing(self) -> ResourceTiming:\n        return self._timing\n\n\nclass Route(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n\n    @property\n    def request(self) -> Request:\n        return from_channel(self._initializer[\"request\"])\n\n    async def abort(self, errorCode: str = None) -> None:\n        await self._channel.send(\"abort\", locals_to_params(locals()))\n\n    async def fulfill(\n        self,\n        status: int = None,\n        headers: Dict[str, str] = None,\n        body: Union[str, bytes] = None,\n        path: Union[str, Path] = None,\n        contentType: str = None,\n    ) -> None:\n        params = locals_to_params(locals())\n        length = 0\n        if isinstance(body, str):\n            params[\"body\"] = body\n            params[\"isBase64\"] = False\n            length = len(body.encode())\n        elif isinstance(body, bytes):\n            params[\"body\"] = base64.b64encode(body).decode()\n            params[\"isBase64\"] = True\n            length = len(body)\n        elif path:\n            del params[\"path\"]\n            file_content = Path(path).read_bytes()\n            params[\"body\"] = base64.b64encode(file_content).decode()\n            params[\"isBase64\"] = True\n            length = len(file_content)\n\n        headers = {k.lower(): str(v) for k, v in params.get(\"headers\", {}).items()}\n        if params.get(\"contentType\"):\n            headers[\"content-type\"] = params[\"contentType\"]\n        elif path:\n            headers[\"content-type\"] = (\n                mimetypes.guess_type(str(Path(path)))[0] or \"application/octet-stream\"\n            )\n        if length and \"content-length\" not in headers:\n            headers[\"content-length\"] = str(length)\n        params[\"headers\"] = serialize_headers(headers)\n        await self._channel.send(\"fulfill\", params)\n\n    async def continue_(\n        self,\n        url: str = None,\n        method: str = None,\n        headers: Dict[str, str] = None,\n        postData: Union[str, bytes] = None,\n    ) -> None:\n        overrides: ContinueParameters = {}\n        if url:\n            overrides[\"url\"] = url\n        if method:\n            overrides[\"method\"] = method\n        if headers:\n            overrides[\"headers\"] = serialize_headers(headers)\n        if isinstance(postData, str):\n            overrides[\"postData\"] = base64.b64encode(postData.encode()).decode()\n        elif isinstance(postData, bytes):\n            overrides[\"postData\"] = base64.b64encode(postData).decode()\n        await self._channel.send(\"continue\", cast(Any, overrides))\n\n\nclass Response(ChannelOwner):\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._request: Request = from_channel(self._initializer[\"request\"])\n        timing = self._initializer[\"timing\"]\n        self._request._timing[\"startTime\"] = timing[\"startTime\"]\n        self._request._timing[\"domainLookupStart\"] = timing[\"domainLookupStart\"]\n        self._request._timing[\"domainLookupEnd\"] = timing[\"domainLookupEnd\"]\n        self._request._timing[\"connectStart\"] = timing[\"connectStart\"]\n        self._request._timing[\"secureConnectionStart\"] = timing[\"secureConnectionStart\"]\n        self._request._timing[\"connectEnd\"] = timing[\"connectEnd\"]\n        self._request._timing[\"requestStart\"] = timing[\"requestStart\"]\n        self._request._timing[\"responseStart\"] = timing[\"responseStart\"]\n        self._request._headers = parse_headers(self._initializer[\"requestHeaders\"])\n\n    @property\n    def url(self) -> str:\n        return self._initializer[\"url\"]\n\n    @property\n    def ok(self) -> bool:\n        return self._initializer[\"status\"] == 0 or (\n            self._initializer[\"status\"] >= 200 and self._initializer[\"status\"] <= 299\n        )\n\n    @property\n    def status(self) -> int:\n        return self._initializer[\"status\"]\n\n    @property\n    def status_text(self) -> str:\n        return self._initializer[\"statusText\"]\n\n    @property\n    def headers(self) -> Dict[str, str]:\n        return parse_headers(self._initializer[\"headers\"])\n\n    async def finished(self) -> Optional[str]:\n        return await self._channel.send(\"finished\")\n\n    async def body(self) -> bytes:\n        binary = await self._channel.send(\"body\")\n        return base64.b64decode(binary)\n\n    async def text(self) -> str:\n        content = await self.body()\n        return content.decode()\n\n    async def json(self) -> Union[Any]:\n        return json.loads(await self.text())\n\n    @property\n    def request(self) -> Request:\n        return self._request\n\n    @property\n    def frame(self) -> \"Frame\":\n        return self._request.frame\n\n\nclass WebSocket(ChannelOwner):\n\n    Events = SimpleNamespace(\n        Close=\"close\",\n        FrameReceived=\"framereceived\",\n        FrameSent=\"framesent\",\n        Error=\"socketerror\",\n    )\n\n    def __init__(\n        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict\n    ) -> None:\n        super().__init__(parent, type, guid, initializer)\n        self._is_closed = False\n        self._channel.on(\n            \"frameSent\",\n            lambda params: self._on_frame_sent(params[\"opcode\"], params[\"data\"]),\n        )\n        self._channel.on(\n            \"frameReceived\",\n            lambda params: self._on_frame_received(params[\"opcode\"], params[\"data\"]),\n        )\n        self._channel.on(\n            \"error\", lambda params: self.emit(WebSocket.Events.Error, params[\"error\"])\n        )\n        self._channel.on(\"close\", lambda params: self._on_close())\n\n    @property\n    def url(self) -> str:\n        return self._initializer[\"url\"]\n\n    def expect_event(\n        self,\n        event: str,\n        predicate: Callable = None,\n        timeout: float = None,\n    ) -> EventContextManagerImpl:\n        if timeout is None:\n            timeout = cast(Any, self._parent)._timeout_settings.timeout()\n        wait_helper = WaitHelper(self, f\"web_socket.expect_event({event})\")\n        wait_helper.reject_on_timeout(\n            timeout, f'Timeout while waiting for event \"{event}\"'\n        )\n        if event != WebSocket.Events.Close:\n            wait_helper.reject_on_event(\n                self, WebSocket.Events.Close, Error(\"Socket closed\")\n            )\n        if event != WebSocket.Events.Error:\n            wait_helper.reject_on_event(\n                self, WebSocket.Events.Error, Error(\"Socket error\")\n            )\n        wait_helper.reject_on_event(self._parent, \"close\", Error(\"Page closed\"))\n        wait_helper.wait_for_event(self, event, predicate)\n        return EventContextManagerImpl(wait_helper.result())\n\n    async def wait_for_event(\n        self, event: str, predicate: Callable = None, timeout: float = None\n    ) -> Any:\n        async with self.expect_event(event, predicate, timeout) as event_info:\n            pass\n        return await event_info\n\n    def _on_frame_sent(self, opcode: int, data: str) -> None:\n        if opcode == 2:\n            self.emit(WebSocket.Events.FrameSent, base64.b64decode(data))\n        else:\n            self.emit(WebSocket.Events.FrameSent, data)\n\n    def _on_frame_received(self, opcode: int, data: str) -> None:\n        if opcode == 2:\n            self.emit(WebSocket.Events.FrameReceived, base64.b64decode(data))\n        else:\n            self.emit(WebSocket.Events.FrameReceived, data)\n\n    def is_closed(self) -> bool:\n        return self._is_closed\n\n    def _on_close(self) -> None:\n        self._is_closed = True\n        self.emit(WebSocket.Events.Close)\n\n\ndef serialize_headers(headers: Dict[str, str]) -> List[Header]:\n    return [{\"name\": name, \"value\": value} for name, value in headers.items()]\n\n\ndef parse_headers(headers: List[Header]) -> Dict[str, str]:\n    return {header[\"name\"].lower(): header[\"value\"] for header in headers}\n", "path": "playwright/_impl/_network.py"}]}