11from concurrent .futures import ThreadPoolExecutor
22from pathlib import Path
33from types import ModuleType
4- from typing import Callable , List
4+ from typing import Callable , List , Dict , Optional
5+ import threading
56
67import httpx
78from colorama import Fore , Style
@@ -133,6 +134,18 @@ def run_user_full(username: str, configs: ScanConfig) -> List[Result]:
133134
134135
135136
137+ _clients : Dict [tuple , httpx .Client ] = {}
138+ _clients_lock = threading .Lock ()
139+
140+ def get_client (use_http2 : bool , proxy_val : Optional [str ]) -> httpx .Client :
141+ key = (use_http2 , proxy_val )
142+ if key not in _clients :
143+ with _clients_lock :
144+ if key not in _clients :
145+ _clients [key ] = httpx .Client (http2 = use_http2 , proxy = proxy_val , verify = False )
146+ return _clients [key ]
147+
148+
136149def make_request (url : str , ** kwargs ) -> httpx .Response :
137150 """Simple wrapper to **httpx.get** that predefines headers and timeout"""
138151 if "headers" not in kwargs :
@@ -157,8 +170,16 @@ def make_request(url: str, **kwargs) -> httpx.Response:
157170 method = kwargs .pop ("method" , "GET" )
158171 use_http2 = kwargs .pop ("http2" , False )
159172
160- with httpx .Client (http2 = use_http2 , proxy = proxy_val ) as client :
161- return client .request (method .upper (), url , ** kwargs )
173+ client = get_client (use_http2 , proxy_val )
174+
175+ max_retries = 2
176+ for attempt in range (max_retries + 1 ):
177+ try :
178+ return client .request (method .upper (), url , ** kwargs )
179+ except (httpx .ConnectTimeout , httpx .ReadTimeout ) as e :
180+ if attempt == max_retries :
181+ raise e
182+ raise RuntimeError ("Request failed after retries" )
162183
163184
164185def generic_validate (
0 commit comments