fix: 포트 충돌 회피 — note_bridge 8098, intent_service 8099

Jellyfin(8096), OrbStack(8097) 포트 충돌으로 변경.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-03-19 13:53:55 +09:00
parent dc08d29509
commit c2257d3a86
2709 changed files with 619549 additions and 10 deletions

View File

@@ -0,0 +1,453 @@
"""
This module contains provisional support for SOCKS proxies from within
urllib3. This module supports SOCKS4, SOCKS4A (an extension of SOCKS4), and
SOCKS5. To enable its functionality, either install python-socks or install this
module with the ``socks`` extra.
The SOCKS implementation supports the full range of urllib3 features. It also
supports the following SOCKS features:
- SOCKS4A (``proxy_url='socks4a://...``)
- SOCKS4 (``proxy_url='socks4://...``)
- SOCKS5 with remote DNS (``proxy_url='socks5h://...``)
- SOCKS5 with local DNS (``proxy_url='socks5://...``)
- Usernames and passwords for the SOCKS proxy
.. note::
It is recommended to use ``socks5h://`` or ``socks4a://`` schemes in
your ``proxy_url`` to ensure that DNS resolution is done from the remote
server instead of client-side when connecting to a domain name.
SOCKS4 supports IPv4 and domain names with the SOCKS4A extension. SOCKS5
supports IPv4, IPv6, and domain names.
When connecting to a SOCKS4 proxy the ``username`` portion of the ``proxy_url``
will be sent as the ``userid`` section of the SOCKS request:
.. code-block:: python
proxy_url="socks4a://<userid>@proxy-host"
When connecting to a SOCKS5 proxy the ``username`` and ``password`` portion
of the ``proxy_url`` will be sent as the username/password to authenticate
with the proxy:
.. code-block:: python
proxy_url="socks5h://<username>:<password>@proxy-host"
"""
from __future__ import annotations
import warnings
#: We purposely want to support PySocks[...] due to our shadowing of the legacy "urllib3". "Dot not disturb" policy.
BYPASS_SOCKS_LEGACY: bool = False
try:
from python_socks import (
ProxyConnectionError,
ProxyError,
ProxyTimeoutError,
ProxyType,
)
from python_socks.sync import Proxy
from ._socks_override import AsyncioProxy
except ImportError:
from ..exceptions import DependencyWarning
try:
import socks # noqa
except ImportError:
warnings.warn(
(
"SOCKS support in urllib3.future requires the installation of an optional "
"dependency: python-socks. For more information, see "
"https://urllib3future.readthedocs.io/en/latest/contrib.html#socks-proxies"
),
DependencyWarning,
)
else:
from ._socks_legacy import (
SOCKSConnection,
SOCKSHTTPConnectionPool,
SOCKSHTTPSConnection,
SOCKSHTTPSConnectionPool,
SOCKSProxyManager,
)
BYPASS_SOCKS_LEGACY = True
if not BYPASS_SOCKS_LEGACY:
raise
if not BYPASS_SOCKS_LEGACY:
import typing
from socket import socket
from socket import timeout as SocketTimeout
# asynchronous part
from .._async.connection import AsyncHTTPConnection, AsyncHTTPSConnection
from .._async.connectionpool import (
AsyncHTTPConnectionPool,
AsyncHTTPSConnectionPool,
)
from .._async.poolmanager import AsyncPoolManager
from .._typing import _TYPE_SOCKS_OPTIONS
from ..backend import HttpVersion
# synchronous part
from ..connection import HTTPConnection, HTTPSConnection
from ..connectionpool import HTTPConnectionPool, HTTPSConnectionPool
from ..contrib.ssa import AsyncSocket
from ..exceptions import ConnectTimeoutError, NewConnectionError
from ..poolmanager import PoolManager
from ..util.url import parse_url
try:
import ssl
except ImportError:
ssl = None # type: ignore[assignment]
class SOCKSConnection(HTTPConnection): # type: ignore[no-redef]
"""
A plain-text HTTP connection that connects via a SOCKS proxy.
"""
def __init__(
self,
_socks_options: _TYPE_SOCKS_OPTIONS,
*args: typing.Any,
**kwargs: typing.Any,
) -> None:
self._socks_options = _socks_options
super().__init__(*args, **kwargs)
def _new_conn(self) -> socket:
"""
Establish a new connection via the SOCKS proxy.
"""
extra_kw: dict[str, typing.Any] = {}
if self.source_address:
extra_kw["source_address"] = self.source_address
if self.socket_options:
only_tcp_options = []
for opt in self.socket_options:
if len(opt) == 3:
only_tcp_options.append(opt)
elif len(opt) == 4:
protocol: str = opt[3].lower()
if protocol == "udp":
continue
only_tcp_options.append(opt[:3])
extra_kw["socket_options"] = only_tcp_options
try:
assert self._socks_options["proxy_host"] is not None
assert self._socks_options["proxy_port"] is not None
p = Proxy(
proxy_type=self._socks_options["socks_version"], # type: ignore[arg-type]
host=self._socks_options["proxy_host"],
port=int(self._socks_options["proxy_port"]),
username=self._socks_options["username"],
password=self._socks_options["password"],
rdns=self._socks_options["rdns"],
)
_socket = self._resolver.create_connection(
(
self._socks_options["proxy_host"],
int(self._socks_options["proxy_port"]),
),
timeout=self.timeout,
source_address=self.source_address,
socket_options=extra_kw["socket_options"],
quic_upgrade_via_dns_rr=False,
timing_hook=lambda _: setattr(self, "_connect_timings", _),
)
# our dependency started to deprecate passing "_socket"
# which is ... vital for our integration. We'll start by silencing the warning.
# then we'll think on how to proceed.
# A) the maintainer agrees to revert https://github.com/romis2012/python-socks/commit/173a7390469c06aa033f8dca67c827854b462bc3#diff-e4086fa970d1c98b1eb341e58cb70e9ceffe7391b2feecc4b66c7e92ea2de76fR64
# B) the maintainer pursue the removal -> do we vendor our copy of python-socks? is there an alternative?
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
return p.connect(
self.host,
self.port,
self.timeout,
_socket=_socket,
)
except (SocketTimeout, ProxyTimeoutError) as e:
raise ConnectTimeoutError(
self,
f"Connection to {self.host} timed out. (connect timeout={self.timeout})",
) from e
except (ProxyConnectionError, ProxyError) as e:
raise NewConnectionError(
self, f"Failed to establish a new connection: {e}"
) from e
except OSError as e: # Defensive: PySocks should catch all these.
raise NewConnectionError(
self, f"Failed to establish a new connection: {e}"
) from e
# We don't need to duplicate the Verified/Unverified distinction from
# urllib3/connection.py here because the HTTPSConnection will already have been
# correctly set to either the Verified or Unverified form by that module. This
# means the SOCKSHTTPSConnection will automatically be the correct type.
class SOCKSHTTPSConnection(SOCKSConnection, HTTPSConnection): # type: ignore[no-redef]
pass
class SOCKSHTTPConnectionPool(HTTPConnectionPool): # type: ignore[no-redef]
ConnectionCls = SOCKSConnection
class SOCKSHTTPSConnectionPool(HTTPSConnectionPool): # type: ignore[no-redef]
ConnectionCls = SOCKSHTTPSConnection
class SOCKSProxyManager(PoolManager): # type: ignore[no-redef]
"""
A version of the urllib3 ProxyManager that routes connections via the
defined SOCKS proxy.
"""
pool_classes_by_scheme = {
"http": SOCKSHTTPConnectionPool,
"https": SOCKSHTTPSConnectionPool,
}
def __init__(
self,
proxy_url: str,
username: str | None = None,
password: str | None = None,
num_pools: int = 10,
headers: typing.Mapping[str, str] | None = None,
**connection_pool_kw: typing.Any,
):
parsed = parse_url(proxy_url)
if username is None and password is None and parsed.auth is not None:
split = parsed.auth.split(":")
if len(split) == 2:
username, password = split
if parsed.scheme == "socks5":
socks_version = ProxyType.SOCKS5
rdns = False
elif parsed.scheme == "socks5h":
socks_version = ProxyType.SOCKS5
rdns = True
elif parsed.scheme == "socks4":
socks_version = ProxyType.SOCKS4
rdns = False
elif parsed.scheme == "socks4a":
socks_version = ProxyType.SOCKS4
rdns = True
else:
raise ValueError(f"Unable to determine SOCKS version from {proxy_url}")
self.proxy_url = proxy_url
socks_options = {
"socks_version": socks_version,
"proxy_host": parsed.host,
"proxy_port": parsed.port,
"username": username,
"password": password,
"rdns": rdns,
}
connection_pool_kw["_socks_options"] = socks_options
if "disabled_svn" not in connection_pool_kw:
connection_pool_kw["disabled_svn"] = set()
connection_pool_kw["disabled_svn"].add(HttpVersion.h3)
super().__init__(num_pools, headers, **connection_pool_kw)
self.pool_classes_by_scheme = SOCKSProxyManager.pool_classes_by_scheme
class AsyncSOCKSConnection(AsyncHTTPConnection):
"""
A plain-text HTTP connection that connects via a SOCKS proxy.
"""
def __init__(
self,
_socks_options: _TYPE_SOCKS_OPTIONS,
*args: typing.Any,
**kwargs: typing.Any,
) -> None:
self._socks_options = _socks_options
super().__init__(*args, **kwargs)
async def _new_conn(self) -> AsyncSocket: # type: ignore[override]
"""
Establish a new connection via the SOCKS proxy.
"""
extra_kw: dict[str, typing.Any] = {}
if self.source_address:
extra_kw["source_address"] = self.source_address
if self.socket_options:
only_tcp_options = []
for opt in self.socket_options:
if len(opt) == 3:
only_tcp_options.append(opt)
elif len(opt) == 4:
protocol: str = opt[3].lower()
if protocol == "udp":
continue
only_tcp_options.append(opt[:3])
extra_kw["socket_options"] = only_tcp_options
try:
assert self._socks_options["proxy_host"] is not None
assert self._socks_options["proxy_port"] is not None
p = AsyncioProxy(
proxy_type=self._socks_options["socks_version"], # type: ignore[arg-type]
host=self._socks_options["proxy_host"],
port=int(self._socks_options["proxy_port"]),
username=self._socks_options["username"],
password=self._socks_options["password"],
rdns=self._socks_options["rdns"],
)
_socket = await self._resolver.create_connection(
(
self._socks_options["proxy_host"],
int(self._socks_options["proxy_port"]),
),
timeout=self.timeout,
source_address=self.source_address,
socket_options=extra_kw["socket_options"],
quic_upgrade_via_dns_rr=False,
timing_hook=lambda _: setattr(self, "_connect_timings", _),
)
return await p.connect(
self.host,
self.port,
self.timeout,
_socket,
)
except (SocketTimeout, ProxyTimeoutError) as e:
raise ConnectTimeoutError(
self,
f"Connection to {self.host} timed out. (connect timeout={self.timeout})",
) from e
except (ProxyConnectionError, ProxyError) as e:
raise NewConnectionError(
self, f"Failed to establish a new connection: {e}"
) from e
except OSError as e: # Defensive: PySocks should catch all these.
raise NewConnectionError(
self, f"Failed to establish a new connection: {e}"
) from e
# We don't need to duplicate the Verified/Unverified distinction from
# urllib3/connection.py here because the HTTPSConnection will already have been
# correctly set to either the Verified or Unverified form by that module. This
# means the SOCKSHTTPSConnection will automatically be the correct type.
class AsyncSOCKSHTTPSConnection(AsyncSOCKSConnection, AsyncHTTPSConnection):
pass
class AsyncSOCKSHTTPConnectionPool(AsyncHTTPConnectionPool):
ConnectionCls = AsyncSOCKSConnection
class AsyncSOCKSHTTPSConnectionPool(AsyncHTTPSConnectionPool):
ConnectionCls = AsyncSOCKSHTTPSConnection
class AsyncSOCKSProxyManager(AsyncPoolManager):
"""
A version of the urllib3 ProxyManager that routes connections via the
defined SOCKS proxy.
"""
pool_classes_by_scheme = {
"http": AsyncSOCKSHTTPConnectionPool,
"https": AsyncSOCKSHTTPSConnectionPool,
}
def __init__(
self,
proxy_url: str,
username: str | None = None,
password: str | None = None,
num_pools: int = 10,
headers: typing.Mapping[str, str] | None = None,
**connection_pool_kw: typing.Any,
):
parsed = parse_url(proxy_url)
if username is None and password is None and parsed.auth is not None:
split = parsed.auth.split(":")
if len(split) == 2:
username, password = split
if parsed.scheme == "socks5":
socks_version = ProxyType.SOCKS5
rdns = False
elif parsed.scheme == "socks5h":
socks_version = ProxyType.SOCKS5
rdns = True
elif parsed.scheme == "socks4":
socks_version = ProxyType.SOCKS4
rdns = False
elif parsed.scheme == "socks4a":
socks_version = ProxyType.SOCKS4
rdns = True
else:
raise ValueError(f"Unable to determine SOCKS version from {proxy_url}")
self.proxy_url = proxy_url
socks_options = {
"socks_version": socks_version,
"proxy_host": parsed.host,
"proxy_port": parsed.port,
"username": username,
"password": password,
"rdns": rdns,
}
connection_pool_kw["_socks_options"] = socks_options
if "disabled_svn" not in connection_pool_kw:
connection_pool_kw["disabled_svn"] = set()
connection_pool_kw["disabled_svn"].add(HttpVersion.h3)
super().__init__(num_pools, headers, **connection_pool_kw)
self.pool_classes_by_scheme = AsyncSOCKSProxyManager.pool_classes_by_scheme
__all__ = [
"SOCKSConnection",
"SOCKSProxyManager",
"SOCKSHTTPSConnection",
"SOCKSHTTPSConnectionPool",
"SOCKSHTTPConnectionPool",
]
if not BYPASS_SOCKS_LEGACY:
__all__ += [
"AsyncSOCKSConnection",
"AsyncSOCKSHTTPSConnection",
"AsyncSOCKSHTTPConnectionPool",
"AsyncSOCKSHTTPSConnectionPool",
"AsyncSOCKSProxyManager",
]