-
-
Notifications
You must be signed in to change notification settings - Fork 857
/
base.py
129 lines (110 loc) · 5.12 KB
/
base.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import typing
from types import TracebackType
T = typing.TypeVar("T", bound="BaseTransport")
A = typing.TypeVar("A", bound="AsyncBaseTransport")
class BaseTransport:
def __enter__(self: T) -> T:
return self
def __exit__(
self,
exc_type: typing.Type[BaseException] = None,
exc_value: BaseException = None,
traceback: TracebackType = None,
) -> None:
self.close()
def handle_request(
self,
method: bytes,
url: typing.Tuple[bytes, bytes, typing.Optional[int], bytes],
headers: typing.List[typing.Tuple[bytes, bytes]],
stream: typing.Iterable[bytes],
extensions: dict,
) -> typing.Tuple[
int, typing.List[typing.Tuple[bytes, bytes]], typing.Iterable[bytes], dict
]:
"""
Send a single HTTP request and return a response.
At this layer of API we're simply using plain primitives. No `Request` or
`Response` models, no fancy `URL` or `Header` handling. This strict point
of cut-off provides a clear design seperation between the HTTPX API,
and the low-level network handling.
Developers shouldn't typically ever need to call into this API directly,
since the Client class provides all the higher level user-facing API
niceties.
Example usage:
with httpx.HTTPTransport() as transport:
status_code, headers, stream, extensions = transport.handle_request(
method=b'GET',
url=(b'https', b'www.example.com', 443, b'/'),
headers=[(b'Host', b'www.example.com')],
stream=[],
extensions={}
)
try:
body = b''.join([part for part in stream])
finally:
if 'close' in extensions:
extensions['close']()
print(status_code, headers, body)
Arguments:
method: The request method as bytes. Eg. b'GET'.
url: The components of the request URL, as a tuple of `(scheme, host, port, target)`.
The target will usually be the URL path, but also allows for alternative
formulations, such as proxy requests which include the complete URL in
the target portion of the HTTP request, or for "OPTIONS *" requests, which
cannot be expressed in a URL string.
headers: The request headers as a list of byte pairs.
stream: The request body as a bytes iterator.
extensions: An open ended dictionary, including optional extensions to the
core request/response API. Keys may include:
timeout: A dictionary of str:Optional[float] timeout values.
May include values for 'connect', 'read', 'write', or 'pool'.
Returns a tuple of:
status_code: The response status code as an integer. Should be in the range 1xx-5xx.
headers: The response headers as a list of byte pairs.
stream: The response body as a bytes iterator.
extensions: An open ended dictionary, including optional extensions to the
core request/response API. Keys are plain strings, and may include:
reason_phrase: The reason-phrase of the HTTP response, as bytes. Eg b'OK'.
HTTP/2 onwards does not include a reason phrase on the wire.
When no key is included, a default based on the status code may
be used. An empty-string reason phrase should not be substituted
for a default, as it indicates the server left the portion blank
eg. the leading response bytes were b"HTTP/1.1 200 <CRLF>".
http_version: The HTTP version, as bytes. Eg. b"HTTP/1.1".
When no http_version key is included, HTTP/1.1 may be assumed.
close: A callback which should be invoked to release any network
resources.
aclose: An async callback which should be invoked to release any
network resources.
"""
raise NotImplementedError(
"The 'handle_request' method must be implemented."
) # pragma: nocover
def close(self) -> None:
pass
class AsyncBaseTransport:
async def __aenter__(self: A) -> A:
return self
async def __aexit__(
self,
exc_type: typing.Type[BaseException] = None,
exc_value: BaseException = None,
traceback: TracebackType = None,
) -> None:
await self.aclose()
async def handle_async_request(
self,
method: bytes,
url: typing.Tuple[bytes, bytes, typing.Optional[int], bytes],
headers: typing.List[typing.Tuple[bytes, bytes]],
stream: typing.AsyncIterable[bytes],
extensions: dict,
) -> typing.Tuple[
int, typing.List[typing.Tuple[bytes, bytes]], typing.AsyncIterable[bytes], dict
]:
raise NotImplementedError(
"The 'handle_async_request' method must be implemented."
) # pragma: nocover
async def aclose(self) -> None:
pass