[Inteproxy-commits] r213 - in trunk: . inteproxy

scm-commit@wald.intevation.org scm-commit at wald.intevation.org
Tue Sep 15 22:22:21 CEST 2009


Author: bh
Date: 2009-09-15 22:22:20 +0200 (Tue, 15 Sep 2009)
New Revision: 213

Added:
   trunk/inteproxy/httpconnection.py
Modified:
   trunk/ChangeLog
   trunk/inteproxy/proxycore.py
Log:
* inteproxy/proxycore.py
(InteProxyHTTPRequestHandler.create_proxy_headers): New method to
help construct proxy authentication headers
(InteProxyHTTPRequestHandler.open_http_connection): Use the
httpconnection module to establish connections.

* inteproxy/httpconnection.py: New module with a more modular way
to create HTTP connections.  Will hopefully make the complicated
code in inteproxy/proxyconnection.py obsolete


Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog	2009-09-15 19:54:00 UTC (rev 212)
+++ trunk/ChangeLog	2009-09-15 20:22:20 UTC (rev 213)
@@ -1,5 +1,17 @@
 2009-09-15  Bernhard Herzog  <bh at intevation.de>
 
+	* inteproxy/proxycore.py
+	(InteProxyHTTPRequestHandler.create_proxy_headers): New method to
+	help construct proxy authentication headers
+	(InteProxyHTTPRequestHandler.open_http_connection): Use the
+	httpconnection module to establish connections.
+
+	* inteproxy/httpconnection.py: New module with a more modular way
+	to create HTTP connections.  Will hopefully make the complicated
+	code in inteproxy/proxyconnection.py obsolete
+
+2009-09-15  Bernhard Herzog  <bh at intevation.de>
+
 	* inteproxy/httpserver.py: Adapt to changes in Python 2.6.  2.6
 	introduced its own method of shutting down the socket server which
 	interferes with InteProxy's approach and leads to dead locks.  The

Added: trunk/inteproxy/httpconnection.py
===================================================================
--- trunk/inteproxy/httpconnection.py	2009-09-15 19:54:00 UTC (rev 212)
+++ trunk/inteproxy/httpconnection.py	2009-09-15 20:22:20 UTC (rev 213)
@@ -0,0 +1,121 @@
+# Copyright (C) 2008, 2009 by Intevation GmbH
+#
+# Based on code from
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/456195 which
+# is Copyright (C) 2006 by Alessandro Budai
+#
+# This software may be used and distributed according to the terms
+# of the Python License 2.3 or newer, see http://www.python.org/license
+
+"""
+Functions to establish network connections of various kinds.
+In particular, the functions allow vanilla TCP connections and SSL
+connections and HTTP CONNECT based connections combined in a more or
+less arbitrary ways.
+
+In addition there's a httplib.HTTPConnection sub-class that can be
+instantiated with an existing socket object so that one doesn't have to
+override any methods to handle special situations such as connections
+via an HTTPS proxy using the HTTP CONNECT method.
+"""
+
+import httplib
+import socket
+
+# the ssl modules was introduced in Python 2.6.  If available, we use
+# that, otherwise the older ssl support from the socket module
+try:
+    import ssl
+except ImportError:
+    ssl = None
+
+def log_debug(template, *args):
+    pass
+
+def connect_tcp(host, port, log_debug=log_debug):
+    """Establish a TCP connection to port port on host.
+    The return value is the socket object for the connection
+    """
+    msg = "getaddrinfo returns an empty list"
+    for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
+        af, socktype, proto, canonname, sa = res
+        try:
+            sock = socket.socket(af, socktype, proto)
+            log_debug("connect: (%s, %s)", host, port)
+            sock.connect(sa)
+        except socket.error, msg:
+            log_debug("connect fail:", host, port)
+            if sock:
+                sock.close()
+            sock = None
+            continue
+        break
+    if not sock:
+        raise socket.error, msg
+
+    return sock
+
+
+def connect_http_connect(sock, real_host, real_port, headers, debuglevel=0):
+    """Establish a connection through a HTTP proxy using the CONNECT method.
+
+    The function assumes sock is a socket-like object already connected
+    to the proxy by whatever means is suitable for the caller.  The
+    function sends the CONNECT request for the given real_host and
+    real_port, and ready the response.  If the response indicates
+    success, the function returns sock.  The headers parameter should be
+    a list of additional sending any given additional HTTP request
+    header fields.
+    """
+    sock.send("CONNECT %s:%d HTTP/1.0\r\n" % (real_host, real_port))
+    for item in headers:
+        sock.send("%s: %s\r\n" % item)
+    sock.send("\r\n")
+    response = httplib.HTTPResponse(sock, method="CONNECT",
+                                    debuglevel=debuglevel)
+    (version, code, message) = response._read_status()
+    if code != 200:
+        close()
+        raise socket.error, ("Proxy connection failed: %d %s"
+                             % (code, message.strip()))
+    while True:
+        line = response.fp.readline()
+        if not line:
+            raise socket.error, ("Proxy connection failed:"
+                                 " unexpected end of response header")
+
+        if line == '\r\n':
+            break
+
+    return sock
+
+
+def connect_ssl(sock, keyfile=None, certfile=None, log_debug=log_debug):
+    """Establish an SSL connection on the socket-like object sock.
+    If successful, the function returns a new socket like object that
+    uses SSL to encrypt the data.
+    """
+    log_debug("initiate ssl on %r", sock)
+    if ssl is not None:
+        wrapped_sock = ssl.wrap_socket(sock, keyfile, certfile)
+    else:
+        sslsock = socket.ssl(sock, keyfile, certfile)
+        wrapped_sock = httplib.FakeSocket(sock, sslsock)
+    log_debug("ssl initiated on %r", sock)
+    return wrapped_sock
+
+
+class SocketHTTPConnection(httplib.HTTPConnection):
+
+    """Extends HTTPConnection to accept a predefined socket-like object.
+    The base class tries to establish the connection itself, which is
+    awkward given all the different ways a connection can be established
+    in inteproxy, such as directly per tcp, or SSL over an HTTPs proxy.
+    """
+
+    def __init__(self, sock, host, port=None, *args, **kw):
+        httplib.HTTPConnection.__init__(self, host, port, *args, **kw)
+        self.sock = sock
+
+    def connect(self):
+        pass


Property changes on: trunk/inteproxy/httpconnection.py
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + native

Modified: trunk/inteproxy/proxycore.py
===================================================================
--- trunk/inteproxy/proxycore.py	2009-09-15 19:54:00 UTC (rev 212)
+++ trunk/inteproxy/proxycore.py	2009-09-15 20:22:20 UTC (rev 213)
@@ -9,7 +9,6 @@
 """The HTTP proxy at the core of InteProxy"""
 
 import sys
-import httplib
 import traceback
 import time
 import urlparse
@@ -21,7 +20,9 @@
 from inteproxy.feesdialog import handle_fees_and_access_constraints
 from inteproxy.httpserver import HTTPServer
 from inteproxy.httpmessage import HTTPRequestMessage, HTTPResponseMessage
-from inteproxy.proxyconnection import HTTPSProxyConnection, parse_netloc
+from inteproxy.proxyconnection import parse_netloc
+from inteproxy.httpconnection import connect_tcp, connect_http_connect, \
+     connect_ssl, SocketHTTPConnection
 
 
 # same as the BaseHTTPRequestHandler method, but as a standalone function:
@@ -106,6 +107,16 @@
         return HTTPRequestMessage(self.command, self.path, self.request_version,
                                   self.headers, self.rfile)
 
+    def create_proxy_headers(self, proxy):
+        headers = []
+        if proxy is not None:
+            if proxy.username and proxy.password:
+                userpass = base64.b64encode("%s:%s" % (proxy.username,
+                                                       proxy.password))
+                headers.append(("Proxy-Authorization",
+                                "Basic %s" % userpass))
+        return headers
+
     def open_http_connection(self, remote_url, client_request):
         """Open a HTTP connection to remote_url and send client_request
 
@@ -114,7 +125,7 @@
         containing the remote server's or proxy's response.
         """
         scheme, netloc, path, query, fragment = urlparse.urlsplit(remote_url)
-        remote_address = connect_address = parse_netloc(scheme, netloc)
+        remote_address = parse_netloc(scheme, netloc)
 
         # the URI used in the request.  Usually, it's like remote_url
         # but with schem and netloc removed.
@@ -123,25 +134,33 @@
         proxy = None
         extra_headers = [("Host", "%s:%d" % remote_address)]
 
-        if scheme == "http":
-            connection_class = httplib.HTTPConnection
+        sock = None
+
+        if scheme == "https":
+            proxy = self.server.https_proxy
+            if proxy:
+                sock = connect_tcp(proxy.host, proxy.port,
+                                   log_debug=self.log_debug)
+                sock = connect_http_connect(sock, remote_address[0],
+                                            remote_address[1],
+                                            self.create_proxy_headers(proxy),
+                                            debuglevel=self.debug_level)
+            else:
+                sock = connect_tcp(remote_address[0], remote_address[1],
+                                   log_debug=self.log_debug)
+            sock = connect_ssl(sock, log_debug=self.log_debug)
+        elif scheme == "http":
             proxy = self.server.http_proxy
-        elif scheme == "https":
-            connection_class = httplib.HTTPSConnection
-            proxy = self.server.https_proxy
-            if proxy is not None:
-                connection_class = HTTPSProxyConnection
+            if proxy:
+                sock = connect_tcp(proxy.host, proxy.port,
+                                   log_debug=self.log_debug)
+                request_uri = remote_url
+                extra_headers.extend(self.create_proxy_headers(proxy))
+            else:
+                sock = connect_tcp(remote_address[0], remote_address[1],
+                                   log_debug=self.log_debug)
 
-        if proxy is not None:
-            connect_address = (proxy.host, proxy.port)
-            request_uri = remote_url
-            if proxy.username and proxy.password:
-                userpass = base64.b64encode("%s:%s" % (proxy.username,
-                                                       proxy.password))
-                extra_headers.append(("Proxy-Authorization",
-                                      "Basic %s" % userpass))
-
-        connection = connection_class(*connect_address)
+        connection = SocketHTTPConnection(sock, *remote_address)
         if self.debug_level > 1:
             connection.set_debuglevel(self.debug_level)
         if self.request_version == "HTTP/1.0":



More information about the Inteproxy-commits mailing list