summaryrefslogtreecommitdiffstats
path: root/venv/lib/python3.9/site-packages/tls_client
diff options
context:
space:
mode:
authornoptuno <repollo.marrero@gmail.com>2023-04-28 02:29:30 +0200
committernoptuno <repollo.marrero@gmail.com>2023-04-28 02:29:30 +0200
commit355dee533bb34a571b9367820a63cccb668cf866 (patch)
tree838af886b4fec07320aeb10f0d1e74ba79e79b5c /venv/lib/python3.9/site-packages/tls_client
parentadded pyproject.toml file (diff)
downloadgpt4free-355dee533bb34a571b9367820a63cccb668cf866.tar
gpt4free-355dee533bb34a571b9367820a63cccb668cf866.tar.gz
gpt4free-355dee533bb34a571b9367820a63cccb668cf866.tar.bz2
gpt4free-355dee533bb34a571b9367820a63cccb668cf866.tar.lz
gpt4free-355dee533bb34a571b9367820a63cccb668cf866.tar.xz
gpt4free-355dee533bb34a571b9367820a63cccb668cf866.tar.zst
gpt4free-355dee533bb34a571b9367820a63cccb668cf866.zip
Diffstat (limited to 'venv/lib/python3.9/site-packages/tls_client')
-rw-r--r--venv/lib/python3.9/site-packages/tls_client/__init__.py15
-rw-r--r--venv/lib/python3.9/site-packages/tls_client/__version__.py11
-rw-r--r--venv/lib/python3.9/site-packages/tls_client/cffi.py29
-rw-r--r--venv/lib/python3.9/site-packages/tls_client/cookies.py463
-rw-r--r--venv/lib/python3.9/site-packages/tls_client/dependencies/__init__.py0
-rw-r--r--venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-32.dllbin0 -> 13783646 bytes
-rw-r--r--venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-64.dllbin0 -> 15477617 bytes
-rw-r--r--venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-amd64.sobin0 -> 10091584 bytes
-rw-r--r--venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-arm64.dylibbin0 -> 9358546 bytes
-rw-r--r--venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-arm64.sobin0 -> 9628680 bytes
-rw-r--r--venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-x86.dylibbin0 -> 9824464 bytes
-rw-r--r--venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-x86.sobin0 -> 10096784 bytes
-rw-r--r--venv/lib/python3.9/site-packages/tls_client/exceptions.py3
-rw-r--r--venv/lib/python3.9/site-packages/tls_client/response.py79
-rw-r--r--venv/lib/python3.9/site-packages/tls_client/sessions.py457
-rw-r--r--venv/lib/python3.9/site-packages/tls_client/structures.py74
16 files changed, 1131 insertions, 0 deletions
diff --git a/venv/lib/python3.9/site-packages/tls_client/__init__.py b/venv/lib/python3.9/site-packages/tls_client/__init__.py
new file mode 100644
index 00000000..798dd5eb
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/tls_client/__init__.py
@@ -0,0 +1,15 @@
+# _____ __ __ ___ _ _ _
+# /__ \/ / / _\ / __\ (_) ___ _ __ | |_
+# / /\/ / \ \ _____ / / | | |/ _ \ '_ \| __|
+# / / / /____\ \_____/ /___| | | __/ | | | |_
+# \/ \____/\__/ \____/|_|_|\___|_| |_|\__|
+
+# Disclaimer:
+# Big shout out to Bogdanfinn for open sourcing his tls-client in Golang.
+# Also to requests, as most of the cookie handling is copied from it. :'D
+# I wanted to keep the syntax as similar as possible to requests, as most people use it and are familiar with it!
+# Links:
+# tls-client: https://github.com/bogdanfinn/tls-client
+# requests: https://github.com/psf/requests
+
+from .sessions import Session \ No newline at end of file
diff --git a/venv/lib/python3.9/site-packages/tls_client/__version__.py b/venv/lib/python3.9/site-packages/tls_client/__version__.py
new file mode 100644
index 00000000..8192d66c
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/tls_client/__version__.py
@@ -0,0 +1,11 @@
+# _____ __ __ ___ _ _ _
+# /__ \/ / / _\ / __\ (_) ___ _ __ | |_
+# / /\/ / \ \ _____ / / | | |/ _ \ '_ \| __|
+# / / / /____\ \_____/ /___| | | __/ | | | |_
+# \/ \____/\__/ \____/|_|_|\___|_| |_|\__|
+
+__title__ = "tls_client"
+__description__ = "Advanced Python HTTP Client."
+__version__ = "0.2"
+__author__ = "Florian Zager"
+__license__ = "MIT" \ No newline at end of file
diff --git a/venv/lib/python3.9/site-packages/tls_client/cffi.py b/venv/lib/python3.9/site-packages/tls_client/cffi.py
new file mode 100644
index 00000000..73efc73a
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/tls_client/cffi.py
@@ -0,0 +1,29 @@
+from sys import platform
+from platform import machine
+import ctypes
+import os
+
+
+if platform == 'darwin':
+ file_ext = '-arm64.dylib' if machine() == "arm64" else '-x86.dylib'
+elif platform in ('win32', 'cygwin'):
+ file_ext = '-64.dll' if 8 == ctypes.sizeof(ctypes.c_voidp) else '-32.dll'
+else:
+ if machine() == "aarch64":
+ file_ext = '-arm64.so'
+ elif "x86" in machine():
+ file_ext = '-x86.so'
+ else:
+ file_ext = '-amd64.so'
+
+root_dir = os.path.abspath(os.path.dirname(__file__))
+library = ctypes.cdll.LoadLibrary(f'{root_dir}/dependencies/tls-client{file_ext}')
+
+# extract the exposed request function from the shared package
+request = library.request
+request.argtypes = [ctypes.c_char_p]
+request.restype = ctypes.c_char_p
+
+freeMemory = library.freeMemory
+freeMemory.argtypes = [ctypes.c_char_p]
+freeMemory.restype = ctypes.c_char_p \ No newline at end of file
diff --git a/venv/lib/python3.9/site-packages/tls_client/cookies.py b/venv/lib/python3.9/site-packages/tls_client/cookies.py
new file mode 100644
index 00000000..5b48fed3
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/tls_client/cookies.py
@@ -0,0 +1,463 @@
+from .structures import CaseInsensitiveDict
+
+from http.cookiejar import CookieJar, Cookie
+from typing import MutableMapping, Union, Any
+from urllib.parse import urlparse, urlunparse
+from http.client import HTTPMessage
+import copy
+
+try:
+ import threading
+except ImportError:
+ import dummy_threading as threading
+
+
+class MockRequest:
+ """
+ Mimic a urllib2.Request to get the correct cookie string for the request.
+ """
+
+ def __init__(self, request_url: str, request_headers: CaseInsensitiveDict):
+ self.request_url = request_url
+ self.request_headers = request_headers
+ self._new_headers = {}
+ self.type = urlparse(self.request_url).scheme
+
+ def get_type(self):
+ return self.type
+
+ def get_host(self):
+ return urlparse(self.request_url).netloc
+
+ def get_origin_req_host(self):
+ return self.get_host()
+
+ def get_full_url(self):
+ # Only return the response's URL if the user hadn't set the Host
+ # header
+ if not self.request_headers.get("Host"):
+ return self.request_url
+ # If they did set it, retrieve it and reconstruct the expected domain
+ host = self.request_headers["Host"]
+ parsed = urlparse(self.request_url)
+ # Reconstruct the URL as we expect it
+ return urlunparse(
+ [
+ parsed.scheme,
+ host,
+ parsed.path,
+ parsed.params,
+ parsed.query,
+ parsed.fragment,
+ ]
+ )
+
+ def is_unverifiable(self):
+ return True
+
+ def has_header(self, name):
+ return name in self.request_headers or name in self._new_headers
+
+ def get_header(self, name, default=None):
+ return self.request_headers.get(name, self._new_headers.get(name, default))
+
+ def add_unredirected_header(self, name, value):
+ self._new_headers[name] = value
+
+ def get_new_headers(self):
+ return self._new_headers
+
+ @property
+ def unverifiable(self):
+ return self.is_unverifiable()
+
+ @property
+ def origin_req_host(self):
+ return self.get_origin_req_host()
+
+ @property
+ def host(self):
+ return self.get_host()
+
+
+class MockResponse:
+ """
+ Wraps a httplib.HTTPMessage to mimic a urllib.addinfourl.
+ The objective is to retrieve the response cookies correctly.
+ """
+
+ def __init__(self, headers):
+ self._headers = headers
+
+ def info(self):
+ return self._headers
+
+ def getheaders(self, name):
+ self._headers.getheaders(name)
+
+
+class CookieConflictError(RuntimeError):
+ """There are two cookies that meet the criteria specified in the cookie jar.
+ Use .get and .set and include domain and path args in order to be more specific.
+ """
+
+
+class RequestsCookieJar(CookieJar, MutableMapping):
+ """ Origin: requests library (https://github.com/psf/requests)
+ Compatibility class; is a cookielib.CookieJar, but exposes a dict
+ interface.
+
+ This is the CookieJar we create by default for requests and sessions that
+ don't specify one, since some clients may expect response.cookies and
+ session.cookies to support dict operations.
+
+ Requests does not use the dict interface internally; it's just for
+ compatibility with external client code. All requests code should work
+ out of the box with externally provided instances of ``CookieJar``, e.g.
+ ``LWPCookieJar`` and ``FileCookieJar``.
+
+ Unlike a regular CookieJar, this class is pickleable.
+
+ .. warning:: dictionary operations that are normally O(1) may be O(n).
+ """
+
+ def get(self, name, default=None, domain=None, path=None):
+ """Dict-like get() that also supports optional domain and path args in
+ order to resolve naming collisions from using one cookie jar over
+ multiple domains.
+
+ .. warning:: operation is O(n), not O(1).
+ """
+ try:
+ return self._find_no_duplicates(name, domain, path)
+ except KeyError:
+ return default
+
+ def set(self, name, value, **kwargs):
+ """Dict-like set() that also supports optional domain and path args in
+ order to resolve naming collisions from using one cookie jar over
+ multiple domains.
+ """
+ # support client code that unsets cookies by assignment of a None value:
+ if value is None:
+ remove_cookie_by_name(
+ self, name, domain=kwargs.get("domain"), path=kwargs.get("path")
+ )
+ return
+
+ c = create_cookie(name, value, **kwargs)
+ self.set_cookie(c)
+ return c
+
+ def iterkeys(self):
+ """Dict-like iterkeys() that returns an iterator of names of cookies
+ from the jar.
+
+ .. seealso:: itervalues() and iteritems().
+ """
+ for cookie in iter(self):
+ yield cookie.name
+
+ def keys(self):
+ """Dict-like keys() that returns a list of names of cookies from the
+ jar.
+
+ .. seealso:: values() and items().
+ """
+ return list(self.iterkeys())
+
+ def itervalues(self):
+ """Dict-like itervalues() that returns an iterator of values of cookies
+ from the jar.
+
+ .. seealso:: iterkeys() and iteritems().
+ """
+ for cookie in iter(self):
+ yield cookie.value
+
+ def values(self):
+ """Dict-like values() that returns a list of values of cookies from the
+ jar.
+
+ .. seealso:: keys() and items().
+ """
+ return list(self.itervalues())
+
+ def iteritems(self):
+ """Dict-like iteritems() that returns an iterator of name-value tuples
+ from the jar.
+
+ .. seealso:: iterkeys() and itervalues().
+ """
+ for cookie in iter(self):
+ yield cookie.name, cookie.value
+
+ def items(self):
+ """Dict-like items() that returns a list of name-value tuples from the
+ jar. Allows client-code to call ``dict(RequestsCookieJar)`` and get a
+ vanilla python dict of key value pairs.
+
+ .. seealso:: keys() and values().
+ """
+ return list(self.iteritems())
+
+ def list_domains(self):
+ """Utility method to list all the domains in the jar."""
+ domains = []
+ for cookie in iter(self):
+ if cookie.domain not in domains:
+ domains.append(cookie.domain)
+ return domains
+
+ def list_paths(self):
+ """Utility method to list all the paths in the jar."""
+ paths = []
+ for cookie in iter(self):
+ if cookie.path not in paths:
+ paths.append(cookie.path)
+ return paths
+
+ def multiple_domains(self):
+ """Returns True if there are multiple domains in the jar.
+ Returns False otherwise.
+
+ :rtype: bool
+ """
+ domains = []
+ for cookie in iter(self):
+ if cookie.domain is not None and cookie.domain in domains:
+ return True
+ domains.append(cookie.domain)
+ return False # there is only one domain in jar
+
+ def get_dict(self, domain=None, path=None):
+ """Takes as an argument an optional domain and path and returns a plain
+ old Python dict of name-value pairs of cookies that meet the
+ requirements.
+
+ :rtype: dict
+ """
+ dictionary = {}
+ for cookie in iter(self):
+ if (domain is None or cookie.domain == domain) and (
+ path is None or cookie.path == path
+ ):
+ dictionary[cookie.name] = cookie.value
+ return dictionary
+
+ def __contains__(self, name):
+ try:
+ return super().__contains__(name)
+ except CookieConflictError:
+ return True
+
+ def __getitem__(self, name):
+ """Dict-like __getitem__() for compatibility with client code. Throws
+ exception if there are more than one cookie with name. In that case,
+ use the more explicit get() method instead.
+
+ .. warning:: operation is O(n), not O(1).
+ """
+ return self._find_no_duplicates(name)
+
+ def __setitem__(self, name, value):
+ """Dict-like __setitem__ for compatibility with client code. Throws
+ exception if there is already a cookie of that name in the jar. In that
+ case, use the more explicit set() method instead.
+ """
+ self.set(name, value)
+
+ def __delitem__(self, name):
+ """Deletes a cookie given a name. Wraps ``cookielib.CookieJar``'s
+ ``remove_cookie_by_name()``.
+ """
+ remove_cookie_by_name(self, name)
+
+ def set_cookie(self, cookie, *args, **kwargs):
+ if (
+ hasattr(cookie.value, "startswith")
+ and cookie.value.startswith('"')
+ and cookie.value.endswith('"')
+ ):
+ cookie.value = cookie.value.replace('\\"', "")
+ return super().set_cookie(cookie, *args, **kwargs)
+
+ def update(self, other):
+ """Updates this jar with cookies from another CookieJar or dict-like"""
+ if isinstance(other, CookieJar):
+ for cookie in other:
+ self.set_cookie(copy.copy(cookie))
+ else:
+ super().update(other)
+
+ def _find(self, name, domain=None, path=None):
+ """Requests uses this method internally to get cookie values.
+
+ If there are conflicting cookies, _find arbitrarily chooses one.
+ See _find_no_duplicates if you want an exception thrown if there are
+ conflicting cookies.
+
+ :param name: a string containing name of cookie
+ :param domain: (optional) string containing domain of cookie
+ :param path: (optional) string containing path of cookie
+ :return: cookie.value
+ """
+ for cookie in iter(self):
+ if cookie.name == name:
+ if domain is None or cookie.domain == domain:
+ if path is None or cookie.path == path:
+ return cookie.value
+
+ raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}")
+
+ def _find_no_duplicates(self, name, domain=None, path=None):
+ """Both ``__get_item__`` and ``get`` call this function: it's never
+ used elsewhere in Requests.
+
+ :param name: a string containing name of cookie
+ :param domain: (optional) string containing domain of cookie
+ :param path: (optional) string containing path of cookie
+ :raises KeyError: if cookie is not found
+ :raises CookieConflictError: if there are multiple cookies
+ that match name and optionally domain and path
+ :return: cookie.value
+ """
+ toReturn = None
+ for cookie in iter(self):
+ if cookie.name == name:
+ if domain is None or cookie.domain == domain:
+ if path is None or cookie.path == path:
+ if toReturn is not None:
+ # if there are multiple cookies that meet passed in criteria
+ raise CookieConflictError(
+ f"There are multiple cookies with name, {name!r}"
+ )
+ # we will eventually return this as long as no cookie conflict
+ toReturn = cookie.value
+
+ if toReturn:
+ return toReturn
+ raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}")
+
+ def __getstate__(self):
+ """Unlike a normal CookieJar, this class is pickleable."""
+ state = self.__dict__.copy()
+ # remove the unpickleable RLock object
+ state.pop("_cookies_lock")
+ return state
+
+ def __setstate__(self, state):
+ """Unlike a normal CookieJar, this class is pickleable."""
+ self.__dict__.update(state)
+ if "_cookies_lock" not in self.__dict__:
+ self._cookies_lock = threading.RLock()
+
+ def copy(self):
+ """Return a copy of this RequestsCookieJar."""
+ new_cj = RequestsCookieJar()
+ new_cj.set_policy(self.get_policy())
+ new_cj.update(self)
+ return new_cj
+
+ def get_policy(self):
+ """Return the CookiePolicy instance used."""
+ return self._policy
+
+
+def remove_cookie_by_name(cookiejar: RequestsCookieJar, name: str, domain: str = None, path: str = None):
+ """Removes a cookie by name, by default over all domains and paths."""
+ clearables = []
+ for cookie in cookiejar:
+ if cookie.name != name:
+ continue
+ if domain is not None and domain != cookie.domain:
+ continue
+ if path is not None and path != cookie.path:
+ continue
+ clearables.append((cookie.domain, cookie.path, cookie.name))
+
+ for domain, path, name in clearables:
+ cookiejar.clear(domain, path, name)
+
+
+def create_cookie(name: str, value: str, **kwargs: Any) -> Cookie:
+ """Make a cookie from underspecified parameters."""
+ result = {
+ "version": 0,
+ "name": name,
+ "value": value,
+ "port": None,
+ "domain": "",
+ "path": "/",
+ "secure": False,
+ "expires": None,
+ "discard": True,
+ "comment": None,
+ "comment_url": None,
+ "rest": {"HttpOnly": None},
+ "rfc2109": False,
+ }
+
+ badargs = set(kwargs) - set(result)
+ if badargs:
+ raise TypeError(
+ f"create_cookie() got unexpected keyword arguments: {list(badargs)}"
+ )
+
+ result.update(kwargs)
+ result["port_specified"] = bool(result["port"])
+ result["domain_specified"] = bool(result["domain"])
+ result["domain_initial_dot"] = result["domain"].startswith(".")
+ result["path_specified"] = bool(result["path"])
+
+ return Cookie(**result)
+
+
+def cookiejar_from_dict(cookie_dict: dict) -> RequestsCookieJar:
+ """transform a dict to CookieJar"""
+ cookie_jar = RequestsCookieJar()
+ if cookie_dict is not None:
+ for name, value in cookie_dict.items():
+ cookie_jar.set_cookie(create_cookie(name=name, value=value))
+ return cookie_jar
+
+
+def merge_cookies(cookiejar: RequestsCookieJar, cookies: Union[dict, RequestsCookieJar]) -> RequestsCookieJar:
+ """Merge cookies in session and cookies provided in request"""
+ if type(cookies) is dict:
+ cookies = cookiejar_from_dict(cookies)
+
+ for cookie in cookies:
+ cookiejar.set_cookie(cookie)
+
+ return cookiejar
+
+
+def get_cookie_header(request_url: str, request_headers: CaseInsensitiveDict, cookie_jar: RequestsCookieJar) -> str:
+ r = MockRequest(request_url, request_headers)
+ cookie_jar.add_cookie_header(r)
+ return r.get_new_headers().get("Cookie")
+
+
+def extract_cookies_to_jar(
+ request_url: str,
+ request_headers: CaseInsensitiveDict,
+ cookie_jar: RequestsCookieJar,
+ response_headers: dict
+ ) -> RequestsCookieJar:
+ response_cookie_jar = cookiejar_from_dict({})
+
+ req = MockRequest(request_url, request_headers)
+ # mimic HTTPMessage
+ http_message = HTTPMessage()
+ http_message._headers = []
+ for header_name, header_values in response_headers.items():
+ for header_value in header_values:
+ http_message._headers.append(
+ (header_name, header_value)
+ )
+ res = MockResponse(http_message)
+ response_cookie_jar.extract_cookies(res, req)
+
+ merge_cookies(cookie_jar, response_cookie_jar)
+ return response_cookie_jar
diff --git a/venv/lib/python3.9/site-packages/tls_client/dependencies/__init__.py b/venv/lib/python3.9/site-packages/tls_client/dependencies/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/tls_client/dependencies/__init__.py
diff --git a/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-32.dll b/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-32.dll
new file mode 100644
index 00000000..45bc8e6d
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-32.dll
Binary files differ
diff --git a/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-64.dll b/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-64.dll
new file mode 100644
index 00000000..29c9523b
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-64.dll
Binary files differ
diff --git a/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-amd64.so b/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-amd64.so
new file mode 100644
index 00000000..c6e5967d
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-amd64.so
Binary files differ
diff --git a/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-arm64.dylib b/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-arm64.dylib
new file mode 100644
index 00000000..468b2269
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-arm64.dylib
Binary files differ
diff --git a/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-arm64.so b/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-arm64.so
new file mode 100644
index 00000000..b428ac11
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-arm64.so
Binary files differ
diff --git a/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-x86.dylib b/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-x86.dylib
new file mode 100644
index 00000000..89df9419
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-x86.dylib
Binary files differ
diff --git a/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-x86.so b/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-x86.so
new file mode 100644
index 00000000..452bdb8f
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/tls_client/dependencies/tls-client-x86.so
Binary files differ
diff --git a/venv/lib/python3.9/site-packages/tls_client/exceptions.py b/venv/lib/python3.9/site-packages/tls_client/exceptions.py
new file mode 100644
index 00000000..bbe2f11f
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/tls_client/exceptions.py
@@ -0,0 +1,3 @@
+
+class TLSClientExeption(IOError):
+ """General error with the TLS client""" \ No newline at end of file
diff --git a/venv/lib/python3.9/site-packages/tls_client/response.py b/venv/lib/python3.9/site-packages/tls_client/response.py
new file mode 100644
index 00000000..a7bdf609
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/tls_client/response.py
@@ -0,0 +1,79 @@
+from .cookies import cookiejar_from_dict, RequestsCookieJar
+from .structures import CaseInsensitiveDict
+
+from http.cookiejar import CookieJar
+from typing import Union
+import json
+
+
+class Response:
+ """object, which contains the response to an HTTP request."""
+
+ def __init__(self):
+
+ # Reference of URL the response is coming from (especially useful with redirects)
+ self.url = None
+
+ # Integer Code of responded HTTP Status, e.g. 404 or 200.
+ self.status_code = None
+
+ # String of responded HTTP Body.
+ self.text = None
+
+ # Case-insensitive Dictionary of Response Headers.
+ self.headers = CaseInsensitiveDict()
+
+ # A CookieJar of Cookies the server sent back.
+ self.cookies = cookiejar_from_dict({})
+
+ self._content = False
+
+ def __enter__(self):
+ return self
+
+ def __repr__(self):
+ return f"<Response [{self.status_code}]>"
+
+ def json(self, **kwargs):
+ """parse response body to json (dict/list)"""
+ return json.loads(self.text, **kwargs)
+
+ @property
+ def content(self):
+ """Content of the response, in bytes."""
+
+ if self._content is False:
+ if self._content_consumed:
+ raise RuntimeError("The content for this response was already consumed")
+
+ if self.status_code == 0:
+ self._content = None
+ else:
+ self._content = b"".join(self.iter_content(10 * 1024)) or b""
+ self._content_consumed = True
+ return self._content
+
+
+def build_response(res: Union[dict, list], res_cookies: RequestsCookieJar) -> Response:
+ """Builds a Response object """
+ response = Response()
+ # Add target / url
+ response.url = res["target"]
+ # Add status code
+ response.status_code = res["status"]
+ # Add headers
+ response_headers = {}
+ if res["headers"] is not None:
+ for header_key, header_value in res["headers"].items():
+ if len(header_value) == 1:
+ response_headers[header_key] = header_value[0]
+ else:
+ response_headers[header_key] = header_value
+ response.headers = response_headers
+ # Add cookies
+ response.cookies = res_cookies
+ # Add response body
+ response.text = res["body"]
+ # Add response content (bytes)
+ response._content = res["body"].encode()
+ return response
diff --git a/venv/lib/python3.9/site-packages/tls_client/sessions.py b/venv/lib/python3.9/site-packages/tls_client/sessions.py
new file mode 100644
index 00000000..dbe8b85e
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/tls_client/sessions.py
@@ -0,0 +1,457 @@
+from .cffi import request, freeMemory
+from .cookies import cookiejar_from_dict, get_cookie_header, merge_cookies, extract_cookies_to_jar
+from .exceptions import TLSClientExeption
+from .response import build_response
+from .structures import CaseInsensitiveDict
+from .__version__ import __version__
+
+from typing import Any, Optional, Union
+from json import dumps, loads
+import urllib.parse
+import base64
+import ctypes
+import uuid
+
+
+class Session:
+
+ def __init__(
+ self,
+ client_identifier: Optional[str] = None,
+ ja3_string: Optional[str] = None,
+ h2_settings: Optional[dict] = None, # Optional[dict[str, int]]
+ h2_settings_order: Optional[list] = None, # Optional[list[str]]
+ supported_signature_algorithms: Optional[list] = None, # Optional[list[str]]
+ supported_delegated_credentials_algorithms: Optional[list] = None, # Optional[list[str]]
+ supported_versions: Optional[list] = None, # Optional[list[str]]
+ key_share_curves: Optional[list] = None, # Optional[list[str]]
+ cert_compression_algo: str = None,
+ additional_decode: str = None,
+ pseudo_header_order: Optional[list] = None, # Optional[list[str]
+ connection_flow: Optional[int] = None,
+ priority_frames: Optional[list] = None,
+ header_order: Optional[list] = None, # Optional[list[str]]
+ header_priority: Optional[dict] = None, # Optional[list[str]]
+ random_tls_extension_order: Optional = False,
+ force_http1: Optional = False,
+ catch_panics: Optional = False,
+ ) -> None:
+ self._session_id = str(uuid.uuid4())
+ # --- Standard Settings ----------------------------------------------------------------------------------------
+
+ # Case-insensitive dictionary of headers, send on each request
+ self.headers = CaseInsensitiveDict(
+ {
+ "User-Agent": f"tls-client/{__version__}",
+ "Accept-Encoding": "gzip, deflate, br",
+ "Accept": "*/*",
+ "Connection": "keep-alive",
+ }
+ )
+
+ # Example:
+ # {
+ # "http": "http://user:pass@ip:port",
+ # "https": "http://user:pass@ip:port"
+ # }
+ self.proxies = {}
+
+ # Dictionary of querystring data to attach to each request. The dictionary values may be lists for representing
+ # multivalued query parameters.
+ self.params = {}
+
+ # CookieJar containing all currently outstanding cookies set on this session
+ self.cookies = cookiejar_from_dict({})
+
+ # --- Advanced Settings ----------------------------------------------------------------------------------------
+
+ # Examples:
+ # Chrome --> chrome_103, chrome_104, chrome_105, chrome_106
+ # Firefox --> firefox_102, firefox_104
+ # Opera --> opera_89, opera_90
+ # Safari --> safari_15_3, safari_15_6_1, safari_16_0
+ # iOS --> safari_ios_15_5, safari_ios_15_6, safari_ios_16_0
+ # iPadOS --> safari_ios_15_6
+ self.client_identifier = client_identifier
+
+ # Set JA3 --> TLSVersion, Ciphers, Extensions, EllipticCurves, EllipticCurvePointFormats
+ # Example:
+ # 771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0
+ self.ja3_string = ja3_string
+
+ # HTTP2 Header Frame Settings
+ # Possible Settings:
+ # HEADER_TABLE_SIZE
+ # SETTINGS_ENABLE_PUSH
+ # MAX_CONCURRENT_STREAMS
+ # INITIAL_WINDOW_SIZE
+ # MAX_FRAME_SIZE
+ # MAX_HEADER_LIST_SIZE
+ #
+ # Example:
+ # {
+ # "HEADER_TABLE_SIZE": 65536,
+ # "MAX_CONCURRENT_STREAMS": 1000,
+ # "INITIAL_WINDOW_SIZE": 6291456,
+ # "MAX_HEADER_LIST_SIZE": 262144
+ # }
+ self.h2_settings = h2_settings
+
+ # HTTP2 Header Frame Settings Order
+ # Example:
+ # [
+ # "HEADER_TABLE_SIZE",
+ # "MAX_CONCURRENT_STREAMS",
+ # "INITIAL_WINDOW_SIZE",
+ # "MAX_HEADER_LIST_SIZE"
+ # ]
+ self.h2_settings_order = h2_settings_order
+
+ # Supported Signature Algorithms
+ # Possible Settings:
+ # PKCS1WithSHA256
+ # PKCS1WithSHA384
+ # PKCS1WithSHA512
+ # PSSWithSHA256
+ # PSSWithSHA384
+ # PSSWithSHA512
+ # ECDSAWithP256AndSHA256
+ # ECDSAWithP384AndSHA384
+ # ECDSAWithP521AndSHA512
+ # PKCS1WithSHA1
+ # ECDSAWithSHA1
+ #
+ # Example:
+ # [
+ # "ECDSAWithP256AndSHA256",
+ # "PSSWithSHA256",
+ # "PKCS1WithSHA256",
+ # "ECDSAWithP384AndSHA384",
+ # "PSSWithSHA384",
+ # "PKCS1WithSHA384",
+ # "PSSWithSHA512",
+ # "PKCS1WithSHA512",
+ # ]
+ self.supported_signature_algorithms = supported_signature_algorithms
+
+ # Supported Delegated Credentials Algorithms
+ # Possible Settings:
+ # PKCS1WithSHA256
+ # PKCS1WithSHA384
+ # PKCS1WithSHA512
+ # PSSWithSHA256
+ # PSSWithSHA384
+ # PSSWithSHA512
+ # ECDSAWithP256AndSHA256
+ # ECDSAWithP384AndSHA384
+ # ECDSAWithP521AndSHA512
+ # PKCS1WithSHA1
+ # ECDSAWithSHA1
+ #
+ # Example:
+ # [
+ # "ECDSAWithP256AndSHA256",
+ # "PSSWithSHA256",
+ # "PKCS1WithSHA256",
+ # "ECDSAWithP384AndSHA384",
+ # "PSSWithSHA384",
+ # "PKCS1WithSHA384",
+ # "PSSWithSHA512",
+ # "PKCS1WithSHA512",
+ # ]
+ self.supported_delegated_credentials_algorithms = supported_delegated_credentials_algorithms
+
+ # Supported Versions
+ # Possible Settings:
+ # GREASE
+ # 1.3
+ # 1.2
+ # 1.1
+ # 1.0
+ #
+ # Example:
+ # [
+ # "GREASE",
+ # "1.3",
+ # "1.2"
+ # ]
+ self.supported_versions = supported_versions
+
+ # Key Share Curves
+ # Possible Settings:
+ # GREASE
+ # P256
+ # P384
+ # P521
+ # X25519
+ #
+ # Example:
+ # [
+ # "GREASE",
+ # "X25519"
+ # ]
+ self.key_share_curves = key_share_curves
+
+ # Cert Compression Algorithm
+ # Examples: "zlib", "brotli", "zstd"
+ self.cert_compression_algo = cert_compression_algo
+
+ # Additional Decode
+ # Make sure the go code decodes the response body once explicit by provided algorithm.
+ # Examples: null, "gzip", "br", "deflate"
+ self.additional_decode = additional_decode
+
+ # Pseudo Header Order (:authority, :method, :path, :scheme)
+ # Example:
+ # [
+ # ":method",
+ # ":authority",
+ # ":scheme",
+ # ":path"
+ # ]
+ self.pseudo_header_order = pseudo_header_order
+
+ # Connection Flow / Window Size Increment
+ # Example:
+ # 15663105
+ self.connection_flow = connection_flow
+
+ # Example:
+ # [
+ # {
+ # "streamID": 3,
+ # "priorityParam": {
+ # "weight": 201,
+ # "streamDep": 0,
+ # "exclusive": false
+ # }
+ # },
+ # {
+ # "streamID": 5,
+ # "priorityParam": {
+ # "weight": 101,
+ # "streamDep": false,
+ # "exclusive": 0
+ # }
+ # }
+ # ]
+ self.priority_frames = priority_frames
+
+ # Order of your headers
+ # Example:
+ # [
+ # "key1",
+ # "key2"
+ # ]
+ self.header_order = header_order
+
+ # Header Priority
+ # Example:
+ # {
+ # "streamDep": 1,
+ # "exclusive": true,
+ # "weight": 1
+ # }
+ self.header_priority = header_priority
+
+ # randomize tls extension order
+ self.random_tls_extension_order = random_tls_extension_order
+
+ # force HTTP1
+ self.force_http1 = force_http1
+
+ # catch panics
+ # avoid the tls client to print the whole stacktrace when a panic (critical go error) happens
+ self.catch_panics = catch_panics
+
+ def execute_request(
+ self,
+ method: str,
+ url: str,
+ params: Optional[dict] = None, # Optional[dict[str, str]]
+ data: Optional[Union[str, dict]] = None,
+ headers: Optional[dict] = None, # Optional[dict[str, str]]
+ cookies: Optional[dict] = None, # Optional[dict[str, str]]
+ json: Optional[dict] = None, # Optional[dict]
+ allow_redirects: Optional[bool] = False,
+ insecure_skip_verify: Optional[bool] = False,
+ timeout_seconds: Optional[int] = 30,
+ proxy: Optional[dict] = None # Optional[dict[str, str]]
+ ):
+ # --- URL ------------------------------------------------------------------------------------------------------
+ # Prepare URL - add params to url
+ if params is not None:
+ url = f"{url}?{urllib.parse.urlencode(params, doseq=True)}"
+
+ # --- Request Body ---------------------------------------------------------------------------------------------
+ # Prepare request body - build request body
+ # Data has priority. JSON is only used if data is None.
+ if data is None and json is not None:
+ if type(json) in [dict, list]:
+ json = dumps(json)
+ request_body = json
+ content_type = "application/json"
+ elif data is not None and type(data) not in [str, bytes]:
+ request_body = urllib.parse.urlencode(data, doseq=True)
+ content_type = "application/x-www-form-urlencoded"
+ else:
+ request_body = data
+ content_type = None
+ # set content type if it isn't set
+ if content_type is not None and "content-type" not in self.headers:
+ self.headers["Content-Type"] = content_type
+
+ # --- Headers --------------------------------------------------------------------------------------------------
+ # merge headers of session and of the request
+ if headers is not None:
+ for header_key, header_value in headers.items():
+ # check if all header keys and values are strings
+ if type(header_key) is str and type(header_value) is str:
+ self.headers[header_key] = header_value
+ headers = self.headers
+ else:
+ headers = self.headers
+
+ # --- Cookies --------------------------------------------------------------------------------------------------
+ cookies = cookies or {}
+ # Merge with session cookies
+ cookies = merge_cookies(self.cookies, cookies)
+ # turn cookie jar into dict
+ request_cookies = [
+ {'domain': c.domain, 'expires': c.expires, 'name': c.name, 'path': c.path, 'value': c.value}
+ for c in cookies
+ ]
+
+ # --- Proxy ----------------------------------------------------------------------------------------------------
+ proxy = proxy or self.proxies
+
+ if type(proxy) is dict and "http" in proxy:
+ proxy = proxy["http"]
+ elif type(proxy) is str:
+ proxy = proxy
+ else:
+ proxy = ""
+
+ # --- Request --------------------------------------------------------------------------------------------------
+ is_byte_request = isinstance(request_body, (bytes, bytearray))
+ request_payload = {
+ "sessionId": self._session_id,
+ "followRedirects": allow_redirects,
+ "forceHttp1": self.force_http1,
+ "catchPanics": self.catch_panics,
+ "headers": dict(headers),
+ "headerOrder": self.header_order,
+ "insecureSkipVerify": insecure_skip_verify,
+ "isByteRequest": is_byte_request,
+ "additionalDecode": self.additional_decode,
+ "proxyUrl": proxy,
+ "requestUrl": url,
+ "requestMethod": method,
+ "requestBody": base64.b64encode(request_body).decode() if is_byte_request else request_body,
+ "requestCookies": request_cookies,
+ "timeoutSeconds": timeout_seconds,
+ }
+ if self.client_identifier is None:
+ request_payload["customTlsClient"] = {
+ "ja3String": self.ja3_string,
+ "h2Settings": self.h2_settings,
+ "h2SettingsOrder": self.h2_settings_order,
+ "pseudoHeaderOrder": self.pseudo_header_order,
+ "connectionFlow": self.connection_flow,
+ "priorityFrames": self.priority_frames,
+ "headerPriority": self.header_priority,
+ "certCompressionAlgo": self.cert_compression_algo,
+ "supportedVersions": self.supported_versions,
+ "supportedSignatureAlgorithms": self.supported_signature_algorithms,
+ "supportedDelegatedCredentialsAlgorithms": self.supported_delegated_credentials_algorithms ,
+ "keyShareCurves": self.key_share_curves,
+ }
+ else:
+ request_payload["tlsClientIdentifier"] = self.client_identifier
+ request_payload["withRandomTLSExtensionOrder"] = self.random_tls_extension_order
+
+ # this is a pointer to the response
+ response = request(dumps(request_payload).encode('utf-8'))
+ # dereference the pointer to a byte array
+ response_bytes = ctypes.string_at(response)
+ # convert our byte array to a string (tls client returns json)
+ response_string = response_bytes.decode('utf-8')
+ # convert response string to json
+ response_object = loads(response_string)
+ # free the memory
+ freeMemory(response_object['id'].encode('utf-8'))
+ # --- Response -------------------------------------------------------------------------------------------------
+ # Error handling
+ if response_object["status"] == 0:
+ raise TLSClientExeption(response_object["body"])
+ # Set response cookies
+ response_cookie_jar = extract_cookies_to_jar(
+ request_url=url,
+ request_headers=headers,
+ cookie_jar=cookies,
+ response_headers=response_object["headers"]
+ )
+ # build response class
+ return build_response(response_object, response_cookie_jar)
+
+ def get(
+ self,
+ url: str,
+ **kwargs: Any
+ ):
+ """Sends a GET request"""
+ return self.execute_request(method="GET", url=url, **kwargs)
+
+ def options(
+ self,
+ url: str,
+ **kwargs: Any
+ ):
+ """Sends a OPTIONS request"""
+ return self.execute_request(method="OPTIONS", url=url, **kwargs)
+
+ def head(
+ self,
+ url: str,
+ **kwargs: Any
+ ):
+ """Sends a HEAD request"""
+ return self.execute_request(method="HEAD", url=url, **kwargs)
+
+ def post(
+ self,
+ url: str,
+ data: Optional[Union[str, dict]] = None,
+ json: Optional[dict] = None,
+ **kwargs: Any
+ ):
+ """Sends a POST request"""
+ return self.execute_request(method="POST", url=url, data=data, json=json, **kwargs)
+
+ def put(
+ self,
+ url: str,
+ data: Optional[Union[str, dict]] = None,
+ json: Optional[dict] = None,
+ **kwargs: Any
+ ):
+ """Sends a PUT request"""
+ return self.execute_request(method="PUT", url=url, data=data, json=json, **kwargs)
+
+ def patch(
+ self,
+ url: str,
+ data: Optional[Union[str, dict]] = None,
+ json: Optional[dict] = None,
+ **kwargs: Any
+ ):
+ """Sends a PUT request"""
+ return self.execute_request(method="PATCH", url=url, data=data, json=json, **kwargs)
+
+ def delete(
+ self,
+ url: str,
+ **kwargs: Any
+ ):
+ """Sends a DELETE request"""
+ return self.execute_request(method="DELETE", url=url, **kwargs)
diff --git a/venv/lib/python3.9/site-packages/tls_client/structures.py b/venv/lib/python3.9/site-packages/tls_client/structures.py
new file mode 100644
index 00000000..4a1e1732
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/tls_client/structures.py
@@ -0,0 +1,74 @@
+from typing import MutableMapping, Mapping
+from collections import OrderedDict
+
+
+class CaseInsensitiveDict(MutableMapping):
+ """Origin: requests library (https://github.com/psf/requests)
+
+ A case-insensitive ``dict``-like object.
+
+ Implements all methods and operations of
+ ``MutableMapping`` as well as dict's ``copy``. Also
+ provides ``lower_items``.
+
+ All keys are expected to be strings. The structure remembers the
+ case of the last key to be set, and ``iter(instance)``,
+ ``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()``
+ will contain case-sensitive keys. However, querying and contains
+ testing is case insensitive::
+
+ cid = CaseInsensitiveDict()
+ cid['Accept'] = 'application/json'
+ cid['aCCEPT'] == 'application/json' # True
+ list(cid) == ['Accept'] # True
+
+ For example, ``headers['content-encoding']`` will return the
+ value of a ``'Content-Encoding'`` response header, regardless
+ of how the header name was originally stored.
+
+ If the constructor, ``.update``, or equality comparison
+ operations are given keys that have equal ``.lower()``s, the
+ behavior is undefined.
+ """
+
+ def __init__(self, data=None, **kwargs):
+ self._store = OrderedDict()
+ if data is None:
+ data = {}
+ self.update(data, **kwargs)
+
+ def __setitem__(self, key, value):
+ # Use the lowercased key for lookups, but store the actual
+ # key alongside the value.
+ self._store[key.lower()] = (key, value)
+
+ def __getitem__(self, key):
+ return self._store[key.lower()][1]
+
+ def __delitem__(self, key):
+ del self._store[key.lower()]
+
+ def __iter__(self):
+ return (casedkey for casedkey, mappedvalue in self._store.values())
+
+ def __len__(self):
+ return len(self._store)
+
+ def lower_items(self):
+ """Like iteritems(), but with all lowercase keys."""
+ return ((lowerkey, keyval[1]) for (lowerkey, keyval) in self._store.items())
+
+ def __eq__(self, other):
+ if isinstance(other, Mapping):
+ other = CaseInsensitiveDict(other)
+ else:
+ return NotImplemented
+ # Compare insensitively
+ return dict(self.lower_items()) == dict(other.lower_items())
+
+ # Copy is required
+ def copy(self):
+ return CaseInsensitiveDict(self._store.values())
+
+ def __repr__(self):
+ return str(dict(self.items()))