.oO SearXNG Developer Documentation Oo.
Loading...
Searching...
No Matches
searx.network.client Namespace Reference

Classes

class  AsyncHTTPTransportNoHttp
class  AsyncProxyTransportFixed

Functions

 shuffle_ciphers (SSLContext ssl_context)
SSLContext get_sslcontexts (str|None proxy_url=None, CertTypes|None cert=None, bool verify=True, bool trust_env=True)
 get_transport_for_socks_proxy (bool verify, bool http2, str local_address, str proxy_url, httpx.Limits limit, int retries)
 get_transport (bool verify, bool http2, str local_address, str|None proxy_url, httpx.Limits limit, int retries)
httpx.AsyncClient new_client (bool enable_http, bool verify, bool enable_http2, int max_connections, int max_keepalive_connections, float keepalive_expiry, dict[str, str] proxies, str local_address, int retries, int max_redirects, t.Callable[..., t.Any]|None hook_log_response)
asyncio.AbstractEventLoop get_loop ()
 init ()

Variables

 CertTypes = str | tuple[str, str] | tuple[str, str, str]
 SslContextKeyType = tuple[str | None, CertTypes | None, bool, bool]
 logger = logger.getChild('searx.network.client')
asyncio LOOP = None
dict SSLCONTEXTS = {}

Function Documentation

◆ get_loop()

asyncio.AbstractEventLoop searx.network.client.get_loop ( )

Definition at line 213 of file client.py.

213def get_loop() -> asyncio.AbstractEventLoop:
214 return LOOP
215
216

Referenced by get_transport_for_socks_proxy().

Here is the caller graph for this function:

◆ get_sslcontexts()

SSLContext searx.network.client.get_sslcontexts ( str | None proxy_url = None,
CertTypes | None cert = None,
bool verify = True,
bool trust_env = True )

Definition at line 55 of file client.py.

57) -> SSLContext:
58 key: SslContextKeyType = (proxy_url, cert, verify, trust_env)
59 if key not in SSLCONTEXTS:
60 SSLCONTEXTS[key] = httpx.create_ssl_context(verify, cert, trust_env)
61 shuffle_ciphers(SSLCONTEXTS[key])
62 return SSLCONTEXTS[key]
63
64

References shuffle_ciphers().

Referenced by get_transport(), and get_transport_for_socks_proxy().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_transport()

searx.network.client.get_transport ( bool verify,
bool http2,
str local_address,
str | None proxy_url,
httpx.Limits limit,
int retries )

Definition at line 149 of file client.py.

151):
152 _verify = get_sslcontexts(None, None, verify, True) if verify is True else verify
153 return httpx.AsyncHTTPTransport(
154 # pylint: disable=protected-access
155 verify=_verify,
156 http2=http2,
157 limits=limit,
158 proxy=httpx._config.Proxy(proxy_url) if proxy_url else None, # pyright: ignore[reportPrivateUsage]
159 local_address=local_address,
160 retries=retries,
161 )
162
163

References get_sslcontexts().

Referenced by new_client().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_transport_for_socks_proxy()

searx.network.client.get_transport_for_socks_proxy ( bool verify,
bool http2,
str local_address,
str proxy_url,
httpx.Limits limit,
int retries )

Definition at line 118 of file client.py.

120):
121 # support socks5h (requests compatibility):
122 # https://requests.readthedocs.io/en/master/user/advanced/#socks
123 # socks5:// hostname is resolved on client side
124 # socks5h:// hostname is resolved on proxy side
125 rdns = False
126 socks5h = 'socks5h://'
127 if proxy_url.startswith(socks5h):
128 proxy_url = 'socks5://' + proxy_url[len(socks5h) :]
129 rdns = True
130
131 proxy_type, proxy_host, proxy_port, proxy_username, proxy_password = parse_proxy_url(proxy_url)
132 _verify = get_sslcontexts(proxy_url, None, verify, True) if verify is True else verify
133 return AsyncProxyTransportFixed(
134 proxy_type=proxy_type,
135 proxy_host=proxy_host,
136 proxy_port=proxy_port,
137 username=proxy_username,
138 password=proxy_password,
139 rdns=rdns,
140 loop=get_loop(),
141 verify=_verify,
142 http2=http2,
143 local_address=local_address,
144 limits=limit,
145 retries=retries,
146 )
147
148

References get_loop(), and get_sslcontexts().

Referenced by new_client().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ init()

searx.network.client.init ( )

Definition at line 217 of file client.py.

217def init():
218 # log
219 for logger_name in (
220 'httpx',
221 'httpcore.proxy',
222 'httpcore.connection',
223 'httpcore.http11',
224 'httpcore.http2',
225 'hpack.hpack',
226 'hpack.table',
227 ):
228 logging.getLogger(logger_name).setLevel(logging.WARNING)
229
230 # loop
231 def loop_thread():
232 global LOOP
233 LOOP = asyncio.new_event_loop()
234 LOOP.run_forever()
235
236 thread = threading.Thread(
237 target=loop_thread,
238 name='asyncio_loop',
239 daemon=True,
240 )
241 thread.start()
242
243

References init().

Referenced by init().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ new_client()

httpx.AsyncClient searx.network.client.new_client ( bool enable_http,
bool verify,
bool enable_http2,
int max_connections,
int max_keepalive_connections,
float keepalive_expiry,
dict[str, str] proxies,
str local_address,
int retries,
int max_redirects,
t.Callable[..., t.Any] | None hook_log_response )

Definition at line 164 of file client.py.

177) -> httpx.AsyncClient:
178 limit = httpx.Limits(
179 max_connections=max_connections,
180 max_keepalive_connections=max_keepalive_connections,
181 keepalive_expiry=keepalive_expiry,
182 )
183 # See https://www.python-httpx.org/advanced/#routing
184 mounts = {}
185 mounts: None | (dict[str, t.Any | None]) = {}
186 for pattern, proxy_url in proxies.items():
187 if not enable_http and pattern.startswith('http://'):
188 continue
189 if proxy_url.startswith('socks4://') or proxy_url.startswith('socks5://') or proxy_url.startswith('socks5h://'):
190 mounts[pattern] = get_transport_for_socks_proxy(
191 verify, enable_http2, local_address, proxy_url, limit, retries
192 )
193 else:
194 mounts[pattern] = get_transport(verify, enable_http2, local_address, proxy_url, limit, retries)
195
196 if not enable_http:
197 mounts['http://'] = AsyncHTTPTransportNoHttp()
198
199 transport = get_transport(verify, enable_http2, local_address, None, limit, retries)
200
201 event_hooks = None
202 if hook_log_response:
203 event_hooks = {'response': [hook_log_response]}
204
205 return httpx.AsyncClient(
206 transport=transport,
207 mounts=mounts,
208 max_redirects=max_redirects,
209 event_hooks=event_hooks,
210 )
211
212

References get_transport(), and get_transport_for_socks_proxy().

Here is the call graph for this function:

◆ shuffle_ciphers()

searx.network.client.shuffle_ciphers ( SSLContext ssl_context)
Shuffle httpx's default ciphers of a SSL context randomly.

From `What Is TLS Fingerprint and How to Bypass It`_

> When implementing TLS fingerprinting, servers can't operate based on a
> locked-in whitelist database of fingerprints.  New fingerprints appear
> when web clients or TLS libraries release new versions. So, they have to
> live off a blocklist database instead.
> ...
> It's safe to leave the first three as is but shuffle the remaining ciphers
> and you can bypass the TLS fingerprint check.

.. _What Is TLS Fingerprint and How to Bypass It:
   https://www.zenrows.com/blog/what-is-tls-fingerprint#how-to-bypass-tls-fingerprinting

Definition at line 32 of file client.py.

32def shuffle_ciphers(ssl_context: SSLContext):
33 """Shuffle httpx's default ciphers of a SSL context randomly.
34
35 From `What Is TLS Fingerprint and How to Bypass It`_
36
37 > When implementing TLS fingerprinting, servers can't operate based on a
38 > locked-in whitelist database of fingerprints. New fingerprints appear
39 > when web clients or TLS libraries release new versions. So, they have to
40 > live off a blocklist database instead.
41 > ...
42 > It's safe to leave the first three as is but shuffle the remaining ciphers
43 > and you can bypass the TLS fingerprint check.
44
45 .. _What Is TLS Fingerprint and How to Bypass It:
46 https://www.zenrows.com/blog/what-is-tls-fingerprint#how-to-bypass-tls-fingerprinting
47
48 """
49 c_list = [cipher["name"] for cipher in ssl_context.get_ciphers()]
50 sc_list, c_list = c_list[:3], c_list[3:]
51 random.shuffle(c_list)
52 ssl_context.set_ciphers(":".join(sc_list + c_list))
53
54

Referenced by get_sslcontexts().

Here is the caller graph for this function:

Variable Documentation

◆ CertTypes

searx.network.client.CertTypes = str | tuple[str, str] | tuple[str, str, str]

Definition at line 23 of file client.py.

◆ logger

searx.network.client.logger = logger.getChild('searx.network.client')

Definition at line 26 of file client.py.

◆ LOOP

asyncio searx.network.client.LOOP = None

Definition at line 27 of file client.py.

◆ SslContextKeyType

searx.network.client.SslContextKeyType = tuple[str | None, CertTypes | None, bool, bool]

Definition at line 24 of file client.py.

◆ SSLCONTEXTS

dict searx.network.client.SSLCONTEXTS = {}

Definition at line 29 of file client.py.