157
157
# Unless told otherwise, redirections are not followed
158
158
self.follow_redirections = False
159
159
self.set_auth(None, None, None) # Until the first 401
160
self.set_proxy_auth(None, None, None) # Until the first 407
161
162
def set_auth(self, auth_scheme, user, password=None):
162
163
self.auth_scheme = auth_scheme
164
165
self.password = password
167
# FIXME: this is not called, we incur a rountrip for each
169
def set_proxy_auth(self, auth_scheme, user, password=None):
170
self.proxy_auth_scheme = auth_scheme
171
self.proxy_user = user
172
self.proxy_password = password
166
174
def get_method(self):
167
175
return self.method
178
def extract_credentials(url):
179
"""Extracts credentials information from url.
181
Get user and password from url of the form: http://user:pass@host/path
182
:returns: (clean_url, user, password)
184
scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
187
auth, netloc = netloc.split('@', 1)
189
user, password = auth.split(':', 1)
191
user, password = auth, None
192
user = urllib.unquote(user)
193
if password is not None:
194
password = urllib.unquote(password)
199
# Build the clean url
200
clean_url = urlparse.urlunsplit((scheme, netloc, path, query, fragment))
202
return clean_url, user, password
170
205
# The urlib2.xxxAuthHandler handle the authentication of the
171
206
# requests, to do that, they need an urllib2 PasswordManager *at
172
207
# build time*. We also need one to reuse the passwords already
594
629
handler_order = 100
595
630
_debuglevel = DEBUG
597
def __init__(self, proxies=None):
632
def __init__(self, password_manager, proxies=None):
598
633
urllib2.ProxyHandler.__init__(self, proxies)
634
self.password_manager = password_manager
599
635
# First, let's get rid of urllib2 implementation
600
636
for type, proxy in self.proxies.items():
601
637
if self._debuglevel > 0:
675
711
raise errors.InvalidURL(proxy,
676
712
'Invalid syntax in proxy env variable')
677
713
elif '@' in host:
678
user_pass, host = host.split('@', 1)
680
user, password = user_pass.split(':', 1)
684
user_pass = '%s:%s' % (urllib.unquote(user),
685
urllib.unquote(password))
686
user_pass = user_pass.encode('base64').strip()
687
request.add_header('Proxy-authorization', 'Basic ' + user_pass)
714
# Extract credentials from the url and store them in
715
# the password manager so that the
716
# ProxyxxxAuthHandler can use them later.
717
clean_url, user, password = extract_credentials(proxy)
718
if user and password is not None: # '' is a valid password
719
pm = self.password_manager
720
pm.add_password(None, self.base, self._user, self._password)
688
721
host = urllib.unquote(host)
689
722
request.set_proxy(host, type)
690
723
if self._debuglevel > 0:
775
class ProxyBasicAuthHandler(urllib2.ProxyBasicAuthHandler):
776
"""Custom proxy basic authentication handler.
778
Send the authentication preventively to avoid the roundtrip
779
associated with the 407 error.
782
def get_auth(self, user, password):
783
raw = '%s:%s' % (user, password)
784
auth = 'Basic ' + raw.encode('base64').strip()
787
def set_auth(self, request):
788
"""Add the authentication header if needed.
790
All required informations should be part of the request.
792
if request.proxy_password is not None:
793
request.add_header(self.auth_header,
794
self.get_auth(request.proxy_user,
795
request.proxy_password))
797
def http_request(self, request):
798
"""Insert an authentication header if information is available"""
799
if request.proxy_auth_scheme == 'basic' \
800
and request.proxy_password is not None:
801
self.set_auth(request)
804
https_request = http_request # FIXME: Need test
806
def http_error_407(self, req, fp, code, msg, headers):
807
"""Trap the 401 to gather the auth type."""
808
response = urllib2.ProxyBasicAuthHandler.http_error_407(self, req, fp,
811
if response is not None:
812
# We capture the auth_scheme to be able to send the
813
# authentication header with the next requests
814
# without waiting for a 407 error.
815
# The urllib2.ProxyBasicAuthHandler will return a
816
# response *only* if the basic authentication
817
# succeeds. If another scheme is used or the
818
# authentication fails, the response will be None.
819
req.proxy_auth_scheme = 'basic'
742
824
class HTTPErrorProcessor(urllib2.HTTPErrorProcessor):
743
825
"""Process HTTP error responses.
797
879
# commented out below
798
880
self._opener = urllib2.build_opener( \
799
881
connection, redirect, error,
882
ProxyHandler(self.password_manager),
801
883
HTTPBasicAuthHandler(self.password_manager),
802
884
#urllib2.HTTPDigestAuthHandler(self.password_manager),
803
#urllib2.ProxyBasicAuthHandler,
885
ProxyBasicAuthHandler(self.password_manager),
804
886
#urllib2.ProxyDigestAuthHandler,