""" 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", "InterfaceAddress", "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 # ============================================================ # InterfaceAddress # ============================================================ class InterfaceAddress(Stub): """java.net.InterfaceAddress — 接口地址信息 (地址/广播/前缀长度)""" def __init__(self, address=None, broadcast=None, prefix_length=0): super().__init__() self._address = address # InetAddress self._broadcast = broadcast # InetAddress or None self._prefix_length = prefix_length def getAddress(self): return self._address def getBroadcast(self): return self._broadcast def getNetworkPrefixLength(self): return self._prefix_length def equals(self, other): if isinstance(other, InterfaceAddress): return (self._address == other._address and self._prefix_length == other._prefix_length) return False def hashCode(self): return hash((repr(self._address), self._prefix_length)) & 0x7FFFFFFF def toString(self): addr = self._address.getHostAddress() if self._address else "0.0.0.0" return f"{addr}/{self._prefix_length}" # ============================================================ # NetworkInterface # ============================================================ class NetworkInterface(Stub): _cache = {} _initialized = False def __init__(self, name, display_name, addrs): super().__init__() self._name = name self._display_name = display_name self._addrs = addrs self._mac = None # bytes, hardware address self._mtu = 1500 self._state = "unknown" self._flags = 0 self._interface_addrs = [] # List[InterfaceAddress] @staticmethod def _read_sys_file(path): try: with open(path, "r") as f: return f.read().strip() except Exception: return None @staticmethod def getByName(name): if name is None: return None NetworkInterface._ensure_init() return NetworkInterface._cache.get(name) @staticmethod def getByInetAddress(addr): NetworkInterface._ensure_init() for ni in NetworkInterface._cache.values(): for a in ni._addrs: if a.equals(addr): return ni return None @staticmethod def getNetworkInterfaces(): NetworkInterface._ensure_init() return list(NetworkInterface._cache.values()) @staticmethod def _ensure_init(): if not NetworkInterface._initialized: NetworkInterface._initialized = True try: NetworkInterface._init_from_socket() except Exception: pass def getName(self): return self._name def getDisplayName(self): return self._display_name def getInetAddresses(self): return iter(self._addrs) def getInterfaceAddresses(self): return list(self._interface_addrs) def isUp(self): return self._state.lower() == "up" or (self._flags & 0x1) != 0 def isLoopback(self): return (self._flags & 0x8) != 0 or self._name == "lo" def isPointToPoint(self): return (self._flags & 0x10) != 0 def supportsMulticast(self): return (self._flags & 0x1000) != 0 def getHardwareAddress(self): return self._mac def getMTU(self): return self._mtu 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(): """从 /sys/class/net/ 和 socket/fcntl 枚举真实网络接口 (不依赖 netifaces)""" import os as _os net_dir = "/sys/class/net" if not _os.path.isdir(net_dir): # 非 Linux 环境: 尝试用 socket 获取本机地址 try: hostname = _socket.gethostname() addr = InetAddress.getByName(hostname) ni = NetworkInterface(hostname, hostname, [addr]) NetworkInterface._cache[hostname] = ni except Exception: pass return # 尝试用 fcntl.ioctl 获取 IPv4 地址/掩码/广播 fcntl_available = True try: import fcntl as _fcntl except ImportError: fcntl_available = False for iface_name in _os.listdir(net_dir): addrs = [] interface_addrs = [] mac = None mtu = 1500 state = "unknown" flags = 0 # --- MAC 地址 --- mac_str = NetworkInterface._read_sys_file(f"{net_dir}/{iface_name}/address") if mac_str: try: mac = bytes(int(b, 16) for b in mac_str.split(":")) except Exception: mac = None # --- MTU --- mtu_str = NetworkInterface._read_sys_file(f"{net_dir}/{iface_name}/mtu") if mtu_str: try: mtu = int(mtu_str) except ValueError: pass # --- operstate --- state = NetworkInterface._read_sys_file( f"{net_dir}/{iface_name}/operstate") or "unknown" # --- flags (十六进制字符串, 如 0x1003) --- flags_str = NetworkInterface._read_sys_file( f"{net_dir}/{iface_name}/flags") if flags_str: try: flags = int(flags_str, 16) except ValueError: pass # --- IPv4 地址 (fcntl.ioctl) --- if fcntl_available: sock = None try: sock = _socket.socket(_socket.AF_INET, _socket.SOCK_DGRAM) packed = _struct.pack('256s', iface_name.encode()[:15]) # SIOCGIFADDR = 0x8915 result = _fcntl.ioctl(sock.fileno(), 0x8915, packed) ip_raw = result[20:24] inet_addr = InetAddress(iface_name, bytes(ip_raw)) addrs.append(inet_addr) # SIOCGIFNETMASK = 0x891b prefix = 24 try: result = _fcntl.ioctl(sock.fileno(), 0x891b, packed) netmask_str = _socket.inet_ntoa(result[20:24]) prefix = sum(bin(int(b)).count('1') for b in netmask_str.split('.')) except Exception: pass # SIOCGIFBRDADDR = 0x8919 (广播地址) broadcast_addr = None try: result = _fcntl.ioctl(sock.fileno(), 0x8919, packed) bcast_raw = result[20:24] broadcast_addr = InetAddress(iface_name, bytes(bcast_raw)) except Exception: pass interface_addrs.append(InterfaceAddress( address=inet_addr, broadcast=broadcast_addr, prefix_length=prefix )) except OSError: pass # 接口没有 IPv4 地址 finally: if sock is not None: try: sock.close() except Exception: pass # --- IPv6 地址 (/proc/net/if_inet6) --- try: with open("/proc/net/if_inet6", "r") as f: for line in f: parts = line.split() if len(parts) >= 6 and parts[5] == iface_name: hex_addr = parts[0] raw = bytes.fromhex(hex_addr) prefix6 = int(parts[2], 16) if parts[2] else 64 inet6_addr = InetAddress(iface_name, raw) addrs.append(inet6_addr) interface_addrs.append(InterfaceAddress( address=inet6_addr, broadcast=None, prefix_length=prefix6 )) except Exception: pass ni = NetworkInterface(iface_name, iface_name, addrs) ni._mac = mac ni._mtu = mtu ni._state = state ni._flags = flags ni._interface_addrs = interface_addrs NetworkInterface._cache[iface_name] = ni # 初始化缓存 try: NetworkInterface._ensure_init() 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)