Skip to content

Commit 88abb3e

Browse files
committed
HTTP docs
1 parent b152d7f commit 88abb3e

File tree

3 files changed

+157
-1
lines changed

3 files changed

+157
-1
lines changed

arangoasync/client.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,9 @@ def version(self) -> str:
139139

140140
async def close(self) -> None:
141141
"""Close HTTP sessions."""
142-
await asyncio.gather(*(session.close() for session in self._sessions))
142+
await asyncio.gather(
143+
*(self._http_client.close_session(session) for session in self._sessions)
144+
)
143145

144146
async def db(
145147
self,

arangoasync/http.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ class HTTPClient(ABC): # pragma: no cover
3333
class MyCustomHTTPClient(HTTPClient):
3434
def create_session(self, host):
3535
pass
36+
async def close_session(self, session):
37+
pass
3638
async def send_request(self, session, request):
3739
pass
3840
"""
@@ -52,6 +54,18 @@ def create_session(self, host: str) -> Any:
5254
"""
5355
raise NotImplementedError
5456

57+
@abstractmethod
58+
async def close_session(self, session: Any) -> None:
59+
"""Close the session.
60+
61+
Note:
62+
This method must be overridden by the user.
63+
64+
Args:
65+
session (Any): Client session object.
66+
"""
67+
raise NotImplementedError
68+
5569
@abstractmethod
5670
async def send_request(
5771
self,
@@ -129,6 +143,14 @@ def create_session(self, host: str) -> ClientSession:
129143
read_bufsize=self._read_bufsize,
130144
)
131145

146+
async def close_session(self, session: ClientSession) -> None:
147+
"""Close the session.
148+
149+
Args:
150+
session (Any): Client session object.
151+
"""
152+
await session.close()
153+
132154
async def send_request(
133155
self,
134156
session: ClientSession,

docs/http.rst

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,135 @@
22

33
HTTP
44
----
5+
6+
You can define your own HTTP client for sending requests to
7+
ArangoDB server. The default implementation uses the aiohttp_ library.
8+
9+
Your HTTP client must inherit :class:`arangoasync.http.HTTPClient` and implement the
10+
following abstract methods:
11+
12+
* :func:`arangoasync.http.HTTPClient.create_session`
13+
* :func:`arangoasync.http.HTTPClient.close_session`
14+
* :func:`arangoasync.http.HTTPClient.send_request`
15+
16+
Let's take for example, the default implementation of :class:`arangoasync.http.AioHTTPClient`:
17+
18+
* The **create_session** method returns a :class:`aiohttp.ClientSession` instance per
19+
connected host (coordinator). The session objects are stored in the client.
20+
* The **close_session** method performs the necessary cleanup for a :class:`aiohttp.ClientSession` instance.
21+
This is usually called only by the client.
22+
* The **send_request** method must uses the session to send an HTTP request, and
23+
returns a fully populated instance of :class:`arangoasync.response.Response`.
24+
25+
**Example:**
26+
27+
Suppose you're working on a project that uses httpx_ as a dependency and you want your
28+
own HTTP client implementation on top of :class:`httpx.AsyncClient`. Your ``HttpxHTTPClient``
29+
class might look something like this:
30+
31+
.. code-block:: python
32+
33+
import httpx
34+
import ssl
35+
from typing import Any, Optional
36+
from arangoasync.exceptions import ClientConnectionError
37+
from arangoasync.http import HTTPClient
38+
from arangoasync.request import Request
39+
from arangoasync.response import Response
40+
41+
class HttpxHTTPClient(HTTPClient):
42+
"""HTTP client implementation on top of httpx.AsyncClient.
43+
44+
Args:
45+
limits (httpx.Limits | None): Connection pool limits.n
46+
timeout (httpx.Timeout | float | None): Request timeout settings.
47+
ssl_context (ssl.SSLContext | bool): SSL validation mode.
48+
`True` (default) uses httpx’s default validation (system CAs).
49+
`False` disables SSL checks.
50+
Or pass a custom `ssl.SSLContext`.
51+
"""
52+
53+
def __init__(
54+
self,
55+
limits: Optional[httpx.Limits] = None,
56+
timeout: Optional[httpx.Timeout | float] = None,
57+
ssl_context: bool | ssl.SSLContext = True,
58+
) -> None:
59+
self._limits = limits or httpx.Limits(
60+
max_connections=100,
61+
max_keepalive_connections=20
62+
)
63+
self._timeout = timeout or httpx.Timeout(300.0, connect=60.0)
64+
if ssl_context is True:
65+
self._verify: bool | ssl.SSLContext = True
66+
elif ssl_context is False:
67+
self._verify = False
68+
else:
69+
self._verify = ssl_context
70+
71+
def create_session(self, host: str) -> httpx.AsyncClient:
72+
return httpx.AsyncClient(
73+
base_url=host,
74+
limits=self._limits,
75+
timeout=self._timeout,
76+
verify=self._verify,
77+
)
78+
79+
async def close_session(self, session: httpx.AsyncClient) -> None:
80+
await session.aclose()
81+
82+
async def send_request(
83+
self,
84+
session: httpx.AsyncClient,
85+
request: Request,
86+
) -> Response:
87+
auth: Any = None
88+
if request.auth is not None:
89+
auth = httpx.BasicAuth(
90+
username=request.auth.username,
91+
password=request.auth.password,
92+
)
93+
94+
try:
95+
resp = await session.request(
96+
method=request.method.name,
97+
url=request.endpoint,
98+
headers=request.normalized_headers(),
99+
params=request.normalized_params(),
100+
content=request.data,
101+
auth=auth,
102+
)
103+
raw_body = resp.content
104+
return Response(
105+
method=request.method,
106+
url=str(resp.url),
107+
headers=resp.headers,
108+
status_code=resp.status_code,
109+
status_text=resp.reason_phrase,
110+
raw_body=raw_body,
111+
)
112+
except httpx.HTTPError as e:
113+
raise ClientConnectionError(str(e)) from e
114+
115+
Then you would inject your client as follows:
116+
117+
.. code-block:: python
118+
119+
from arangoasync import ArangoClient
120+
from arangoasync.auth import Auth
121+
122+
# Initialize the client for ArangoDB.
123+
async with ArangoClient(
124+
hosts="http://localhost:8529",
125+
http_client=HttpxHTTPClient(),
126+
) as client:
127+
auth = Auth(username="root", password="passwd")
128+
129+
# Connect to "test" database as root user.
130+
db = await client.db("test", auth=auth, verify=True)
131+
132+
# List all collections.
133+
cols = await db.collections()
134+
135+
.. _aiohttp: https://docs.aiohttp.org/en/stable/
136+
.. _httpx: https://www.python-httpx.org/

0 commit comments

Comments
 (0)