request.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. from __future__ import annotations
  2. __all__ = ["Headers", "RequestContext"]
  3. import time
  4. from typing import TYPE_CHECKING, Generic, TypeVar
  5. from ._headers import Headers
  6. if TYPE_CHECKING:
  7. from .method import MethodInfo
  8. REQ = TypeVar("REQ")
  9. RES = TypeVar("RES")
  10. class RequestContext(Generic[REQ, RES]):
  11. """Additional context for an RPC request message."""
  12. _method: MethodInfo[REQ, RES]
  13. _http_method: str
  14. _request_headers: Headers
  15. _response_headers: Headers | None
  16. _response_trailers: Headers | None
  17. def __init__(
  18. self,
  19. *,
  20. method: MethodInfo[REQ, RES],
  21. http_method: str,
  22. request_headers: Headers,
  23. timeout_ms: int | None = None,
  24. server_address: str | None = None,
  25. client_address: str | None = None,
  26. ) -> None:
  27. """
  28. Initialize a Context object.
  29. """
  30. self._method = method
  31. self._http_method = http_method
  32. self._request_headers = request_headers
  33. self._response_headers = None
  34. self._response_trailers = None
  35. self._server_address = server_address
  36. self._client_address = client_address
  37. if timeout_ms is None:
  38. self._end_time = None
  39. else:
  40. self._end_time = time.monotonic() + (timeout_ms / 1000.0)
  41. @property
  42. def method(self) -> MethodInfo[REQ, RES]:
  43. """Returns information about the RPC method being invoked."""
  44. return self._method
  45. @property
  46. def http_method(self) -> str:
  47. """Returns the HTTP method for this request.
  48. This is nearly always POST, but side-effect-free unary RPCs could be made
  49. via GET.
  50. """
  51. return self._http_method
  52. @property
  53. def request_headers(self) -> Headers:
  54. """Returns the request headers associated with the context."""
  55. return self._request_headers
  56. @property
  57. def response_headers(self) -> Headers:
  58. """
  59. Returns the response headers that will be sent before the response.
  60. """
  61. if self._response_headers is None:
  62. self._response_headers = Headers()
  63. return self._response_headers
  64. @property
  65. def response_trailers(self) -> Headers:
  66. """
  67. Returns the response trailers that will be sent after the response.
  68. """
  69. if self._response_trailers is None:
  70. self._response_trailers = Headers()
  71. return self._response_trailers
  72. @property
  73. def timeout_ms(self) -> float | None:
  74. """Returns the remaining time until the timeout in milliseconds, or None if no timeout is set."""
  75. if self._end_time is None:
  76. return None
  77. return (self._end_time - time.monotonic()) * 1000.0
  78. @property
  79. def server_address(self) -> str | None:
  80. """
  81. Returns the server address for this request, if available, as a "address:port" string.
  82. - On the client, this is components from the URL configured when constructing the client.
  83. - On the server, this is determined from the Host header and scheme of the request.
  84. """
  85. return self._server_address
  86. @property
  87. def client_address(self) -> str | None:
  88. """
  89. Returns the client address for this request, if available, as a "address:port" string.
  90. - On the client, this is never populated.
  91. - On the server, this is the value provided by the server implementation, generally
  92. the IP address and ephemeral port of the client.
  93. """
  94. return self._client_address