Index: Lib/httplib.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/httplib.py,v retrieving revision 1.70 diff -w -u -r1.70 httplib.py --- Lib/httplib.py 24 Nov 2002 02:35:33 -0000 1.70 +++ Lib/httplib.py 11 Jan 2003 00:15:14 -0000 @@ -780,6 +780,70 @@ 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) + 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) + self._host, self._port = self.host, self.port + 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): + """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) + HTTPConnection.putrequest(self, method, newurl) + 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 + + import base64 + userpass = "%s:%s" % (self._username, self._password) + enc_userpass = base64.encodestring(userpass).strip() + self.putheader("Proxy-Authorization", "Basic %s" % enc_userpass) + + # The next several classes are used to define FakeSocket,a socket-like # interface to an SSL connection. @@ -954,6 +1018,113 @@ realsock = sock._sock ssl = socket.ssl(realsock, self.key_file, self.cert_file) 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 HTTPS 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 HTTPS documents from 'www.webserver.net', + port 443 (default HTTPS port): + + conn = HTTPSProxyConnection('proxy.your.org:8080', 'www.webserver.net', + port=443, 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. + + The next example (which really works) uses cliente certificate, + BASIC authtentication and what is almost a miracle with the buggy + IIS :-) + + import httplib + import base64, urllib + + body_dict= {'sexo': 'X', 'email': 'ded@yahoo', 'name': 'juancho pere3z', + 'checkboxito': 'nagual'} + body = urllib.urlencode(body_dict) + authorization = "%s:%s" % ('isaac', 'mysecret') + authorization = "Basic %s" % + base64.encodestring(authorization).replace("\012", "") + headers = {"Content-type": "application/x-www-form-urlencoded", + "Accept": "text/plain,text/html", + "AUTHORIZATION": authorization} + + conn = httplib.HTTPSProxyConnection("svrcvca:3128","dell",port=443, + key_file='ISS_Aut_Desarrollo_Dell_NOpwd.pem', + cert_file='ISS_Aut_Desarrollo_Dell_NOpwd.pem') + + conn.connect() + conn.request("POST", "/scripts/PruebaISS.py", body, headers=headers) + resp = conn.getresponse() + data = resp.read() + + """ + 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') + 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 + HTTPProxyConnection.connect(self) + host = "%s:%s" % (self._host, self._port) + HTTPConnection.putrequest(self, "CONNECT", host) + self._add_auth_proxy_header() + HTTPConnection.endheaders(self) + + response_class = self.response_class + # Replace the response class with our own one, which does not + # close the connection + self.response_class = HTTPSProxyResponse + response = HTTPConnection.getresponse(self) + self.response_class = response_class + + # Close the response object manually + response.close() + if response.status != 200: + 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): + """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) class HTTP: