Commit 19711efb authored by Jason Madden's avatar Jason Madden

Make portforwarder and test__example_portforwarder.py behave the same on...

Make portforwarder and test__example_portforwarder.py behave the same on Windows and POSIX. Hopefully fixes the intermittant failures on Windows.
parent 9152795b
......@@ -11,6 +11,7 @@ it closes the listening socket and waits for all existing
connections to finish. The existing connections will remain unaffected.
The program will exit once the last connection has been closed.
"""
import socket
import sys
import signal
import gevent
......@@ -31,8 +32,8 @@ class PortForwarder(StreamServer):
except IOError as ex:
log('%s:%s failed to connect to %s:%s: %s', address[0], address[1], self.dest[0], self.dest[1], ex)
return
forwarders = (gevent.spawn(forward, source, dest),
gevent.spawn(forward, dest, source))
forwarders = (gevent.spawn(forward, source, dest, self),
gevent.spawn(forward, dest, source, self))
# if we return from this method, the stream will be closed out
# from under us, so wait for our children
gevent.joinall(forwarders)
......@@ -45,19 +46,31 @@ class PortForwarder(StreamServer):
StreamServer.close(self)
def forward(source, dest):
def forward(source, dest, server):
source_address = '%s:%s' % source.getpeername()[:2]
dest_address = '%s:%s' % dest.getpeername()[:2]
try:
while True:
data = source.recv(1024)
log('%s->%s: %r', source_address, dest_address, data)
if not data:
try:
data = source.recv(1024)
log('%s->%s: %r', source_address, dest_address, data)
if not data:
break
dest.sendall(data)
except KeyboardInterrupt:
# On Windows, a Ctrl-C signal (sent by a program) usually winds
# up here, not in the installed signal handler.
if not server.closed:
server.close()
break
except socket.error:
if not server.closed:
server.close()
break
dest.sendall(data)
finally:
source.close()
dest.close()
server = None
def parse_address(address):
......
from __future__ import print_function
from gevent import monkey; monkey.patch_all(subprocess=True)
import signal
import sys
import socket
from time import sleep
......@@ -14,6 +15,12 @@ class Test(util.TestServer):
server = 'portforwarder.py'
args = ['127.0.0.1:10011', '127.0.0.1:10012']
if sys.platform.startswith('win'):
from subprocess import CREATE_NEW_PROCESS_GROUP
# Must be in a new process group to use CTRL_C_EVENT, otherwise
# we get killed too
start_kwargs = {'creationflags': CREATE_NEW_PROCESS_GROUP}
def after(self):
if sys.platform == 'win32':
assert self.popen.poll() is not None
......@@ -37,27 +44,22 @@ class Test(util.TestServer):
conn = socket.create_connection(('127.0.0.1', 10011))
conn.sendall(b'msg1')
sleep(0.1)
self.popen.send_signal(15)
# On Windows, SIGTERM actually abruptly terminates the process;
# it can't be caught. However, CTRL_C_EVENT results in a KeyboardInterrupt
# being raised, so we can shut down properly.
self.popen.send_signal(getattr(signal, 'CTRL_C_EVENT') if hasattr(signal, 'CTRL_C_EVENT')
else signal.SIGTERM)
sleep(0.1)
try:
conn.sendall(b'msg2')
conn.close()
except socket.error:
if sys.platform != 'win32':
raise
# On Windows, signal/15 kills the process rather than actually sends a signal
# so, sendall('msg2') fails with
# error: [Errno 10054] An existing connection was forcibly closed by the remote host
# XXX maybe it could be made working with CTRL_C_EVENT somehow?
with gevent.Timeout(0.1):
conn.sendall(b'msg2')
conn.close()
with gevent.Timeout(1.1):
self.popen.wait()
finally:
server.close()
if sys.platform == 'win32':
self.assertEqual([b'msg1'], log)
else:
self.assertEqual([b'msg1', b'msg2'], log)
self.assertEqual([b'msg1', b'msg2'], log)
if __name__ == '__main__':
......
......@@ -214,9 +214,11 @@ class TestServer(unittest.TestCase):
after_delay = 0.5
popen = None
server = None # subclasses define this to be the path to the server.py
start_kwargs = None
def start(self):
return start([sys.executable, '-u', self.server] + self.args, cwd=self.cwd)
kwargs = self.start_kwargs or {}
return start([sys.executable, '-u', self.server] + self.args, cwd=self.cwd, **kwargs)
def running_server(self):
from contextlib import contextmanager
......
......@@ -81,7 +81,7 @@ if sys.version_info[0] == 3:
ignored_files = ['gevent/_util_py2.py', 'gevent/_socket2.py', 'gevent/_fileobject2.py',
'gevent/builtins.py']
else:
ignored_files = ['gevent/_socket3.py']
ignored_files = ['gevent/_socket3.py', 'gevent/_fileobject3.py']
ignored_files.append('gevent/wsgi.py')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment