88import os
99import re
1010import os
11- from playwright .async_api import async_playwright
11+ from playwright .async_api import async_playwright , Playwright , Browser , BrowserContext
1212
1313_browsers : dict [int , dict [str , any ]] = {}
1414
@@ -28,39 +28,24 @@ def __init__(self, client: AsyncClient, logger: LoggerAdapter):
2828 self .admin_password = "]!V$JuOzx@fi%pvG,lF!"
2929 self .admin_username = "admin"
3030 self .service_url = str (client .base_url )
31-
32- async def _get_browser (self ):
33- pid = os .getpid ()
34- entry = _browsers .get (pid )
35- if entry and entry .get ("browser" ):
36- return entry ["browser" ]
37-
38- playwright = await async_playwright ().start ()
39- browser = await playwright .chromium .launch (
40- headless = True ,
41- args = ['--no-sandbox' ,
42- '--disable-setuid-sandbox' ,]
43- )
44- _browsers [pid ] = {"playwright" : playwright , "browser" : browser }
45- return browser
4631
4732 async def _solve_admin_challenge (self , encrypted_b64 : str ):
4833 if not CRYPTO_AVAILABLE :
49- raise MumbleException ("cryptography package missing for admin challenge" )
34+ raise MumbleException ("Cryptography package missing for admin challenge" )
5035
5136 try :
5237 ciphertext = base64 .b64decode (encrypted_b64 )
5338 except Exception as e :
54- raise MumbleException (f"Invalid base64 in challenge: { e } " )
39+ raise MumbleException (f"Invalid base64 in challenge" )
5540
5641 pem_path = os .path .join (os .path .dirname (__file__ ), 'admin_private.pem' )
5742 try :
5843 with open (pem_path , 'rb' ) as f :
5944 private_key = serialization .load_pem_private_key (f .read (), password = None )
6045 except FileNotFoundError :
61- raise MumbleException ("admin_private.pem not found for admin challenge " )
46+ raise MumbleException ("checker's side (1) " )
6247 except Exception as e :
63- raise MumbleException (f"Unable to load admin private key: { e } " )
48+ raise MumbleException (f"checker's side (2) " )
6449
6550 try :
6651 plaintext = private_key .decrypt (
@@ -69,7 +54,7 @@ async def _solve_admin_challenge(self, encrypted_b64: str):
6954 )
7055 decrypted_text = plaintext .decode ()
7156 except Exception as e :
72- raise MumbleException (f"RSA decryption failed: { e } " )
57+ raise MumbleException (f"checker's side (3) " )
7358
7459 return decrypted_text
7560
@@ -95,11 +80,11 @@ async def load_feedback_page(self) -> None:
9580
9681 response = await self .client .get ("/admin-challenge" )
9782 if response .status_code != 200 :
98- raise MumbleException ("Failed to get admin challenge " )
83+ raise MumbleException ("Failed to login as admin " )
9984
10085 match = re .search (r'<pre[^>]*>(.*?)</pre>' , response .text , re .DOTALL )
10186 if not match :
102- raise MumbleException ("Could not find encrypted challenge on page" )
87+ raise MumbleException ("Could not find encrypted challenge on page (please wrap it in <pre> tags) " )
10388
10489 encrypted_b64 = match .group (1 ).strip ()
10590
@@ -112,7 +97,7 @@ async def load_feedback_page(self) -> None:
11297 )
11398
11499 if response .status_code not in [200 , 201 , 302 ]:
115- raise MumbleException (f"Failed to submit admin challenge solution: { response . text } , { response . status_code } " )
100+ raise MumbleException (f"Failed to login as admin " )
116101
117102 cookies = []
118103 for ck in self .client .cookies .jar :
@@ -125,9 +110,32 @@ async def load_feedback_page(self) -> None:
125110 'secure' : ck .secure
126111 })
127112
128- browser = await self ._get_browser ()
129113 try :
130- context = await browser .new_context ()
114+ pid = os .getpid ()
115+ entry = _browsers .get (pid )
116+ if not entry or not entry ["browser" ].is_connected ():
117+ if entry :
118+ await entry ["browser" ].close ()
119+ await entry ["playwright" ].stop ()
120+
121+ p : Playwright = await async_playwright ().start ()
122+ b : Browser = await p .chromium .launch (
123+ headless = True ,
124+ args = ["--no-sandbox" , "--disable-setuid-sandbox" ]
125+ )
126+ _browsers [pid ] = {"playwright" : p , "browser" : b }
127+ browser = _browsers [pid ]["browser" ]
128+ except Exception as e :
129+ if browser :
130+ await browser .close ()
131+ if _browsers [pid ]["playwright" ]:
132+ await _browsers [pid ]["playwright" ].stop ()
133+ if pid in _browsers :
134+ _browsers .pop (pid , None )
135+ raise MumbleException (f"checker's side (4)" )
136+
137+ try :
138+ context : BrowserContext = await browser .new_context ()
131139 await context .route ("**/*" , lambda route , request :
132140 route .abort () if request .resource_type in ("image" , "stylesheet" , "font" )
133141 else route .continue_ ()
@@ -141,9 +149,16 @@ async def load_feedback_page(self) -> None:
141149 if "/admin/feedback" not in page .url :
142150 raise MumbleException (f"Admin was redirected to problems page - service unavailable" )
143151 except Exception as e :
144- self .logger .warning (f"Admin simulation failed: { e } " )
145- raise MumbleException (f"Admin simulation error: { e } " )
146- finally :
152+ if context :
153+ await context .close ()
154+ if browser :
155+ await browser .close ()
156+ if _browsers [pid ]["playwright" ]:
157+ await _browsers [pid ]["playwright" ].stop ()
158+ if pid in _browsers :
159+ _browsers .pop (pid , None )
160+ raise MumbleException (f"checker's side (5)" )
161+ else :
147162 await context .close ()
148163
149164 async def post_new_message (self ) -> None :
0 commit comments