Coverage for src/httpx/_request.py: 97%

1 import typing

2

3 from ._content import Content

4 from ._streams import ByteStream, Stream

5 from ._headers import Headers

6 from ._urls import URL

7

8 __all__ = ["Request"]

9

10

11 class 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"")

23

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)

30

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)}')

44

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))

56

57 def read(self):

58 self.body = b"".join([part for part in self.stream])

59 self.stream = ByteStream(self.body)

60

61 def __repr__(self):

62 return f"<Request [{self.method} {str(self.url)!r}]>"