Spaces:
Running
Running
| """ | |
| Shared HTTP client with connection pooling. | |
| Provides a singleton aiohttp ClientSession for efficient connection reuse | |
| across the application. | |
| """ | |
| import aiohttp | |
| import logging | |
| from contextlib import asynccontextmanager | |
| logger = logging.getLogger(__name__) | |
| # Singleton session instance | |
| _session: aiohttp.ClientSession | None = None | |
| # Default timeout configuration | |
| DEFAULT_TIMEOUT = aiohttp.ClientTimeout(total=60, connect=10) | |
| # Default headers | |
| DEFAULT_HEADERS = { | |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', | |
| 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', | |
| 'Accept-Language': 'en-US,en;q=0.5', | |
| 'Accept-Encoding': 'gzip, deflate', | |
| } | |
| async def get_session() -> aiohttp.ClientSession: | |
| """ | |
| Get or create a shared aiohttp session for connection pooling. | |
| The session is created lazily on first call and reused for all subsequent requests. | |
| This provides significant performance improvements by reusing TCP connections. | |
| Returns: | |
| A configured aiohttp.ClientSession instance | |
| """ | |
| global _session | |
| if _session is None or _session.closed: | |
| connector = aiohttp.TCPConnector( | |
| limit=100, # Max concurrent connections | |
| limit_per_host=10, # Max connections per host | |
| enable_cleanup_closed=True, | |
| ) | |
| _session = aiohttp.ClientSession( | |
| connector=connector, | |
| timeout=DEFAULT_TIMEOUT, | |
| headers=DEFAULT_HEADERS, | |
| ) | |
| logger.debug("Created new HTTP session with connection pooling") | |
| return _session | |
| async def close_session() -> None: | |
| """ | |
| Close the shared session. | |
| Should be called during application shutdown to cleanly close all connections. | |
| """ | |
| global _session | |
| if _session and not _session.closed: | |
| await _session.close() | |
| logger.debug("Closed HTTP session") | |
| _session = None | |
| async def http_session(): | |
| """ | |
| Async context manager for the HTTP session. | |
| Usage: | |
| async with http_session() as session: | |
| async with session.get(url) as response: | |
| data = await response.text() | |
| """ | |
| session = await get_session() | |
| try: | |
| yield session | |
| finally: | |
| # Don't close here - session is reused | |
| pass | |
| async def fetch_url(url: str, **kwargs) -> str: | |
| """ | |
| Convenience function to fetch content from a URL. | |
| Args: | |
| url: The URL to fetch | |
| **kwargs: Additional arguments to pass to session.get() | |
| Returns: | |
| The response text content | |
| Raises: | |
| aiohttp.ClientError: If the request fails | |
| """ | |
| session = await get_session() | |
| async with session.get(url, **kwargs) as response: | |
| response.raise_for_status() | |
| return await response.text() | |
| async def fetch_json(url: str, **kwargs) -> dict: | |
| """ | |
| Convenience function to fetch JSON from a URL. | |
| Args: | |
| url: The URL to fetch | |
| **kwargs: Additional arguments to pass to session.get() | |
| Returns: | |
| The parsed JSON response | |
| Raises: | |
| aiohttp.ClientError: If the request fails | |
| """ | |
| session = await get_session() | |
| async with session.get(url, **kwargs) as response: | |
| response.raise_for_status() | |
| return await response.json() | |