@@ -24,33 +24,85 @@ def close(self):
2424 self .is_closed = True
2525 asyncio .create_task (super ().aclose ())
2626
27+ async def aclose (self ):
28+ self .is_closed = True
29+ await super ().aclose ()
30+
31+ async def __aenter__ (self ):
32+ return self
33+
34+ async def __aexit__ (self , exc_type , exc_val , exc_tb ):
35+ await self .aclose ()
36+
2737
2838class AsyncHttpxRequestHandler (BaseRequestHandler ):
2939 """ PubNub Python SDK asychronous requests handler based on the `httpx` HTTP library. """
3040 ENDPOINT_THREAD_COUNTER : int = 0
3141 _connector : httpx .AsyncHTTPTransport = None
3242 _session : httpx .AsyncClient = None
43+ _is_closing : bool = False
44+ _max_connections : int = 100
45+ _connection_timeout : float = 30.0
3346
3447 def __init__ (self , pubnub ):
3548 self .pubnub = pubnub
36- self ._connector = PubNubAsyncHTTPTransport (verify = True , http2 = True )
49+ self ._connector = PubNubAsyncHTTPTransport (
50+ verify = True ,
51+ http2 = True ,
52+ limits = httpx .Limits (max_connections = self ._max_connections )
53+ )
54+ self ._is_closing = False
3755
3856 async def create_session (self ):
39- self ._session = httpx .AsyncClient (
40- timeout = httpx .Timeout (self .pubnub .config .connect_timeout ),
41- transport = self ._connector
42- )
57+ if self ._session is None and not self ._is_closing :
58+ self ._session = httpx .AsyncClient (
59+ timeout = httpx .Timeout (
60+ connect = self ._connection_timeout ,
61+ read = self .pubnub .config .connect_timeout ,
62+ write = self .pubnub .config .connect_timeout ,
63+ pool = self ._connection_timeout
64+ ),
65+ transport = self ._connector ,
66+ http2 = True
67+ )
4368
4469 async def close_session (self ):
45- if self ._session is not None :
46- self ._connector .close ()
47- await self ._session .aclose ()
70+ if self ._session is not None and not self ._is_closing :
71+ self ._is_closing = True
72+ try :
73+ # Cancel any pending requests
74+ if hasattr (self ._session , '_transport' ):
75+ for task in asyncio .all_tasks ():
76+ if not task .done () and task is not asyncio .current_task ():
77+ task .cancel ()
78+
79+ # Close transport and session
80+ await self ._connector .aclose ()
81+ await self ._session .aclose ()
82+ except Exception as e :
83+ logger .error (f"Error during session cleanup: { str (e )} " )
84+ finally :
85+ self ._session = None
86+ self ._is_closing = False
4887
4988 async def set_connector (self , connector ):
50- await self ._session .aclose ()
89+ if self ._session is not None :
90+ await self .close_session ()
5191 self ._connector = connector
5292 await self .create_session ()
5393
94+ async def __aenter__ (self ):
95+ await self .create_session ()
96+ return self
97+
98+ async def __aexit__ (self , exc_type , exc_val , exc_tb ):
99+ await self .close_session ()
100+
101+ def __del__ (self ):
102+ ...
103+ # if self._session is not None and not self._is_closing:
104+ # asyncio.create_task(self.close_session())
105+
54106 def sync_request (self , ** _ ):
55107 raise NotImplementedError ("sync_request is not implemented for asyncio handler" )
56108
@@ -123,13 +175,17 @@ async def async_request(self, options_func, cancellation_event):
123175 except Exception as e :
124176 logger .error ("session.request exception: %s" % str (e ))
125177 raise
178+ try :
179+ response_body = await response .aread ()
180+ except Exception as e :
181+ logger .error (f"Error reading response body: { str (e )} " )
182+ response_body = None
126183
127- response_body = response .read ()
128184 if not options .non_json_response :
129185 body = response_body
130186 else :
131187 if isinstance (response .content , bytes ):
132- body = response .content # TODO: simplify this logic within the v5 release
188+ body = response .content
133189 else :
134190 body = response_body
135191
@@ -192,7 +248,6 @@ async def async_request(self, options_func, cancellation_event):
192248 logger .debug (data )
193249
194250 if response .status_code not in (200 , 307 , 204 ):
195-
196251 if response .status_code >= 500 :
197252 err = PNERR_SERVER_ERROR
198253 else :
0 commit comments