1
- __all__ = ["HTTPClient" , "DefaultHTTPClient" ]
1
+ __all__ = ["HTTPClient" , "DefaultHTTPClient" , "DEFAULT_REQUEST_TIMEOUT" ]
2
2
3
+ import typing
3
4
from abc import ABC , abstractmethod
4
- from typing import MutableMapping , Optional , Tuple , Union
5
+ from typing import Any , MutableMapping , Optional , Tuple , Union
5
6
6
7
from requests import Session
7
- from requests .adapters import HTTPAdapter
8
+ from requests .adapters import DEFAULT_POOLBLOCK , DEFAULT_POOLSIZE , HTTPAdapter
8
9
from requests_toolbelt import MultipartEncoder
10
+ from urllib3 .poolmanager import PoolManager
9
11
from urllib3 .util .retry import Retry
10
12
11
13
from arango .response import Response
12
14
from arango .typings import Headers
13
15
16
+ DEFAULT_REQUEST_TIMEOUT = 60
17
+
14
18
15
19
class HTTPClient (ABC ): # pragma: no cover
16
20
"""Abstract base class for HTTP clients."""
@@ -63,12 +67,92 @@ def send_request(
63
67
raise NotImplementedError
64
68
65
69
66
- class DefaultHTTPClient (HTTPClient ):
67
- """Default HTTP client implementation."""
70
+ class DefaultHTTPAdapter (HTTPAdapter ):
71
+ """Default transport adapter implementation
72
+
73
+ :param connection_timeout: Socket timeout in seconds for each individual connection.
74
+ :type connection_timeout: int | float
75
+ :param pool_connections: The number of urllib3 connection pools to cache.
76
+ :type pool_connections: int
77
+ :param pool_maxsize: The maximum number of connections to save in the pool.
78
+ :type pool_maxsize: int
79
+ :param pool_timeout: If set, then the pool will be set to block=True,
80
+ and requests will block for pool_timeout seconds and raise
81
+ EmptyPoolError if no connection is available within the time period.
82
+ :type pool_timeout: int | float | None
83
+ :param kwargs: Additional keyword arguments passed to the HTTPAdapter constructor.
84
+ :type kwargs: Any
85
+ """
86
+
87
+ def __init__ (
88
+ self ,
89
+ connection_timeout : Union [int , float ] = DEFAULT_REQUEST_TIMEOUT ,
90
+ pool_connections : int = DEFAULT_POOLSIZE ,
91
+ pool_maxsize : int = DEFAULT_POOLSIZE ,
92
+ pool_timeout : Union [int , float , None ] = None ,
93
+ ** kwargs : Any
94
+ ) -> None :
95
+ self ._connection_timeout = connection_timeout
96
+ self ._pool_timeout = pool_timeout
97
+ super ().__init__ (
98
+ pool_connections = pool_connections , pool_maxsize = pool_maxsize , ** kwargs
99
+ )
100
+
101
+ @typing .no_type_check
102
+ def init_poolmanager (
103
+ self , connections , maxsize , block = DEFAULT_POOLBLOCK , ** pool_kwargs
104
+ ) -> None :
105
+ kwargs = pool_kwargs
106
+ kwargs .update (
107
+ dict (
108
+ num_pools = connections ,
109
+ maxsize = maxsize ,
110
+ strict = True ,
111
+ timeout = self ._connection_timeout ,
112
+ )
113
+ )
114
+ if self ._pool_timeout is not None :
115
+ kwargs ["block" ] = True
116
+ kwargs ["timeout" ] = self ._pool_timeout
117
+ else :
118
+ kwargs ["block" ] = False
119
+ self .poolmanager = PoolManager (** kwargs )
68
120
69
- REQUEST_TIMEOUT = 60
70
- RETRY_ATTEMPTS = 3
71
- BACKOFF_FACTOR = 1
121
+
122
+ class DefaultHTTPClient (HTTPClient ):
123
+ """Default HTTP client implementation.
124
+
125
+ :param request_timeout: Timeout in seconds for each individual connection.
126
+ :type request_timeout: int | float
127
+ :param retry_attempts: Number of retry attempts.
128
+ :type retry_attempts: int
129
+ :param backoff_factor: Backoff factor for retry attempts.
130
+ :type backoff_factor: float
131
+ :param pool_connections: The number of urllib3 connection pools to cache.
132
+ :type pool_connections: int
133
+ :param pool_maxsize: The maximum number of connections to save in the pool.
134
+ :type pool_maxsize: int
135
+ :param pool_timeout: If set, then the pool will be set to block=True,
136
+ and requests will block for pool_timeout seconds and raise
137
+ EmptyPoolError if no connection is available within the time period.
138
+ :type pool_timeout: int | float | None
139
+ """
140
+
141
+ def __init__ (
142
+ self ,
143
+ request_timeout : Union [int , float ] = DEFAULT_REQUEST_TIMEOUT ,
144
+ retry_attempts : int = 3 ,
145
+ backoff_factor : float = 1.0 ,
146
+ pool_connections : int = 10 ,
147
+ pool_maxsize : int = 10 ,
148
+ pool_timeout : Union [int , float , None ] = None ,
149
+ ) -> None :
150
+ self .request_timeout = request_timeout
151
+ self ._retry_attempts = retry_attempts
152
+ self ._backoff_factor = backoff_factor
153
+ self ._pool_connections = pool_connections
154
+ self ._pool_maxsize = pool_maxsize
155
+ self ._pool_timeout = pool_timeout
72
156
73
157
def create_session (self , host : str ) -> Session :
74
158
"""Create and return a new session/connection.
@@ -79,12 +163,18 @@ def create_session(self, host: str) -> Session:
79
163
:rtype: requests.Session
80
164
"""
81
165
retry_strategy = Retry (
82
- total = self .RETRY_ATTEMPTS ,
83
- backoff_factor = self .BACKOFF_FACTOR ,
166
+ total = self ._retry_attempts ,
167
+ backoff_factor = self ._backoff_factor ,
84
168
status_forcelist = [429 , 500 , 502 , 503 , 504 ],
85
169
allowed_methods = ["HEAD" , "GET" , "OPTIONS" ],
86
170
)
87
- http_adapter = HTTPAdapter (max_retries = retry_strategy )
171
+ http_adapter = DefaultHTTPAdapter (
172
+ connection_timeout = self .request_timeout ,
173
+ pool_connections = self ._pool_connections ,
174
+ pool_maxsize = self ._pool_maxsize ,
175
+ pool_timeout = self ._pool_timeout ,
176
+ max_retries = retry_strategy ,
177
+ )
88
178
89
179
session = Session ()
90
180
session .mount ("https://" , http_adapter )
@@ -128,7 +218,7 @@ def send_request(
128
218
data = data ,
129
219
headers = headers ,
130
220
auth = auth ,
131
- timeout = self .REQUEST_TIMEOUT ,
221
+ timeout = self .request_timeout ,
132
222
)
133
223
return Response (
134
224
method = method ,
0 commit comments