interfaces.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. from __future__ import annotations
  2. import contextlib
  3. import typing
  4. from .._models import (
  5. URL,
  6. Extensions,
  7. HeaderTypes,
  8. Origin,
  9. Request,
  10. Response,
  11. enforce_bytes,
  12. enforce_headers,
  13. enforce_url,
  14. include_request_headers,
  15. )
  16. class RequestInterface:
  17. def request(
  18. self,
  19. method: bytes | str,
  20. url: URL | bytes | str,
  21. *,
  22. headers: HeaderTypes = None,
  23. content: bytes | typing.Iterator[bytes] | None = None,
  24. extensions: Extensions | None = None,
  25. ) -> Response:
  26. # Strict type checking on our parameters.
  27. method = enforce_bytes(method, name="method")
  28. url = enforce_url(url, name="url")
  29. headers = enforce_headers(headers, name="headers")
  30. # Include Host header, and optionally Content-Length or Transfer-Encoding.
  31. headers = include_request_headers(headers, url=url, content=content)
  32. request = Request(
  33. method=method,
  34. url=url,
  35. headers=headers,
  36. content=content,
  37. extensions=extensions,
  38. )
  39. response = self.handle_request(request)
  40. try:
  41. response.read()
  42. finally:
  43. response.close()
  44. return response
  45. @contextlib.contextmanager
  46. def stream(
  47. self,
  48. method: bytes | str,
  49. url: URL | bytes | str,
  50. *,
  51. headers: HeaderTypes = None,
  52. content: bytes | typing.Iterator[bytes] | None = None,
  53. extensions: Extensions | None = None,
  54. ) -> typing.Iterator[Response]:
  55. # Strict type checking on our parameters.
  56. method = enforce_bytes(method, name="method")
  57. url = enforce_url(url, name="url")
  58. headers = enforce_headers(headers, name="headers")
  59. # Include Host header, and optionally Content-Length or Transfer-Encoding.
  60. headers = include_request_headers(headers, url=url, content=content)
  61. request = Request(
  62. method=method,
  63. url=url,
  64. headers=headers,
  65. content=content,
  66. extensions=extensions,
  67. )
  68. response = self.handle_request(request)
  69. try:
  70. yield response
  71. finally:
  72. response.close()
  73. def handle_request(self, request: Request) -> Response:
  74. raise NotImplementedError() # pragma: nocover
  75. class ConnectionInterface(RequestInterface):
  76. def close(self) -> None:
  77. raise NotImplementedError() # pragma: nocover
  78. def info(self) -> str:
  79. raise NotImplementedError() # pragma: nocover
  80. def can_handle_request(self, origin: Origin) -> bool:
  81. raise NotImplementedError() # pragma: nocover
  82. def is_available(self) -> bool:
  83. """
  84. Return `True` if the connection is currently able to accept an
  85. outgoing request.
  86. An HTTP/1.1 connection will only be available if it is currently idle.
  87. An HTTP/2 connection will be available so long as the stream ID space is
  88. not yet exhausted, and the connection is not in an error state.
  89. While the connection is being established we may not yet know if it is going
  90. to result in an HTTP/1.1 or HTTP/2 connection. The connection should be
  91. treated as being available, but might ultimately raise `NewConnectionRequired`
  92. required exceptions if multiple requests are attempted over a connection
  93. that ends up being established as HTTP/1.1.
  94. """
  95. raise NotImplementedError() # pragma: nocover
  96. def has_expired(self) -> bool:
  97. """
  98. Return `True` if the connection is in a state where it should be closed.
  99. This either means that the connection is idle and it has passed the
  100. expiry time on its keep-alive, or that server has sent an EOF.
  101. """
  102. raise NotImplementedError() # pragma: nocover
  103. def is_idle(self) -> bool:
  104. """
  105. Return `True` if the connection is currently idle.
  106. """
  107. raise NotImplementedError() # pragma: nocover
  108. def is_closed(self) -> bool:
  109. """
  110. Return `True` if the connection has been closed.
  111. Used when a response is closed to determine if the connection may be
  112. returned to the connection pool or not.
  113. """
  114. raise NotImplementedError() # pragma: nocover