[Zope-CVS] CVS: Packages/tcpwatch - setup.py:1.2 tcpwatch.py:1.2
Shane Hathaway
shane@zope.com
Tue, 27 May 2003 10:12:40 -0400
Update of /cvs-repository/Packages/tcpwatch
In directory cvs.zope.org:/tmp/cvs-serv19098
Modified Files:
setup.py tcpwatch.py
Log Message:
Merged tseaver-logging-branch with minor changes:
- Indicate to the user that logging is enabled
- Bumped version to 1.2
- Updated TCPWatch homepage URL
=== Packages/tcpwatch/setup.py 1.1 => 1.2 ===
--- Packages/tcpwatch/setup.py:1.1 Sat May 24 13:14:38 2003
+++ Packages/tcpwatch/setup.py Tue May 27 10:12:40 2003
@@ -4,11 +4,11 @@
from distutils.sysconfig import get_python_lib
setup( name="tcpwatch"
- , version="1.1"
+ , version="1.2"
, description="Connection forwarder / HTTP proxy"
, author="Shane Hathaway"
, author_email="shane@zope.com"
- , url="http://cvs.zope.org/Packages/tcpwatch/"
+ , url="http://hathaway.freezope.org/Software/TCPWatch"
, py_modules=[ 'tcpwatch' ]
, scripts=[ 'launch' ]
)
=== Packages/tcpwatch/tcpwatch.py 1.1 => 1.2 ===
--- Packages/tcpwatch/tcpwatch.py:1.1 Sat Mar 2 16:18:21 2002
+++ Packages/tcpwatch/tcpwatch.py Tue May 27 10:12:40 2003
@@ -73,12 +73,13 @@
#############################################################################
from __future__ import nested_scopes
-VERSION = '1.1'
+VERSION = '1.2'
COPYRIGHT = (
- 'TCPWatch %s Copyright 2001, 2002 Shane Hathaway, Zope Corporation'
+ 'TCPWatch %s Copyright 2001 Shane Hathaway, Zope Corporation'
% VERSION)
import sys
+import os
import socket
import asyncore
import getopt
@@ -385,6 +386,74 @@
sys.stdout.flush()
+class RecordingObserver (BasicObserver):
+ """Log request to a file.
+
+ o Filenames mangle connection and transaction numbers from the
+ ForwardedConnectionInfo passed as 'fci'.
+
+ o Decorates an underlying observer, created via the passed 'sub_factory'.
+
+ o Files are created in the supplied 'record_directory'.
+
+ o Unless suppressed, log response and error to corresponding files.
+ """
+ _ERROR_SOURCES = ('Server', 'Client')
+
+ # __implements__ = IConnectionObserver
+
+ def __init__(self, fci, sub_factory, record_directory,
+ record_prefix='watch', record_responses=1, record_errors=1):
+ self._connection_number = fci.connection_number
+ self._transaction = fci.transaction
+ self._decorated = sub_factory(fci)
+ self._directory = record_directory
+ self._prefix = record_prefix
+ self._response = record_responses
+ self._errors = record_errors
+
+ def connected(self, from_client):
+ """See IConnectionObserver.
+ """
+ self._decorated.connected(from_client)
+
+ def received(self, data, from_client):
+ """See IConnectionObserver.
+ """
+ if from_client or self._response:
+ extension = from_client and 'request' or 'response'
+ file = self._openForAppend(extension=extension)
+ file.write(data)
+ file.close()
+ self._decorated.received(data, from_client)
+
+ def closed(self, from_client):
+ """See IConnectionObserver.
+ """
+ self._decorated.closed(from_client)
+
+ def error(self, from_client, type, value):
+ """See IConnectionObserver.
+ """
+ if self._errors:
+ file = self._openForAppend(extension='errors')
+ file.write('(%s) %s: %s\n' % (self._ERROR_SOURCES[from_client],
+ type, value))
+ self._decorated.error(from_client, type, value)
+
+ def _openForAppend(self, extension):
+ """Open a file with the given extension for appending.
+
+ o File should be in the directory indicated by self._directory.
+
+ o File should have a filename '<prefix>_<conn #>.<extension>'.
+ """
+ filename = '%s_%07d%02d.%s' % (self._prefix, self._connection_number,
+ self._transaction, extension)
+ fqpath = os.path.join(self._directory, filename)
+ return open(fqpath, 'a')
+
+
#############################################################################
#
# Tkinter GUI
@@ -1198,6 +1267,20 @@
-c Extra color (colorizes escaped characters)
-r Show carriage returns (ASCII 13)
-s Output to stdout instead of a Tkinter window
+
+Recording options:
+ -R (or --record-directory) <path>
+ Write recorded data to <path>. By default, creates request and
+ response files for each request, and writes a corresponding error file
+ for any error detected by tcpwatch. Requires either running as an
+ HTTP proxy ('-p'), or with splitting turned on ('-h').
+ --record-prefix=<prefix>
+ Use <prefix> as the file prefix for logged request / response / error
+ files (defaults to 'watch').
+ --no-record-responses
+ Suppress writing '.response' files.
+ --no-record-errors
+ Suppress writing '.error' files.
"""
sys.exit()
@@ -1210,7 +1293,15 @@
def main(args):
global show_cr
- optlist, extra = getopt.getopt(args, 'chL:np:rs', ['help', 'http'])
+ try:
+ optlist, extra = getopt.getopt(args, 'chL:np:rsR:',
+ ['help', 'http',
+ 'record-directory=', 'record-prefix='
+ 'no-record-responses',
+ 'no-record-errors',
+ ])
+ except getopt.GetoptError, msg:
+ usageError(msg)
fwd_params = []
proxy_params = []
@@ -1218,6 +1309,11 @@
show_config = 0
split_http = 0
colorized = 1
+ record_directory = None
+ record_prefix = 'watch'
+ record_responses = 1
+ record_errors = 1
+ recording = {}
for option, value in optlist:
if option == '--help':
@@ -1266,10 +1362,21 @@
usageError('-L requires 2, 3, or 4 colon-separated parameters')
fwd_params.append(
(listen_host, listen_port, dest_host, dest_port))
+ elif option == '-R' or option == '--record-directory':
+ record_directory = value
+ elif option == '--record-prefix':
+ record_prefix = value
+ elif option == '--no-record-responses':
+ record_responses = 0
+ elif option == '--no-record-errors':
+ record_errors = 0
if not fwd_params and not proxy_params:
usage()
+ if record_directory and not split_http and not proxy_params:
+ usageError( 'Recording requires enabling either proxy or splitting.' )
+
# Prepare the configuration display.
config_info_lines = []
title_lst = []
@@ -1285,12 +1392,26 @@
lambda args: '%s:%d -> proxy' % args, proxy_params))
if split_http:
config_info_lines.append('HTTP connection splitting enabled.')
+ if record_directory:
+ config_info_lines.append(
+ 'Recording to directory %s.' % record_directory)
config_info = '\n'.join(config_info_lines)
titlepart = ', '.join(title_lst)
if obs_factory is None:
# If no observer factory has been specified, use Tkinter.
obs_factory = setupTk(titlepart, config_info, colorized)
+
+ if record_directory:
+ def _decorateRecorder(fci, sub_factory=obs_factory,
+ record_directory=record_directory,
+ record_prefix=record_prefix,
+ record_responses=record_responses,
+ record_errors=record_errors):
+ return RecordingObserver(fci, sub_factory, record_directory,
+ record_prefix, record_responses,
+ record_errors)
+ obs_factory = _decorateRecorder
chosen_factory = obs_factory
if split_http: