One of the rough edges of the tiny port forwarder app that I wrote as an example a while back was that it involved too much socket fiddling. Making things less complicated is Eventlet’s reason for existence, and it is undeniably difficult to get from “I want to do this” to “here’s the functions to call” with the standard socket interface, plus a lot of the defaults are not what you want. These are some of the reasons we added convenience functions back to Eventlet.
So here’s the port forwarder example cleaned up with those convenience functions. It forwards connections received on port 7000 to port 22. Test it out by running ssh -p 7000 localhost and logging in to your own machine. It’s very fast; there’s no perceptible difference between using the forwarder and connecting directly.
import eventlet
def closed_callback():
print "called back"
def forward(source, dest, cb = lambda: None):
while True:
d = source.recv(8096)
if d == '':
cb()
break
dest.sendall(d)
listener = eventlet.listen(('localhost', 7000))
while True:
client, addr = listener.accept()
server = eventlet.connect(('localhost', 22))
eventlet.spawn_n(forward, client, server, closed_callback)
eventlet.spawn_n(forward, server, client)
When using the forwarder to scp a large file around on localhost, I observed nearly the same transfer rates with the forwarder as without: 22 versus 23 MB/s. It consumed very little CPU, as well.
I’m not putting this forward as a production solution; there are clearly far better port forwarders out there, and better solutions to the original problem of needing a callback when the connection closes. The point is to illustrate how you can bang together a pretty damn decent special-purpose application with an astonishingly small amount of work. It’s the duct tape of network programming!
