tcpclient,netutil: Set FD_CLOEXEC on all sockets created by Tornado

PR #1984 was based on the mistaken belief that we were already
doing this (and in python 3.4+, it's true, thanks to PEP 446). This
fixes a regression introduced in Tornado 4.5 in which autoreload would
leak file descriptors and leave client connections hanging.

Fixes #2057
This commit is contained in:
Ben Darnell 2017-05-28 20:10:01 -04:00
parent 79b2683437
commit c85dfd2e11
3 changed files with 7 additions and 0 deletions

View File

@ -273,6 +273,7 @@ def add_accept_handler(sock, callback, io_loop=None):
if errno_from_exception(e) == errno.ECONNABORTED:
continue
raise
set_close_exec(connection.fileno())
callback(connection, address)
io_loop.add_handler(sock, accept_handler, IOLoop.READ)

View File

@ -32,10 +32,12 @@ class Waker(interface.Waker):
and Jython.
"""
def __init__(self):
from .auto import set_close_exec
# Based on Zope select_trigger.py:
# https://github.com/zopefoundation/Zope/blob/master/src/ZServer/medusa/thread/select_trigger.py
self.writer = socket.socket()
set_close_exec(self.writer.fileno())
# Disable buffering -- pulling the trigger sends 1 byte,
# and we want that sent immediately, to wake up ASAP.
self.writer.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
@ -54,6 +56,7 @@ class Waker(interface.Waker):
# http://mail.zope.org/pipermail/zope/2005-July/160433.html
# for hideous details.
a = socket.socket()
set_close_exec(a.fileno())
a.bind(("127.0.0.1", 0))
a.listen(1)
connect_address = a.getsockname() # assigned (host, port) pair
@ -78,6 +81,7 @@ class Waker(interface.Waker):
a.close()
self.reader, addr = a.accept()
set_close_exec(self.reader.fileno())
self.reader.setblocking(0)
self.writer.setblocking(0)
a.close()

View File

@ -26,6 +26,7 @@ from tornado.ioloop import IOLoop
from tornado.iostream import IOStream
from tornado import gen
from tornado.netutil import Resolver
from tornado.platform.auto import set_close_exec
_INITIAL_CONNECT_TIMEOUT = 0.3
@ -202,6 +203,7 @@ class TCPClient(object):
# - 127.0.0.1 for IPv4
# - ::1 for IPv6
socket_obj = socket.socket(af)
set_close_exec(socket_obj.fileno())
if source_port_bind or source_ip_bind:
# If the user requires binding also to a specific IP/port.
try: