convenience: skip SO_REUSEPORT for bind on random port (0)
https://github.com/eventlet/eventlet/issues/411
This commit is contained in:
parent
f72cc96a70
commit
0ec4df6cbe
|
@ -1,6 +1,6 @@
|
|||
import sys
|
||||
import warnings
|
||||
|
||||
from eventlet import greenio
|
||||
from eventlet import greenpool
|
||||
from eventlet import greenthread
|
||||
from eventlet.green import socket
|
||||
|
@ -22,7 +22,11 @@ def connect(addr, family=socket.AF_INET, bind=None):
|
|||
return sock
|
||||
|
||||
|
||||
def listen(addr, family=socket.AF_INET, backlog=50):
|
||||
class ReuseRandomPortWarning(Warning):
|
||||
pass
|
||||
|
||||
|
||||
def listen(addr, family=socket.AF_INET, backlog=50, reuse_addr=True, reuse_port=None):
|
||||
"""Convenience function for opening server sockets. This
|
||||
socket can be used in :func:`~eventlet.serve` or a custom ``accept()`` loop.
|
||||
|
||||
|
@ -38,9 +42,18 @@ def listen(addr, family=socket.AF_INET, backlog=50):
|
|||
:return: The listening green socket object.
|
||||
"""
|
||||
sock = socket.socket(family, socket.SOCK_STREAM)
|
||||
if sys.platform[:3] != "win":
|
||||
if reuse_addr and sys.platform[:3] != 'win':
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
if hasattr(socket, 'SO_REUSEPORT'):
|
||||
if family in (socket.AF_INET, socket.AF_INET6) and addr[1] == 0:
|
||||
if reuse_port:
|
||||
warnings.warn(
|
||||
'''listen on random port (0) with SO_REUSEPORT is dangerous.
|
||||
Double check your intent.
|
||||
Example problem: https://github.com/eventlet/eventlet/issues/411''',
|
||||
ReuseRandomPortWarning, stacklevel=3)
|
||||
elif reuse_port is None:
|
||||
reuse_port = True
|
||||
if reuse_port and hasattr(socket, 'SO_REUSEPORT'):
|
||||
# NOTE(zhengwei): linux kernel >= 3.9
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
||||
sock.bind(addr)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import os
|
||||
import warnings
|
||||
|
||||
import eventlet
|
||||
from eventlet import debug, event
|
||||
from eventlet import convenience, debug
|
||||
from eventlet.green import socket
|
||||
from eventlet.support import six
|
||||
import tests
|
||||
|
@ -90,7 +91,7 @@ class TestServe(tests.LimitedTestCase):
|
|||
gt.wait()
|
||||
|
||||
def test_concurrency(self):
|
||||
evt = event.Event()
|
||||
evt = eventlet.Event()
|
||||
|
||||
def waiter(sock, addr):
|
||||
sock.sendall(b'hi')
|
||||
|
@ -128,18 +129,41 @@ class TestServe(tests.LimitedTestCase):
|
|||
client.sendall(b"echo")
|
||||
self.assertEqual(b"echo", client.recv(1024))
|
||||
|
||||
def test_socket_reuse(self):
|
||||
|
||||
def test_socket_reuse():
|
||||
# pick a free port with bind to 0 - without SO_REUSEPORT
|
||||
# then close it and try to bind to same port with SO_REUSEPORT
|
||||
# loop helps in case something else used the chosen port before second bind
|
||||
addr = None
|
||||
errors = []
|
||||
for _ in range(5):
|
||||
lsock1 = eventlet.listen(('localhost', 0))
|
||||
port = lsock1.getsockname()[1]
|
||||
|
||||
if hasattr(socket, 'SO_REUSEPORT'):
|
||||
lsock2 = eventlet.listen(('localhost', port))
|
||||
else:
|
||||
try:
|
||||
lsock2 = eventlet.listen(('localhost', port))
|
||||
assert lsock2
|
||||
lsock2.close()
|
||||
except socket.error:
|
||||
pass
|
||||
|
||||
addr = lsock1.getsockname()
|
||||
lsock1.close()
|
||||
try:
|
||||
lsock1 = eventlet.listen(addr)
|
||||
except socket.error as e:
|
||||
errors.append(e)
|
||||
continue
|
||||
break
|
||||
else:
|
||||
assert False, errors
|
||||
|
||||
if hasattr(socket, 'SO_REUSEPORT'):
|
||||
lsock2 = eventlet.listen(addr)
|
||||
else:
|
||||
try:
|
||||
lsock2 = eventlet.listen(addr)
|
||||
assert lsock2
|
||||
lsock2.close()
|
||||
except socket.error:
|
||||
pass
|
||||
|
||||
lsock1.close()
|
||||
|
||||
|
||||
def test_reuse_random_port_warning():
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
eventlet.listen(('localhost', 0), reuse_port=True).close()
|
||||
assert len(w) == 1
|
||||
assert issubclass(w[0].category, convenience.ReuseRandomPortWarning)
|
||||
|
|
Loading…
Reference in New Issue