mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-03-12 17:39:01 +00:00
feat(session): add custom fingerprint and preset support
Add support for custom TLS/HTTP fingerprints to session() function, enabling services to impersonate Android/OkHttp clients instead of just browsers.
This commit is contained in:
@@ -21,6 +21,20 @@ warnings.filterwarnings(
|
|||||||
"ignore", message="Make sure you are using https over https proxy.*", category=RuntimeWarning, module="curl_cffi.*"
|
"ignore", message="Make sure you are using https over https proxy.*", category=RuntimeWarning, module="curl_cffi.*"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
FINGERPRINT_PRESETS = {
|
||||||
|
"okhttp4": {
|
||||||
|
"ja3": (
|
||||||
|
"771," # TLS 1.2
|
||||||
|
"4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53," # Ciphers
|
||||||
|
"0-23-65281-10-11-35-16-5-13-18-51-45-43-27-21," # Extensions
|
||||||
|
"29-23-24," # Named groups (x25519, secp256r1, secp384r1)
|
||||||
|
"0" # EC point formats
|
||||||
|
),
|
||||||
|
"akamai": "1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p",
|
||||||
|
"description": "OkHttp 4.x on Android (BoringSSL TLS stack)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class MaxRetriesError(exceptions.RequestException):
|
class MaxRetriesError(exceptions.RequestException):
|
||||||
def __init__(self, message, cause=None):
|
def __init__(self, message, cause=None):
|
||||||
@@ -107,18 +121,34 @@ class CurlSession(Session):
|
|||||||
raise MaxRetriesError(f"Max retries exceeded for {method} {url}", cause=last_exception)
|
raise MaxRetriesError(f"Max retries exceeded for {method} {url}", cause=last_exception)
|
||||||
|
|
||||||
|
|
||||||
def session(browser: str | None = None, **kwargs) -> CurlSession:
|
def session(
|
||||||
|
browser: str | None = None,
|
||||||
|
ja3: str | None = None,
|
||||||
|
akamai: str | None = None,
|
||||||
|
extra_fp: dict | None = None,
|
||||||
|
**kwargs,
|
||||||
|
) -> CurlSession:
|
||||||
"""
|
"""
|
||||||
Create a curl_cffi session that impersonates a browser.
|
Create a curl_cffi session that impersonates a browser or custom TLS/HTTP fingerprint.
|
||||||
|
|
||||||
This is a full replacement for requests.Session with browser impersonation
|
This is a full replacement for requests.Session with browser impersonation
|
||||||
and anti-bot capabilities. The session uses curl-impersonate under the hood
|
and anti-bot capabilities. The session uses curl-impersonate under the hood
|
||||||
to mimic real browser behavior.
|
to mimic real browser behavior.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
browser: Browser to impersonate (e.g. "chrome124", "firefox", "safari").
|
browser: Browser to impersonate (e.g. "chrome124", "firefox", "safari") OR
|
||||||
|
fingerprint preset name (e.g. "okhttp4").
|
||||||
Uses the configured default from curl_impersonate.browser if not specified.
|
Uses the configured default from curl_impersonate.browser if not specified.
|
||||||
See https://github.com/lexiforest/curl_cffi#sessions for available options.
|
Available presets: okhttp4
|
||||||
|
See https://github.com/lexiforest/curl_cffi#sessions for browser options.
|
||||||
|
ja3: Custom JA3 TLS fingerprint string (format: "SSLVersion,Ciphers,Extensions,Curves,PointFormats").
|
||||||
|
When provided, curl_cffi will use this exact TLS fingerprint instead of the browser's default.
|
||||||
|
See https://curl-cffi.readthedocs.io/en/latest/impersonate/customize.html
|
||||||
|
akamai: Custom Akamai HTTP/2 fingerprint string (format: "SETTINGS|WINDOW_UPDATE|PRIORITY|PSEUDO_HEADERS").
|
||||||
|
When provided, curl_cffi will use this exact HTTP/2 fingerprint instead of the browser's default.
|
||||||
|
See https://curl-cffi.readthedocs.io/en/latest/impersonate/customize.html
|
||||||
|
extra_fp: Additional fingerprint parameters dict for advanced customization.
|
||||||
|
See https://curl-cffi.readthedocs.io/en/latest/impersonate/customize.html
|
||||||
**kwargs: Additional arguments passed to CurlSession constructor:
|
**kwargs: Additional arguments passed to CurlSession constructor:
|
||||||
- headers: Additional headers (dict)
|
- headers: Additional headers (dict)
|
||||||
- cookies: Cookie jar or dict
|
- cookies: Cookie jar or dict
|
||||||
@@ -129,8 +159,6 @@ def session(browser: str | None = None, **kwargs) -> CurlSession:
|
|||||||
- allow_redirects: Follow redirects (bool, default True)
|
- allow_redirects: Follow redirects (bool, default True)
|
||||||
- max_redirects: Maximum redirect count (int)
|
- max_redirects: Maximum redirect count (int)
|
||||||
- cert: Client certificate (str or tuple)
|
- cert: Client certificate (str or tuple)
|
||||||
- ja3: JA3 fingerprint (str)
|
|
||||||
- akamai: Akamai fingerprint (str)
|
|
||||||
|
|
||||||
Extra arguments for retry handler:
|
Extra arguments for retry handler:
|
||||||
- max_retries: Maximum number of retries (int, default 10)
|
- max_retries: Maximum number of retries (int, default 10)
|
||||||
@@ -141,30 +169,70 @@ def session(browser: str | None = None, **kwargs) -> CurlSession:
|
|||||||
- catch_exceptions: List of exceptions to catch (tuple, default (exceptions.ConnectionError, exceptions.ProxyError, exceptions.SSLError, exceptions.Timeout))
|
- catch_exceptions: List of exceptions to catch (tuple, default (exceptions.ConnectionError, exceptions.ProxyError, exceptions.SSLError, exceptions.Timeout))
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
curl_cffi.requests.Session configured with browser impersonation, common headers,
|
curl_cffi.requests.Session configured with browser impersonation or custom fingerprints,
|
||||||
and equivalent retry behavior to requests.Session.
|
common headers, and equivalent retry behavior to requests.Session.
|
||||||
|
|
||||||
Example:
|
Examples:
|
||||||
from unshackle.core.session import session as CurlSession
|
# Standard browser impersonation
|
||||||
|
from unshackle.core.session import session
|
||||||
|
|
||||||
class MyService(Service):
|
class MyService(Service):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_session() -> CurlSession:
|
def get_session():
|
||||||
session = CurlSession(
|
return session() # Uses config default browser
|
||||||
impersonate="chrome",
|
|
||||||
ja3="...",
|
# Use OkHttp 4.x preset for Android TV
|
||||||
akamai="...",
|
class AndroidService(Service):
|
||||||
|
@staticmethod
|
||||||
|
def get_session():
|
||||||
|
return session("okhttp4")
|
||||||
|
|
||||||
|
# Custom fingerprint (manual)
|
||||||
|
class CustomService(Service):
|
||||||
|
@staticmethod
|
||||||
|
def get_session():
|
||||||
|
return session(
|
||||||
|
ja3="771,4865-4866-4867-49195...",
|
||||||
|
akamai="1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p",
|
||||||
|
)
|
||||||
|
|
||||||
|
# With retry configuration
|
||||||
|
class MyService(Service):
|
||||||
|
@staticmethod
|
||||||
|
def get_session():
|
||||||
|
return session(
|
||||||
|
"okhttp4",
|
||||||
max_retries=5,
|
max_retries=5,
|
||||||
status_forcelist=[429, 500],
|
status_forcelist=[429, 500],
|
||||||
allowed_methods={"GET", "HEAD", "OPTIONS"},
|
allowed_methods={"GET", "HEAD", "OPTIONS"},
|
||||||
)
|
)
|
||||||
return session # Uses config default browser
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
session_config = {
|
if browser and browser in FINGERPRINT_PRESETS:
|
||||||
"impersonate": browser or config.curl_impersonate.get("browser", "chrome"),
|
preset = FINGERPRINT_PRESETS[browser]
|
||||||
**kwargs,
|
if ja3 is None:
|
||||||
}
|
ja3 = preset.get("ja3")
|
||||||
|
if akamai is None:
|
||||||
|
akamai = preset.get("akamai")
|
||||||
|
if extra_fp is None:
|
||||||
|
extra_fp = preset.get("extra_fp")
|
||||||
|
browser = None
|
||||||
|
|
||||||
|
if browser is None and ja3 is None and akamai is None:
|
||||||
|
browser = config.curl_impersonate.get("browser", "chrome")
|
||||||
|
|
||||||
|
session_config = {}
|
||||||
|
if browser:
|
||||||
|
session_config["impersonate"] = browser
|
||||||
|
|
||||||
|
if ja3:
|
||||||
|
session_config["ja3"] = ja3
|
||||||
|
if akamai:
|
||||||
|
session_config["akamai"] = akamai
|
||||||
|
if extra_fp:
|
||||||
|
session_config["extra_fp"] = extra_fp
|
||||||
|
|
||||||
|
session_config.update(kwargs)
|
||||||
|
|
||||||
session_obj = CurlSession(**session_config)
|
session_obj = CurlSession(**session_config)
|
||||||
session_obj.headers.update(config.headers)
|
session_obj.headers.update(config.headers)
|
||||||
|
|||||||
Reference in New Issue
Block a user