ksjsb/core/http_transport.py
2026-07-30 20:25:56 +08:00

143 lines
4.1 KiB
Python

"""HTTP transport sessions used by the login protocol client."""
from __future__ import annotations
from typing import Any, Callable
OKHTTP4_ANDROID10_JA3 = ",".join(
[
"771",
"4865-4866-4867-49195-49196-52393-49199-49200-52392-49171-49172-156-157-47-53",
"0-23-65281-10-11-35-16-5-13-51-45-43-21",
"29-23-24",
"0",
]
)
OKHTTP4_ANDROID10_AKAMAI = "4:16777216|16711681|0|m,p,a,s"
OKHTTP4_ANDROID10_EXTRA_FP = {
"tls_signature_algorithms": [
"ecdsa_secp256r1_sha256",
"rsa_pss_rsae_sha256",
"rsa_pkcs1_sha256",
"ecdsa_secp384r1_sha384",
"rsa_pss_rsae_sha384",
"rsa_pkcs1_sha384",
"rsa_pss_rsae_sha512",
"rsa_pkcs1_sha512",
"rsa_pkcs1_sha1",
]
}
class _CurlCookieAdapter:
"""Accept Playwright cookie attributes not represented by curl_cffi."""
def __init__(self, cookies: Any) -> None:
self._cookies = cookies
def set(
self,
name: str,
value: str,
*,
domain: str = "",
path: str = "/",
secure: bool = False,
expires: int | None = None,
**_: Any,
) -> None:
del expires
self._cookies.set(
name,
value,
domain=domain,
path=path,
secure=secure,
)
def __iter__(self):
return iter(self._cookies)
def __getattr__(self, name: str) -> Any:
return getattr(self._cookies, name)
class OkHttp4Android10Session:
"""Requests-like session with the documented OkHttp 4 Android 10 profile."""
def __init__(self, session: Any) -> None:
self._session = session
self.cookies = _CurlCookieAdapter(session.cookies)
def request(self, method: str, url: str, **kwargs: Any) -> Any:
kwargs.setdefault("http_version", "v2")
kwargs.setdefault("default_headers", False)
# The protocol client supplies its own Accept-Encoding header.
kwargs.setdefault("accept_encoding", None)
headers = kwargs.get("headers")
if headers is not None:
# HTTP/2 represents connection state at the framing layer.
kwargs["headers"] = {
str(name): value
for name, value in headers.items()
if str(name).lower() != "connection"
}
return self._session.request(method, url, **kwargs)
def get(self, url: str, **kwargs: Any) -> Any:
return self.request("GET", url, **kwargs)
def post(self, url: str, **kwargs: Any) -> Any:
return self.request("POST", url, **kwargs)
def close(self) -> None:
self._session.close()
def create_http_session(
transport: str,
*,
curl_session: Any | None = None,
curl_session_factory: Callable[..., Any] | None = None,
) -> Any:
"""Create a shared requests-compatible session for one transport profile."""
if transport == "requests":
import requests
session = requests.Session()
# OkHttp does not add requests' default ``Accept: */*`` header.
headers = getattr(session, "headers", None)
if headers is not None:
headers.pop("Accept", None)
return session
if transport != "okhttp4-android10":
raise ValueError(f"unsupported HTTP transport: {transport}")
if curl_session is None:
if curl_session_factory is None:
try:
from curl_cffi.requests import Session as curl_session_factory
except ImportError as exc:
raise RuntimeError(
"okhttp4-android10 transport requires the curl-cffi dependency"
) from exc
curl_session = curl_session_factory(
ja3=OKHTTP4_ANDROID10_JA3,
akamai=OKHTTP4_ANDROID10_AKAMAI,
extra_fp=OKHTTP4_ANDROID10_EXTRA_FP,
default_headers=False,
)
return OkHttp4Android10Session(curl_session)
__all__ = [
"OKHTTP4_ANDROID10_AKAMAI",
"OKHTTP4_ANDROID10_EXTRA_FP",
"OKHTTP4_ANDROID10_JA3",
"OkHttp4Android10Session",
"create_http_session",
]