Skip to content

Commit

Permalink
Add Parameter socket_options to HTTPXRequest (#3935)
Browse files Browse the repository at this point in the history
  • Loading branch information
Bibo-Joshi committed Oct 22, 2023
1 parent f67e8c0 commit ea7e5a6
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 19 deletions.
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ repos:
- --jobs=0

additional_dependencies:
- httpx~=0.24.1
- httpx~=0.25.0
- tornado~=6.3.3
- APScheduler~=3.10.4
- cachetools~=5.3.1
Expand All @@ -44,7 +44,7 @@ repos:
- types-pytz
- types-cryptography
- types-cachetools
- httpx~=0.24.1
- httpx~=0.25.0
- tornado~=6.3.3
- APScheduler~=3.10.4
- cachetools~=5.3.1
Expand Down Expand Up @@ -83,7 +83,7 @@ repos:
name: ruff
files: ^(telegram|examples|tests)/.*\.py$
additional_dependencies:
- httpx~=0.24.1
- httpx~=0.25.0
- tornado~=6.3.3
- APScheduler~=3.10.4
- cachetools~=5.3.1
Expand Down
48 changes: 32 additions & 16 deletions telegram/request/_httpxrequest.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains methods to make POST and GET requests using the httpx library."""
from typing import Optional, Tuple
from typing import Collection, Optional, Tuple, Union

import httpx

Expand All @@ -35,6 +35,12 @@

_LOGGER = get_logger(__name__, "HTTPXRequest")

_SocketOpt = Union[
Tuple[int, int, int],
Tuple[int, int, Union[bytes, bytearray]],
Tuple[int, int, None, int],
]


class HTTPXRequest(BaseRequest):
"""Implementation of :class:`~telegram.request.BaseRequest` using the library
Expand Down Expand Up @@ -91,6 +97,16 @@ class HTTPXRequest(BaseRequest):
.. versionchanged:: 20.5
Accept ``"2"`` as a valid value.
socket_options (Collection[:obj:`tuple`], optional): Socket options to be passed to the
underlying `library \
<https://www.encode.io/httpcore/async/#httpcore.AsyncConnectionPool.__init__>`_.
Note:
The values accepted by this parameter depend on the operating system.
This is a low-level parameter and should only be used if you are familiar with
these concepts.
.. versionadded:: NEXT.VERSION
"""

Expand All @@ -105,6 +121,7 @@ def __init__(
connect_timeout: Optional[float] = 5.0,
pool_timeout: Optional[float] = 1.0,
http_version: HTTPVersion = "1.1",
socket_options: Optional[Collection[_SocketOpt]] = None,
):
self._http_version = http_version
timeout = httpx.Timeout(
Expand All @@ -122,16 +139,21 @@ def __init__(
raise ValueError("`http_version` must be either '1.1', '2.0' or '2'.")

http1 = http_version == "1.1"

# See https://github.com/python-telegram-bot/python-telegram-bot/pull/3542
# for why we need to use `dict()` here.
self._client_kwargs = dict( # pylint: disable=use-dict-literal # noqa: C408
timeout=timeout,
proxies=proxy_url,
limits=limits,
http1=http1,
http2=not http1,
http_kwargs = {"http1": http1, "http2": not http1}
transport = (
httpx.AsyncHTTPTransport(
socket_options=socket_options,
)
if socket_options
else None
)
self._client_kwargs = {
"timeout": timeout,
"proxies": proxy_url,
"limits": limits,
"transport": transport,
**http_kwargs,
}

try:
self._client = self._build_client()
Expand Down Expand Up @@ -206,12 +228,6 @@ async def do_request(
pool=pool_timeout,
)

# TODO p0: On Linux, use setsockopt to properly set socket level keepalive.
# (socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 120)
# (socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 30)
# (socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 8)
# TODO p4: Support setsockopt on lesser platforms than Linux.

files = request_data.multipart_data if request_data else None
data = request_data.json_parameters if request_data else None

Expand Down
2 changes: 2 additions & 0 deletions tests/ext/test_applicationbuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ class Client:
limits: object
http1: object
http2: object
transport: object = None

monkeypatch.setattr(httpx, "AsyncClient", Client)

Expand Down Expand Up @@ -322,6 +323,7 @@ class Client:
limits: object
http1: object
http2: object
transport: object = None

monkeypatch.setattr(httpx, "AsyncClient", Client)

Expand Down
20 changes: 20 additions & 0 deletions tests/request/test_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

import httpx
import pytest
from httpx import AsyncHTTPTransport

from telegram._utils.defaultvalue import DEFAULT_NONE
from telegram.error import (
Expand Down Expand Up @@ -366,6 +367,7 @@ class Client:
limits: object
http1: object
http2: object
transport: object = None

monkeypatch.setattr(httpx, "AsyncClient", Client)

Expand Down Expand Up @@ -618,6 +620,24 @@ async def request(_, **kwargs):

assert exc_info.value.__cause__ is pool_timeout

async def test_socket_opts(self, monkeypatch):
transport_kwargs = {}
transport_init = AsyncHTTPTransport.__init__

def init_transport(*args, **kwargs):
nonlocal transport_kwargs
transport_kwargs = kwargs.copy()
transport_init(*args, **kwargs)

monkeypatch.setattr(AsyncHTTPTransport, "__init__", init_transport)

HTTPXRequest()
assert "socket_options" not in transport_kwargs

transport_kwargs = {}
HTTPXRequest(socket_options=((1, 2, 3),))
assert transport_kwargs["socket_options"] == ((1, 2, 3),)


@pytest.mark.skipif(not TEST_WITH_OPT_DEPS, reason="No need to run this twice")
class TestHTTPXRequestWithRequest:
Expand Down

0 comments on commit ea7e5a6

Please sign in to comment.