aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/Twisted/py3/twisted/protocols/haproxy
diff options
context:
space:
mode:
authorshmel1k <shmel1k@ydb.tech>2023-11-26 18:16:14 +0300
committershmel1k <shmel1k@ydb.tech>2023-11-26 18:43:30 +0300
commitb8cf9e88f4c5c64d9406af533d8948deb050d695 (patch)
tree218eb61fb3c3b96ec08b4d8cdfef383104a87d63 /contrib/python/Twisted/py3/twisted/protocols/haproxy
parent523f645a83a0ec97a0332dbc3863bb354c92a328 (diff)
downloadydb-b8cf9e88f4c5c64d9406af533d8948deb050d695.tar.gz
add kikimr_configure
Diffstat (limited to 'contrib/python/Twisted/py3/twisted/protocols/haproxy')
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/haproxy/__init__.py10
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/haproxy/_exceptions.py49
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/haproxy/_info.py34
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/haproxy/_interfaces.py63
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/haproxy/_parser.py75
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/haproxy/_v1parser.py142
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/haproxy/_v2parser.py217
-rw-r--r--contrib/python/Twisted/py3/twisted/protocols/haproxy/_wrapper.py109
8 files changed, 699 insertions, 0 deletions
diff --git a/contrib/python/Twisted/py3/twisted/protocols/haproxy/__init__.py b/contrib/python/Twisted/py3/twisted/protocols/haproxy/__init__.py
new file mode 100644
index 0000000000..2d13bf5b4c
--- /dev/null
+++ b/contrib/python/Twisted/py3/twisted/protocols/haproxy/__init__.py
@@ -0,0 +1,10 @@
+# -*- test-case-name: twisted.protocols.haproxy.test -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+HAProxy PROXY protocol implementations.
+"""
+__all__ = ["proxyEndpoint"]
+
+from ._wrapper import proxyEndpoint
diff --git a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_exceptions.py b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_exceptions.py
new file mode 100644
index 0000000000..9a521ea249
--- /dev/null
+++ b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_exceptions.py
@@ -0,0 +1,49 @@
+# -*- test-case-name: twisted.protocols.haproxy.test -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+HAProxy specific exceptions.
+"""
+
+import contextlib
+from typing import Callable, Generator, Type
+
+
+class InvalidProxyHeader(Exception):
+ """
+ The provided PROXY protocol header is invalid.
+ """
+
+
+class InvalidNetworkProtocol(InvalidProxyHeader):
+ """
+ The network protocol was not one of TCP4 TCP6 or UNKNOWN.
+ """
+
+
+class MissingAddressData(InvalidProxyHeader):
+ """
+ The address data is missing or incomplete.
+ """
+
+
+@contextlib.contextmanager
+def convertError(
+ sourceType: Type[BaseException], targetType: Callable[[], BaseException]
+) -> Generator[None, None, None]:
+ """
+ Convert an error into a different error type.
+
+ @param sourceType: The type of exception that should be caught and
+ converted.
+ @type sourceType: L{BaseException}
+
+ @param targetType: The type of exception to which the original should be
+ converted.
+ @type targetType: L{BaseException}
+ """
+ try:
+ yield
+ except sourceType as e:
+ raise targetType().with_traceback(e.__traceback__)
diff --git a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_info.py b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_info.py
new file mode 100644
index 0000000000..9dda6e06ef
--- /dev/null
+++ b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_info.py
@@ -0,0 +1,34 @@
+# -*- test-case-name: twisted.protocols.haproxy.test -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+IProxyInfo implementation.
+"""
+from typing import Optional
+
+from zope.interface import implementer
+
+import attr
+
+from twisted.internet.interfaces import IAddress
+from ._interfaces import IProxyInfo
+
+
+@implementer(IProxyInfo)
+@attr.s(frozen=True, slots=True, auto_attribs=True)
+class ProxyInfo:
+ """
+ A data container for parsed PROXY protocol information.
+
+ @ivar header: The raw header bytes extracted from the connection.
+ @type header: C{bytes}
+ @ivar source: The connection source address.
+ @type source: L{twisted.internet.interfaces.IAddress}
+ @ivar destination: The connection destination address.
+ @type destination: L{twisted.internet.interfaces.IAddress}
+ """
+
+ header: bytes
+ source: Optional[IAddress]
+ destination: Optional[IAddress]
diff --git a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_interfaces.py b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_interfaces.py
new file mode 100644
index 0000000000..8fe90ea37a
--- /dev/null
+++ b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_interfaces.py
@@ -0,0 +1,63 @@
+# -*- test-case-name: twisted.protocols.haproxy.test -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+Interfaces used by the PROXY protocol modules.
+"""
+from typing import Tuple, Union
+
+import zope.interface
+
+
+class IProxyInfo(zope.interface.Interface):
+ """
+ Data container for PROXY protocol header data.
+ """
+
+ header = zope.interface.Attribute(
+ "The raw byestring that represents the PROXY protocol header.",
+ )
+ source = zope.interface.Attribute(
+ "An L{twisted.internet.interfaces.IAddress} representing the "
+ "connection source."
+ )
+ destination = zope.interface.Attribute(
+ "An L{twisted.internet.interfaces.IAddress} representing the "
+ "connection destination."
+ )
+
+
+class IProxyParser(zope.interface.Interface):
+ """
+ Streaming parser that handles PROXY protocol headers.
+ """
+
+ def feed(data: bytes) -> Union[Tuple[IProxyInfo, bytes], Tuple[None, None]]:
+ """
+ Consume a chunk of data and attempt to parse it.
+
+ @param data: A bytestring.
+ @type data: bytes
+
+ @return: A two-tuple containing, in order, an L{IProxyInfo} and any
+ bytes fed to the parser that followed the end of the header. Both
+ of these values are None until a complete header is parsed.
+
+ @raises InvalidProxyHeader: If the bytes fed to the parser create an
+ invalid PROXY header.
+ """
+
+ def parse(line: bytes) -> IProxyInfo:
+ """
+ Parse a bytestring as a full PROXY protocol header line.
+
+ @param line: A bytestring that represents a valid HAProxy PROXY
+ protocol header line.
+ @type line: bytes
+
+ @return: An L{IProxyInfo} containing the parsed data.
+
+ @raises InvalidProxyHeader: If the bytestring does not represent a
+ valid PROXY header.
+ """
diff --git a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_parser.py b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_parser.py
new file mode 100644
index 0000000000..834ccb7354
--- /dev/null
+++ b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_parser.py
@@ -0,0 +1,75 @@
+# -*- test-case-name: twisted.protocols.haproxy.test.test_parser -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+Parser for 'haproxy:' string endpoint.
+"""
+from typing import Mapping, Tuple
+
+from zope.interface import implementer
+
+from twisted.internet import interfaces
+from twisted.internet.endpoints import (
+ IStreamServerEndpointStringParser,
+ _WrapperServerEndpoint,
+ quoteStringArgument,
+ serverFromString,
+)
+from twisted.plugin import IPlugin
+from . import proxyEndpoint
+
+
+def unparseEndpoint(args: Tuple[object, ...], kwargs: Mapping[str, object]) -> str:
+ """
+ Un-parse the already-parsed args and kwargs back into endpoint syntax.
+
+ @param args: C{:}-separated arguments
+
+ @param kwargs: C{:} and then C{=}-separated keyword arguments
+
+ @return: a string equivalent to the original format which this was parsed
+ as.
+ """
+
+ description = ":".join(
+ [quoteStringArgument(str(arg)) for arg in args]
+ + sorted(
+ "{}={}".format(
+ quoteStringArgument(str(key)), quoteStringArgument(str(value))
+ )
+ for key, value in kwargs.items()
+ )
+ )
+ return description
+
+
+@implementer(IPlugin, IStreamServerEndpointStringParser)
+class HAProxyServerParser:
+ """
+ Stream server endpoint string parser for the HAProxyServerEndpoint type.
+
+ @ivar prefix: See L{IStreamServerEndpointStringParser.prefix}.
+ """
+
+ prefix = "haproxy"
+
+ def parseStreamServer(
+ self, reactor: interfaces.IReactorCore, *args: object, **kwargs: object
+ ) -> _WrapperServerEndpoint:
+ """
+ Parse a stream server endpoint from a reactor and string-only arguments
+ and keyword arguments.
+
+ @param reactor: The reactor.
+
+ @param args: The parsed string arguments.
+
+ @param kwargs: The parsed keyword arguments.
+
+ @return: a stream server endpoint
+ @rtype: L{IStreamServerEndpoint}
+ """
+ subdescription = unparseEndpoint(args, kwargs)
+ wrappedEndpoint = serverFromString(reactor, subdescription)
+ return proxyEndpoint(wrappedEndpoint)
diff --git a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_v1parser.py b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_v1parser.py
new file mode 100644
index 0000000000..fed987c33a
--- /dev/null
+++ b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_v1parser.py
@@ -0,0 +1,142 @@
+# -*- test-case-name: twisted.protocols.haproxy.test.test_v1parser -*-
+
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+IProxyParser implementation for version one of the PROXY protocol.
+"""
+from typing import Tuple, Union
+
+from zope.interface import implementer
+
+from twisted.internet import address
+from . import _info, _interfaces
+from ._exceptions import (
+ InvalidNetworkProtocol,
+ InvalidProxyHeader,
+ MissingAddressData,
+ convertError,
+)
+
+
+@implementer(_interfaces.IProxyParser)
+class V1Parser:
+ """
+ PROXY protocol version one header parser.
+
+ Version one of the PROXY protocol is a human readable format represented
+ by a single, newline delimited binary string that contains all of the
+ relevant source and destination data.
+ """
+
+ PROXYSTR = b"PROXY"
+ UNKNOWN_PROTO = b"UNKNOWN"
+ TCP4_PROTO = b"TCP4"
+ TCP6_PROTO = b"TCP6"
+ ALLOWED_NET_PROTOS = (
+ TCP4_PROTO,
+ TCP6_PROTO,
+ UNKNOWN_PROTO,
+ )
+ NEWLINE = b"\r\n"
+
+ def __init__(self) -> None:
+ self.buffer = b""
+
+ def feed(
+ self, data: bytes
+ ) -> Union[Tuple[_info.ProxyInfo, bytes], Tuple[None, None]]:
+ """
+ Consume a chunk of data and attempt to parse it.
+
+ @param data: A bytestring.
+ @type data: L{bytes}
+
+ @return: A two-tuple containing, in order, a
+ L{_interfaces.IProxyInfo} and any bytes fed to the
+ parser that followed the end of the header. Both of these values
+ are None until a complete header is parsed.
+
+ @raises InvalidProxyHeader: If the bytes fed to the parser create an
+ invalid PROXY header.
+ """
+ self.buffer += data
+ if len(self.buffer) > 107 and self.NEWLINE not in self.buffer:
+ raise InvalidProxyHeader()
+ lines = (self.buffer).split(self.NEWLINE, 1)
+ if not len(lines) > 1:
+ return (None, None)
+ self.buffer = b""
+ remaining = lines.pop()
+ header = lines.pop()
+ info = self.parse(header)
+ return (info, remaining)
+
+ @classmethod
+ def parse(cls, line: bytes) -> _info.ProxyInfo:
+ """
+ Parse a bytestring as a full PROXY protocol header line.
+
+ @param line: A bytestring that represents a valid HAProxy PROXY
+ protocol header line.
+ @type line: bytes
+
+ @return: A L{_interfaces.IProxyInfo} containing the parsed data.
+
+ @raises InvalidProxyHeader: If the bytestring does not represent a
+ valid PROXY header.
+
+ @raises InvalidNetworkProtocol: When no protocol can be parsed or is
+ not one of the allowed values.
+
+ @raises MissingAddressData: When the protocol is TCP* but the header
+ does not contain a complete set of addresses and ports.
+ """
+ originalLine = line
+ proxyStr = None
+ networkProtocol = None
+ sourceAddr = None
+ sourcePort = None
+ destAddr = None
+ destPort = None
+
+ with convertError(ValueError, InvalidProxyHeader):
+ proxyStr, line = line.split(b" ", 1)
+
+ if proxyStr != cls.PROXYSTR:
+ raise InvalidProxyHeader()
+
+ with convertError(ValueError, InvalidNetworkProtocol):
+ networkProtocol, line = line.split(b" ", 1)
+
+ if networkProtocol not in cls.ALLOWED_NET_PROTOS:
+ raise InvalidNetworkProtocol()
+
+ if networkProtocol == cls.UNKNOWN_PROTO:
+ return _info.ProxyInfo(originalLine, None, None)
+
+ with convertError(ValueError, MissingAddressData):
+ sourceAddr, line = line.split(b" ", 1)
+
+ with convertError(ValueError, MissingAddressData):
+ destAddr, line = line.split(b" ", 1)
+
+ with convertError(ValueError, MissingAddressData):
+ sourcePort, line = line.split(b" ", 1)
+
+ with convertError(ValueError, MissingAddressData):
+ destPort = line.split(b" ")[0]
+
+ if networkProtocol == cls.TCP4_PROTO:
+ return _info.ProxyInfo(
+ originalLine,
+ address.IPv4Address("TCP", sourceAddr.decode(), int(sourcePort)),
+ address.IPv4Address("TCP", destAddr.decode(), int(destPort)),
+ )
+
+ return _info.ProxyInfo(
+ originalLine,
+ address.IPv6Address("TCP", sourceAddr.decode(), int(sourcePort)),
+ address.IPv6Address("TCP", destAddr.decode(), int(destPort)),
+ )
diff --git a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_v2parser.py b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_v2parser.py
new file mode 100644
index 0000000000..5b8e587401
--- /dev/null
+++ b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_v2parser.py
@@ -0,0 +1,217 @@
+# -*- test-case-name: twisted.protocols.haproxy.test.test_v2parser -*-
+
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+IProxyParser implementation for version two of the PROXY protocol.
+"""
+
+import binascii
+import struct
+from typing import Callable, Tuple, Type, Union
+
+from zope.interface import implementer
+
+from constantly import ValueConstant, Values # type: ignore[import]
+from typing_extensions import Literal
+
+from twisted.internet import address
+from twisted.python import compat
+from . import _info, _interfaces
+from ._exceptions import (
+ InvalidNetworkProtocol,
+ InvalidProxyHeader,
+ MissingAddressData,
+ convertError,
+)
+
+
+class NetFamily(Values):
+ """
+ Values for the 'family' field.
+ """
+
+ UNSPEC = ValueConstant(0x00)
+ INET = ValueConstant(0x10)
+ INET6 = ValueConstant(0x20)
+ UNIX = ValueConstant(0x30)
+
+
+class NetProtocol(Values):
+ """
+ Values for 'protocol' field.
+ """
+
+ UNSPEC = ValueConstant(0)
+ STREAM = ValueConstant(1)
+ DGRAM = ValueConstant(2)
+
+
+_HIGH = 0b11110000
+_LOW = 0b00001111
+_LOCALCOMMAND = "LOCAL"
+_PROXYCOMMAND = "PROXY"
+
+
+@implementer(_interfaces.IProxyParser)
+class V2Parser:
+ """
+ PROXY protocol version two header parser.
+
+ Version two of the PROXY protocol is a binary format.
+ """
+
+ PREFIX = b"\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
+ VERSIONS = [32]
+ COMMANDS = {0: _LOCALCOMMAND, 1: _PROXYCOMMAND}
+ ADDRESSFORMATS = {
+ # TCP4
+ 17: "!4s4s2H",
+ 18: "!4s4s2H",
+ # TCP6
+ 33: "!16s16s2H",
+ 34: "!16s16s2H",
+ # UNIX
+ 49: "!108s108s",
+ 50: "!108s108s",
+ }
+
+ def __init__(self) -> None:
+ self.buffer = b""
+
+ def feed(
+ self, data: bytes
+ ) -> Union[Tuple[_info.ProxyInfo, bytes], Tuple[None, None]]:
+ """
+ Consume a chunk of data and attempt to parse it.
+
+ @param data: A bytestring.
+ @type data: bytes
+
+ @return: A two-tuple containing, in order, a L{_interfaces.IProxyInfo}
+ and any bytes fed to the parser that followed the end of the
+ header. Both of these values are None until a complete header is
+ parsed.
+
+ @raises InvalidProxyHeader: If the bytes fed to the parser create an
+ invalid PROXY header.
+ """
+ self.buffer += data
+ if len(self.buffer) < 16:
+ raise InvalidProxyHeader()
+
+ size = struct.unpack("!H", self.buffer[14:16])[0] + 16
+ if len(self.buffer) < size:
+ return (None, None)
+
+ header, remaining = self.buffer[:size], self.buffer[size:]
+ self.buffer = b""
+ info = self.parse(header)
+ return (info, remaining)
+
+ @staticmethod
+ def _bytesToIPv4(bytestring: bytes) -> bytes:
+ """
+ Convert packed 32-bit IPv4 address bytes into a dotted-quad ASCII bytes
+ representation of that address.
+
+ @param bytestring: 4 octets representing an IPv4 address.
+ @type bytestring: L{bytes}
+
+ @return: a dotted-quad notation IPv4 address.
+ @rtype: L{bytes}
+ """
+ return b".".join(
+ ("%i" % (ord(b),)).encode("ascii") for b in compat.iterbytes(bytestring)
+ )
+
+ @staticmethod
+ def _bytesToIPv6(bytestring: bytes) -> bytes:
+ """
+ Convert packed 128-bit IPv6 address bytes into a colon-separated ASCII
+ bytes representation of that address.
+
+ @param bytestring: 16 octets representing an IPv6 address.
+ @type bytestring: L{bytes}
+
+ @return: a dotted-quad notation IPv6 address.
+ @rtype: L{bytes}
+ """
+ hexString = binascii.b2a_hex(bytestring)
+ return b":".join(
+ (f"{int(hexString[b : b + 4], 16):x}").encode("ascii")
+ for b in range(0, 32, 4)
+ )
+
+ @classmethod
+ def parse(cls, line: bytes) -> _info.ProxyInfo:
+ """
+ Parse a bytestring as a full PROXY protocol header.
+
+ @param line: A bytestring that represents a valid HAProxy PROXY
+ protocol version 2 header.
+ @type line: bytes
+
+ @return: A L{_interfaces.IProxyInfo} containing the
+ parsed data.
+
+ @raises InvalidProxyHeader: If the bytestring does not represent a
+ valid PROXY header.
+ """
+ prefix = line[:12]
+ addrInfo = None
+ with convertError(IndexError, InvalidProxyHeader):
+ # Use single value slices to ensure bytestring values are returned
+ # instead of int in PY3.
+ versionCommand = ord(line[12:13])
+ familyProto = ord(line[13:14])
+
+ if prefix != cls.PREFIX:
+ raise InvalidProxyHeader()
+
+ version, command = versionCommand & _HIGH, versionCommand & _LOW
+ if version not in cls.VERSIONS or command not in cls.COMMANDS:
+ raise InvalidProxyHeader()
+
+ if cls.COMMANDS[command] == _LOCALCOMMAND:
+ return _info.ProxyInfo(line, None, None)
+
+ family, netproto = familyProto & _HIGH, familyProto & _LOW
+ with convertError(ValueError, InvalidNetworkProtocol):
+ family = NetFamily.lookupByValue(family)
+ netproto = NetProtocol.lookupByValue(netproto)
+ if family is NetFamily.UNSPEC or netproto is NetProtocol.UNSPEC:
+ return _info.ProxyInfo(line, None, None)
+
+ addressFormat = cls.ADDRESSFORMATS[familyProto]
+ addrInfo = line[16 : 16 + struct.calcsize(addressFormat)]
+ if family is NetFamily.UNIX:
+ with convertError(struct.error, MissingAddressData):
+ source, dest = struct.unpack(addressFormat, addrInfo)
+ return _info.ProxyInfo(
+ line,
+ address.UNIXAddress(source.rstrip(b"\x00")),
+ address.UNIXAddress(dest.rstrip(b"\x00")),
+ )
+
+ addrType: Union[Literal["TCP"], Literal["UDP"]] = "TCP"
+ if netproto is NetProtocol.DGRAM:
+ addrType = "UDP"
+ addrCls: Union[
+ Type[address.IPv4Address], Type[address.IPv6Address]
+ ] = address.IPv4Address
+ addrParser: Callable[[bytes], bytes] = cls._bytesToIPv4
+ if family is NetFamily.INET6:
+ addrCls = address.IPv6Address
+ addrParser = cls._bytesToIPv6
+
+ with convertError(struct.error, MissingAddressData):
+ info = struct.unpack(addressFormat, addrInfo)
+ source, dest, sPort, dPort = info
+
+ return _info.ProxyInfo(
+ line,
+ addrCls(addrType, addrParser(source).decode(), sPort),
+ addrCls(addrType, addrParser(dest).decode(), dPort),
+ )
diff --git a/contrib/python/Twisted/py3/twisted/protocols/haproxy/_wrapper.py b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_wrapper.py
new file mode 100644
index 0000000000..935dbfa9e2
--- /dev/null
+++ b/contrib/python/Twisted/py3/twisted/protocols/haproxy/_wrapper.py
@@ -0,0 +1,109 @@
+# -*- test-case-name: twisted.protocols.haproxy.test.test_wrapper -*-
+
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+Protocol wrapper that provides HAProxy PROXY protocol support.
+"""
+from typing import Optional, Union
+
+from twisted.internet import interfaces
+from twisted.internet.endpoints import _WrapperServerEndpoint
+from twisted.protocols import policies
+from . import _info
+from ._exceptions import InvalidProxyHeader
+from ._v1parser import V1Parser
+from ._v2parser import V2Parser
+
+
+class HAProxyProtocolWrapper(policies.ProtocolWrapper):
+ """
+ A Protocol wrapper that provides HAProxy support.
+
+ This protocol reads the PROXY stream header, v1 or v2, parses the provided
+ connection data, and modifies the behavior of getPeer and getHost to return
+ the data provided by the PROXY header.
+ """
+
+ def __init__(
+ self, factory: policies.WrappingFactory, wrappedProtocol: interfaces.IProtocol
+ ):
+ super().__init__(factory, wrappedProtocol)
+ self._proxyInfo: Optional[_info.ProxyInfo] = None
+ self._parser: Union[V2Parser, V1Parser, None] = None
+
+ def dataReceived(self, data: bytes) -> None:
+ if self._proxyInfo is not None:
+ return self.wrappedProtocol.dataReceived(data)
+
+ parser = self._parser
+ if parser is None:
+ if (
+ len(data) >= 16
+ and data[:12] == V2Parser.PREFIX
+ and ord(data[12:13]) & 0b11110000 == 0x20
+ ):
+ self._parser = parser = V2Parser()
+ elif len(data) >= 8 and data[:5] == V1Parser.PROXYSTR:
+ self._parser = parser = V1Parser()
+ else:
+ self.loseConnection()
+ return None
+
+ try:
+ self._proxyInfo, remaining = parser.feed(data)
+ if remaining:
+ self.wrappedProtocol.dataReceived(remaining)
+ except InvalidProxyHeader:
+ self.loseConnection()
+
+ def getPeer(self) -> interfaces.IAddress:
+ if self._proxyInfo and self._proxyInfo.source:
+ return self._proxyInfo.source
+ assert self.transport
+ return self.transport.getPeer()
+
+ def getHost(self) -> interfaces.IAddress:
+ if self._proxyInfo and self._proxyInfo.destination:
+ return self._proxyInfo.destination
+ assert self.transport
+ return self.transport.getHost()
+
+
+class HAProxyWrappingFactory(policies.WrappingFactory):
+ """
+ A Factory wrapper that adds PROXY protocol support to connections.
+ """
+
+ protocol = HAProxyProtocolWrapper
+
+ def logPrefix(self) -> str:
+ """
+ Annotate the wrapped factory's log prefix with some text indicating
+ the PROXY protocol is in use.
+
+ @rtype: C{str}
+ """
+ if interfaces.ILoggingContext.providedBy(self.wrappedFactory):
+ logPrefix = self.wrappedFactory.logPrefix()
+ else:
+ logPrefix = self.wrappedFactory.__class__.__name__
+ return f"{logPrefix} (PROXY)"
+
+
+def proxyEndpoint(
+ wrappedEndpoint: interfaces.IStreamServerEndpoint,
+) -> _WrapperServerEndpoint:
+ """
+ Wrap an endpoint with PROXY protocol support, so that the transport's
+ C{getHost} and C{getPeer} methods reflect the attributes of the proxied
+ connection rather than the underlying connection.
+
+ @param wrappedEndpoint: The underlying listening endpoint.
+ @type wrappedEndpoint: L{IStreamServerEndpoint}
+
+ @return: a new listening endpoint that speaks the PROXY protocol.
+ @rtype: L{IStreamServerEndpoint}
+ """
+ return _WrapperServerEndpoint(wrappedEndpoint, HAProxyWrappingFactory)