*** httplib.py.orig Sun Jun 23 22:48:53 2002 --- httplib.py Sun Jun 23 22:38:27 2002 *************** *** 608,613 **** --- 608,681 ---- return response + class HTTPProxyConnection(HTTPConnection): + """ + This class provides HTTP access through (authenticated) proxies. + + Example: + If the HTTP proxy address is proxy.your.org:8080, an authenticated proxy + (one which requires a username/password combination in order to serve + requests), one can fetch HTTP documents from 'www.webserver.net', port 81: + + conn = HTTPProxyConnection('proxy.your.org:8080', 'www.webserver.net', + port=81, username='username', password='password') + conn.connect() + conn.request("HEAD", "/index.html", headers={'X-Custom-Header-1' : 'Value-1'}) + resp = conn.getresponse() + ... + + """ + def __init__(self, proxy, host, port=None, username=None, password=None): + # The connection goes through the proxy + HTTPConnection.__init__(self, proxy) + # save the proxy connection settings + self.__proxy, self.__proxy_port = self.host, self.port + # self.host and self.port will point to the real host + self._set_hostport(host, port) + # save the host and port + self._host, self._port = self.host, self.port + # Authenticated proxies support + self.__username = username + self.__password = password + + def connect(self): + """Connect to the host and port specified in __init__ (through a + proxy).""" + # We are connecting to the proxy, so use the proxy settings + self._set_hostport(self.__proxy, self.__proxy_port) + HTTPConnection.connect(self) + # Restore the real host and port + self._set_hostport(self._host, self._port) + + def putrequest(self, method, url, skip_host=0): + """Send a request to the server. + + `method' specifies an HTTP request method, e.g. 'GET'. + `url' specifies the object being requested, e.g. '/index.html'. + """ + # The URL has to include the real host + hostname = self._host + if self._port != self.default_port: + hostname = hostname + ':' + str(self._port) + newurl = "http://%s%s" % (hostname, url) + # Piggyback on the parent class + HTTPConnection.putrequest(self, method, newurl, skip_host=skip_host) + # Add proxy-specific headers + self._add_auth_proxy_header() + + def _add_auth_proxy_header(self): + """Adds an HTTP header for authenticated proxies + """ + if not self.__username: + # No username, so assume not an authenticated proxy + return + # Authenticated proxy + import base64 + userpass = "%s:%s" % (self.__username, self.__password) + enc_userpass = string.strip(base64.encodestring(userpass)) + self.putheader("Proxy-Authorization", "Basic %s" % enc_userpass) + + class FakeSocket: def __init__(self, sock, ssl): self.__sock = sock *************** *** 692,697 **** --- 760,847 ---- self.sock = FakeSocket(sock, ssl) + class HTTPSProxyResponse(HTTPResponse): + """ + Replacement class for HTTPResponse + Proxy responses (made through SSL) have to keep the connection open + after the initial request, since the connection is tunneled to the SSL + host with the CONNECT method. + """ + def begin(self): + HTTPResponse.begin(self) + self.will_close = 0 + + class HTTPSProxyConnection(HTTPProxyConnection): + """This class provides HTTP access through (authenticated) proxies. + + Example: + If the HTTP proxy address is proxy.your.org:8080, an authenticated proxy + (one which requires a username/password combination in order to serve + requests), one can fetch HTTP documents from 'www.webserver.net', port 81: + + conn = HTTPProxyConnection('proxy.your.org:8080', 'www.webserver.net', + port=81, username='username', password='password') + conn.connect() + conn.request("HEAD", "/index.html", headers={'X-Custom-Header-1' : 'Value-1'}) + resp = conn.getresponse() + ... + + To avoid dealing with multiple inheritance, this class only inherits from + HTTPProxyConnection. + """ + default_port = HTTPSConnection.default_port + + def __init__(self, proxy, host, port=None, username=None, password=None, **x509): + for key in x509.keys(): + if key not in ['cert_file', 'key_file']: + raise IllegalKeywordArgument() + self.key_file = x509.get('key_file') + self.cert_file = x509.get('cert_file') + # Piggybacking on HTTPProxyConnection + HTTPProxyConnection.__init__(self, proxy, host, port, username, password) + + def connect(self): + """Connect (using SSL) to the host and port specified in __init__ + (through a proxy).""" + import socket + # Set the connection with the proxy + HTTPProxyConnection.connect(self) + # Use the stock HTTPConnection putrequest + host = "%s:%s" % (self._host, self._port) + HTTPConnection.putrequest(self, "CONNECT", host, skip_host=0) + # Add proxy-specific stuff + self._add_auth_proxy_header() + # And send the request + HTTPConnection.endheaders(self) + # Save the response class + response_class = self.response_class + # And replace the response class with our own one, which does not + # close the connection + self.response_class = HTTPSProxyResponse + response = HTTPConnection.getresponse(self) + # Restore the response class + self.response_class = response_class + # Close the response object manually + response.close() + if response.status != 200: + # Close the connection manually + self.close() + # XXX Find the appropriate error code + raise socket.error(1001, response.status, response.value) + # Fake the socket + ssl = socket.ssl(self.sock, self.key_file, self.cert_file) + self.sock = FakeSocket(self.sock, ssl) + + def putrequest(self, method, url, skip_host=0): + """Send a request to the server. + + `method' specifies an HTTP request method, e.g. 'GET'. + `url' specifies the object being requested, e.g. '/index.html'. + """ + # bypass the parent class's putrequest: use the grandparent's one :-) + return HTTPConnection.putrequest(self, method, url, skip_host=skip_host) + + class HTTP: "Compatibility class with httplib.py from 1.5."