Skip to content

Commit 8e3425e

Browse files
committed
HttpClient first version
1 parent ba3d617 commit 8e3425e

File tree

4 files changed

+85
-75
lines changed

4 files changed

+85
-75
lines changed

arangoasync/http.py

Lines changed: 73 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,121 @@
1-
# TODO __all__ = []
1+
__all__ = [
2+
"HTTPClient",
3+
"AioHTTPClient",
4+
"DefaultHTTPClient",
5+
]
26

37
from abc import ABC, abstractmethod
4-
from typing import Optional
8+
from typing import Any, Optional
59

6-
from aiohttp import BaseConnector, BasicAuth, ClientSession, ClientTimeout
10+
from aiohttp import BaseConnector, BasicAuth, ClientSession, ClientTimeout, TcpConnector
711
from request import Request
812
from response import Response
913

1014

11-
class Session(ABC): # pragma: no cover
12-
"""Abstract base class for HTTP sessions."""
13-
14-
@abstractmethod
15-
async def request(self, request: Request) -> Response:
16-
"""Send an HTTP request.
17-
18-
This method must be overridden by the user.
19-
20-
:param request: HTTP request.
21-
:type request: arangoasync.request.Request
22-
:returns: HTTP response.
23-
:rtype: arangoasync.response.Response
24-
"""
25-
raise NotImplementedError
26-
27-
@abstractmethod
28-
async def close(self) -> None:
29-
"""Close the session.
30-
31-
This method must be overridden by the user.
32-
"""
33-
raise NotImplementedError
34-
35-
3615
class HTTPClient(ABC): # pragma: no cover
37-
"""Abstract base class for HTTP clients."""
16+
"""Abstract base class for HTTP clients.
17+
Custom HTTP clients should inherit from this class.
18+
"""
3819

3920
@abstractmethod
40-
def create_session(self, host: str) -> Session:
41-
"""Return a new requests session given the host URL.
21+
def create_session(self, host: str) -> Any:
22+
"""Return a new session given the base host URL.
4223
4324
This method must be overridden by the user.
4425
4526
:param host: ArangoDB host URL.
4627
:type host: str
4728
:returns: Requests session object.
48-
:rtype: arangoasync.http.Session
29+
:rtype: Any
4930
"""
5031
raise NotImplementedError
5132

5233
@abstractmethod
5334
async def send_request(
5435
self,
55-
session: Session,
56-
url: str,
36+
session: Any,
5737
request: Request,
5838
) -> Response:
5939
"""Send an HTTP request.
6040
6141
This method must be overridden by the user.
6242
6343
:param session: Session object.
64-
:type session: arangoasync.http.Session
65-
:param url: Request URL.
66-
:type url: str
44+
:type session: Any
6745
:param request: HTTP request.
6846
:type request: arangoasync.request.Request
6947
:returns: HTTP response.
70-
:rtype: arango.response.Response
48+
:rtype: arangoasync.response.Response
7149
"""
7250
raise NotImplementedError
7351

7452

75-
class DefaultSession(Session):
76-
"""Wrapper on top of an aiohttp.ClientSession."""
53+
class AioHTTPClient(HTTPClient):
54+
"""HTTP client implemented on top of [aiohttp](https://docs.aiohttp.org/en/stable/).
55+
56+
:param connector: Supports connection pooling.
57+
By default, 100 simultaneous connections are supported, with a 60-second timeout
58+
for connection reusing after release.
59+
:type connector: aiohttp.BaseConnector | None
60+
:param timeout: Timeout settings.
61+
300s total timeout by default for a complete request/response operation.
62+
:type timeout: aiohttp.ClientTimeout | None
63+
:param read_bufsize: Size of read buffer (64KB default).
64+
:type read_bufsize: int
65+
:param auth: HTTP authentication helper.
66+
Should be used for specifying authorization data in client API.
67+
:type auth: aiohttp.BasicAuth | None
68+
:param compression_threshold: Will compress requests to the server if
69+
the size of the request body (in bytes) is at least the value of this
70+
option.
71+
:type compression_threshold: int
72+
"""
7773

7874
def __init__(
7975
self,
80-
host: str,
81-
connector: BaseConnector,
82-
timeout: ClientTimeout,
76+
connector: Optional[BaseConnector] = None,
77+
timeout: Optional[ClientTimeout] = None,
8378
read_bufsize: int = 2**16,
8479
auth: Optional[BasicAuth] = None,
80+
compression_threshold: int = 1024,
8581
) -> None:
86-
"""Initialize the session.
82+
self._connector = connector or TcpConnector(
83+
keepalive_timeout=60, # timeout for connection reusing after releasing
84+
limit=100, # total number simultaneous connections
85+
)
86+
self._timeout = timeout or ClientTimeout(
87+
total=300, # total number of seconds for the whole request
88+
connect=60, # max number of seconds for acquiring a pool connection
89+
)
90+
self._read_bufsize = read_bufsize
91+
self._auth = auth
92+
self._compression_threshold = compression_threshold
8793

88-
:param host: ArangoDB coordinator URL (eg http://localhost:8530).
94+
def create_session(self, host: str) -> ClientSession:
95+
"""Return a new session given the base host URL.
96+
97+
:param host: ArangoDB host URL. Typically the address and port of a coordinator,
98+
for example "http://127.0.0.1:8529".
8999
:type host: str
90-
:param connector: Supports connection pooling.
91-
:type connector: aiohttp.BaseConnector
92-
:param timeout: Request timeout settings.
93-
:type timeout: aiohttp.ClientTimeout
94-
:param read_bufsize: Size of read buffer. 64 Kib by default.
95-
:type read_bufsize: int
96-
:param auth: HTTP Authorization.
97-
:type auth: aiohttp.BasicAuth | None
100+
:returns: Session object.
101+
:rtype: aiohttp.ClientSession
98102
"""
99-
self._session = ClientSession(
100-
base_url=host,
101-
connector=connector,
102-
timeout=timeout,
103-
auth=auth,
104-
read_bufsize=read_bufsize,
105-
connector_owner=False,
106-
auto_decompress=True,
103+
return ClientSession(
104+
connector=self._connector,
105+
timeout=self._timeout,
106+
auth=self._auth,
107+
read_bufsize=self._read_bufsize,
107108
)
108109

109-
async def request(self, request: Request) -> Response:
110+
async def send_request(
111+
self,
112+
session: ClientSession,
113+
request: Request,
114+
) -> Response:
110115
"""Send an HTTP request.
111116
117+
:param session: Session object.
118+
:type session: aiohttp.ClientSession
112119
:param request: HTTP request.
113120
:type request: arangoasync.request.Request
114121
:returns: HTTP response.
@@ -119,13 +126,15 @@ async def request(self, request: Request) -> Response:
119126
headers = request.headers
120127
params = request.params
121128
data = request.data
129+
compress = len(data) >= self._compression_threshold
122130

123-
async with self._session.request(
131+
async with session.request(
124132
method.name,
125133
endpoint,
126134
headers=headers,
127135
params=params,
128136
data=data,
137+
compress=compress,
129138
) as response:
130139
raw_body = await response.read()
131140
return Response(
@@ -137,9 +146,5 @@ async def request(self, request: Request) -> Response:
137146
raw_body=raw_body,
138147
)
139148

140-
async def close(self) -> None:
141-
"""Close the session."""
142-
await self._session.close()
143-
144149

145-
# TODO implement DefaultHTTPClient
150+
DefaultHTTPClient = AioHTTPClient

arangoasync/request.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from typing import Generic, Optional, TypeVar
88

99
from typings import Headers, Params
10-
from version import __version__ as driver_version
10+
from version import __version__
1111

1212
T = TypeVar("T")
1313

@@ -33,7 +33,7 @@ class Request(Generic[T]):
3333
:type endpoint: str
3434
:param headers: Request headers.
3535
:type headers: dict | None
36-
:param params: URL (query) parameters.
36+
:param params: URL parameters.
3737
:type params: dict | None
3838
:param data: Request payload.
3939
:type data: Any
@@ -42,7 +42,7 @@ class Request(Generic[T]):
4242
4343
:ivar method: HTTP method.
4444
:vartype method: request.Method
45-
:ivar endpoint: API endpoint.
45+
:ivar endpoint: API endpoint, for example "_api/version".
4646
:vartype endpoint: str
4747
:ivar headers: Request headers.
4848
:vartype headers: dict | None
@@ -88,7 +88,7 @@ def _normalize_headers(headers: Optional[Headers]) -> Headers:
8888
:returns: Normalized request headers.
8989
:rtype: dict
9090
"""
91-
driver_header = f"arangoasync/{driver_version}"
91+
driver_header = f"arangoasync/{__version__}"
9292
normalized_headers: Headers = {
9393
"charset": "utf-8",
9494
"content-type": "application/json",
@@ -103,11 +103,11 @@ def _normalize_headers(headers: Optional[Headers]) -> Headers:
103103

104104
@staticmethod
105105
def _normalize_params(params: Optional[Params]) -> Params:
106-
"""Normalize URL (query) parameters.
106+
"""Normalize URL parameters.
107107
108-
:param params: URL (query) parameters.
108+
:param params: URL parameters.
109109
:type params: dict | None
110-
:returns: Normalized URL (query) parameters.
110+
:returns: Normalized URL parameters.
111111
:rtype: dict
112112
"""
113113
normalized_params: Params = {}

arangoasync/response.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
__all__ = [
2+
"Response",
3+
]
4+
15
from typing import Generic, Optional, TypeVar
26

37
from request import Method

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ dependencies = [
4040
"packaging>=23.1",
4141
"setuptools>=42",
4242
"aiohttp>=3.9",
43+
"multidict>=6.0",
4344
]
4445

4546
[tool.setuptools.dynamic]

0 commit comments

Comments
 (0)