diff --git a/curl_cffi/requests/__init__.py b/curl_cffi/requests/__init__.py index da71b19..13fb1f7 100644 --- a/curl_cffi/requests/__init__.py +++ b/curl_cffi/requests/__init__.py @@ -32,7 +32,7 @@ from .cookies import Cookies, CookieTypes from .errors import RequestsError from .headers import Headers, HeaderTypes -from .impersonate import ExtraFingerprints +from .impersonate import ExtraFingerprints, ExtraFpDict from .models import Request, Response from .session import AsyncSession, BrowserType, ProxySpec, Session, ThreadType from .websockets import WebSocket, WebSocketError, WsCloseCode @@ -61,7 +61,7 @@ def request( impersonate: Optional[Union[str, BrowserType]] = None, ja3: Optional[str] = None, akamai: Optional[str] = None, - extra_fp: Optional[ExtraFingerprints] = None, + extra_fp: Optional[Union[ExtraFingerprints, ExtraFpDict]] = None, thread: Optional[ThreadType] = None, default_headers: Optional[bool] = None, default_encoding: Union[str, Callable[[bytes], str]] = "utf-8", diff --git a/curl_cffi/requests/impersonate.py b/curl_cffi/requests/impersonate.py index 8641e41..382a211 100644 --- a/curl_cffi/requests/impersonate.py +++ b/curl_cffi/requests/impersonate.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import List, Literal, Optional +from typing import List, Literal, Optional, TypedDict import warnings from enum import Enum @@ -57,6 +57,16 @@ class ExtraFingerprints: http2_stream_exclusive: int = 1 +class ExtraFpDict(TypedDict, total=False): + tls_min_version: int + tls_grease: bool + tls_permute_extensions: bool + tls_cert_compression: Literal["zlib", "brotli"] + tls_signature_algorithms: Optional[List[str]] + http2_stream_weight: int + http2_stream_exclusive: int + + # TLS version are in the format of 0xAABB, where AA is major version and BB is minor # version. As of today, the major version is always 03. TLS_VERSION_MAP = { diff --git a/curl_cffi/requests/session.py b/curl_cffi/requests/session.py index de0786e..e2597da 100644 --- a/curl_cffi/requests/session.py +++ b/curl_cffi/requests/session.py @@ -30,7 +30,15 @@ from .cookies import Cookies, CookieTypes, CurlMorsel from .errors import RequestsError, SessionClosed from .headers import Headers, HeaderTypes -from .impersonate import ExtraFingerprints, toggle_extension, BrowserType, TLS_VERSION_MAP, TLS_CIPHER_NAME_MAP, TLS_EC_CURVES_MAP +from .impersonate import ( + ExtraFingerprints, + ExtraFpDict, + toggle_extension, + BrowserType, + TLS_VERSION_MAP, + TLS_CIPHER_NAME_MAP, + TLS_EC_CURVES_MAP +) from .models import Request, Response from .websockets import WebSocket @@ -172,7 +180,7 @@ def __init__( impersonate: Optional[Union[str, BrowserType]] = None, ja3: Optional[str] = None, akamai: Optional[str] = None, - extra_fp: Optional[ExtraFingerprints] = None, + extra_fp: Optional[Union[ExtraFingerprints, ExtraFpDict]] = None, default_headers: bool = True, default_encoding: Union[str, Callable[[bytes], str]] = "utf-8", curl_options: Optional[dict] = None, @@ -328,7 +336,7 @@ def _set_curl_options( impersonate: Optional[Union[str, BrowserType]] = None, ja3: Optional[str] = None, akamai: Optional[str] = None, - extra_fp: Optional[ExtraFingerprints] = None, + extra_fp: Optional[Union[ExtraFingerprints, ExtraFpDict]] = None, default_headers: Optional[bool] = None, http_version: Optional[CurlHttpVersion] = None, interface: Optional[str] = None, @@ -576,8 +584,12 @@ def _set_curl_options( if ja3: if impersonate: warnings.warn("JA3 was altered after browser version was set.") - raise RequestsError("Cannot use both impersonate and ja3 string") - self._set_ja3_options(c, ja3, permute=bool(extra_fp and extra_fp.tls_permute_extensions)) + permute = False + if isinstance(extra_fp, ExtraFingerprints) and extra_fp.tls_permute_extensions: + permute = True + if isinstance(extra_fp, dict) and extra_fp.get("tls_permute_extensions"): + permute = True + self._set_ja3_options(c, ja3, permute=permute) # akamai string akamai = akamai or self.akamai @@ -589,6 +601,8 @@ def _set_curl_options( # extra_fp options extra_fp = extra_fp or self.extra_fp if extra_fp: + if isinstance(extra_fp, dict): + extra_fp = ExtraFingerprints(**extra_fp) if impersonate: warnings.warn("Extra fingerprints was altered after browser version was set.") self._set_extra_fp(c, extra_fp) @@ -860,7 +874,7 @@ def request( impersonate: Optional[Union[str, BrowserType]] = None, ja3: Optional[str] = None, akamai: Optional[str] = None, - extra_fp: Optional[ExtraFingerprints] = None, + extra_fp: Optional[Union[ExtraFingerprints, ExtraFpDict]] = None, default_headers: Optional[bool] = None, default_encoding: Union[str, Callable[[bytes], str]] = "utf-8", http_version: Optional[CurlHttpVersion] = None, @@ -1151,7 +1165,7 @@ async def request( impersonate: Optional[Union[str, BrowserType]] = None, ja3: Optional[str] = None, akamai: Optional[str] = None, - extra_fp: Optional[ExtraFingerprints] = None, + extra_fp: Optional[Union[ExtraFingerprints, ExtraFpDict]] = None, default_headers: Optional[bool] = None, default_encoding: Union[str, Callable[[bytes], str]] = "utf-8", http_version: Optional[CurlHttpVersion] = None, diff --git a/examples/impersonate.py b/examples/impersonate.py index 704e903..e349ac8 100644 --- a/examples/impersonate.py +++ b/examples/impersonate.py @@ -1,5 +1,4 @@ from curl_cffi import requests -from curl_cffi.requests.impersonate import ExtraFingerprints # OKHTTP impersonatation examples # credits: https://github.com/bogdanfinn/tls-client/blob/master/profiles/contributed_custom_profiles.go @@ -18,8 +17,8 @@ okhttp4_android10_akamai = "4:16777216|16711681|0|m,p,a,s" -extra_fp = ExtraFingerprints( - tls_signature_algorithms=[ +extra_fp = { + "tls_signature_algorithms": [ "ecdsa_secp256r1_sha256", "rsa_pss_rsae_sha256", "rsa_pkcs1_sha256", @@ -30,7 +29,7 @@ "rsa_pkcs1_sha512", "rsa_pkcs1_sha1", ] -) +} r = requests.get(