Add create_connection and open_connection helpers to the shared proxy module, covering portcheck, whois, tlscheck, and crtsh live-cert check. UDP-based plugins (dns, blacklist, subdomain) stay direct. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
107 lines
3.2 KiB
Python
107 lines
3.2 KiB
Python
"""Tests for the SOCKS5 proxy HTTP/TCP module."""
|
|
|
|
import ssl
|
|
import urllib.request
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import socks
|
|
from socks import SOCKS5
|
|
|
|
from derp.http import (
|
|
_PROXY_ADDR,
|
|
_PROXY_PORT,
|
|
_ProxyHandler,
|
|
build_opener,
|
|
create_connection,
|
|
)
|
|
|
|
|
|
class TestProxyHandler:
|
|
def test_uses_socks5(self):
|
|
handler = _ProxyHandler()
|
|
assert handler.args[0] == SOCKS5
|
|
|
|
def test_proxy_address(self):
|
|
handler = _ProxyHandler()
|
|
assert handler.args[1] == _PROXY_ADDR
|
|
assert handler.args[2] == _PROXY_PORT
|
|
|
|
def test_rdns_enabled(self):
|
|
handler = _ProxyHandler()
|
|
assert handler.args[3] is True
|
|
|
|
def test_default_ssl_context(self):
|
|
handler = _ProxyHandler()
|
|
assert isinstance(handler._ssl_context, ssl.SSLContext)
|
|
|
|
def test_custom_ssl_context(self):
|
|
ctx = ssl.create_default_context()
|
|
ctx.check_hostname = False
|
|
handler = _ProxyHandler(context=ctx)
|
|
assert handler._ssl_context is ctx
|
|
|
|
def test_is_https_handler(self):
|
|
handler = _ProxyHandler()
|
|
assert isinstance(handler, urllib.request.HTTPSHandler)
|
|
|
|
|
|
class TestBuildOpener:
|
|
def test_includes_proxy_handler(self):
|
|
opener = build_opener()
|
|
proxy = [h for h in opener.handlers if isinstance(h, _ProxyHandler)]
|
|
assert len(proxy) == 1
|
|
|
|
def test_passes_extra_handlers(self):
|
|
class Custom(urllib.request.HTTPRedirectHandler):
|
|
pass
|
|
|
|
opener = build_opener(Custom)
|
|
custom = [h for h in opener.handlers if isinstance(h, Custom)]
|
|
assert len(custom) == 1
|
|
|
|
def test_passes_ssl_context(self):
|
|
ctx = ssl.create_default_context()
|
|
ctx.check_hostname = False
|
|
opener = build_opener(context=ctx)
|
|
proxy = [h for h in opener.handlers if isinstance(h, _ProxyHandler)][0]
|
|
assert proxy._ssl_context is ctx
|
|
|
|
|
|
class TestCreateConnection:
|
|
@patch("derp.http.socks.socksocket")
|
|
def test_sets_socks5_proxy(self, mock_cls):
|
|
sock = MagicMock()
|
|
mock_cls.return_value = sock
|
|
create_connection(("example.com", 443), timeout=5)
|
|
sock.set_proxy.assert_called_once_with(
|
|
SOCKS5, _PROXY_ADDR, _PROXY_PORT, rdns=True,
|
|
)
|
|
|
|
@patch("derp.http.socks.socksocket")
|
|
def test_connects_to_target(self, mock_cls):
|
|
sock = MagicMock()
|
|
mock_cls.return_value = sock
|
|
create_connection(("example.com", 443))
|
|
sock.connect.assert_called_once_with(("example.com", 443))
|
|
|
|
@patch("derp.http.socks.socksocket")
|
|
def test_sets_timeout(self, mock_cls):
|
|
sock = MagicMock()
|
|
mock_cls.return_value = sock
|
|
create_connection(("example.com", 80), timeout=7)
|
|
sock.settimeout.assert_called_once_with(7)
|
|
|
|
@patch("derp.http.socks.socksocket")
|
|
def test_no_timeout_when_none(self, mock_cls):
|
|
sock = MagicMock()
|
|
mock_cls.return_value = sock
|
|
create_connection(("example.com", 80))
|
|
sock.settimeout.assert_not_called()
|
|
|
|
@patch("derp.http.socks.socksocket")
|
|
def test_returns_socket(self, mock_cls):
|
|
sock = MagicMock()
|
|
mock_cls.return_value = sock
|
|
result = create_connection(("example.com", 443))
|
|
assert result is sock
|