aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Nordberg <linus@torproject.org>2013-06-05 15:48:57 +0200
committerLinus Nordberg <linus@torproject.org>2013-06-18 13:54:22 +0200
commit69301e1cb215576ff116b285f9098c243d1eeaf5 (patch)
tree889ac75a04897ad56241df23ad679396cb3af025
parentacb1aa3c2987eab96f86df87c26ddeaa7c646d45 (diff)
downloadchutney-69301e1cb215576ff116b285f9098c243d1eeaf5.tar
chutney-69301e1cb215576ff116b285f9098c243d1eeaf5.tar.gz
Add some documentation and comments.
-rw-r--r--lib/chutney/Traffic.py49
1 files changed, 47 insertions, 2 deletions
diff --git a/lib/chutney/Traffic.py b/lib/chutney/Traffic.py
index 9b0a1f7..ab9a578 100644
--- a/lib/chutney/Traffic.py
+++ b/lib/chutney/Traffic.py
@@ -6,19 +6,38 @@
# restrict, so long as you retain the above notice(s) and this license
# in all redistributed copies and derived works. There is no warranty.
+# Do select/read/write for binding to a port, connecting to it and
+# write, read what's written and verify it. You can connect over a
+# SOCKS proxy (like Tor).
+#
+# You can create a TrafficTester and give it an IP address/host and
+# port to bind to. If a Source is created and added to the
+# TrafficTester, it will connect to the address/port it was given at
+# instantiation and send its data. A Source can be configured to
+# connect over a SOCKS proxy. When everything is set up, you can
+# invoke TrafficTester.run() to start running. The TrafficTester will
+# accept the incoming connection and read from it, verifying the data.
+#
+# For example code, see main() below.
+
import socket
import select
import struct
import errno
+# Set debug_flag=True in order to debug this program or to get hints
+# about what's going wrong in your system.
debug_flag = False
def debug(s):
+ "Print a debug message on stdout if debug_flag is True."
if debug_flag:
print("DEBUG: %s" % s)
def socks_cmd(addr_port):
"""
+ Return a SOCKS command for connecting to addr_port.
+
SOCKSv4: https://en.wikipedia.org/wiki/SOCKS#Protocol
SOCKSv5: RFC1928, RFC1929
"""
@@ -63,6 +82,7 @@ class TestSuite(object):
return('%d/%d/%d' % (self.not_done, self.successes, self.failures))
class Peer(object):
+ "Base class for Listener, Source and Sink."
LISTENER = 1
SOURCE = 2
SINK = 3
@@ -84,6 +104,7 @@ class Peer(object):
return self.type == self.SINK
class Listener(Peer):
+ "A TCP listener, binding, listening and accepting new connections."
def __init__(self, tt, endpoint):
super(Listener, self).__init__(Peer.LISTENER, tt)
self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
@@ -97,11 +118,17 @@ class Listener(Peer):
self.tt.add(Sink(self.tt, newsock))
class Sink(Peer):
+ "A data sink, reading from its peer and verifying the data."
def __init__(self, tt, s):
super(Sink, self).__init__(Peer.SINK, tt, s)
self.inbuf = ''
def on_readable(self):
+ """Invoked when the socket becomes readable.
+ Return 0 on finished, successful verification.
+ -1 on failed verification
+ >0 if more data needs to be read
+ """
return self.verify(self.tt.data)
def verify(self, data):
@@ -115,6 +142,8 @@ class Sink(Peer):
return len(data) - len(self.inbuf)
class Source(Peer):
+ """A data source, connecting to a TCP server, optionally over a
+ SOCKS proxy, sending data."""
NOT_CONNECTED = 0
CONNECTING = 1
CONNECTING_THROUGH_PROXY = 2
@@ -143,6 +172,10 @@ class Source(Peer):
raise
def on_readable(self):
+ """Invoked when the socket becomes readable.
+ Return -1 on failure
+ >0 if more data needs to be read or written
+ """
if self.state == self.CONNECTING_THROUGH_PROXY:
self.inbuf += self.s.recv(8 - len(self.inbuf))
if len(self.inbuf) == 8:
@@ -168,6 +201,11 @@ class Source(Peer):
return False
def on_writable(self):
+ """Invoked when the socket becomes writable.
+ Return 0 when done writing
+ -1 on failure (like connection refused)
+ >0 if more data needs to be written
+ """
if self.state == self.CONNECTING:
if self.proxy is None:
self.state = self.CONNECTED
@@ -189,6 +227,13 @@ class Source(Peer):
return len(self.outbuf) # When 0, we're being removed.
class TrafficTester():
+ """
+ Hang on select.select() and dispatch to Sources and Sinks.
+ Time out after self.timeout seconds.
+ Keep track of successful and failed data verification using a
+ TestSuite.
+ Return True if all tests succeed, else False.
+ """
def __init__(self, endpoint, data={}, timeout=3):
self.listener = Listener(self, endpoint)
self.pending_close = []
@@ -246,8 +291,8 @@ class TrafficTester():
self.remove(p)
for fd in sets[1]: # writable fd's
- p = self.peers.get(fd) # Might have been removed above.
- if p is not None:
+ p = self.peers.get(fd)
+ if p is not None: # Might have been removed above.
n = p.on_writable()
if n == 0:
self.remove(p)