[Zope-CVS] CVS: Packages/tcpwatch - tcpwatch:1.5
Shane Hathaway
shane@cvs.zope.org
Thu, 21 Feb 2002 00:08:18 -0500
Update of /cvs-repository/Packages/tcpwatch
In directory cvs.zope.org:/tmp/cvs-serv16075
Modified Files:
tcpwatch
Log Message:
Functional proxy (needs further testing)
=== Packages/tcpwatch/tcpwatch 1.4 => 1.5 ===
def received(self, data):
- for d in self._dests:
- d.write(data)
+ if data:
+ for d in self._dests:
+ d.write(data)
def handle_read(self):
data = self.recv(RECV_BUFFER_SIZE)
- if data:
- self.received(data)
+ self.received(data)
def handle_write(self):
if not self.connected:
@@ -382,7 +382,7 @@
#############################################################################
-def setupTk(fwd_params, config_info, colorized=1):
+def setupTk(titlepart, config_info, colorized=1):
"""Starts the Tk application and returns an observer factory.
"""
@@ -533,19 +533,17 @@
- def createApp(fwd_params):
+ def createApp(titlepart):
app = TkTCPWatch()
try:
wm_title = app.master.wm_title
except AttributeError:
pass # No wm_title method available.
else:
- titlepart = ', '.join(map(
- lambda args: '%s:%d -> %s:%d' % args, fwd_params))
wm_title('TCPWatch [%s]' % titlepart)
return app
- app = createApp(fwd_params)
+ app = createApp(titlepart)
def tkObserverFactory(fci, app=app, colorized=colorized):
return TkConnectionObserver(app, fci, colorized)
@@ -594,6 +592,8 @@
def __init__(self, cl, buf=None):
self.remain = cl
self.buf = buf
+ if cl < 1:
+ self.completed = 1
def received(self, data):
rm = self.remain
@@ -711,7 +711,7 @@
A structure that parses the HTTP stream.
"""
- completed = 0 # Set once request is completed.
+ completed = 0 # Set once request is completed.
empty = 0 # Set if no request was made.
header_plus = ''
chunked = 0
@@ -752,7 +752,7 @@
self.completed = 1
else:
self.parse_header(header_plus)
- if self.body_rcv is None:
+ if self.body_rcv is None or self.body_rcv.completed:
self.completed = 1
return consumed
else:
@@ -940,7 +940,7 @@
def flush(self):
if self.held and self._isMyTurn():
- data = ''.join(self.held) + data
+ data = ''.join(self.held)
del self.held[:]
self.proxy_conn.write(data)
if self.finished:
@@ -957,16 +957,21 @@
self.finished = 1
self.flush()
+ def error(self, t, v):
+ observer_stream = self.observer_stream
+ if observer_stream is not None:
+ observer_stream.error(t, v)
+
class HTTPProxyConnection (ForwardingEndpoint):
_req_parser = None
- _obs = None
_transaction = 0
+ _obs = None
def __init__(self, conn, factory, counter, addr):
- asyncore.dispatcher.__init__(self, conn)
+ ForwardingEndpoint.__init__(self, conn)
self._obs_factory = factory
self._counter = counter
self._client_addr = addr
@@ -983,6 +988,7 @@
self._transaction = self._transaction + 1
obs.transaction = self._transaction
self._obs = obs
+ self.set_dests((EndpointObserver(obs, 1),))
def received(self, data):
while data:
@@ -991,31 +997,43 @@
# Begin another request.
self._newRequest()
parser = self._req_parser
- obs = self._obs
if not parser.completed:
- # Not yet connected to a server.
+ # Waiting for a complete request.
consumed = parser.received(data)
- if obs is not None:
- obs.received(data[:consumed], 1)
- data = data[:consumed]
+ ForwardingEndpoint.received(self, data[:consumed])
+ data = data[consumed:]
if parser.completed:
- # Connected to a server.
+ # Connect to a server.
self.openProxyConnection(parser)
# Expect a new request or a closed connection.
- self._obs = None
self._req_parser = None
def openProxyConnection(self, request):
- url = request.first_line.strip().split(' ', 1)[-1]
+ first_line = request.first_line.strip()
+ if not ' ' in first_line:
+ raise ValueError, 'Malformed request'
+ command, url = first_line.split(' ', 1)
pos = url.rfind(' HTTP/')
if pos >= 0:
+ protocol = url[pos + 1:]
url = url[:pos].rstrip()
+ else:
+ protocol = 'HTTP/1.0'
if url.startswith('http://'):
- host = url[7:].split('/', 1)[0]
+ # Standard proxy
+ urlpart = url[7:]
+ if '/' in urlpart:
+ host, path = url[7:].split('/', 1)
+ path = '/' + path
+ else:
+ host = urlpart
+ path = '/'
else:
+ # Transparent proxy
host = request.headers.get('HOST')
+ path = url
if not host:
- raise NotImplementedError, 'Not a full HTTP proxy'
+ raise ValueError, 'Request not supported'
if ':' in host:
host, port = host.split(':')
@@ -1024,7 +1042,6 @@
port = 80
obs = self._obs
-
if obs is not None:
eo = EndpointObserver(obs, 0)
buf = HTTPProxyResponseBuffer(self, eo)
@@ -1033,8 +1050,13 @@
self._response_buffers.append(buf)
ep = ForwardingEndpoint() # connects server to buf (to self)
+ ep.write('%s %s %s\r\n' % (command, path, protocol))
+ ep.write(request.header)
+ ep.write('\r\n')
ep.set_dests((buf,))
ep.create_socket(socket.AF_INET, socket.SOCK_STREAM)
+ print 'connecting to', repr((host, port))
+ print repr(request.header_plus)
ep.connect((host, port))
@@ -1075,8 +1097,10 @@
def usage():
print COPYRIGHT
+ print 'Utility for monitoring TCP and HTTP connections'
print 'Simple usage: tcpwatch -L listen_port:dest_hostname:dest_port'
print """
+Forwarded connection setup:
-L <listen_port>:<dest_port>
Set up a local forwarded connection
-L <listen_port>:<dest_host>:<dest_port>
@@ -1084,10 +1108,13 @@
-L <listen_host>:<listen_port>:<dest_host>:<dest_port>
Set up a forwarded connection to a specified host, bound to an interface
+HTTP setup:
-h (or --http) Parse as HTTP, splitting up multi-transaction connections
-p [<listen_host>:]<listen_port> Run an HTTP proxy (implies -h)
+
+Output options:
+ -n No color in GUI (faster and consumes less RAM)
-s Output to stdout instead of a Tkinter window
- -n No color in GUI (consumes less memory)
"""
sys.exit()
@@ -1102,6 +1129,7 @@
optlist, extra = getopt.getopt(args, 'hL:np:s', ['help', 'http'])
fwd_params = []
+ proxy_params = []
obs_factory = None
show_config = 0
split_http = 0
@@ -1117,20 +1145,18 @@
elif option == '-s':
show_config = 1
obs_factory = StdoutObserver
-## elif option == '-p':
-## # HTTP proxy (also turns on -h)
-## info = value.split(':')
-## listen_host = ''
-## if len(info) == 1:
-## listen_port = int(info[0])
-## elif len(info) == 2:
-## listen_host = info[0]
-## listen_port = int(info[1])
-## else:
-## usageError('-p requires a port or a host:port parameter')
-## proxy_params.append(
-## (listen_host, listen_port, '(HTTP)', 0))
-## split_http = 1
+ elif option == '-p':
+ # HTTP proxy
+ info = value.split(':')
+ listen_host = ''
+ if len(info) == 1:
+ listen_port = int(info[0])
+ elif len(info) == 2:
+ listen_host = info[0]
+ listen_port = int(info[1])
+ else:
+ usageError('-p requires a port or a host:port parameter')
+ proxy_params.append((listen_host, listen_port))
elif option == '-L':
info = value.split(':')
listen_host = ''
@@ -1152,37 +1178,55 @@
fwd_params.append(
(listen_host, listen_port, dest_host, dest_port))
- if not fwd_params:
+ if not fwd_params and not proxy_params:
usage()
- config_info = '\n'.join(map(
- lambda args: 'Forwarding %s:%d -> %s:%d' % args, fwd_params))
+ config_info_lines = []
+ title_lst = []
+ if fwd_params:
+ config_info_lines.extend(map(
+ lambda args: 'Forwarding %s:%d -> %s:%d' % args, fwd_params))
+ title_lst.extend(map(
+ lambda args: '%s:%d -> %s:%d' % args, fwd_params))
+ if proxy_params:
+ config_info_lines.extend(map(
+ lambda args: 'HTTP proxy listening on %s:%d' % args, proxy_params))
+ title_lst.extend(map(
+ lambda args: '%s:%d -> proxy' % args, proxy_params))
if split_http:
- config_info += '\n\n*** HTTP connection splitting enabled. ***'
+ config_info_lines.append('HTTP connection splitting enabled.')
+ config_info = '\n'.join(config_info_lines)
+ titlepart = ', '.join(title_lst)
if obs_factory is None:
# Use Tk by default.
- obs_factory = setupTk(fwd_params, config_info, colorized)
+ obs_factory = setupTk(titlepart, config_info, colorized)
+ chosen_factory = obs_factory
if split_http:
def _factory(fci, sub_factory=obs_factory):
return HTTPConnectionSplitter(sub_factory, fci)
- obs_factory = _factory
+ chosen_factory = _factory
services = []
try:
for params in fwd_params:
- args = params + (obs_factory,)
+ args = params + (chosen_factory,)
s = ForwardingService(*args)
services.append(s)
+ for params in proxy_params:
+ args = params + (obs_factory,) # Don't parse HTTP twice.
+ s = HTTPProxyService(*args)
+ services.append(s)
+
if show_config:
print config_info
try:
asyncore.loop(timeout=1.0)
except KeyboardInterrupt:
- print >> sys.stderr, 'tcpwatch finished.'
+ print >> sys.stderr, 'TCPWatch finished.'
finally:
for s in services:
s.close()