[Zope-CVS] CVS: Packages/tcpwatch - CHANGES.txt:1.7 tcpwatch.py:1.10

Shane Hathaway shane at zope.com
Thu Aug 19 12:16:40 EDT 2004


Update of /cvs-repository/Packages/tcpwatch
In directory cvs.zope.org:/tmp/cvs-serv11320

Modified Files:
	CHANGES.txt tcpwatch.py 
Log Message:
Added support for CONNECT, allowing tcpwatch to work as an HTTPS proxy


=== Packages/tcpwatch/CHANGES.txt 1.6 => 1.7 ===
--- Packages/tcpwatch/CHANGES.txt:1.6	Wed Jun 16 20:03:46 2004
+++ Packages/tcpwatch/CHANGES.txt	Thu Aug 19 12:16:38 2004
@@ -1,4 +1,10 @@
 
+Next release
+
+  - Added support for HTTP CONNECT, allowing tcpwatch to work as an
+    HTTPS proxy.
+
+
 Version 1.3
 
   - Made compatible with versions of tcl that have threads enabled.


=== Packages/tcpwatch/tcpwatch.py 1.9 => 1.10 ===
--- Packages/tcpwatch/tcpwatch.py:1.9	Wed Jun 16 20:03:46 2004
+++ Packages/tcpwatch/tcpwatch.py	Thu Aug 19 12:16:38 2004
@@ -73,7 +73,7 @@
 
 from __future__ import nested_scopes
 
-VERSION = '1.3'
+VERSION = '1.3+'
 COPYRIGHT = (
     'TCPWatch %s Copyright 2001 Shane Hathaway, Zope Corporation'
     % VERSION)
@@ -284,7 +284,9 @@
 
 
 def escape(s):
-    # XXX This might be a brittle trick. :-(
+    # Encode a string with backslashes.  For example, a string
+    # containing characters 0 and 1 will be rendered as \x00\x01.
+    # XXX This implementation might be a brittle trick. :-(
     return repr('"\'' + str(s))[4:-1]
 
 
@@ -1039,11 +1041,21 @@
         ForwardingEndpoint.__init__(self)
         self.response_parser = HTTPStreamParser(0)
         self.proxy_conn = proxy_conn
+        self.direct = 0
         self.set_dests(dests)
 
         # Data for the client held until previous responses are sent
         self.held = []
 
+    def set_direct(self):
+        self.direct = 1
+
+    def handle_connect(self):
+        ForwardingEndpoint.handle_connect(self)
+        if self.direct:
+            # Inject the success reply into the stream
+            self.received('HTTP/1.1 200 Connection established\r\n\r\n')
+
     def _isMyTurn(self):
         """Returns a true value if it's time for this response
         to respond to the client."""
@@ -1054,13 +1066,18 @@
 
     def received(self, data):
         """Receives data from the HTTP server to be sent back to the client."""
+        if self.direct:
+            ForwardingEndpoint.received(self, data)
+            self.held.append(data)
+            self.flush()
+            return
         while 1:
             parser = self.response_parser
             if parser.completed:
                 self.finished = 1
-                self.flush()
-                # Note that any extra data returned from the server is
-                # ignored. Should it be? :-(
+                self.close()
+                # TODO: it would be nice to reuse proxy connections
+                # rather than close them every time.
                 return
             if not data:
                 break
@@ -1102,7 +1119,7 @@
         If there is unsent response data, an error is generated.
         """
         self.flush()
-        if not self.finished:
+        if not self.finished and not self.direct:
             t = IOError
             v = 'Closed without finishing response to client'
             for d in self._dests:
@@ -1124,6 +1141,7 @@
         self._obs_factory = factory
         self._counter = counter
         self._client_addr = addr
+        self._direct_receiver = None
         self._response_order = []
         self._newRequest()
 
@@ -1143,6 +1161,11 @@
     def received(self, data):
         """Accepts data received from the client."""
         while data:
+            if self._direct_receiver is not None:
+                # Direct-connect mode
+                self._direct_receiver.write(data)
+                ForwardingEndpoint.received(self, data)
+                return
             parser = self._req_parser
             if parser is None:
                 # Begin another request.
@@ -1182,6 +1205,10 @@
             else:
                 host = urlpart
                 path = '/'
+        elif '/' not in url:
+            # Only a host name (probably using CONNECT)
+            host = url
+            path = ''
         else:
             # Transparent proxy
             host = request.headers.get('HOST')
@@ -1189,15 +1216,15 @@
         if not host:
             raise ValueError, ('Request type not supported: %s' % url)
 
+        if '@' in host:
+            username, host = host.split('@')
+
         if ':' in host:
-            host, port = host.split(':')
+            host, port = host.split(':', 1)
             port = int(port)
         else:
             port = 80
 
-        if '@' in host:
-            username, host = host.split('@')
-
         obs = self._obs
         if obs is not None:
             eo = EndpointObserver(obs, 0)
@@ -1207,14 +1234,20 @@
 
         self._response_order.append(ptos)
 
-        ptos.write('%s %s %s\r\n' % (command, path, protocol))
-        # Duplicate the headers sent by the client.
-        if request.header:
-            ptos.write(request.header)
+        if command == 'CONNECT':
+            # Reply, then send the remainder of the connection
+            # directly to the server.
+            self._direct_receiver = ptos
+            ptos.set_direct()
         else:
-            ptos.write('\r\n')
-        if request.body_data:
-            ptos.write(''.join(request.body_data))
+            ptos.write('%s %s %s\r\n' % (command, path, protocol))
+            # Duplicate the headers sent by the client.
+            if request.header:
+                ptos.write(request.header)
+            else:
+                ptos.write('\r\n')
+            if request.body_data:
+                ptos.write(''.join(request.body_data))
         ptos.create_socket(socket.AF_INET, socket.SOCK_STREAM)
         ptos.connect((host, port))
 



More information about the Zope-CVS mailing list