@@ -24,33 +24,85 @@ def close(self):
24
24
self .is_closed = True
25
25
asyncio .create_task (super ().aclose ())
26
26
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
+
27
37
28
38
class AsyncHttpxRequestHandler (BaseRequestHandler ):
29
39
""" PubNub Python SDK asychronous requests handler based on the `httpx` HTTP library. """
30
40
ENDPOINT_THREAD_COUNTER : int = 0
31
41
_connector : httpx .AsyncHTTPTransport = None
32
42
_session : httpx .AsyncClient = None
43
+ _is_closing : bool = False
44
+ _max_connections : int = 100
45
+ _connection_timeout : float = 30.0
33
46
34
47
def __init__ (self , pubnub ):
35
48
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
37
55
38
56
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
+ )
43
68
44
69
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
48
87
49
88
async def set_connector (self , connector ):
50
- await self ._session .aclose ()
89
+ if self ._session is not None :
90
+ await self .close_session ()
51
91
self ._connector = connector
52
92
await self .create_session ()
53
93
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
+
54
106
def sync_request (self , ** _ ):
55
107
raise NotImplementedError ("sync_request is not implemented for asyncio handler" )
56
108
@@ -123,13 +175,17 @@ async def async_request(self, options_func, cancellation_event):
123
175
except Exception as e :
124
176
logger .error ("session.request exception: %s" % str (e ))
125
177
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
126
183
127
- response_body = response .read ()
128
184
if not options .non_json_response :
129
185
body = response_body
130
186
else :
131
187
if isinstance (response .content , bytes ):
132
- body = response .content # TODO: simplify this logic within the v5 release
188
+ body = response .content
133
189
else :
134
190
body = response_body
135
191
@@ -192,7 +248,6 @@ async def async_request(self, options_func, cancellation_event):
192
248
logger .debug (data )
193
249
194
250
if response .status_code not in (200 , 307 , 204 ):
195
-
196
251
if response .status_code >= 500 :
197
252
err = PNERR_SERVER_ERROR
198
253
else :
0 commit comments