[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