[Zope-CVS] CVS: Packages/tcpwatch - tcpwatch:1.7
Shane Hathaway
shane@cvs.zope.org
Thu, 21 Feb 2002 12:23:59 -0500
Update of /cvs-repository/Packages/tcpwatch
In directory cvs.zope.org:/tmp/cvs-serv26657
Modified Files:
tcpwatch
Log Message:
Added docstrings, comments, and changed a variable name for clarity.
Also provided a "medium color" option which is now the default.
=== Packages/tcpwatch/tcpwatch 1.6 => 1.7 ===
-"""TCPWatch, a connection forwarder for monitoring connections.
+"""TCPWatch, a connection forwarder and HTTP proxy for monitoring connections.
Requires Python 2.1 or above.
"""
@@ -74,7 +74,6 @@
COPYRIGHT = ('TCPWatch %s Copyright 2002 Shane Hathaway, Zope Corporation'
% VERSION)
-
import sys
import socket
import asyncore
@@ -297,7 +296,7 @@
who = 'server'
t = time() - self._start
- min, sec = divmod(int(t), 60)
+ min, sec = divmod(t, 60)
self.write('[%02d:%06.3f - %s %s]\n' % (min, sec, who, m))
self.flush()
@@ -498,14 +497,30 @@
BasicObserver.received(self, data, from_client)
return
output = []
- def append(ss, escaped, output=output,
- from_client=from_client, escape=escape):
- if escaped:
- output.append((escape(ss), from_client
- and 'clientesc' or 'serveresc'))
- else:
- output.append((ss, from_client
- and 'client' or 'server'))
+
+ extra_color = (self._colorized == 2)
+
+ if extra_color:
+ # 4 colors: Change the color client/server and escaped chars
+ def append(ss, escaped, output=output,
+ from_client=from_client, escape=escape):
+ if escaped:
+ output.append((escape(ss), from_client
+ and 'clientesc' or 'serveresc'))
+ else:
+ output.append((ss, from_client
+ and 'client' or 'server'))
+ else:
+ # 2 colors: Only change color for client/server
+ segments = []
+ def append(ss, escaped, segments=segments,
+ escape=escape):
+ if escaped:
+ segments.append(escape(ss))
+ else:
+ segments.append(ss)
+
+ # Escape the input data.
was_escaped = 0
start_idx = 0
for idx in xrange(len(data)):
@@ -521,6 +536,11 @@
if ss:
append(ss, was_escaped)
+ if not extra_color:
+ output.append((''.join(segments),
+ from_client and 'client' or 'server'))
+
+ # Send output to the frame.
self._output.extend(output)
self._frame.updateConnection(self._id, output)
if data.endswith('\n'):
@@ -586,6 +606,7 @@
class StreamedReceiver:
+ """Accepts data up to a specific limit."""
completed = 0
@@ -617,6 +638,7 @@
class UnlimitedReceiver:
+ """Accepts data without limits."""
completed = 0
@@ -627,6 +649,7 @@
class ChunkedReceiver:
+ """Accepts all chunks."""
chunk_remainder = 0
control_line = ''
@@ -707,8 +730,7 @@
class HTTPStreamParser:
- """
- A structure that parses the HTTP stream.
+ """A structure that parses the HTTP stream.
"""
completed = 0 # Set once request is completed.
@@ -726,8 +748,8 @@
self.is_a_request = is_a_request
def received(self, data):
- """
- Receives the HTTP stream for one request.
+ """Receives the HTTP stream for one request.
+
Returns the number of bytes consumed.
Sets the completed flag once both the header and the
body have been received.
@@ -768,9 +790,9 @@
def parse_header(self, header_plus):
- """
- Parses the header_plus block of text (the headers plus the
- first line of the request).
+ """Parses the header_plus block of text.
+
+ (header_plus is the headers plus the first line of the request).
"""
index = header_plus.find('\n')
if index >= 0:
@@ -819,8 +841,7 @@
def get_header_lines(self):
- """
- Splits the header into lines, putting multi-line headers together.
+ """Splits the header into lines, putting multi-line headers together.
"""
r = []
lines = self.header.split('\n')
@@ -836,6 +857,8 @@
class HTTPConnectionSplitter:
+ """Makes a new observer for each HTTP subconnection and forwards events.
+ """
# __implements__ = IConnectionObserver
req_index = 0
@@ -902,25 +925,27 @@
class HTTPProxyResponseBuffer:
- """Ensures that responses on a persistent HTTP connection occur
+ """Ensures that responses to a persistent HTTP connection occur
in the correct order."""
finished = 0
- def __init__(self, proxy_conn, observers=()):
+ def __init__(self, proxy_conn, watching_streams=()):
self.response_parser = HTTPStreamParser(0)
self.proxy_conn = proxy_conn
- self.observers = observers
- self.held = []
+ self.watching_streams = watching_streams
+ self.held = [] # Data held in the buffer
def _isMyTurn(self):
+ """Returns a true value if it's time for this response
+ to respond to the client."""
bufs = self.proxy_conn._response_buffers
if bufs:
return (bufs[0] is self)
return 1
def write(self, data):
- # Received data from the server.
+ """Receives data from the HTTP server to be sent back to the client."""
while data:
parser = self.response_parser
if parser.completed:
@@ -930,16 +955,14 @@
consumed = parser.received(data)
fragment = data[:consumed]
data = data[consumed:]
- for o in self.observers:
- o.write(fragment)
- if not self._isMyTurn():
- # Wait for earlier responses to finish.
- self.held.append(fragment)
- return
+ for s in self.watching_streams:
+ s.write(fragment)
+ self.held.append(fragment)
self.flush()
- self.proxy_conn.write(fragment)
def flush(self):
+ """Flushes buffers and, if done, allows the next response to take over.
+ """
if self.held and self._isMyTurn():
data = ''.join(self.held)
del self.held[:]
@@ -949,21 +972,25 @@
if bufs and bufs[0] is self:
del bufs[0]
if bufs:
- bufs[0].flush() # kick.
+ bufs[0].flush() # kick!
def close(self):
- for o in self.observers:
- o.close()
+ """The HTTP server closed the connection.
+ """
+ for s in self.watching_streams:
+ s.close()
self.finished = 1
self.flush()
def error(self, t, v):
- for o in self.observers:
- o.error(t, v)
+ for s in self.watching_streams:
+ if hasattr(s, 'error'):
+ s.error(t, v)
class HTTPProxyConnection (ForwardingEndpoint):
+ """A connection from a client to the proxy server"""
_req_parser = None
_transaction = 0
@@ -978,18 +1005,20 @@
self._newRequest()
def _newRequest(self):
+ """Starts a new request on a persistent connection."""
if self._req_parser is None:
self._req_parser = HTTPStreamParser(1)
factory = self._obs_factory
if factory is not None:
fci = ForwardedConnectionInfo(self._counter, self._client_addr)
- obs = factory(fci)
self._transaction = self._transaction + 1
- obs.transaction = self._transaction
+ fci.transaction = self._transaction
+ obs = factory(fci)
self._obs = obs
self.set_dests((EndpointObserver(obs, 1),))
def received(self, data):
+ """Accepts data received from the client."""
while data:
parser = self._req_parser
if parser is None:
@@ -1008,9 +1037,12 @@
self._req_parser = None
def openProxyConnection(self, request):
+ """Parses the client connection and opens a connection to an
+ HTTP server.
+ """
first_line = request.first_line.strip()
if not ' ' in first_line:
- raise ValueError, 'Malformed request'
+ raise ValueError, ('Malformed request: %s' % first_line)
command, url = first_line.split(' ', 1)
pos = url.rfind(' HTTP/')
if pos >= 0:
@@ -1032,7 +1064,7 @@
host = request.headers.get('HOST')
path = url
if not host:
- raise ValueError, 'Request not supported'
+ raise ValueError, ('Request type not supported: %s' % url)
if ':' in host:
host, port = host.split(':')
@@ -1050,6 +1082,7 @@
ep = ForwardingEndpoint() # connects server to buf (to self)
ep.write('%s %s %s\r\n' % (command, path, protocol))
+ # Duplicate the headers sent by the client.
ep.write(request.header)
ep.write('\r\n')
ep.set_dests((buf,))
@@ -1059,6 +1092,7 @@
class HTTPProxyService (asyncore.dispatcher):
+ """A minimal HTTP proxy server"""
_counter = 0
@@ -1111,6 +1145,7 @@
Output options:
-n No color in GUI (faster and consumes less RAM)
+ -c Extra color (colorizes escaped characters)
-s Output to stdout instead of a Tkinter window
"""
sys.exit()
@@ -1123,7 +1158,7 @@
def main(args):
- optlist, extra = getopt.getopt(args, 'hL:np:s', ['help', 'http'])
+ optlist, extra = getopt.getopt(args, 'chL:np:s', ['help', 'http'])
fwd_params = []
proxy_params = []
@@ -1139,6 +1174,8 @@
split_http = 1
elif option == '-n':
colorized = 0
+ elif option == '-c':
+ colorized = 2
elif option == '-s':
show_config = 1
obs_factory = StdoutObserver
@@ -1155,6 +1192,7 @@
usageError('-p requires a port or a host:port parameter')
proxy_params.append((listen_host, listen_port))
elif option == '-L':
+ # TCP forwarder
info = value.split(':')
listen_host = ''
dest_host = ''
@@ -1178,6 +1216,7 @@
if not fwd_params and not proxy_params:
usage()
+ # Prepare the configuration display.
config_info_lines = []
title_lst = []
if fwd_params:
@@ -1196,22 +1235,25 @@
titlepart = ', '.join(title_lst)
if obs_factory is None:
- # Use Tk by default.
+ # If no observer factory has been specified, use Tkinter.
obs_factory = setupTk(titlepart, config_info, colorized)
chosen_factory = obs_factory
if split_http:
+ # Put an HTTPConnectionSplitter between the events and the output.
def _factory(fci, sub_factory=obs_factory):
return HTTPConnectionSplitter(sub_factory, fci)
chosen_factory = _factory
services = []
try:
+ # Start forwarding services.
for params in fwd_params:
args = params + (chosen_factory,)
s = ForwardingService(*args)
services.append(s)
+ # Start proxy services.
for params in proxy_params:
args = params + (obs_factory,) # Don't parse HTTP twice.
s = HTTPProxyService(*args)
@@ -1220,6 +1262,7 @@
if show_config:
print config_info
+ # Run the main loop.
try:
asyncore.loop(timeout=1.0)
except KeyboardInterrupt: