1582 lines
47 KiB
Python
1582 lines
47 KiB
Python
"""
|
|
Java net standard library APIs implemented in Python.
|
|
java.net package - URL, Socket, HTTP connection, and networking classes.
|
|
"""
|
|
|
|
from .._core import Stub, AndroidOnlyError, HAS_REQUESTS, HAS_HTTPX
|
|
from .io import IOException
|
|
|
|
import os as _os
|
|
import socket as _socket
|
|
import struct as _struct
|
|
import threading as _threading
|
|
from urllib.parse import (
|
|
urlparse as _urlparse,
|
|
urlunparse as _urlunparse,
|
|
quote as _quote,
|
|
unquote as _unquote,
|
|
)
|
|
from datetime import datetime as _datetime
|
|
from collections import OrderedDict as _OrderedDict
|
|
|
|
__all__ = [
|
|
"URL", "URLConnection", "HttpURLConnection",
|
|
"InetAddress", "InetSocketAddress",
|
|
"Socket", "ServerSocket", "DatagramSocket", "DatagramPacket",
|
|
"Proxy", "ProxySelector", "Authenticator", "PasswordAuthentication",
|
|
"NetworkInterface", "URI",
|
|
"CookieHandler", "CookieManager", "HttpCookie",
|
|
"URLDecoder", "URLEncoder",
|
|
"SocketException", "ConnectException", "BindException",
|
|
"NoRouteToHostException", "PortUnreachableException",
|
|
"ProtocolException", "SocketTimeoutException",
|
|
"MalformedURLException", "UnknownHostException", "UnknownServiceException",
|
|
]
|
|
|
|
|
|
# ============================================================
|
|
# Exception classes
|
|
# ============================================================
|
|
|
|
class SocketException(IOException):
|
|
pass
|
|
|
|
class ConnectException(SocketException):
|
|
pass
|
|
|
|
class BindException(SocketException):
|
|
pass
|
|
|
|
class NoRouteToHostException(SocketException):
|
|
pass
|
|
|
|
class PortUnreachableException(SocketException):
|
|
pass
|
|
|
|
class ProtocolException(IOException):
|
|
pass
|
|
|
|
class SocketTimeoutException(IOException):
|
|
pass
|
|
|
|
class MalformedURLException(IOException):
|
|
pass
|
|
|
|
class UnknownHostException(IOException):
|
|
pass
|
|
|
|
class UnknownServiceException(IOException):
|
|
pass
|
|
|
|
|
|
# ============================================================
|
|
# InetAddress
|
|
# ============================================================
|
|
|
|
class InetAddress(Stub):
|
|
def __init__(self, host, addr):
|
|
super().__init__()
|
|
self._host = host
|
|
self._addr = addr # bytes, 4 or 16
|
|
|
|
@staticmethod
|
|
def getByName(host):
|
|
try:
|
|
addr = _socket.getaddrinfo(host, None, family=_socket.AF_UNSPEC)
|
|
if addr:
|
|
raw = addr[0][4][0]
|
|
return InetAddress(host, _socket.inet_pton(_socket.AF_INET, raw) if "." in raw else _socket.inet_pton(_socket.AF_INET6, raw))
|
|
except Exception:
|
|
raise UnknownHostException(host)
|
|
|
|
@staticmethod
|
|
def getAllByName(host):
|
|
try:
|
|
addrs = _socket.getaddrinfo(host, None, family=_socket.AF_UNSPEC)
|
|
results = []
|
|
seen = set()
|
|
for a in addrs:
|
|
raw = a[4][0]
|
|
if raw not in seen:
|
|
seen.add(raw)
|
|
if "." in raw:
|
|
results.append(InetAddress(host, _socket.inet_pton(_socket.AF_INET, raw)))
|
|
else:
|
|
results.append(InetAddress(host, _socket.inet_pton(_socket.AF_INET6, raw)))
|
|
return results
|
|
except Exception:
|
|
raise UnknownHostException(host)
|
|
|
|
@staticmethod
|
|
def getLocalHost():
|
|
hostname = _socket.gethostname()
|
|
return InetAddress.getByName(hostname)
|
|
|
|
@staticmethod
|
|
def getLoopbackAddress():
|
|
return InetAddress("localhost", b"\x7f\x00\x00\x01")
|
|
|
|
@staticmethod
|
|
def getByAddress(host, addr):
|
|
return InetAddress(host, addr)
|
|
|
|
def getHostName(self):
|
|
return self._host
|
|
|
|
def getHostAddress(self):
|
|
if len(self._addr) == 4:
|
|
return _socket.inet_ntoa(self._addr)
|
|
return _socket.inet_ntop(_socket.AF_INET6, self._addr)
|
|
|
|
def getCanonicalHostName(self):
|
|
try:
|
|
name, _, _ = _socket.gethostbyaddr(self.getHostAddress())
|
|
return name
|
|
except Exception:
|
|
return self._host
|
|
|
|
def isReachable(self, timeout=5000):
|
|
try:
|
|
s = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM)
|
|
s.settimeout(timeout / 1000.0)
|
|
result = s.connect_ex((self.getHostAddress(), 7))
|
|
s.close()
|
|
return result == 0
|
|
except Exception:
|
|
return False
|
|
|
|
def isLoopbackAddress(self):
|
|
if len(self._addr) == 4:
|
|
return self._addr[0] == 127
|
|
return self._addr == b"\x00" * 15 + b"\x01"
|
|
|
|
def isLinkLocalAddress(self):
|
|
if len(self._addr) == 4:
|
|
return self._addr[0] == 169 and self._addr[1] == 254
|
|
return (self._addr[0] & 0xFF) == 0xFE and (self._addr[1] & 0xC0) == 0x80
|
|
|
|
def isSiteLocalAddress(self):
|
|
if len(self._addr) == 4:
|
|
b0 = self._addr[0]
|
|
return b0 == 10 or (b0 == 172 and 16 <= self._addr[1] <= 31) or (b0 == 192 and self._addr[1] == 168)
|
|
return False
|
|
|
|
def isMCGlobal(self):
|
|
if len(self._addr) == 4:
|
|
return 224 <= self._addr[0] <= 239 and self._addr[0] != 224 and self._addr[0] != 239
|
|
return (self._addr[0] & 0xFF) == 0xFF and (self._addr[1] & 0x0F) == 0x0E and (self._addr[2] & 0x20) == 0
|
|
|
|
def isMCLinkLocal(self):
|
|
if len(self._addr) == 4:
|
|
return self._addr[0] == 224 and self._addr[1] == 0 and self._addr[2] == 0
|
|
return (self._addr[0] & 0xFF) == 0xFF and (self._addr[1] & 0x0F) == 0x02
|
|
|
|
def isMCNodeLocal(self):
|
|
if len(self._addr) == 4:
|
|
return self._addr[0] == 224 and self._addr[1] == 0 and self._addr[2] == 0 and self._addr[3] == 1
|
|
return (self._addr[0] & 0xFF) == 0xFF and (self._addr[1] & 0x0F) == 0x01
|
|
|
|
def isMCOrgLocal(self):
|
|
if len(self._addr) == 4:
|
|
return self._addr[0] == 239 and 192 <= self._addr[1] <= 195
|
|
return (self._addr[0] & 0xFF) == 0xFF and (self._addr[1] & 0x0F) == 0x08
|
|
|
|
def isMCSiteLocal(self):
|
|
if len(self._addr) == 4:
|
|
return self._addr[0] == 239 and 255 <= self._addr[1] <= 255
|
|
return (self._addr[0] & 0xFF) == 0xFF and (self._addr[1] & 0x0F) == 0x05
|
|
|
|
def getAddress(self):
|
|
return self._addr
|
|
|
|
def toString(self):
|
|
return f"{self._host}/{self.getHostAddress()}"
|
|
|
|
def equals(self, other):
|
|
if isinstance(other, InetAddress):
|
|
return self._addr == other._addr
|
|
return False
|
|
|
|
def hashCode(self):
|
|
return hash(bytes(self._addr)) & 0x7FFFFFFF
|
|
|
|
|
|
# ============================================================
|
|
# InetSocketAddress
|
|
# ============================================================
|
|
|
|
class InetSocketAddress(Stub):
|
|
def __init__(self, *args):
|
|
super().__init__()
|
|
if len(args) == 2 and isinstance(args[0], int):
|
|
self._addr = None
|
|
self._port = args[0] if isinstance(args[0], int) else args[1]
|
|
elif len(args) == 2:
|
|
hostname, port = args
|
|
self._addr = InetAddress.getByName(hostname) if isinstance(hostname, str) else hostname
|
|
self._port = port
|
|
elif len(args) == 1 and isinstance(args[0], int):
|
|
self._addr = None
|
|
self._port = args[0]
|
|
|
|
def getAddress(self):
|
|
return self._addr
|
|
|
|
def getPort(self):
|
|
return self._port
|
|
|
|
def getHostName(self):
|
|
return self._addr.getHostName() if self._addr else None
|
|
|
|
def getHostString(self):
|
|
return self.getHostName()
|
|
|
|
def isUnresolved(self):
|
|
return self._addr is None
|
|
|
|
def toString(self):
|
|
return f"{self.getHostName()}:{self._port}"
|
|
|
|
def equals(self, other):
|
|
if isinstance(other, InetSocketAddress):
|
|
return self._port == other._port and self._addr == other._addr
|
|
return False
|
|
|
|
def hashCode(self):
|
|
return hash((self._port, self._addr)) & 0x7FFFFFFF
|
|
|
|
|
|
# ============================================================
|
|
# URI
|
|
# ============================================================
|
|
|
|
class URI(Stub):
|
|
def __init__(self, *args):
|
|
super().__init__()
|
|
if len(args) == 1:
|
|
self._uri = args[0]
|
|
parsed = _urlparse(self._uri)
|
|
self._scheme = parsed.scheme
|
|
self._host = parsed.hostname
|
|
self._port = parsed.port
|
|
self._path = parsed.path
|
|
self._query = parsed.query
|
|
self._fragment = parsed.fragment
|
|
self._user_info = parsed.username
|
|
self._authority = parsed.netloc
|
|
elif len(args) == 5:
|
|
self._scheme = args[0]
|
|
self._authority = args[1] or ""
|
|
self._path = args[2] or ""
|
|
self._query = args[3] or ""
|
|
self._fragment = args[4] or ""
|
|
parsed = _urlparse(f"{self._scheme}://{self._authority}{self._path}")
|
|
self._host = parsed.hostname
|
|
self._port = parsed.port
|
|
self._user_info = parsed.username
|
|
self._uri = f"{self._scheme}://{self._authority}{self._path}"
|
|
if self._query:
|
|
self._uri += "?" + self._query
|
|
if self._fragment:
|
|
self._uri += "#" + self._fragment
|
|
else:
|
|
raise ValueError("URI constructor requires 1 or 5 arguments")
|
|
|
|
def getScheme(self):
|
|
return self._scheme
|
|
|
|
def getHost(self):
|
|
return self._host
|
|
|
|
def getPort(self):
|
|
return self._port if self._port else -1
|
|
|
|
def getPath(self):
|
|
return self._path
|
|
|
|
def getQuery(self):
|
|
return self._query
|
|
|
|
def getFragment(self):
|
|
return self._fragment
|
|
|
|
def getAuthority(self):
|
|
return self._authority
|
|
|
|
def getRawAuthority(self):
|
|
return self._authority
|
|
|
|
def getRawFragment(self):
|
|
return self._fragment
|
|
|
|
def getRawPath(self):
|
|
return self._path
|
|
|
|
def getRawQuery(self):
|
|
return self._query
|
|
|
|
def getRawSchemeSpecificPart(self):
|
|
return f"//{self._authority}{self._path}"
|
|
|
|
def getRawUserInfo(self):
|
|
return self._user_info or ""
|
|
|
|
def getSchemeSpecificPart(self):
|
|
return self.getRawSchemeSpecificPart()
|
|
|
|
def getUserInfo(self):
|
|
return self._user_info or ""
|
|
|
|
def isAbsolute(self):
|
|
return bool(self._scheme)
|
|
|
|
def isOpaque(self):
|
|
return not self._path and not self._authority and self._scheme
|
|
|
|
def normalize(self):
|
|
import posixpath
|
|
normalized = posixpath.normpath(self._path)
|
|
return URI(f"{self._scheme}://{self._authority}{normalized}")
|
|
|
|
def resolve(self, uri_or_str):
|
|
if isinstance(uri_or_str, URI):
|
|
other = uri_or_str
|
|
else:
|
|
other = URI(uri_or_str)
|
|
if other.isAbsolute():
|
|
return other
|
|
base_path = self._path or "/"
|
|
if base_path.endswith("/"):
|
|
new_path = base_path + other._path
|
|
else:
|
|
new_path = "/".join(base_path.rsplit("/", 1)[:-1] + [other._path]) if other._path else base_path
|
|
return URI(f"{self._scheme}://{self._authority}{new_path}")
|
|
|
|
def relativize(self, other):
|
|
if self._path and other._path and other._path.startswith(self._path):
|
|
rel = other._path[len(self._path):]
|
|
if rel.startswith("/"):
|
|
rel = rel[1:]
|
|
return URI(rel)
|
|
return other
|
|
|
|
def toASCIIString(self):
|
|
return _quote(self._uri, safe=":/?#[]@!$&'()*+,;=-._~")
|
|
|
|
def toString(self):
|
|
return self._uri
|
|
|
|
def toURL(self):
|
|
return URL(self._uri)
|
|
|
|
def compareTo(self, other):
|
|
return -1 if self._uri < other._uri else (1 if self._uri > other._uri else 0)
|
|
|
|
def equals(self, other):
|
|
if isinstance(other, URI):
|
|
return self._uri == other._uri
|
|
return False
|
|
|
|
def hashCode(self):
|
|
return hash(self._uri) & 0x7FFFFFFF
|
|
|
|
def parseServerAuthority(self):
|
|
return self
|
|
|
|
|
|
# ============================================================
|
|
# URL
|
|
# ============================================================
|
|
|
|
class URL(Stub):
|
|
def __init__(self, *args):
|
|
super().__init__()
|
|
if len(args) == 1:
|
|
spec = args[0]
|
|
parsed = _urlparse(str(spec))
|
|
self._protocol = parsed.scheme
|
|
self._host = parsed.hostname or ""
|
|
self._port = parsed.port
|
|
self._path = parsed.path or ""
|
|
self._file = parsed.path or ""
|
|
if parsed.query:
|
|
self._file += "?" + parsed.query
|
|
self._query = parsed.query or ""
|
|
self._ref = parsed.fragment or ""
|
|
self._authority = parsed.netloc or ""
|
|
self._user_info = parsed.username or ""
|
|
self._url_str = str(spec)
|
|
elif len(args) == 4:
|
|
protocol, host, port, file = args
|
|
self._protocol = protocol or ""
|
|
self._host = host or ""
|
|
self._port = port
|
|
self._path = file or ""
|
|
self._file = file or ""
|
|
self._query = ""
|
|
self._ref = ""
|
|
self._authority = f"{host}:{port}" if port and port > 0 else host
|
|
self._user_info = ""
|
|
parts = [file or ""]
|
|
self._url_str = f"{protocol}://{self._authority}{file}"
|
|
|
|
def getProtocol(self):
|
|
return self._protocol
|
|
|
|
def getHost(self):
|
|
return self._host
|
|
|
|
def getPort(self):
|
|
return self._port if self._port else -1
|
|
|
|
def getDefaultPort(self):
|
|
defaults = {
|
|
"http": 80, "https": 443, "ftp": 21, "smtp": 25,
|
|
"ssh": 22, "telnet": 23, "ldap": 389,
|
|
}
|
|
return defaults.get(self._protocol, -1)
|
|
|
|
def getPath(self):
|
|
return self._path
|
|
|
|
def getFile(self):
|
|
return self._file
|
|
|
|
def getQuery(self):
|
|
return self._query
|
|
|
|
def getRef(self):
|
|
return self._ref
|
|
|
|
def getAuthority(self):
|
|
return self._authority
|
|
|
|
def getUserInfo(self):
|
|
return self._user_info
|
|
|
|
def getContent(self, classes=None):
|
|
return self.openConnection().getContent(classes)
|
|
|
|
def openConnection(self, proxy=None):
|
|
return HttpURLConnection(self._url_str, proxy)
|
|
|
|
def openStream(self):
|
|
return self.openConnection().getInputStream()
|
|
|
|
def sameFile(self, other):
|
|
return (self._protocol == other._protocol and self._host == other._host
|
|
and self._port == other._port and self._path == other._path)
|
|
|
|
def toExternalForm(self):
|
|
return self._url_str
|
|
|
|
def toString(self):
|
|
return self._url_str
|
|
|
|
def toURI(self):
|
|
return URI(self._url_str)
|
|
|
|
def equals(self, other):
|
|
if isinstance(other, URL):
|
|
return self._url_str == other._url_str
|
|
return False
|
|
|
|
def hashCode(self):
|
|
return hash(self._url_str) & 0x7FFFFFFF
|
|
|
|
def openConnection(self, proxy=None):
|
|
return HttpURLConnection(self._url_str, proxy)
|
|
|
|
def __str__(self):
|
|
return self._url_str
|
|
|
|
|
|
# ============================================================
|
|
# URLConnection
|
|
# ============================================================
|
|
|
|
class URLConnection(Stub):
|
|
def __init__(self, url_str):
|
|
super().__init__()
|
|
self._url_str = url_str
|
|
self._connected = False
|
|
self._do_input = True
|
|
self._do_output = False
|
|
self._allow_user_interaction = False
|
|
self._use_caches = True
|
|
self._default_use_caches = True
|
|
self._if_modified_since = 0
|
|
self._read_timeout = 0
|
|
self._connect_timeout = 0
|
|
self._request_properties = _OrderedDict()
|
|
self._response_code = None
|
|
self._response_message = None
|
|
self._response_headers = _OrderedDict()
|
|
self._content = None
|
|
|
|
def connect(self):
|
|
if not self._connected:
|
|
self._connected = True
|
|
|
|
def getContent(self, classes=None):
|
|
raise UnknownServiceException("getContent not implemented")
|
|
|
|
def getContentEncoding(self):
|
|
return self._response_headers.get("Content-Encoding")
|
|
|
|
def getContentLength(self):
|
|
val = self._response_headers.get("Content-Length")
|
|
return int(val) if val else -1
|
|
|
|
def getContentLengthLong(self):
|
|
return self.getContentLength()
|
|
|
|
def getContentType(self):
|
|
return self._response_headers.get("Content-Type")
|
|
|
|
def getDate(self):
|
|
val = self._response_headers.get("Date")
|
|
if val:
|
|
try:
|
|
return int(_datetime.strptime(val, "%a, %d %b %Y %H:%M:%S %Z").timestamp() * 1000)
|
|
except Exception:
|
|
pass
|
|
return 0
|
|
|
|
def getExpiration(self):
|
|
val = self._response_headers.get("Expires")
|
|
if val:
|
|
try:
|
|
return int(_datetime.strptime(val, "%a, %d %b %Y %H:%M:%S %Z").timestamp() * 1000)
|
|
except Exception:
|
|
pass
|
|
return 0
|
|
|
|
def getHeaderField(self, name):
|
|
return self._response_headers.get(name)
|
|
|
|
def getHeaderFieldDate(self, name, default):
|
|
val = self._response_headers.get(name)
|
|
if val:
|
|
try:
|
|
return int(_datetime.strptime(val, "%a, %d %b %Y %H:%M:%S %Z").timestamp() * 1000)
|
|
except Exception:
|
|
pass
|
|
return default
|
|
|
|
def getHeaderFieldInt(self, name, default):
|
|
val = self._response_headers.get(name)
|
|
return int(val) if val and val.isdigit() else default
|
|
|
|
def getHeaderFieldKey(self, n):
|
|
keys = list(self._response_headers.keys())
|
|
return keys[n] if n < len(keys) else None
|
|
|
|
def getHeaderFields(self):
|
|
return self._response_headers
|
|
|
|
def getInputStream(self):
|
|
raise UnknownServiceException("getInputStream not implemented")
|
|
|
|
def getOutputStream(self):
|
|
raise UnknownServiceException("getOutputStream not implemented")
|
|
|
|
def getPermission(self):
|
|
return None
|
|
|
|
def getReadTimeout(self):
|
|
return self._read_timeout
|
|
|
|
def setReadTimeout(self, timeout):
|
|
self._read_timeout = timeout
|
|
|
|
def getConnectTimeout(self):
|
|
return self._connect_timeout
|
|
|
|
def setConnectTimeout(self, timeout):
|
|
self._connect_timeout = timeout
|
|
|
|
def getRequestProperty(self, key):
|
|
return self._request_properties.get(key)
|
|
|
|
def setRequestProperty(self, key, value):
|
|
self._request_properties[key] = value
|
|
|
|
def addRequestProperty(self, key, value):
|
|
if key in self._request_properties:
|
|
existing = self._request_properties[key]
|
|
if isinstance(existing, list):
|
|
existing.append(value)
|
|
else:
|
|
self._request_properties[key] = [existing, value]
|
|
else:
|
|
self._request_properties[key] = value
|
|
|
|
def getDefaultUseCaches(self):
|
|
return self._default_use_caches
|
|
|
|
def setDefaultUseCaches(self, use):
|
|
self._default_use_caches = use
|
|
|
|
def getUseCaches(self):
|
|
return self._use_caches
|
|
|
|
def setUseCaches(self, use):
|
|
self._use_caches = use
|
|
|
|
def setDoInput(self, do_input):
|
|
self._do_input = do_input
|
|
|
|
def getDoInput(self):
|
|
return self._do_input
|
|
|
|
def setDoOutput(self, do_output):
|
|
self._do_output = do_output
|
|
|
|
def getDoOutput(self):
|
|
return self._do_output
|
|
|
|
def setAllowUserInteraction(self, allow):
|
|
self._allow_user_interaction = allow
|
|
|
|
def getAllowUserInteraction(self):
|
|
return self._allow_user_interaction
|
|
|
|
def setIfModifiedSince(self, time):
|
|
self._if_modified_since = time
|
|
|
|
def getIfModifiedSince(self):
|
|
return self._if_modified_since
|
|
|
|
|
|
# ============================================================
|
|
# HttpURLConnection
|
|
# ============================================================
|
|
|
|
class HttpURLConnection(URLConnection):
|
|
HTTP_OK = 200
|
|
HTTP_CREATED = 201
|
|
HTTP_ACCEPTED = 202
|
|
HTTP_MOVED_PERM = 301
|
|
HTTP_MOVED_TEMP = 302
|
|
HTTP_NOT_MODIFIED = 304
|
|
HTTP_BAD_REQUEST = 400
|
|
HTTP_UNAUTHORIZED = 401
|
|
HTTP_FORBIDDEN = 403
|
|
HTTP_NOT_FOUND = 404
|
|
HTTP_INTERNAL_ERROR = 500
|
|
|
|
def __init__(self, url_str, proxy=None):
|
|
super().__init__(url_str)
|
|
self._request_method = "GET"
|
|
self._instance_follow_redirects = True
|
|
self._chunked = False
|
|
self._fixed_length = -1
|
|
self._input_stream = None
|
|
self._error_stream = None
|
|
self._response_code = -1
|
|
self._response_message = ""
|
|
|
|
def setRequestMethod(self, method):
|
|
method = method.upper()
|
|
valid = ["GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE", "PATCH"]
|
|
if method not in valid:
|
|
raise ProtocolException(f"Invalid HTTP method: {method}")
|
|
self._request_method = method
|
|
|
|
def getRequestMethod(self):
|
|
return self._request_method
|
|
|
|
def getResponseCode(self):
|
|
if self._response_code == -1:
|
|
self._do_request()
|
|
return self._response_code
|
|
|
|
def getResponseMessage(self):
|
|
if self._response_code == -1:
|
|
self._do_request()
|
|
return self._response_message
|
|
|
|
def getHeaderFields(self):
|
|
if self._response_code == -1:
|
|
self._do_request()
|
|
return self._response_headers
|
|
|
|
def setInstanceFollowRedirects(self, follow):
|
|
self._instance_follow_redirects = follow
|
|
|
|
def getInstanceFollowRedirects(self):
|
|
return self._instance_follow_redirects
|
|
|
|
def getErrorStream(self):
|
|
if self._response_code == -1:
|
|
self._do_request()
|
|
if self._response_code >= 400:
|
|
return self._error_stream
|
|
return None
|
|
|
|
def setChunkedStreamingMode(self, chunklen):
|
|
self._chunked = True
|
|
self._chunk_length = chunklen
|
|
|
|
def setFixedLengthStreamingMode(self, content_length):
|
|
self._fixed_length = content_length
|
|
|
|
def setFixedLengthStreamingModeLong(self, content_length):
|
|
self._fixed_length = content_length
|
|
|
|
def disconnect(self):
|
|
self._input_stream = None
|
|
self._error_stream = None
|
|
self._connected = False
|
|
|
|
def usingProxy(self):
|
|
return False
|
|
|
|
def getInputStream(self):
|
|
if self._response_code == -1:
|
|
self._do_request()
|
|
if self._input_stream is None:
|
|
import io as _pyio_mod
|
|
self._input_stream = _pyio_mod.BytesIO(self._content.encode("utf-8") if isinstance(self._content, str) else self._content)
|
|
return self._input_stream
|
|
|
|
def _do_request(self):
|
|
if HAS_REQUESTS:
|
|
self._do_requests()
|
|
elif HAS_HTTPX:
|
|
self._do_httpx()
|
|
else:
|
|
self._do_urllib()
|
|
|
|
def _do_requests(self):
|
|
import requests
|
|
try:
|
|
kwargs = {
|
|
"headers": dict(self._request_properties),
|
|
"timeout": self._read_timeout / 1000.0 if self._read_timeout else None,
|
|
}
|
|
if self._instance_follow_redirects:
|
|
kwargs["allow_redirects"] = True
|
|
else:
|
|
kwargs["allow_redirects"] = False
|
|
method = self._request_method.lower()
|
|
if method == "get":
|
|
r = requests.get(self._url_str, **kwargs)
|
|
elif method == "post":
|
|
body = self._request_properties.pop("__body", None)
|
|
r = requests.post(self._url_str, data=body, **kwargs)
|
|
elif method == "put":
|
|
body = self._request_properties.pop("__body", None)
|
|
r = requests.put(self._url_str, data=body, **kwargs)
|
|
elif method == "delete":
|
|
r = requests.delete(self._url_str, **kwargs)
|
|
elif method == "head":
|
|
r = requests.head(self._url_str, **kwargs)
|
|
elif method == "options":
|
|
r = requests.options(self._url_str, **kwargs)
|
|
elif method == "patch":
|
|
body = self._request_properties.pop("__body", None)
|
|
r = requests.patch(self._url_str, data=body, **kwargs)
|
|
else:
|
|
r = requests.request(self._request_method, self._url_str, **kwargs)
|
|
self._response_code = r.status_code
|
|
self._response_message = r.reason
|
|
self._response_headers = dict(r.headers)
|
|
self._content = r.content
|
|
except Exception as e:
|
|
self._response_code = 0
|
|
self._response_message = str(e)
|
|
|
|
def _do_httpx(self):
|
|
import httpx
|
|
try:
|
|
client = httpx.Client(follow_redirects=self._instance_follow_redirects)
|
|
kwargs = {
|
|
"headers": dict(self._request_properties) if self._request_properties else None,
|
|
"timeout": self._read_timeout / 1000.0 if self._read_timeout else None,
|
|
}
|
|
method = self._request_method.lower()
|
|
if method == "get":
|
|
r = client.get(self._url_str, **kwargs)
|
|
elif method == "post":
|
|
body = self._request_properties.pop("__body", None)
|
|
r = client.post(self._url_str, content=body, **kwargs)
|
|
elif method == "put":
|
|
body = self._request_properties.pop("__body", None)
|
|
r = client.put(self._url_str, content=body, **kwargs)
|
|
elif method == "delete":
|
|
r = client.delete(self._url_str, **kwargs)
|
|
else:
|
|
r = client.request(self._request_method, self._url_str, **kwargs)
|
|
self._response_code = r.status_code
|
|
self._response_message = r.reason_phrase
|
|
self._response_headers = dict(r.headers)
|
|
self._content = r.content
|
|
except Exception as e:
|
|
self._response_code = 0
|
|
self._response_message = str(e)
|
|
|
|
def _do_urllib(self):
|
|
from urllib.request import urlopen, Request
|
|
from urllib.error import HTTPError
|
|
try:
|
|
req = Request(self._url_str, headers=dict(self._request_properties) if self._request_properties else {},
|
|
method=self._request_method)
|
|
resp = urlopen(req, timeout=self._read_timeout / 1000.0 if self._read_timeout else None)
|
|
self._response_code = resp.status
|
|
self._response_message = resp.reason
|
|
self._response_headers = dict(resp.headers)
|
|
self._content = resp.read()
|
|
except HTTPError as e:
|
|
self._response_code = e.code
|
|
self._response_message = e.reason
|
|
self._response_headers = dict(e.headers)
|
|
self._content = e.read()
|
|
except Exception as e:
|
|
self._response_code = 0
|
|
self._response_message = str(e)
|
|
self._content = b""
|
|
|
|
|
|
# ============================================================
|
|
# Socket
|
|
# ============================================================
|
|
|
|
class Socket(Stub):
|
|
def __init__(self, *args):
|
|
super().__init__()
|
|
self._socket = None
|
|
self._connected = False
|
|
self._bound = False
|
|
self._closed = False
|
|
self._input_shutdown = False
|
|
self._output_shutdown = False
|
|
self._input_stream = None
|
|
self._output_stream = None
|
|
self._timeout = 0
|
|
self._keep_alive = False
|
|
self._tcp_no_delay = False
|
|
self._reuse_address = False
|
|
self._send_buffer_size = 0
|
|
self._receive_buffer_size = 0
|
|
self._linger = -1
|
|
self._traffic_class = 0
|
|
if len(args) == 2:
|
|
host, port = args
|
|
self.connect(InetSocketAddress(host, port))
|
|
elif len(args) == 1 and isinstance(args[0], InetAddress):
|
|
addr, port = args[0], 0
|
|
self.connect(InetSocketAddress(addr, port))
|
|
|
|
def connect(self, endpoint, timeout=None):
|
|
if isinstance(endpoint, InetSocketAddress):
|
|
host = endpoint.getHostName()
|
|
port = endpoint.getPort()
|
|
else:
|
|
host = str(endpoint)
|
|
port = 80
|
|
self._socket = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM)
|
|
if self._timeout > 0:
|
|
self._socket.settimeout(self._timeout / 1000.0)
|
|
try:
|
|
self._socket.connect((host, port))
|
|
self._connected = True
|
|
except OSError as e:
|
|
raise ConnectException(str(e))
|
|
|
|
def bind(self, endpoint):
|
|
if isinstance(endpoint, InetSocketAddress):
|
|
host = endpoint.getHostName()
|
|
port = endpoint.getPort()
|
|
else:
|
|
host = "0.0.0.0"
|
|
port = 0
|
|
self._socket = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM)
|
|
self._socket.bind((host or "0.0.0.0", port))
|
|
self._bound = True
|
|
|
|
def close(self):
|
|
if not self._closed:
|
|
self._closed = True
|
|
if self._socket:
|
|
self._socket.close()
|
|
|
|
def getInputStream(self):
|
|
if self._input_stream is None and self._socket:
|
|
import io as _pyio_mod
|
|
fd = self._socket.fileno()
|
|
self._input_stream = _pyio_mod.FileIO(fd, closefd=False)
|
|
return self._input_stream
|
|
|
|
def getOutputStream(self):
|
|
if self._output_stream is None and self._socket:
|
|
import io as _pyio_mod
|
|
fd = self._socket.fileno()
|
|
self._output_stream = _pyio_mod.FileIO(fd, closefd=False, mode="ab")
|
|
return self._output_stream
|
|
|
|
def getInetAddress(self):
|
|
if self._socket and self._connected:
|
|
host, _ = self._socket.getpeername()
|
|
return InetAddress.getByName(host)
|
|
return None
|
|
|
|
def getPort(self):
|
|
if self._socket and self._connected:
|
|
_, port = self._socket.getpeername()
|
|
return port
|
|
return 0
|
|
|
|
def getLocalPort(self):
|
|
if self._socket:
|
|
_, port = self._socket.getsockname()
|
|
return port
|
|
return 0
|
|
|
|
def getLocalAddress(self):
|
|
if self._socket:
|
|
host, _ = self._socket.getsockname()
|
|
return InetAddress.getByName(host)
|
|
return InetAddress.getLoopbackAddress()
|
|
|
|
def getRemoteSocketAddress(self):
|
|
if self._socket and self._connected:
|
|
host, port = self._socket.getpeername()
|
|
return InetSocketAddress(host, port)
|
|
return None
|
|
|
|
def getLocalSocketAddress(self):
|
|
if self._socket:
|
|
host, port = self._socket.getsockname()
|
|
return InetSocketAddress(host, port)
|
|
return None
|
|
|
|
def getSoTimeout(self):
|
|
return self._timeout
|
|
|
|
def setSoTimeout(self, timeout):
|
|
self._timeout = timeout
|
|
if self._socket:
|
|
self._socket.settimeout(timeout / 1000.0 if timeout > 0 else None)
|
|
|
|
def getTcpNoDelay(self):
|
|
return self._tcp_no_delay
|
|
|
|
def setTcpNoDelay(self, on):
|
|
self._tcp_no_delay = on
|
|
if self._socket:
|
|
self._socket.setsockopt(_socket.IPPROTO_TCP, _socket.TCP_NODELAY, 1 if on else 0)
|
|
|
|
def getKeepAlive(self):
|
|
return self._keep_alive
|
|
|
|
def setKeepAlive(self, on):
|
|
self._keep_alive = on
|
|
if self._socket:
|
|
self._socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_KEEPALIVE, 1 if on else 0)
|
|
|
|
def getReuseAddress(self):
|
|
return self._reuse_address
|
|
|
|
def setReuseAddress(self, on):
|
|
self._reuse_address = on
|
|
if self._socket:
|
|
self._socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, 1 if on else 0)
|
|
|
|
def getSendBufferSize(self):
|
|
return self._send_buffer_size
|
|
|
|
def setSendBufferSize(self, size):
|
|
self._send_buffer_size = size
|
|
if self._socket:
|
|
self._socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_SNDBUF, size)
|
|
|
|
def getReceiveBufferSize(self):
|
|
return self._receive_buffer_size
|
|
|
|
def setReceiveBufferSize(self, size):
|
|
self._receive_buffer_size = size
|
|
if self._socket:
|
|
self._socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_RCVBUF, size)
|
|
|
|
def getSoLinger(self):
|
|
return self._linger
|
|
|
|
def setSoLinger(self, on, linger):
|
|
if on:
|
|
self._linger = linger
|
|
if self._socket:
|
|
self._socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_LINGER, _struct.pack("ii", 1, linger))
|
|
else:
|
|
self._linger = -1
|
|
|
|
def getTrafficClass(self):
|
|
return self._traffic_class
|
|
|
|
def setTrafficClass(self, tc):
|
|
self._traffic_class = tc
|
|
|
|
def shutdownInput(self):
|
|
if self._socket:
|
|
self._socket.shutdown(_socket.SHUT_RD)
|
|
self._input_shutdown = True
|
|
|
|
def shutdownOutput(self):
|
|
if self._socket:
|
|
self._socket.shutdown(_socket.SHUT_WR)
|
|
self._output_shutdown = True
|
|
|
|
def isConnected(self):
|
|
return self._connected
|
|
|
|
def isBound(self):
|
|
return self._bound
|
|
|
|
def isClosed(self):
|
|
return self._closed
|
|
|
|
def isInputShutdown(self):
|
|
return self._input_shutdown
|
|
|
|
def isOutputShutdown(self):
|
|
return self._output_shutdown
|
|
|
|
def setPerformancePreferences(self, connection_time, latency, bandwidth):
|
|
pass
|
|
|
|
|
|
# ============================================================
|
|
# ServerSocket
|
|
# ============================================================
|
|
|
|
class ServerSocket(Stub):
|
|
def __init__(self, port=0, backlog=50, bindAddr=None):
|
|
super().__init__()
|
|
self._socket = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM)
|
|
self._socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, 1)
|
|
self._port = port
|
|
self._backlog = backlog
|
|
self._bind_addr = bindAddr
|
|
self._closed = False
|
|
self._timeout = 0
|
|
if port > 0 or bindAddr:
|
|
self.bind(InetSocketAddress(bindAddr or "0.0.0.0", port), backlog)
|
|
|
|
def bind(self, endpoint, backlog=50):
|
|
host = endpoint.getHostName() if isinstance(endpoint, InetSocketAddress) else str(endpoint)
|
|
port = endpoint.getPort() if isinstance(endpoint, InetSocketAddress) else 0
|
|
self._socket.bind((host or "0.0.0.0", port))
|
|
self._socket.listen(backlog)
|
|
self._port = self._socket.getsockname()[1]
|
|
|
|
def accept(self):
|
|
if self._closed:
|
|
raise SocketException("Socket closed")
|
|
if self._timeout > 0:
|
|
self._socket.settimeout(self._timeout / 1000.0)
|
|
try:
|
|
client_sock, addr = self._socket.accept()
|
|
sock = Socket()
|
|
sock._socket = client_sock
|
|
sock._connected = True
|
|
return sock
|
|
except _socket.timeout:
|
|
raise SocketTimeoutException("Accept timed out")
|
|
|
|
def close(self):
|
|
if not self._closed:
|
|
self._closed = True
|
|
self._socket.close()
|
|
|
|
def getInetAddress(self):
|
|
host, _ = self._socket.getsockname()
|
|
return InetAddress.getByName(host)
|
|
|
|
def getLocalPort(self):
|
|
return self._socket.getsockname()[1] if not self._closed else -1
|
|
|
|
def getSoTimeout(self):
|
|
return self._timeout
|
|
|
|
def setSoTimeout(self, timeout):
|
|
self._timeout = timeout
|
|
self._socket.settimeout(timeout / 1000.0 if timeout > 0 else None)
|
|
|
|
def getReceiveBufferSize(self):
|
|
return self._socket.getsockopt(_socket.SOL_SOCKET, _socket.SO_RCVBUF)
|
|
|
|
def setReceiveBufferSize(self, size):
|
|
self._socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_RCVBUF, size)
|
|
|
|
def getReuseAddress(self):
|
|
return bool(self._socket.getsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR))
|
|
|
|
def setReuseAddress(self, on):
|
|
self._socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, 1 if on else 0)
|
|
|
|
|
|
# ============================================================
|
|
# DatagramSocket / DatagramPacket
|
|
# ============================================================
|
|
|
|
class DatagramPacket(Stub):
|
|
def __init__(self, buf, length, address=None, port=None):
|
|
super().__init__()
|
|
if address is not None:
|
|
self._buf = bytearray(buf)
|
|
self._length = length
|
|
self._address = address
|
|
self._port = port
|
|
else:
|
|
self._buf = bytearray(buf)
|
|
self._length = length
|
|
self._address = None
|
|
self._port = -1
|
|
|
|
def getAddress(self):
|
|
return self._address
|
|
|
|
def getPort(self):
|
|
return self._port
|
|
|
|
def getData(self):
|
|
return bytes(self._buf)
|
|
|
|
def getLength(self):
|
|
return self._length
|
|
|
|
def setAddress(self, address):
|
|
self._address = address
|
|
|
|
def setPort(self, port):
|
|
self._port = port
|
|
|
|
def setData(self, buf, offset=None, length=None):
|
|
if offset is not None and length is not None:
|
|
self._buf = bytearray(buf[offset:offset+length])
|
|
self._length = length
|
|
else:
|
|
self._buf = bytearray(buf)
|
|
self._length = len(buf)
|
|
|
|
def setLength(self, length):
|
|
self._length = length
|
|
|
|
|
|
class DatagramSocket(Stub):
|
|
def __init__(self, port=None):
|
|
super().__init__()
|
|
self._socket = _socket.socket(_socket.AF_INET, _socket.SOCK_DGRAM)
|
|
self._bound = False
|
|
self._closed = False
|
|
if port is not None:
|
|
self.bind(port)
|
|
|
|
def bind(self, port):
|
|
self._socket.bind(("0.0.0.0", port))
|
|
self._bound = True
|
|
|
|
def send(self, p):
|
|
addr = p.getAddress()
|
|
port = p.getPort()
|
|
self._socket.sendto(p.getData()[:p.getLength()], (addr.getHostAddress(), port))
|
|
|
|
def receive(self, p):
|
|
data, addr = self._socket.recvfrom(p.getLength())
|
|
p.setData(data)
|
|
p.setAddress(InetAddress(addr[0], _socket.inet_pton(_socket.AF_INET, addr[0])))
|
|
p.setPort(addr[1])
|
|
|
|
def close(self):
|
|
if not self._closed:
|
|
self._closed = True
|
|
self._socket.close()
|
|
|
|
def getLocalPort(self):
|
|
return self._socket.getsockname()[1] if not self._closed else -1
|
|
|
|
def isBound(self):
|
|
return self._bound
|
|
|
|
def isClosed(self):
|
|
return self._closed
|
|
|
|
def setSoTimeout(self, timeout):
|
|
self._socket.settimeout(timeout / 1000.0 if timeout > 0 else None)
|
|
|
|
def getSoTimeout(self):
|
|
return self._socket.gettimeout() * 1000 if self._socket.gettimeout() else 0
|
|
|
|
|
|
# ============================================================
|
|
# Proxy
|
|
# ============================================================
|
|
|
|
class Proxy(Stub):
|
|
class Type:
|
|
DIRECT = "DIRECT"
|
|
HTTP = "HTTP"
|
|
SOCKS = "SOCKS"
|
|
|
|
NO_PROXY = None
|
|
|
|
def __init__(self, typ, sa=None):
|
|
super().__init__()
|
|
self._type = typ
|
|
self._address = sa
|
|
|
|
def type(self):
|
|
return self._type
|
|
|
|
def address(self):
|
|
return self._address
|
|
|
|
def toString(self):
|
|
return f"{self._type} proxy @ {self._address}"
|
|
|
|
|
|
class ProxySelector(Stub):
|
|
_default = None
|
|
|
|
@staticmethod
|
|
def getDefault():
|
|
return ProxySelector._default
|
|
|
|
@staticmethod
|
|
def setDefault(sel):
|
|
ProxySelector._default = sel
|
|
|
|
def select(self, uri):
|
|
return [Proxy.NO_PROXY]
|
|
|
|
def connectFailed(self, uri, sa, ioe):
|
|
pass
|
|
|
|
|
|
# ============================================================
|
|
# Authenticator / PasswordAuthentication
|
|
# ============================================================
|
|
|
|
class Authenticator(Stub):
|
|
_default = None
|
|
|
|
@staticmethod
|
|
def getDefault():
|
|
return Authenticator._default
|
|
|
|
@staticmethod
|
|
def setDefault(auth):
|
|
Authenticator._default = auth
|
|
|
|
def getPasswordAuthentication(self):
|
|
return self._pwd_auth
|
|
|
|
def requestPasswordAuthentication(self, addr, port, protocol, prompt, scheme):
|
|
return self._pwd_auth
|
|
|
|
|
|
class PasswordAuthentication(Stub):
|
|
def __init__(self, user, password):
|
|
super().__init__()
|
|
self._user = user
|
|
self._password = password
|
|
|
|
def getUserName(self):
|
|
return self._user
|
|
|
|
def getPassword(self):
|
|
return self._password
|
|
|
|
|
|
# ============================================================
|
|
# NetworkInterface
|
|
# ============================================================
|
|
|
|
class NetworkInterface(Stub):
|
|
_cache = {}
|
|
|
|
def __init__(self, name, display_name, addrs):
|
|
super().__init__()
|
|
self._name = name
|
|
self._display_name = display_name
|
|
self._addrs = addrs
|
|
|
|
@staticmethod
|
|
def getByName(name):
|
|
if name in NetworkInterface._cache:
|
|
return NetworkInterface._cache[name]
|
|
|
|
@staticmethod
|
|
def getByInetAddress(addr):
|
|
for ni in NetworkInterface._cache.values():
|
|
for a in ni._addrs:
|
|
if a.equals(addr):
|
|
return ni
|
|
return None
|
|
|
|
@staticmethod
|
|
def getNetworkInterfaces():
|
|
return list(NetworkInterface._cache.values())
|
|
|
|
def getName(self):
|
|
return self._name
|
|
|
|
def getDisplayName(self):
|
|
return self._display_name
|
|
|
|
def getInetAddresses(self):
|
|
return iter(self._addrs)
|
|
|
|
def getInterfaceAddresses(self):
|
|
return self._addrs
|
|
|
|
def isUp(self):
|
|
return True
|
|
|
|
def isLoopback(self):
|
|
return self._name == "lo"
|
|
|
|
def isPointToPoint(self):
|
|
return False
|
|
|
|
def supportsMulticast(self):
|
|
return True
|
|
|
|
def getHardwareAddress(self):
|
|
return None
|
|
|
|
def getMTU(self):
|
|
return 1500
|
|
|
|
def getSubInterfaces(self):
|
|
return iter([])
|
|
|
|
def getParent(self):
|
|
return None
|
|
|
|
def isVirtual(self):
|
|
return False
|
|
|
|
def toString(self):
|
|
return f"{self._display_name} ({self._name})"
|
|
|
|
@staticmethod
|
|
def _init_from_socket():
|
|
import netifaces
|
|
for iface_name in netifaces.interfaces():
|
|
addrs = []
|
|
ifaddresses = netifaces.ifaddresses(iface_name)
|
|
for family in [netifaces.AF_INET, netifaces.AF_INET6]:
|
|
if family in ifaddresses:
|
|
for addr_info in ifaddresses[family]:
|
|
try:
|
|
addr = addr_info["addr"]
|
|
if "." in addr:
|
|
addrs.append(InetAddress(addr, _socket.inet_pton(_socket.AF_INET, addr)))
|
|
else:
|
|
addrs.append(InetAddress(addr, _socket.inet_pton(_socket.AF_INET6, addr)))
|
|
except Exception:
|
|
pass
|
|
NetworkInterface._cache[iface_name] = NetworkInterface(iface_name, iface_name, addrs)
|
|
|
|
try:
|
|
NetworkInterface._init_from_socket()
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
# ============================================================
|
|
# CookieHandler / CookieManager / HttpCookie
|
|
# ============================================================
|
|
|
|
class CookieHandler(Stub):
|
|
_default = None
|
|
|
|
@staticmethod
|
|
def getDefault():
|
|
return CookieHandler._default
|
|
|
|
@staticmethod
|
|
def setDefault(handler):
|
|
CookieHandler._default = handler
|
|
|
|
def get(self, uri, requestHeaders):
|
|
raise NotImplementedError
|
|
|
|
def put(self, uri, responseHeaders):
|
|
raise NotImplementedError
|
|
|
|
|
|
class HttpCookie(Stub):
|
|
def __init__(self, name, value):
|
|
super().__init__()
|
|
self._name = name
|
|
self._value = value
|
|
self._comment = None
|
|
self._comment_url = None
|
|
self._discard = False
|
|
self._domain = None
|
|
self._max_age = -1
|
|
self._path = None
|
|
self._portlist = None
|
|
self._secure = False
|
|
self._version = 0
|
|
self._http_only = False
|
|
self._creation_time = _datetime.now()
|
|
|
|
def getName(self):
|
|
return self._name
|
|
|
|
def getValue(self):
|
|
return self._value
|
|
|
|
def getComment(self):
|
|
return self._comment
|
|
|
|
def getCommentURL(self):
|
|
return self._comment_url
|
|
|
|
def getDiscard(self):
|
|
return self._discard
|
|
|
|
def getDomain(self):
|
|
return self._domain
|
|
|
|
def getMaxAge(self):
|
|
return self._max_age
|
|
|
|
def getPath(self):
|
|
return self._path
|
|
|
|
def getPortlist(self):
|
|
return self._portlist
|
|
|
|
def getSecure(self):
|
|
return self._secure
|
|
|
|
def getVersion(self):
|
|
return self._version
|
|
|
|
def getHttpOnly(self):
|
|
return self._http_only
|
|
|
|
def setComment(self, comment):
|
|
self._comment = comment
|
|
|
|
def setCommentURL(self, url):
|
|
self._comment_url = url
|
|
|
|
def setDiscard(self, discard):
|
|
self._discard = discard
|
|
|
|
def setDomain(self, domain):
|
|
self._domain = domain
|
|
|
|
def setMaxAge(self, max_age):
|
|
self._max_age = max_age
|
|
|
|
def setPath(self, path):
|
|
self._path = path
|
|
|
|
def setPortlist(self, portlist):
|
|
self._portlist = portlist
|
|
|
|
def setSecure(self, secure):
|
|
self._secure = secure
|
|
|
|
def setVersion(self, version):
|
|
self._version = version
|
|
|
|
def setHttpOnly(self, http_only):
|
|
self._http_only = http_only
|
|
|
|
def hasExpired(self):
|
|
if self._max_age < 0:
|
|
return False
|
|
elapsed = (_datetime.now() - self._creation_time).total_seconds()
|
|
return elapsed > self._max_age
|
|
|
|
def equals(self, other):
|
|
if isinstance(other, HttpCookie):
|
|
return self._name == other._name
|
|
return False
|
|
|
|
def toString(self):
|
|
return f"{self._name}={self._value}"
|
|
|
|
def validate(self, name):
|
|
if not name or name.strip() == "":
|
|
raise ValueError("Cookie name cannot be empty")
|
|
|
|
def __str__(self):
|
|
return self.toString()
|
|
|
|
|
|
class CookieManager(CookieHandler):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._cookie_store = {}
|
|
|
|
def get(self, uri, requestHeaders):
|
|
cookies = []
|
|
for domain, path, cookie in self._cookie_store.values():
|
|
if not cookie.hasExpired():
|
|
cookies.append(cookie.toString())
|
|
return {"Cookie": "; ".join(cookies)} if cookies else {}
|
|
|
|
def put(self, uri, responseHeaders):
|
|
set_cookie = responseHeaders.get("Set-Cookie")
|
|
if set_cookie:
|
|
parts = set_cookie.split(";")
|
|
name_val = parts[0].split("=", 1)
|
|
if len(name_val) == 2:
|
|
cookie = HttpCookie(name_val[0], name_val[1])
|
|
for part in parts[1:]:
|
|
part = part.strip()
|
|
if "=" in part:
|
|
k, v = part.split("=", 1)
|
|
if k.lower() == "domain":
|
|
cookie.setDomain(v)
|
|
elif k.lower() == "path":
|
|
cookie.setPath(v)
|
|
elif k.lower() == "max-age":
|
|
cookie.setMaxAge(int(v))
|
|
elif k.lower() == "version":
|
|
cookie.setVersion(int(v))
|
|
else:
|
|
if part.lower() == "secure":
|
|
cookie.setSecure(True)
|
|
elif part.lower() == "httponly":
|
|
cookie.setHttpOnly(True)
|
|
elif part.lower() == "discard":
|
|
cookie.setDiscard(True)
|
|
self._cookie_store[(cookie.getDomain(), cookie.getPath(), cookie.getName())] = (cookie.getDomain(), cookie.getPath(), cookie)
|
|
|
|
|
|
# ============================================================
|
|
# URLEncoder / URLDecoder
|
|
# ============================================================
|
|
|
|
class URLEncoder(Stub):
|
|
@staticmethod
|
|
def encode(s, charset="UTF-8"):
|
|
return _quote(str(s), safe="")
|
|
|
|
@staticmethod
|
|
def encode(s):
|
|
return _quote(str(s), safe="")
|
|
|
|
|
|
class URLDecoder(Stub):
|
|
@staticmethod
|
|
def decode(s, charset="UTF-8"):
|
|
return _unquote(str(s), encoding=charset)
|
|
|
|
@staticmethod
|
|
def decode(s):
|
|
return _unquote(str(s))
|
|
|
|
|
|
# Initialize NO_PROXY
|
|
Proxy.NO_PROXY = Proxy(Proxy.Type.DIRECT, None) |