From ced3b1ea190b66f7de99022033b72e0267c23d96 Mon Sep 17 00:00:00 2001 From: ZhymabekRoman Date: Sun, 10 Nov 2024 18:25:54 +0500 Subject: [PATCH] feat(http): add proxy support in HTTP client configuration --- freedium-library/pdm.lock | 29 ++++++++++++- freedium-library/pyproject.toml | 1 + .../src/freedium_library/utils/http/client.py | 41 +++++++++++++++++-- .../src/freedium_library/utils/http/proxy.py | 0 4 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 freedium-library/src/freedium_library/utils/http/proxy.py diff --git a/freedium-library/pdm.lock b/freedium-library/pdm.lock index bc60f9a..199d459 100644 --- a/freedium-library/pdm.lock +++ b/freedium-library/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:f33cda53180165f833ddded2b624db8f9a24af2e825c9e137db33c5813de495d" +content_hash = "sha256:0a0ad998a9bb20803cebe4b225c0ba40df6c4fd880cda5bfad973483a0d9ef35" [[metadata.targets]] requires_python = "==3.12.*" @@ -183,6 +183,22 @@ files = [ {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, ] +[[package]] +name = "httpx" +version = "0.27.2" +extras = ["socks"] +requires_python = ">=3.8" +summary = "The next generation HTTP client." +groups = ["default"] +dependencies = [ + "httpx==0.27.2", + "socksio==1.*", +] +files = [ + {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, + {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, +] + [[package]] name = "identify" version = "2.6.1" @@ -643,6 +659,17 @@ files = [ {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] +[[package]] +name = "socksio" +version = "1.0.0" +requires_python = ">=3.6" +summary = "Sans-I/O implementation of SOCKS4, SOCKS4A, and SOCKS5." +groups = ["default"] +files = [ + {file = "socksio-1.0.0-py3-none-any.whl", hash = "sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3"}, + {file = "socksio-1.0.0.tar.gz", hash = "sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac"}, +] + [[package]] name = "time-machine" version = "2.16.0" diff --git a/freedium-library/pyproject.toml b/freedium-library/pyproject.toml index 45ea1ce..03dc503 100644 --- a/freedium-library/pyproject.toml +++ b/freedium-library/pyproject.toml @@ -11,6 +11,7 @@ dependencies = [ "orjson>=3.10.11", "pendulum>=3.0.0", "ujson>=5.10.0", + "httpx[socks]>=0.27.2", ] requires-python = "==3.12.*" readme = "README.md" diff --git a/freedium-library/src/freedium_library/utils/http/client.py b/freedium-library/src/freedium_library/utils/http/client.py index 4b039bc..6265910 100644 --- a/freedium-library/src/freedium_library/utils/http/client.py +++ b/freedium-library/src/freedium_library/utils/http/client.py @@ -1,6 +1,6 @@ import warnings from dataclasses import dataclass -from typing import Any, Dict, Optional +from typing import Any, Dict, Literal, Optional from httpx import ( AsyncClient, @@ -12,10 +12,33 @@ from httpx import ( ) +@dataclass +class RequestProxyConfig: + type: Literal["http", "https", "socks5"] + host: str + port: int + username: Optional[str] = None + password: Optional[str] = None + + @property + def url(self) -> str: + type = self.type.replace( + "https", "http" + ) # See: https://www.python-httpx.org/advanced/proxies/ + + proxy_url = f"{type}://" + if self.username and self.password: + proxy_url += f"{self.username}:{self.password}@" + + proxy_url += f"{self.host}:{self.port}" + return proxy_url + + @dataclass class RequestConfig: timeout: int = 6 retries: int = 3 + proxy: Optional[RequestProxyConfig] = None # backoff_factor: float = 0.1 # not possible. Default value: 0.5. https://github.com/encode/httpx/discussions/1895 @@ -30,13 +53,20 @@ class Request: stacklevel=2, ) + @property + def proxy_url(self) -> Optional[str]: + return self.config.proxy.url if self.config.proxy else None + @property def _transport(self) -> HTTPTransport: return HTTPTransport(retries=self.config.retries) @property def _client(self) -> Client: - return Client(transport=self._transport) + return Client( + transport=self._transport, + proxies=self.proxy_url, + ) @property def _async_transport(self) -> AsyncHTTPTransport: @@ -44,7 +74,10 @@ class Request: @property def _async_client(self) -> AsyncClient: - return AsyncClient(transport=self._async_transport) + return AsyncClient( + transport=self._async_transport, + proxies=self.proxy_url, + ) @property def _timeout_client(self) -> Timeout: @@ -67,7 +100,7 @@ class Request: def __del__(self): self._client.close() - # asyncio.run(self._async_client.aclose()) # TODO: doesnt works + # asyncio.run(self._async_client.aclose()) # TODO: doesn't work def _check_context_manager(self): if not self._in_context_manager: diff --git a/freedium-library/src/freedium_library/utils/http/proxy.py b/freedium-library/src/freedium_library/utils/http/proxy.py new file mode 100644 index 0000000..e69de29