When a URL is opened, the opener-director is responsible for locating the proper handler for the specified protocol. Frequently, an existing protocol handler will be subclassed and then added to the collection maintained by the director. When urlopen is called, the specified request is immediately handed off to the director's "open" method which finds the correct handler and invokes the protocol-specific XXX_open method. At least in the case of the HTTP protocols, if an error occurs then the director is called again to find and invoke a handler for the error; these handlers generally open a new connection after adding headers to avoid the error going forward. Finally, it is important to note that at the present time, the HTTP handlers in urllib2 are built using a class (infourl) that isn't prepared to deal with a persistent connection, so they always add a "Connection: close" header to the request.
Unfortunately, NTLM only certifies the current connection, meaning that a "Connection: keep-alive" header must be used to keep it open throughout the authentication process. Furthermore, because the opener director only provides a do_open method, there is no way to discover the type of connection without also opening it. This means that the HTTPNtlmAuthHandler cannot use the normal HTTPHandler and must therefore must hardcode the HTTPConnection class. If a custom class is required for whatever reason, the only way to cause it to be used is to monkey-patch the code. For an example, see http://code.google.com/p/python-ntlm/source/browse/trunk/python26/ntlm_examples/test_ntlmauth.py
This can be avoided if, instead of putting the instantiation of the desired HTTP connection class inline, the HTTPHandler classes used a class instance variable. Something like the following should work without breaking any existing code:
class HTTPHandler(AbstractHTTPHandler):
_connection = httplib.HTTPConnection
@property
def connection(self):
"""Returns the class of connection being handled."""
return self._connection
def http_open(self, req):
return self.do_open(_connection, req)
http_request = AbstractHTTPHandler.do_request_
|