Coverage for src/httpx/_request.py: 97%
35 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-06-09 10:38 +0100
« prev ^ index » next coverage.py v7.6.12, created at 2025-06-09 10:38 +0100
1import typing
3from ._content import Content
4from ._streams import ByteStream, Stream
5from ._headers import Headers
6from ._urls import URL
8__all__ = ["Request"]
11class Request:
12 def __init__(
13 self,
14 method: str,
15 url: URL | str,
16 headers: Headers | typing.Mapping[str, str] | None = None,
17 content: Content | Stream | bytes | None = None,
18 ):
19 self.method = method
20 self.url = URL(url)
21 self.headers = Headers(headers)
22 self.stream: Stream = ByteStream(b"")
24 # https://datatracker.ietf.org/doc/html/rfc2616#section-14.23
25 # RFC 2616, Section 14.23, Host.
26 #
27 # A client MUST include a Host header field in all HTTP/1.1 request messages.
28 if "Host" not in self.headers:
29 self.headers = self.headers.copy_set("Host", self.url.netloc)
31 if content is not None:
32 if isinstance(content, bytes):
33 self.stream = ByteStream(content)
34 elif isinstance(content, Stream):
35 self.stream = content
36 elif isinstance(content, Content):
37 assert isinstance(content, Content)
38 # Eg. Request("POST", "https://www.example.com", content=Form(...))
39 stream, content_type = content.encode()
40 self.headers = self.headers.copy_set("Content-Type", content_type)
41 self.stream = stream
42 else:
43 raise TypeError(f'Expected `Content | Stream | bytes | None` got {type(content)}')
45 # https://datatracker.ietf.org/doc/html/rfc2616#section-4.3
46 # RFC 2616, Section 4.3, Message Body.
47 #
48 # The presence of a message-body in a request is signaled by the
49 # inclusion of a Content-Length or Transfer-Encoding header field in
50 # the request's message-headers.
51 content_length: int | None = self.stream.size
52 if content_length is None:
53 self.headers = self.headers.copy_set("Transfer-Encoding", "chunked")
54 elif content_length > 0:
55 self.headers = self.headers.copy_set("Content-Length", str(content_length))
57 def read(self):
58 self.body = b"".join([part for part in self.stream])
59 self.stream = ByteStream(self.body)
61 def __repr__(self):
62 return f"<Request [{self.method} {str(self.url)!r}]>"