Skip to content

Commit 6299ab9

Browse files
committed
add prefer_ipv6 option, remember if we are in v4 or v6 mode
1 parent 4a30c09 commit 6299ab9

File tree

3 files changed

+80
-12
lines changed

3 files changed

+80
-12
lines changed

README.rst

+8
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ can also specify remote logger by passing the options.
8080
# for remote fluent
8181
logger = sender.FluentSender('app', host='host', port=24224)
8282
83+
The logger will prefer using IPv4 and fall back to IPv6 by default. Should you wish to prefer
84+
IPv6 and fall back to IPv4, specify `prefer_ipv6` option as `True`.
85+
86+
.. code:: python
87+
88+
# for remote fluent preferring IPv6, falling back to IPv4
89+
logger = sender.FluentSender('app', host='host', port=24224, prefer_ipv6=True)
90+
8391
For sending event, call `emit` method with your event. Following example will send the event to
8492
fluentd, with tag 'app.follow' and the attributes 'from' and 'to'.
8593

fluent/sender.py

+20-12
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def __init__(self,
5454
buffer_overflow_handler=None,
5555
nanosecond_precision=False,
5656
msgpack_kwargs=None,
57+
prefer_ipv6=False,
5758
**kwargs):
5859
"""
5960
:param kwargs: This kwargs argument is not used in __init__. This will be removed in the next major version.
@@ -69,6 +70,8 @@ def __init__(self,
6970
self.msgpack_kwargs = {} if msgpack_kwargs is None else msgpack_kwargs
7071

7172
self.socket = None
73+
self.prefer_ipv6 = prefer_ipv6
74+
self.ip_addr_family = None
7275
self.pendings = None
7376
self.lock = threading.Lock()
7477
self._closed = False
@@ -120,12 +123,19 @@ def close(self):
120123
self._close()
121124
self.pendings = None
122125

123-
def _is_ipv4_host(self):
124-
try:
125-
socket.getaddrinfo(self.host, None, socket.AF_INET)
126-
return True
127-
except socket.error:
128-
return False
126+
def _find_ip_addr_family(self):
127+
if not self.prefer_ipv6:
128+
try:
129+
socket.getaddrinfo(self.host, None, socket.AF_INET)
130+
return socket.AF_INET
131+
except socket.error:
132+
return socket.AF_INET6
133+
else:
134+
try:
135+
socket.getaddrinfo(self.host, None, socket.AF_INET6)
136+
return socket.AF_INET6
137+
except socket.error:
138+
return socket.AF_INET
129139

130140
def _make_packet(self, label, timestamp, data):
131141
if label:
@@ -208,12 +218,10 @@ def _reconnect(self):
208218
sock.settimeout(self.timeout)
209219
sock.connect(self.host[len('unix://'):])
210220
else:
211-
if self._is_ipv4_host():
212-
sock = socket.socket(socket.AF_INET,
213-
socket.SOCK_STREAM)
214-
else:
215-
sock = socket.socket(socket.AF_INET6,
216-
socket.SOCK_STREAM)
221+
if not self.ip_addr_family:
222+
self.ip_addr_family = self._find_ip_addr_family()
223+
sock = socket.socket(self.ip_addr_family,
224+
socket.SOCK_STREAM)
217225
sock.settimeout(self.timeout)
218226
# This might be controversial and may need to be removed
219227
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)

tests/test_sender.py

+52
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,25 @@ def recv(self, bufsize, flags=0):
292292
finally:
293293
self._sender.socket = old_sock
294294

295+
def test_ipv6_preferred_but_not_avail(self):
296+
real_getaddrinfo = socket.getaddrinfo
297+
298+
def _fake_getaddrinfo(host, port, family=0, type=0, proto=0, flags=0):
299+
if family == socket.AF_INET6:
300+
raise socket.gaierror("mock: IPv4 Only")
301+
else:
302+
return real_getaddrinfo(host, port, family, type, proto, flags)
303+
with patch('socket.getaddrinfo', side_effect=_fake_getaddrinfo):
304+
sender = fluent.sender.FluentSender(tag='test',
305+
host='localhost',
306+
port=self._server.port,
307+
prefer_ipv6=True)
308+
sender.emit('foo', {'bar': 'baz'})
309+
sender._close()
310+
data = self.get_data()
311+
self.assertEqual(len(data), 1)
312+
self.assertEqual(data[0][2], {'bar': 'baz'})
313+
295314
def test_ipv6_only(self):
296315
# Test if our host supports IPv6 before running this test
297316
try:
@@ -323,6 +342,39 @@ def _fake_getaddrinfo(host, port, family=0, type=0, proto=0, flags=0):
323342
self.assertEqual(len(data), 1)
324343
self.assertEqual(data[0][2], {'bar': 'baz'})
325344

345+
def test_ipv6_preferred(self):
346+
# Test if our host supports IPv6 before running this test
347+
try:
348+
socket.gethostbyaddr('::1')
349+
except socket.herror:
350+
self.skipTest("Host does not support IPv6, cannot run this test")
351+
352+
self.tearDown()
353+
354+
real_getaddrinfo = socket.getaddrinfo
355+
356+
def _fake_getaddrinfo(host, port, family=0, type=0, proto=0, flags=0):
357+
if family == socket.AF_INET:
358+
raise socket.gaierror("mock: IPv6 Only")
359+
else:
360+
return real_getaddrinfo(host, port, family, type, proto, flags)
361+
362+
self._server = mockserver.MockRecvServer(host='localhost',
363+
inet_family=socket.AF_INET6)
364+
365+
366+
with patch('socket.getaddrinfo', side_effect=_fake_getaddrinfo):
367+
sender = fluent.sender.FluentSender(tag='test',
368+
host='localhost',
369+
port=self._server.port,
370+
prefer_ipv6=True)
371+
sender.emit('foo', {'bar': 'baz'})
372+
sender._close()
373+
data = self.get_data()
374+
self.assertEqual(len(data), 1)
375+
self.assertEqual(data[0][2], {'bar': 'baz'})
376+
377+
326378
@unittest.skipIf(sys.platform == "win32", "Unix socket not supported")
327379
def test_unix_socket(self):
328380
self.tearDown()

0 commit comments

Comments
 (0)