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 ...@@ -11,6 +11,7 @@ it closes the listening socket and waits for all existing
connections to finish. The existing connections will remain unaffected. connections to finish. The existing connections will remain unaffected.
The program will exit once the last connection has been closed. The program will exit once the last connection has been closed.
""" """
import socket
import sys import sys
import signal import signal
import gevent import gevent
...@@ -31,8 +32,8 @@ class PortForwarder(StreamServer): ...@@ -31,8 +32,8 @@ class PortForwarder(StreamServer):
except IOError as ex: 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) log('%s:%s failed to connect to %s:%s: %s', address[0], address[1], self.dest[0], self.dest[1], ex)
return return
forwarders = (gevent.spawn(forward, source, dest), forwarders = (gevent.spawn(forward, source, dest, self),
gevent.spawn(forward, dest, source)) gevent.spawn(forward, dest, source, self))
# if we return from this method, the stream will be closed out # if we return from this method, the stream will be closed out
# from under us, so wait for our children # from under us, so wait for our children
gevent.joinall(forwarders) gevent.joinall(forwarders)
...@@ -45,19 +46,31 @@ class PortForwarder(StreamServer): ...@@ -45,19 +46,31 @@ class PortForwarder(StreamServer):
StreamServer.close(self) StreamServer.close(self)
def forward(source, dest): def forward(source, dest, server):
source_address = '%s:%s' % source.getpeername()[:2] source_address = '%s:%s' % source.getpeername()[:2]
dest_address = '%s:%s' % dest.getpeername()[:2] dest_address = '%s:%s' % dest.getpeername()[:2]
try: try:
while True: while True:
try:
data = source.recv(1024) data = source.recv(1024)
log('%s->%s: %r', source_address, dest_address, data) log('%s->%s: %r', source_address, dest_address, data)
if not data: if not data:
break break
dest.sendall(data) 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
finally: finally:
source.close() source.close()
dest.close() dest.close()
server = None
def parse_address(address): def parse_address(address):
......
from __future__ import print_function from __future__ import print_function
from gevent import monkey; monkey.patch_all(subprocess=True) from gevent import monkey; monkey.patch_all(subprocess=True)
import signal
import sys import sys
import socket import socket
from time import sleep from time import sleep
...@@ -14,6 +15,12 @@ class Test(util.TestServer): ...@@ -14,6 +15,12 @@ class Test(util.TestServer):
server = 'portforwarder.py' server = 'portforwarder.py'
args = ['127.0.0.1:10011', '127.0.0.1:10012'] 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): def after(self):
if sys.platform == 'win32': if sys.platform == 'win32':
assert self.popen.poll() is not None assert self.popen.poll() is not None
...@@ -37,26 +44,21 @@ class Test(util.TestServer): ...@@ -37,26 +44,21 @@ class Test(util.TestServer):
conn = socket.create_connection(('127.0.0.1', 10011)) conn = socket.create_connection(('127.0.0.1', 10011))
conn.sendall(b'msg1') conn.sendall(b'msg1')
sleep(0.1) 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) sleep(0.1)
try:
conn.sendall(b'msg2') conn.sendall(b'msg2')
conn.close() conn.close()
except socket.error:
if sys.platform != 'win32': with gevent.Timeout(1.1):
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):
self.popen.wait() self.popen.wait()
finally: finally:
server.close() 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)
......
...@@ -214,9 +214,11 @@ class TestServer(unittest.TestCase): ...@@ -214,9 +214,11 @@ class TestServer(unittest.TestCase):
after_delay = 0.5 after_delay = 0.5
popen = None popen = None
server = None # subclasses define this to be the path to the server.py server = None # subclasses define this to be the path to the server.py
start_kwargs = None
def start(self): 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): def running_server(self):
from contextlib import contextmanager from contextlib import contextmanager
......
...@@ -81,7 +81,7 @@ if sys.version_info[0] == 3: ...@@ -81,7 +81,7 @@ if sys.version_info[0] == 3:
ignored_files = ['gevent/_util_py2.py', 'gevent/_socket2.py', 'gevent/_fileobject2.py', ignored_files = ['gevent/_util_py2.py', 'gevent/_socket2.py', 'gevent/_fileobject2.py',
'gevent/builtins.py'] 'gevent/builtins.py']
else: else:
ignored_files = ['gevent/_socket3.py'] ignored_files = ['gevent/_socket3.py', 'gevent/_fileobject3.py']
ignored_files.append('gevent/wsgi.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