Skip to content

Commit 5379909

Browse files
authored
Merge pull request #271 from shredzwho/feature/performance-optimizations
Optimize architecture: HTTP connection pooling and LRU caching
2 parents 9aa625a + 1493745 commit 5379909

2 files changed

Lines changed: 27 additions & 3 deletions

File tree

user_scanner/core/helpers.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import inspect
77
import random
88
import threading
9+
import functools
910
from concurrent.futures import ThreadPoolExecutor, as_completed
1011
from dataclasses import dataclass
1112
from typing import Any, Callable
@@ -40,6 +41,7 @@ def get_site_name(module) -> str:
4041
return name
4142

4243

44+
@functools.lru_cache(maxsize=None)
4345
def load_modules(category_path: Path) -> List[ModuleType]:
4446
modules = []
4547
for file in category_path.glob("*.py"):
@@ -55,6 +57,7 @@ def load_modules(category_path: Path) -> List[ModuleType]:
5557
return modules
5658

5759

60+
@functools.lru_cache(maxsize=None)
5861
def load_categories(is_email: bool = False) -> Dict[str, Path]:
5962
folder_name = "email_scan" if is_email else "user_scan"
6063
root = Path(__file__).resolve().parent.parent / folder_name

user_scanner/core/orchestrator.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from concurrent.futures import ThreadPoolExecutor
22
from pathlib import Path
33
from types import ModuleType
4-
from typing import Callable, List
4+
from typing import Callable, List, Dict, Optional
5+
import threading
56

67
import httpx
78
from 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+
136149
def 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

164185
def generic_validate(

0 commit comments

Comments
 (0)