Ausgabe der neuen DB Einträge

This commit is contained in:
hubobel 2022-01-02 21:50:48 +01:00
parent bad48e1627
commit cfbbb9ee3d
2399 changed files with 843193 additions and 43 deletions

View file

@ -0,0 +1,13 @@
# -*- test-case-name: twisted.protocols.haproxy.test -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
HAProxy PROXY protocol implementations.
"""
from ._wrapper import proxyEndpoint
__all__ = [
'proxyEndpoint',
]

View file

@ -0,0 +1,52 @@
# -*- test-case-name: twisted.protocols.haproxy.test -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
HAProxy specific exceptions.
"""
import contextlib
import sys
from twisted.python import compat
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, targetType):
"""
Convert an error into a different error type.
@param sourceType: The type of exception that should be caught and
converted.
@type sourceType: L{Exception}
@param targetType: The type of exception to which the original should be
converted.
@type targetType: L{Exception}
"""
try:
yield None
except sourceType:
compat.reraise(targetType(), sys.exc_info()[-1])

View file

@ -0,0 +1,36 @@
# -*- test-case-name: twisted.protocols.haproxy.test -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
IProxyInfo implementation.
"""
from zope.interface import implementer
from ._interfaces import IProxyInfo
@implementer(IProxyInfo)
class ProxyInfo(object):
"""
A data container for parsed PROXY protocol information.
@ivar header: The raw header bytes extracted from the connection.
@type header: 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}
"""
__slots__ = (
'header',
'source',
'destination',
)
def __init__(self, header, source, destination):
self.header = header
self.source = source
self.destination = destination

View file

@ -0,0 +1,64 @@
# -*- test-case-name: twisted.protocols.haproxy.test -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Interfaces used by the PROXY protocol modules.
"""
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(self, data):
"""
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(self, line):
"""
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.
"""

View file

@ -0,0 +1,71 @@
# -*- test-case-name: twisted.protocols.haproxy.test.test_parser -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Parser for 'haproxy:' string endpoint.
"""
from zope.interface import implementer
from twisted.plugin import IPlugin
from twisted.internet.endpoints import (
quoteStringArgument, serverFromString, IStreamServerEndpointStringParser
)
from twisted.python.compat import iteritems
from . import proxyEndpoint
def unparseEndpoint(args, kwargs):
"""
Un-parse the already-parsed args and kwargs back into endpoint syntax.
@param args: C{:}-separated arguments
@type args: L{tuple} of native L{str}
@param kwargs: C{:} and then C{=}-separated keyword arguments
@type arguments: L{tuple} of native L{str}
@return: a string equivalent to the original format which this was parsed
as.
@rtype: native L{str}
"""
description = ':'.join(
[quoteStringArgument(str(arg)) for arg in args] +
sorted(['%s=%s' % (quoteStringArgument(str(key)),
quoteStringArgument(str(value)))
for key, value in iteritems(kwargs)
]))
return description
@implementer(IPlugin, IStreamServerEndpointStringParser)
class HAProxyServerParser(object):
"""
Stream server endpoint string parser for the HAProxyServerEndpoint type.
@ivar prefix: See L{IStreamServerEndpointStringParser.prefix}.
"""
prefix = "haproxy"
def parseStreamServer(self, reactor, *args, **kwargs):
"""
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)

View file

@ -0,0 +1,143 @@
# -*- 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 zope.interface import implementer
from twisted.internet import address
from ._exceptions import (
convertError, InvalidProxyHeader, InvalidNetworkProtocol,
MissingAddressData
)
from . import _info
from . import _interfaces
@implementer(_interfaces.IProxyParser)
class V1Parser(object):
"""
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):
self.buffer = b''
def feed(self, data):
"""
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):
"""
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, int(sourcePort)),
address.IPv4Address('TCP', destAddr, int(destPort)),
)
return _info.ProxyInfo(
originalLine,
address.IPv6Address('TCP', sourceAddr, int(sourcePort)),
address.IPv6Address('TCP', destAddr, int(destPort)),
)

View file

@ -0,0 +1,215 @@
# -*- 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 constantly import Values, ValueConstant
from zope.interface import implementer
from twisted.internet import address
from twisted.python import compat
from ._exceptions import (
convertError, InvalidProxyHeader, InvalidNetworkProtocol,
MissingAddressData
)
from . import _info
from . import _interfaces
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(object):
"""
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):
self.buffer = b''
def feed(self, data):
"""
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):
"""
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):
"""
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(
('%x' % (int(hexString[b:b+4], 16),)).encode('ascii')
for b in range(0, 32, 4)
)
@classmethod
def parse(cls, line):
"""
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 = 'TCP'
if netproto is NetProtocol.DGRAM:
addrType = 'UDP'
addrCls = address.IPv4Address
addrParser = 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), sPort),
addrCls(addrType, addrParser(dest), dPort),
)

View file

@ -0,0 +1,106 @@
# -*- 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 twisted.protocols import policies
from twisted.internet import interfaces
from twisted.internet.endpoints import _WrapperServerEndpoint
from ._exceptions import InvalidProxyHeader
from ._v1parser import V1Parser
from ._v2parser import V2Parser
class HAProxyProtocolWrapper(policies.ProtocolWrapper, object):
"""
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, wrappedProtocol):
policies.ProtocolWrapper.__init__(self, factory, wrappedProtocol)
self._proxyInfo = None
self._parser = None
def dataReceived(self, data):
if self._proxyInfo is not None:
return self.wrappedProtocol.dataReceived(data)
if self._parser is None:
if (
len(data) >= 16 and
data[:12] == V2Parser.PREFIX and
ord(data[12:13]) & 0b11110000 == 0x20
):
self._parser = V2Parser()
elif len(data) >= 8 and data[:5] == V1Parser.PROXYSTR:
self._parser = V1Parser()
else:
self.loseConnection()
return None
try:
self._proxyInfo, remaining = self._parser.feed(data)
if remaining:
self.wrappedProtocol.dataReceived(remaining)
except InvalidProxyHeader:
self.loseConnection()
def getPeer(self):
if self._proxyInfo and self._proxyInfo.source:
return self._proxyInfo.source
return self.transport.getPeer()
def getHost(self):
if self._proxyInfo and self._proxyInfo.destination:
return self._proxyInfo.destination
return self.transport.getHost()
class HAProxyWrappingFactory(policies.WrappingFactory):
"""
A Factory wrapper that adds PROXY protocol support to connections.
"""
protocol = HAProxyProtocolWrapper
def logPrefix(self):
"""
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 "%s (PROXY)" % (logPrefix,)
def proxyEndpoint(wrappedEndpoint):
"""
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)

View file

@ -0,0 +1,7 @@
# -*- test-case-name: twisted.protocols.haproxy.test -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Unit tests for L{twisted.protocols.haproxy}.
"""

View file

@ -0,0 +1,132 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Tests for L{twisted.protocols.haproxy._parser}.
"""
from twisted.trial.unittest import SynchronousTestCase as TestCase
from twisted.test.proto_helpers import MemoryReactor
from twisted.internet.endpoints import (
_WrapperServerEndpoint, TCP4ServerEndpoint, TCP6ServerEndpoint,
UNIXServerEndpoint, serverFromString, _parse as parseEndpoint
)
from .._wrapper import HAProxyWrappingFactory
from .._parser import unparseEndpoint
class UnparseEndpointTests(TestCase):
"""
Tests to ensure that un-parsing an endpoint string round trips through
escaping properly.
"""
def check(self, input):
"""
Check that the input unparses into the output, raising an assertion
error if it doesn't.
@param input: an input in endpoint-string-description format. (To
ensure determinism, keyword arguments should be in alphabetical
order.)
@type input: native L{str}
"""
self.assertEqual(unparseEndpoint(*parseEndpoint(input)), input)
def test_basicUnparse(self):
"""
An individual word.
"""
self.check("word")
def test_multipleArguments(self):
"""
Multiple arguments.
"""
self.check("one:two")
def test_keywords(self):
"""
Keyword arguments.
"""
self.check("aleph=one:bet=two")
def test_colonInArgument(self):
"""
Escaped ":".
"""
self.check("hello\\:colon\\:world")
def test_colonInKeywordValue(self):
"""
Escaped ":" in keyword value.
"""
self.check("hello=\\:")
def test_colonInKeywordName(self):
"""
Escaped ":" in keyword name.
"""
self.check("\\:=hello")
class HAProxyServerParserTests(TestCase):
"""
Tests that the parser generates the correct endpoints.
"""
def onePrefix(self, description, expectedClass):
"""
Test the C{haproxy} enpdoint prefix against one sub-endpoint type.
@param description: A string endpoint description beginning with
C{haproxy}.
@type description: native L{str}
@param expectedClass: the expected sub-endpoint class given the
description.
@type expectedClass: L{type}
@return: the parsed endpoint
@rtype: L{IStreamServerEndpoint}
@raise twisted.trial.unittest.Failtest: if the parsed endpoint doesn't
match expectations.
"""
reactor = MemoryReactor()
endpoint = serverFromString(reactor, description)
self.assertIsInstance(endpoint, _WrapperServerEndpoint)
self.assertIsInstance(endpoint._wrappedEndpoint, expectedClass)
self.assertIs(endpoint._wrapperFactory, HAProxyWrappingFactory)
return endpoint
def test_tcp4(self):
"""
Test if the parser generates a wrapped TCP4 endpoint.
"""
self.onePrefix('haproxy:tcp:8080', TCP4ServerEndpoint)
def test_tcp6(self):
"""
Test if the parser generates a wrapped TCP6 endpoint.
"""
self.onePrefix('haproxy:tcp6:8080', TCP6ServerEndpoint)
def test_unix(self):
"""
Test if the parser generates a wrapped UNIX endpoint.
"""
self.onePrefix('haproxy:unix:address=/tmp/socket', UNIXServerEndpoint)

View file

@ -0,0 +1,153 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Test cases for L{twisted.protocols.haproxy.V1Parser}.
"""
from twisted.trial import unittest
from twisted.internet import address
from .._exceptions import (
InvalidProxyHeader, InvalidNetworkProtocol, MissingAddressData
)
from .. import _v1parser
class V1ParserTests(unittest.TestCase):
"""
Test L{twisted.protocols.haproxy.V1Parser} behaviour.
"""
def test_missingPROXYHeaderValue(self):
"""
Test that an exception is raised when the PROXY header is missing.
"""
self.assertRaises(
InvalidProxyHeader,
_v1parser.V1Parser.parse,
b'NOTPROXY ',
)
def test_invalidNetworkProtocol(self):
"""
Test that an exception is raised when the proto is not TCP or UNKNOWN.
"""
self.assertRaises(
InvalidNetworkProtocol,
_v1parser.V1Parser.parse,
b'PROXY WUTPROTO ',
)
def test_missingSourceData(self):
"""
Test that an exception is raised when the proto has no source data.
"""
self.assertRaises(
MissingAddressData,
_v1parser.V1Parser.parse,
b'PROXY TCP4 ',
)
def test_missingDestData(self):
"""
Test that an exception is raised when the proto has no destination.
"""
self.assertRaises(
MissingAddressData,
_v1parser.V1Parser.parse,
b'PROXY TCP4 127.0.0.1 8080 8888',
)
def test_fullParsingSuccess(self):
"""
Test that parsing is successful for a PROXY header.
"""
info = _v1parser.V1Parser.parse(
b'PROXY TCP4 127.0.0.1 127.0.0.1 8080 8888',
)
self.assertIsInstance(info.source, address.IPv4Address)
self.assertEqual(info.source.host, b'127.0.0.1')
self.assertEqual(info.source.port, 8080)
self.assertEqual(info.destination.host, b'127.0.0.1')
self.assertEqual(info.destination.port, 8888)
def test_fullParsingSuccess_IPv6(self):
"""
Test that parsing is successful for an IPv6 PROXY header.
"""
info = _v1parser.V1Parser.parse(
b'PROXY TCP6 ::1 ::1 8080 8888',
)
self.assertIsInstance(info.source, address.IPv6Address)
self.assertEqual(info.source.host, b'::1')
self.assertEqual(info.source.port, 8080)
self.assertEqual(info.destination.host, b'::1')
self.assertEqual(info.destination.port, 8888)
def test_fullParsingSuccess_UNKNOWN(self):
"""
Test that parsing is successful for a UNKNOWN PROXY header.
"""
info = _v1parser.V1Parser.parse(
b'PROXY UNKNOWN anything could go here',
)
self.assertIsNone(info.source)
self.assertIsNone(info.destination)
def test_feedParsing(self):
"""
Test that parsing happens when fed a complete line.
"""
parser = _v1parser.V1Parser()
info, remaining = parser.feed(b'PROXY TCP4 127.0.0.1 127.0.0.1 ')
self.assertFalse(info)
self.assertFalse(remaining)
info, remaining = parser.feed(b'8080 8888')
self.assertFalse(info)
self.assertFalse(remaining)
info, remaining = parser.feed(b'\r\n')
self.assertFalse(remaining)
self.assertIsInstance(info.source, address.IPv4Address)
self.assertEqual(info.source.host, b'127.0.0.1')
self.assertEqual(info.source.port, 8080)
self.assertEqual(info.destination.host, b'127.0.0.1')
self.assertEqual(info.destination.port, 8888)
def test_feedParsingTooLong(self):
"""
Test that parsing fails if no newline is found in 108 bytes.
"""
parser = _v1parser.V1Parser()
info, remaining = parser.feed(b'PROXY TCP4 127.0.0.1 127.0.0.1 ')
self.assertFalse(info)
self.assertFalse(remaining)
info, remaining = parser.feed(b'8080 8888')
self.assertFalse(info)
self.assertFalse(remaining)
self.assertRaises(
InvalidProxyHeader,
parser.feed,
b' ' * 100,
)
def test_feedParsingOverflow(self):
"""
Test that parsing leaves overflow bytes in the buffer.
"""
parser = _v1parser.V1Parser()
info, remaining = parser.feed(
b'PROXY TCP4 127.0.0.1 127.0.0.1 8080 8888\r\nHTTP/1.1 GET /\r\n',
)
self.assertTrue(info)
self.assertEqual(remaining, b'HTTP/1.1 GET /\r\n')
self.assertFalse(parser.buffer)

View file

@ -0,0 +1,380 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Test cases for L{twisted.protocols.haproxy.V2Parser}.
"""
from twisted.trial import unittest
from twisted.internet import address
from .._exceptions import InvalidProxyHeader
from .. import _v2parser
V2_SIGNATURE = b'\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A'
def _makeHeaderIPv6(sig=V2_SIGNATURE, verCom=b'\x21', famProto=b'\x21',
addrLength=b'\x00\x24',
addrs=((b'\x00' * 15) + b'\x01') * 2,
ports=b'\x1F\x90\x22\xB8'):
"""
Construct a version 2 IPv6 header with custom bytes.
@param sig: The protocol signature; defaults to valid L{V2_SIGNATURE}.
@type sig: L{bytes}
@param verCom: Protocol version and command. Defaults to V2 PROXY.
@type verCom: L{bytes}
@param famProto: Address family and protocol. Defaults to AF_INET6/STREAM.
@type famProto: L{bytes}
@param addrLength: Network-endian byte length of payload. Defaults to
description of default addrs/ports.
@type addrLength: L{bytes}
@param addrs: Address payload. Defaults to C{::1} for source and
destination.
@type addrs: L{bytes}
@param ports: Source and destination ports. Defaults to 8080 for source
8888 for destination.
@type ports: L{bytes}
@return: A packet with header, addresses, and ports.
@rtype: L{bytes}
"""
return sig + verCom + famProto + addrLength + addrs + ports
def _makeHeaderIPv4(sig=V2_SIGNATURE, verCom=b'\x21', famProto=b'\x11',
addrLength=b'\x00\x0C',
addrs=b'\x7F\x00\x00\x01\x7F\x00\x00\x01',
ports=b'\x1F\x90\x22\xB8'):
"""
Construct a version 2 IPv4 header with custom bytes.
@param sig: The protocol signature; defaults to valid L{V2_SIGNATURE}.
@type sig: L{bytes}
@param verCom: Protocol version and command. Defaults to V2 PROXY.
@type verCom: L{bytes}
@param famProto: Address family and protocol. Defaults to AF_INET/STREAM.
@type famProto: L{bytes}
@param addrLength: Network-endian byte length of payload. Defaults to
description of default addrs/ports.
@type addrLength: L{bytes}
@param addrs: Address payload. Defaults to 127.0.0.1 for source and
destination.
@type addrs: L{bytes}
@param ports: Source and destination ports. Defaults to 8080 for source
8888 for destination.
@type ports: L{bytes}
@return: A packet with header, addresses, and ports.
@rtype: L{bytes}
"""
return sig + verCom + famProto + addrLength + addrs + ports
def _makeHeaderUnix(sig=V2_SIGNATURE, verCom=b'\x21', famProto=b'\x31',
addrLength=b'\x00\xD8',
addrs=(b'\x2F\x68\x6F\x6D\x65\x2F\x74\x65\x73\x74\x73\x2F'
b'\x6D\x79\x73\x6F\x63\x6B\x65\x74\x73\x2F\x73\x6F'
b'\x63\x6B' + (b'\x00' * 82)) * 2):
"""
Construct a version 2 IPv4 header with custom bytes.
@param sig: The protocol signature; defaults to valid L{V2_SIGNATURE}.
@type sig: L{bytes}
@param verCom: Protocol version and command. Defaults to V2 PROXY.
@type verCom: L{bytes}
@param famProto: Address family and protocol. Defaults to AF_UNIX/STREAM.
@type famProto: L{bytes}
@param addrLength: Network-endian byte length of payload. Defaults to 108
bytes for 2 null terminated paths.
@type addrLength: L{bytes}
@param addrs: Address payload. Defaults to C{/home/tests/mysockets/sock}
for source and destination paths.
@type addrs: L{bytes}
@return: A packet with header, addresses, and8 ports.
@rtype: L{bytes}
"""
return sig + verCom + famProto + addrLength + addrs
class V2ParserTests(unittest.TestCase):
"""
Test L{twisted.protocols.haproxy.V2Parser} behaviour.
"""
def test_happyPathIPv4(self):
"""
Test if a well formed IPv4 header is parsed without error.
"""
header = _makeHeaderIPv4()
self.assertTrue(_v2parser.V2Parser.parse(header))
def test_happyPathIPv6(self):
"""
Test if a well formed IPv6 header is parsed without error.
"""
header = _makeHeaderIPv6()
self.assertTrue(_v2parser.V2Parser.parse(header))
def test_happyPathUnix(self):
"""
Test if a well formed UNIX header is parsed without error.
"""
header = _makeHeaderUnix()
self.assertTrue(_v2parser.V2Parser.parse(header))
def test_invalidSignature(self):
"""
Test if an invalid signature block raises InvalidProxyError.
"""
header = _makeHeaderIPv4(sig=b'\x00'*12)
self.assertRaises(
InvalidProxyHeader,
_v2parser.V2Parser.parse,
header,
)
def test_invalidVersion(self):
"""
Test if an invalid version raises InvalidProxyError.
"""
header = _makeHeaderIPv4(verCom=b'\x11')
self.assertRaises(
InvalidProxyHeader,
_v2parser.V2Parser.parse,
header,
)
def test_invalidCommand(self):
"""
Test if an invalid command raises InvalidProxyError.
"""
header = _makeHeaderIPv4(verCom=b'\x23')
self.assertRaises(
InvalidProxyHeader,
_v2parser.V2Parser.parse,
header,
)
def test_invalidFamily(self):
"""
Test if an invalid family raises InvalidProxyError.
"""
header = _makeHeaderIPv4(famProto=b'\x40')
self.assertRaises(
InvalidProxyHeader,
_v2parser.V2Parser.parse,
header,
)
def test_invalidProto(self):
"""
Test if an invalid protocol raises InvalidProxyError.
"""
header = _makeHeaderIPv4(famProto=b'\x24')
self.assertRaises(
InvalidProxyHeader,
_v2parser.V2Parser.parse,
header,
)
def test_localCommandIpv4(self):
"""
Test that local does not return endpoint data for IPv4 connections.
"""
header = _makeHeaderIPv4(verCom=b'\x20')
info = _v2parser.V2Parser.parse(header)
self.assertFalse(info.source)
self.assertFalse(info.destination)
def test_localCommandIpv6(self):
"""
Test that local does not return endpoint data for IPv6 connections.
"""
header = _makeHeaderIPv6(verCom=b'\x20')
info = _v2parser.V2Parser.parse(header)
self.assertFalse(info.source)
self.assertFalse(info.destination)
def test_localCommandUnix(self):
"""
Test that local does not return endpoint data for UNIX connections.
"""
header = _makeHeaderUnix(verCom=b'\x20')
info = _v2parser.V2Parser.parse(header)
self.assertFalse(info.source)
self.assertFalse(info.destination)
def test_proxyCommandIpv4(self):
"""
Test that proxy returns endpoint data for IPv4 connections.
"""
header = _makeHeaderIPv4(verCom=b'\x21')
info = _v2parser.V2Parser.parse(header)
self.assertTrue(info.source)
self.assertIsInstance(info.source, address.IPv4Address)
self.assertTrue(info.destination)
self.assertIsInstance(info.destination, address.IPv4Address)
def test_proxyCommandIpv6(self):
"""
Test that proxy returns endpoint data for IPv6 connections.
"""
header = _makeHeaderIPv6(verCom=b'\x21')
info = _v2parser.V2Parser.parse(header)
self.assertTrue(info.source)
self.assertIsInstance(info.source, address.IPv6Address)
self.assertTrue(info.destination)
self.assertIsInstance(info.destination, address.IPv6Address)
def test_proxyCommandUnix(self):
"""
Test that proxy returns endpoint data for UNIX connections.
"""
header = _makeHeaderUnix(verCom=b'\x21')
info = _v2parser.V2Parser.parse(header)
self.assertTrue(info.source)
self.assertIsInstance(info.source, address.UNIXAddress)
self.assertTrue(info.destination)
self.assertIsInstance(info.destination, address.UNIXAddress)
def test_unspecFamilyIpv4(self):
"""
Test that UNSPEC does not return endpoint data for IPv4 connections.
"""
header = _makeHeaderIPv4(famProto=b'\x01')
info = _v2parser.V2Parser.parse(header)
self.assertFalse(info.source)
self.assertFalse(info.destination)
def test_unspecFamilyIpv6(self):
"""
Test that UNSPEC does not return endpoint data for IPv6 connections.
"""
header = _makeHeaderIPv6(famProto=b'\x01')
info = _v2parser.V2Parser.parse(header)
self.assertFalse(info.source)
self.assertFalse(info.destination)
def test_unspecFamilyUnix(self):
"""
Test that UNSPEC does not return endpoint data for UNIX connections.
"""
header = _makeHeaderUnix(famProto=b'\x01')
info = _v2parser.V2Parser.parse(header)
self.assertFalse(info.source)
self.assertFalse(info.destination)
def test_unspecProtoIpv4(self):
"""
Test that UNSPEC does not return endpoint data for IPv4 connections.
"""
header = _makeHeaderIPv4(famProto=b'\x10')
info = _v2parser.V2Parser.parse(header)
self.assertFalse(info.source)
self.assertFalse(info.destination)
def test_unspecProtoIpv6(self):
"""
Test that UNSPEC does not return endpoint data for IPv6 connections.
"""
header = _makeHeaderIPv6(famProto=b'\x20')
info = _v2parser.V2Parser.parse(header)
self.assertFalse(info.source)
self.assertFalse(info.destination)
def test_unspecProtoUnix(self):
"""
Test that UNSPEC does not return endpoint data for UNIX connections.
"""
header = _makeHeaderUnix(famProto=b'\x30')
info = _v2parser.V2Parser.parse(header)
self.assertFalse(info.source)
self.assertFalse(info.destination)
def test_overflowIpv4(self):
"""
Test that overflow bits are preserved during feed parsing for IPv4.
"""
testValue = b'TEST DATA\r\n\r\nTEST DATA'
header = _makeHeaderIPv4() + testValue
parser = _v2parser.V2Parser()
info, overflow = parser.feed(header)
self.assertTrue(info)
self.assertEqual(overflow, testValue)
def test_overflowIpv6(self):
"""
Test that overflow bits are preserved during feed parsing for IPv6.
"""
testValue = b'TEST DATA\r\n\r\nTEST DATA'
header = _makeHeaderIPv6() + testValue
parser = _v2parser.V2Parser()
info, overflow = parser.feed(header)
self.assertTrue(info)
self.assertEqual(overflow, testValue)
def test_overflowUnix(self):
"""
Test that overflow bits are preserved during feed parsing for Unix.
"""
testValue = b'TEST DATA\r\n\r\nTEST DATA'
header = _makeHeaderUnix() + testValue
parser = _v2parser.V2Parser()
info, overflow = parser.feed(header)
self.assertTrue(info)
self.assertEqual(overflow, testValue)
def test_segmentTooSmall(self):
"""
Test that an initial payload of less than 16 bytes fails.
"""
testValue = b'NEEDMOREDATA'
parser = _v2parser.V2Parser()
self.assertRaises(
InvalidProxyHeader,
parser.feed,
testValue,
)

View file

@ -0,0 +1,367 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Test cases for L{twisted.protocols.haproxy.HAProxyProtocol}.
"""
from twisted.trial import unittest
from twisted.internet import address
from twisted.internet.protocol import Protocol, Factory
from twisted.test.proto_helpers import StringTransportWithDisconnection
from .._wrapper import HAProxyWrappingFactory
class StaticProtocol(Protocol):
"""
Protocol stand-in that maintains test state.
"""
def __init__(self):
self.source = None
self.destination = None
self.data = b''
self.disconnected = False
def dataReceived(self, data):
self.source = self.transport.getPeer()
self.destination = self.transport.getHost()
self.data += data
class HAProxyWrappingFactoryV1Tests(unittest.TestCase):
"""
Test L{twisted.protocols.haproxy.HAProxyWrappingFactory} with v1 PROXY
headers.
"""
def test_invalidHeaderDisconnects(self):
"""
Test if invalid headers result in connectionLost events.
"""
factory = HAProxyWrappingFactory(Factory.forProtocol(StaticProtocol))
proto = factory.buildProtocol(
address.IPv4Address('TCP', b'127.1.1.1', 8080),
)
transport = StringTransportWithDisconnection()
transport.protocol = proto
proto.makeConnection(transport)
proto.dataReceived(b'NOTPROXY anything can go here\r\n')
self.assertFalse(transport.connected)
def test_invalidPartialHeaderDisconnects(self):
"""
Test if invalid headers result in connectionLost events.
"""
factory = HAProxyWrappingFactory(Factory.forProtocol(StaticProtocol))
proto = factory.buildProtocol(
address.IPv4Address('TCP', b'127.1.1.1', 8080),
)
transport = StringTransportWithDisconnection()
transport.protocol = proto
proto.makeConnection(transport)
proto.dataReceived(b'PROXY TCP4 1.1.1.1\r\n')
proto.dataReceived(b'2.2.2.2 8080\r\n')
self.assertFalse(transport.connected)
def test_validIPv4HeaderResolves_getPeerHost(self):
"""
Test if IPv4 headers result in the correct host and peer data.
"""
factory = HAProxyWrappingFactory(Factory.forProtocol(StaticProtocol))
proto = factory.buildProtocol(
address.IPv4Address('TCP', b'127.0.0.1', 8080),
)
transport = StringTransportWithDisconnection()
proto.makeConnection(transport)
proto.dataReceived(b'PROXY TCP4 1.1.1.1 2.2.2.2 8080 8888\r\n')
self.assertEqual(proto.getPeer().host, b'1.1.1.1')
self.assertEqual(proto.getPeer().port, 8080)
self.assertEqual(
proto.wrappedProtocol.transport.getPeer().host,
b'1.1.1.1',
)
self.assertEqual(
proto.wrappedProtocol.transport.getPeer().port,
8080,
)
self.assertEqual(proto.getHost().host, b'2.2.2.2')
self.assertEqual(proto.getHost().port, 8888)
self.assertEqual(
proto.wrappedProtocol.transport.getHost().host,
b'2.2.2.2',
)
self.assertEqual(
proto.wrappedProtocol.transport.getHost().port,
8888,
)
def test_validIPv6HeaderResolves_getPeerHost(self):
"""
Test if IPv6 headers result in the correct host and peer data.
"""
factory = HAProxyWrappingFactory(Factory.forProtocol(StaticProtocol))
proto = factory.buildProtocol(
address.IPv6Address('TCP', b'::1', 8080),
)
transport = StringTransportWithDisconnection()
proto.makeConnection(transport)
proto.dataReceived(b'PROXY TCP6 ::1 ::2 8080 8888\r\n')
self.assertEqual(proto.getPeer().host, b'::1')
self.assertEqual(proto.getPeer().port, 8080)
self.assertEqual(
proto.wrappedProtocol.transport.getPeer().host,
b'::1',
)
self.assertEqual(
proto.wrappedProtocol.transport.getPeer().port,
8080,
)
self.assertEqual(proto.getHost().host, b'::2')
self.assertEqual(proto.getHost().port, 8888)
self.assertEqual(
proto.wrappedProtocol.transport.getHost().host,
b'::2',
)
self.assertEqual(
proto.wrappedProtocol.transport.getHost().port,
8888,
)
def test_overflowBytesSentToWrappedProtocol(self):
"""
Test if non-header bytes are passed to the wrapped protocol.
"""
factory = HAProxyWrappingFactory(Factory.forProtocol(StaticProtocol))
proto = factory.buildProtocol(
address.IPv6Address('TCP', b'::1', 8080),
)
transport = StringTransportWithDisconnection()
proto.makeConnection(transport)
proto.dataReceived(b'PROXY TCP6 ::1 ::2 8080 8888\r\nHTTP/1.1 / GET')
self.assertEqual(proto.wrappedProtocol.data, b'HTTP/1.1 / GET')
def test_overflowBytesSentToWrappedProtocolChunks(self):
"""
Test if header streaming passes extra data appropriately.
"""
factory = HAProxyWrappingFactory(Factory.forProtocol(StaticProtocol))
proto = factory.buildProtocol(
address.IPv6Address('TCP', b'::1', 8080),
)
transport = StringTransportWithDisconnection()
proto.makeConnection(transport)
proto.dataReceived(b'PROXY TCP6 ::1 ::2 ')
proto.dataReceived(b'8080 8888\r\nHTTP/1.1 / GET')
self.assertEqual(proto.wrappedProtocol.data, b'HTTP/1.1 / GET')
def test_overflowBytesSentToWrappedProtocolAfter(self):
"""
Test if wrapper writes all data to wrapped protocol after parsing.
"""
factory = HAProxyWrappingFactory(Factory.forProtocol(StaticProtocol))
proto = factory.buildProtocol(
address.IPv6Address('TCP', b'::1', 8080),
)
transport = StringTransportWithDisconnection()
proto.makeConnection(transport)
proto.dataReceived(b'PROXY TCP6 ::1 ::2 ')
proto.dataReceived(b'8080 8888\r\nHTTP/1.1 / GET')
proto.dataReceived(b'\r\n\r\n')
self.assertEqual(proto.wrappedProtocol.data, b'HTTP/1.1 / GET\r\n\r\n')
class HAProxyWrappingFactoryV2Tests(unittest.TestCase):
"""
Test L{twisted.protocols.haproxy.HAProxyWrappingFactory} with v2 PROXY
headers.
"""
IPV4HEADER = (
# V2 Signature
b'\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A'
# V2 PROXY command
b'\x21'
# AF_INET/STREAM
b'\x11'
# 12 bytes for 2 IPv4 addresses and two ports
b'\x00\x0C'
# 127.0.0.1 for source and destination
b'\x7F\x00\x00\x01\x7F\x00\x00\x01'
# 8080 for source 8888 for destination
b'\x1F\x90\x22\xB8'
)
IPV6HEADER = (
# V2 Signature
b'\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A'
# V2 PROXY command
b'\x21'
# AF_INET6/STREAM
b'\x21'
# 16 bytes for 2 IPv6 addresses and two ports
b'\x00\x24'
# ::1 for source and destination
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01'
# 8080 for source 8888 for destination
b'\x1F\x90\x22\xB8'
)
_SOCK_PATH = (
b'\x2F\x68\x6F\x6D\x65\x2F\x74\x65\x73\x74\x73\x2F\x6D\x79\x73\x6F'
b'\x63\x6B\x65\x74\x73\x2F\x73\x6F\x63\x6B' + (b'\x00' * 82)
)
UNIXHEADER = (
# V2 Signature
b'\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A'
# V2 PROXY command
b'\x21'
# AF_UNIX/STREAM
b'\x31'
# 108 bytes for 2 null terminated paths
b'\x00\xD8'
# /home/tests/mysockets/sock for source and destination paths
) + _SOCK_PATH + _SOCK_PATH
def test_invalidHeaderDisconnects(self):
"""
Test if invalid headers result in connectionLost events.
"""
factory = HAProxyWrappingFactory(Factory.forProtocol(StaticProtocol))
proto = factory.buildProtocol(
address.IPv6Address('TCP', b'::1', 8080),
)
transport = StringTransportWithDisconnection()
transport.protocol = proto
proto.makeConnection(transport)
proto.dataReceived(b'\x00' + self.IPV4HEADER[1:])
self.assertFalse(transport.connected)
def test_validIPv4HeaderResolves_getPeerHost(self):
"""
Test if IPv4 headers result in the correct host and peer data.
"""
factory = HAProxyWrappingFactory(Factory.forProtocol(StaticProtocol))
proto = factory.buildProtocol(
address.IPv4Address('TCP', b'127.0.0.1', 8080),
)
transport = StringTransportWithDisconnection()
proto.makeConnection(transport)
proto.dataReceived(self.IPV4HEADER)
self.assertEqual(proto.getPeer().host, b'127.0.0.1')
self.assertEqual(proto.getPeer().port, 8080)
self.assertEqual(
proto.wrappedProtocol.transport.getPeer().host,
b'127.0.0.1',
)
self.assertEqual(
proto.wrappedProtocol.transport.getPeer().port,
8080,
)
self.assertEqual(proto.getHost().host, b'127.0.0.1')
self.assertEqual(proto.getHost().port, 8888)
self.assertEqual(
proto.wrappedProtocol.transport.getHost().host,
b'127.0.0.1',
)
self.assertEqual(
proto.wrappedProtocol.transport.getHost().port,
8888,
)
def test_validIPv6HeaderResolves_getPeerHost(self):
"""
Test if IPv6 headers result in the correct host and peer data.
"""
factory = HAProxyWrappingFactory(Factory.forProtocol(StaticProtocol))
proto = factory.buildProtocol(
address.IPv4Address('TCP', b'::1', 8080),
)
transport = StringTransportWithDisconnection()
proto.makeConnection(transport)
proto.dataReceived(self.IPV6HEADER)
self.assertEqual(proto.getPeer().host, b'0:0:0:0:0:0:0:1')
self.assertEqual(proto.getPeer().port, 8080)
self.assertEqual(
proto.wrappedProtocol.transport.getPeer().host,
b'0:0:0:0:0:0:0:1',
)
self.assertEqual(
proto.wrappedProtocol.transport.getPeer().port,
8080,
)
self.assertEqual(proto.getHost().host, b'0:0:0:0:0:0:0:1')
self.assertEqual(proto.getHost().port, 8888)
self.assertEqual(
proto.wrappedProtocol.transport.getHost().host,
b'0:0:0:0:0:0:0:1',
)
self.assertEqual(
proto.wrappedProtocol.transport.getHost().port,
8888,
)
def test_validUNIXHeaderResolves_getPeerHost(self):
"""
Test if UNIX headers result in the correct host and peer data.
"""
factory = HAProxyWrappingFactory(Factory.forProtocol(StaticProtocol))
proto = factory.buildProtocol(
address.UNIXAddress(b'/home/test/sockets/server.sock'),
)
transport = StringTransportWithDisconnection()
proto.makeConnection(transport)
proto.dataReceived(self.UNIXHEADER)
self.assertEqual(proto.getPeer().name, b'/home/tests/mysockets/sock')
self.assertEqual(
proto.wrappedProtocol.transport.getPeer().name,
b'/home/tests/mysockets/sock',
)
self.assertEqual(proto.getHost().name, b'/home/tests/mysockets/sock')
self.assertEqual(
proto.wrappedProtocol.transport.getHost().name,
b'/home/tests/mysockets/sock',
)
def test_overflowBytesSentToWrappedProtocol(self):
"""
Test if non-header bytes are passed to the wrapped protocol.
"""
factory = HAProxyWrappingFactory(Factory.forProtocol(StaticProtocol))
proto = factory.buildProtocol(
address.IPv6Address('TCP', b'::1', 8080),
)
transport = StringTransportWithDisconnection()
proto.makeConnection(transport)
proto.dataReceived(self.IPV6HEADER + b'HTTP/1.1 / GET')
self.assertEqual(proto.wrappedProtocol.data, b'HTTP/1.1 / GET')
def test_overflowBytesSentToWrappedProtocolChunks(self):
"""
Test if header streaming passes extra data appropriately.
"""
factory = HAProxyWrappingFactory(Factory.forProtocol(StaticProtocol))
proto = factory.buildProtocol(
address.IPv6Address('TCP', b'::1', 8080),
)
transport = StringTransportWithDisconnection()
proto.makeConnection(transport)
proto.dataReceived(self.IPV6HEADER[:18])
proto.dataReceived(self.IPV6HEADER[18:] + b'HTTP/1.1 / GET')
self.assertEqual(proto.wrappedProtocol.data, b'HTTP/1.1 / GET')