"""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", ]