6437.25.1
by Vincent Ladeuil
Provide default paths for ca certs for supported platforms |
1 |
# Copyright (C) 2006-2012 Canonical Ltd
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
2 |
#
|
3 |
# This program is free software; you can redistribute it and/or modify
|
|
4 |
# it under the terms of the GNU General Public License as published by
|
|
5 |
# the Free Software Foundation; either version 2 of the License, or
|
|
6 |
# (at your option) any later version.
|
|
7 |
#
|
|
8 |
# This program is distributed in the hope that it will be useful,
|
|
9 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
10 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
11 |
# GNU General Public License for more details.
|
|
12 |
#
|
|
13 |
# You should have received a copy of the GNU General Public License
|
|
14 |
# along with this program; if not, write to the Free Software
|
|
4183.7.1
by Sabin Iacob
update FSF mailing address |
15 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
16 |
|
6238.2.9
by Jelmer Vernooij
Add some tests for ssl.ca_certs config option. |
17 |
"""Implementation of urllib2 tailored to bzr needs
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
18 |
|
2363.4.7
by Vincent Ladeuil
Deeper tests, prepare the auth setting that will avoid the |
19 |
This file complements the urllib2 class hierarchy with custom classes.
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
20 |
|
21 |
For instance, we create a new HTTPConnection and HTTPSConnection that inherit
|
|
22 |
from the original urllib2.HTTP(s)Connection objects, but also have a new base
|
|
3059.2.2
by Vincent Ladeuil
Read http responses on demand without buffering the whole body |
23 |
which implements a custom getresponse and cleanup_pipe handlers.
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
24 |
|
25 |
And then we implement custom HTTPHandler and HTTPSHandler classes, that use
|
|
26 |
the custom HTTPConnection classes.
|
|
27 |
||
28 |
We have a custom Response class, which lets us maintain a keep-alive
|
|
29 |
connection even for requests that urllib2 doesn't expect to contain body data.
|
|
30 |
||
2363.4.10
by Vincent Ladeuil
Complete tests. |
31 |
And a custom Request class that lets us track redirections, and
|
2363.4.12
by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better |
32 |
handle authentication schemes.
|
3430.1.1
by Vincent Ladeuil
Fix bug #229076 by fixing header names before sending the request. |
33 |
|
34 |
For coherency with python libraries, we use capitalized header names throughout
|
|
35 |
the code, even if the header names will be titled just before sending the
|
|
36 |
request (see AbstractHTTPHandler.do_open).
|
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
37 |
"""
|
38 |
||
6379.6.7
by Jelmer Vernooij
Move importing from future until after doc string, otherwise the doc string will disappear. |
39 |
from __future__ import absolute_import |
40 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
41 |
DEBUG = 0 |
42 |
||
43 |
# FIXME: Oversimplifying, two kind of exceptions should be
|
|
44 |
# raised, once a request is issued: URLError before we have been
|
|
45 |
# able to process the response, HTTPError after that. Process the
|
|
46 |
# response means we are able to leave the socket clean, so if we
|
|
47 |
# are not able to do that, we should close the connection. The
|
|
48 |
# actual code more or less do that, tests should be written to
|
|
2004.1.16
by v.ladeuil+lp at free
Add tests against erroneous http status lines. |
49 |
# ensure that.
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
50 |
|
4628.1.2
by Vincent Ladeuil
More complete fix. |
51 |
import errno |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
52 |
import httplib |
6238.2.9
by Jelmer Vernooij
Add some tests for ssl.ca_certs config option. |
53 |
import os |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
54 |
import socket |
55 |
import urllib |
|
56 |
import urllib2 |
|
57 |
import urlparse |
|
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
58 |
import re |
2004.1.16
by v.ladeuil+lp at free
Add tests against erroneous http status lines. |
59 |
import sys |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
60 |
import time |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
61 |
|
2004.1.25
by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :) |
62 |
from bzrlib import __version__ as bzrlib_version |
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
63 |
from bzrlib import ( |
2900.2.6
by Vincent Ladeuil
Make http aware of authentication config. |
64 |
config, |
3052.3.3
by Vincent Ladeuil
Add -Dhttp support. |
65 |
debug, |
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
66 |
errors, |
6238.2.7
by Jelmer Vernooij
Add ssl.cert_reqs. |
67 |
lazy_import, |
2929.3.1
by Vincent Ladeuil
Fix python2.6 deprecation warnings (still 4 failures 5 errors in test suite). |
68 |
osutils, |
3052.3.3
by Vincent Ladeuil
Add -Dhttp support. |
69 |
trace, |
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
70 |
transport, |
4795.4.5
by Vincent Ladeuil
Make sure all redirection code paths can handle authentication. |
71 |
urlutils, |
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
72 |
)
|
6238.2.7
by Jelmer Vernooij
Add ssl.cert_reqs. |
73 |
lazy_import.lazy_import(globals(), """ |
74 |
import ssl
|
|
75 |
""") |
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
76 |
|
6238.2.9
by Jelmer Vernooij
Add some tests for ssl.ca_certs config option. |
77 |
|
6437.25.1
by Vincent Ladeuil
Provide default paths for ca certs for supported platforms |
78 |
# Note for packagers: if there is no package providing certs for your platform,
|
79 |
# the curl project produces http://curl.haxx.se/ca/cacert.pem weekly.
|
|
6437.25.4
by Vincent Ladeuil
Get rid of 'optional' for ssl.ca_reqs to simplify the model. |
80 |
_ssl_ca_certs_known_locations = [ |
6437.25.3
by Vincent Ladeuil
Simplify: all posix systems will try know locations and select the first |
81 |
u'/etc/ssl/certs/ca-certificates.crt', # Ubuntu/debian/gentoo |
82 |
u'/etc/pki/tls/certs/ca-bundle.crt', # Fedora/CentOS/RH |
|
83 |
u'/etc/ssl/ca-bundle.pem', # OpenSuse |
|
84 |
u'/etc/ssl/cert.pem', # OpenSuse |
|
85 |
u"/usr/local/share/certs/ca-root-nss.crt", # FreeBSD |
|
86 |
# XXX: Needs checking, can't trust the interweb ;) -- vila 2012-01-25
|
|
87 |
u'/etc/openssl/certs/ca-certificates.crt', # Solaris |
|
88 |
]
|
|
6238.2.8
by Jelmer Vernooij
Move options to bzrlib/transport/http/_urllib2_wrappers.py |
89 |
def default_ca_certs(): |
6437.25.3
by Vincent Ladeuil
Simplify: all posix systems will try know locations and select the first |
90 |
if sys.platform == 'win32': |
6437.34.1
by Martin Packman
Expect name 'cacert.pem' rather than 'ca_bundle.crt' for curl extracted certs on win32 |
91 |
return os.path.join(os.path.dirname(sys.executable), u"cacert.pem") |
6437.25.1
by Vincent Ladeuil
Provide default paths for ca certs for supported platforms |
92 |
elif sys.platform == 'darwin': |
93 |
# FIXME: Needs some default value for osx, waiting for osx installers
|
|
94 |
# guys feedback -- vila 2012-01-25
|
|
95 |
pass
|
|
6437.25.3
by Vincent Ladeuil
Simplify: all posix systems will try know locations and select the first |
96 |
else: |
97 |
# Try known locations for friendly OSes providing the root certificates
|
|
98 |
# without making them hard to use for any https client.
|
|
6437.25.4
by Vincent Ladeuil
Get rid of 'optional' for ssl.ca_reqs to simplify the model. |
99 |
for path in _ssl_ca_certs_known_locations: |
6437.25.3
by Vincent Ladeuil
Simplify: all posix systems will try know locations and select the first |
100 |
if os.path.exists(path): |
101 |
# First found wins
|
|
102 |
return path |
|
103 |
# A default path that makes sense and will be mentioned in the error
|
|
104 |
# presented to the user, even if not correct for all platforms
|
|
6437.25.4
by Vincent Ladeuil
Get rid of 'optional' for ssl.ca_reqs to simplify the model. |
105 |
return _ssl_ca_certs_known_locations[0] |
6238.2.9
by Jelmer Vernooij
Add some tests for ssl.ca_certs config option. |
106 |
|
6238.2.8
by Jelmer Vernooij
Move options to bzrlib/transport/http/_urllib2_wrappers.py |
107 |
|
6238.2.10
by Jelmer Vernooij
Add more tests for ssl.ca_certs option. |
108 |
def ca_certs_from_store(path): |
109 |
if not os.path.exists(path): |
|
110 |
raise ValueError("ca certs path %s does not exist" % path) |
|
111 |
return path |
|
112 |
||
113 |
||
6238.2.8
by Jelmer Vernooij
Move options to bzrlib/transport/http/_urllib2_wrappers.py |
114 |
def cert_reqs_from_store(unicode_str): |
115 |
import ssl |
|
116 |
try: |
|
117 |
return { |
|
118 |
"required": ssl.CERT_REQUIRED, |
|
119 |
"none": ssl.CERT_NONE |
|
120 |
}[unicode_str] |
|
121 |
except KeyError: |
|
122 |
raise ValueError("invalid value %s" % unicode_str) |
|
123 |
||
6437.31.1
by Vincent Ladeuil
Default to no ssl cert verification on osx and windows |
124 |
def default_ca_reqs(): |
125 |
if sys.platform in ('win32', 'darwin'): |
|
126 |
# FIXME: Once we get a native access to root certificates there, this
|
|
127 |
# won't needed anymore. See http://pad.lv/920455 -- vila 2012-02-15
|
|
128 |
return u'none' |
|
129 |
else: |
|
130 |
return u'required' |
|
6238.2.8
by Jelmer Vernooij
Move options to bzrlib/transport/http/_urllib2_wrappers.py |
131 |
|
132 |
opt_ssl_ca_certs = config.Option('ssl.ca_certs', |
|
6238.2.10
by Jelmer Vernooij
Add more tests for ssl.ca_certs option. |
133 |
from_unicode=ca_certs_from_store, |
6238.2.8
by Jelmer Vernooij
Move options to bzrlib/transport/http/_urllib2_wrappers.py |
134 |
default=default_ca_certs, |
6437.25.6
by Vincent Ladeuil
Feedback from mgz. |
135 |
invalid='warning', |
6238.2.8
by Jelmer Vernooij
Move options to bzrlib/transport/http/_urllib2_wrappers.py |
136 |
help="""\ |
137 |
Path to certification authority certificates to trust.
|
|
6437.25.6
by Vincent Ladeuil
Feedback from mgz. |
138 |
|
139 |
This should be a valid path to a bundle containing all root Certificate
|
|
140 |
Authorities used to verify an https server certificate.
|
|
141 |
||
142 |
Use ssl.cert_reqs=none to disable certificate verification.
|
|
6238.2.8
by Jelmer Vernooij
Move options to bzrlib/transport/http/_urllib2_wrappers.py |
143 |
""") |
144 |
||
145 |
opt_ssl_cert_reqs = config.Option('ssl.cert_reqs', |
|
6437.31.1
by Vincent Ladeuil
Default to no ssl cert verification on osx and windows |
146 |
default=default_ca_reqs, |
6238.2.8
by Jelmer Vernooij
Move options to bzrlib/transport/http/_urllib2_wrappers.py |
147 |
from_unicode=cert_reqs_from_store, |
6238.2.10
by Jelmer Vernooij
Add more tests for ssl.ca_certs option. |
148 |
invalid='error', |
6238.2.8
by Jelmer Vernooij
Move options to bzrlib/transport/http/_urllib2_wrappers.py |
149 |
help="""\ |
150 |
Whether to require a certificate from the remote side. (default:required)
|
|
151 |
||
152 |
Possible values:
|
|
6238.2.22
by Vincent Ladeuil
Create a specific test permutation for urllib https so we can inject our test ca certs. The wiring in _urllib2_wrappers is a bit hackish and it will need to use auth instead so different certs can be used for proxies and real servers but this could wait until authentication.conf is migrated to the config stacks. With this change in place, all https tests pass without the need to create a dedicated GlobalStore. |
153 |
* none: Certificates ignored
|
6437.25.4
by Vincent Ladeuil
Get rid of 'optional' for ssl.ca_reqs to simplify the model. |
154 |
* required: Certificates required and validated
|
6238.2.8
by Jelmer Vernooij
Move options to bzrlib/transport/http/_urllib2_wrappers.py |
155 |
""") |
156 |
||
6238.1.2
by John Arbash Meinel, Jelmer Vernooij
Only attempt to import kerberos module once. |
157 |
checked_kerberos = False |
158 |
kerberos = None |
|
159 |
||
160 |
||
5393.6.1
by Toshio Kuratomi
Fix branching from lp: repositories under python-2.7 |
161 |
class addinfourl(urllib2.addinfourl): |
5393.6.3
by Toshio Kuratomi
Fix typo in implementation of python-2.7 xmlrpclib fix and add comment + entry in NEWS |
162 |
'''Replacement addinfourl class compatible with python-2.7's xmlrpclib
|
163 |
||
164 |
In python-2.7, xmlrpclib expects that the response object that it receives
|
|
165 |
has a getheader method. httplib.HTTPResponse provides this but
|
|
166 |
urllib2.addinfourl does not. Add the necessary functions here, ported to
|
|
167 |
use the internal data structures of addinfourl.
|
|
168 |
'''
|
|
5393.6.1
by Toshio Kuratomi
Fix branching from lp: repositories under python-2.7 |
169 |
|
170 |
def getheader(self, name, default=None): |
|
171 |
if self.headers is None: |
|
172 |
raise httplib.ResponseNotReady() |
|
5393.6.3
by Toshio Kuratomi
Fix typo in implementation of python-2.7 xmlrpclib fix and add comment + entry in NEWS |
173 |
return self.headers.getheader(name, default) |
5393.6.1
by Toshio Kuratomi
Fix branching from lp: repositories under python-2.7 |
174 |
|
175 |
def getheaders(self): |
|
176 |
if self.headers is None: |
|
177 |
raise httplib.ResponseNotReady() |
|
178 |
return self.headers.items() |
|
179 |
||
180 |
||
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
181 |
class _ReportingFileSocket(object): |
182 |
||
183 |
def __init__(self, filesock, report_activity=None): |
|
184 |
self.filesock = filesock |
|
185 |
self._report_activity = report_activity |
|
186 |
||
4776.2.1
by Vincent Ladeuil
Support no activity report on http sockets. |
187 |
def report_activity(self, size, direction): |
188 |
if self._report_activity: |
|
189 |
self._report_activity(size, direction) |
|
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
190 |
|
191 |
def read(self, size=1): |
|
192 |
s = self.filesock.read(size) |
|
4776.2.1
by Vincent Ladeuil
Support no activity report on http sockets. |
193 |
self.report_activity(len(s), 'read') |
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
194 |
return s |
195 |
||
5848.2.1
by John Arbash Meinel
Break compatibility with python <2.6. |
196 |
def readline(self, size=-1): |
197 |
s = self.filesock.readline(size) |
|
198 |
self.report_activity(len(s), 'read') |
|
199 |
return s |
|
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
200 |
|
201 |
def __getattr__(self, name): |
|
202 |
return getattr(self.filesock, name) |
|
203 |
||
204 |
||
205 |
class _ReportingSocket(object): |
|
206 |
||
207 |
def __init__(self, sock, report_activity=None): |
|
3287.3.3
by Andrew Bennetts
A slightly neater hack for forcing buffering, thanks to John. |
208 |
self.sock = sock |
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
209 |
self._report_activity = report_activity |
210 |
||
4776.2.1
by Vincent Ladeuil
Support no activity report on http sockets. |
211 |
def report_activity(self, size, direction): |
212 |
if self._report_activity: |
|
213 |
self._report_activity(size, direction) |
|
214 |
||
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
215 |
def sendall(self, s, *args): |
4105.1.1
by Andrew Bennetts
Clean-up _ReportingSocket.send/sendall slightly. |
216 |
self.sock.sendall(s, *args) |
4776.2.1
by Vincent Ladeuil
Support no activity report on http sockets. |
217 |
self.report_activity(len(s), 'write') |
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
218 |
|
219 |
def recv(self, *args): |
|
220 |
s = self.sock.recv(*args) |
|
4776.2.1
by Vincent Ladeuil
Support no activity report on http sockets. |
221 |
self.report_activity(len(s), 'read') |
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
222 |
return s |
3287.3.3
by Andrew Bennetts
A slightly neater hack for forcing buffering, thanks to John. |
223 |
|
224 |
def makefile(self, mode='r', bufsize=-1): |
|
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
225 |
# httplib creates a fileobject that doesn't do buffering, which
|
226 |
# makes fp.readline() very expensive because it only reads one byte
|
|
227 |
# at a time. So we wrap the socket in an object that forces
|
|
228 |
# sock.makefile to make a buffered file.
|
|
229 |
fsock = self.sock.makefile(mode, 65536) |
|
230 |
# And wrap that into a reporting kind of fileobject
|
|
231 |
return _ReportingFileSocket(fsock, self._report_activity) |
|
3287.3.3
by Andrew Bennetts
A slightly neater hack for forcing buffering, thanks to John. |
232 |
|
233 |
def __getattr__(self, name): |
|
234 |
return getattr(self.sock, name) |
|
235 |
||
236 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
237 |
# We define our own Response class to keep our httplib pipe clean
|
238 |
class Response(httplib.HTTPResponse): |
|
2004.1.16
by v.ladeuil+lp at free
Add tests against erroneous http status lines. |
239 |
"""Custom HTTPResponse, to avoid the need to decorate.
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
240 |
|
241 |
httplib prefers to decorate the returned objects, rather
|
|
242 |
than using a custom object.
|
|
243 |
"""
|
|
244 |
||
2004.1.7
by vila
Better handling of passwords (user should be queried only once). |
245 |
# Some responses have bodies in which we have no interest
|
5504.4.1
by Vincent Ladeuil
Fix http test spurious failures and get rid of some useless messages in log. |
246 |
_body_ignored_responses = [301,302, 303, 307, 400, 401, 403, 404, 501] |
2004.1.7
by vila
Better handling of passwords (user should be queried only once). |
247 |
|
3146.3.4
by Vincent Ladeuil
Review feedback, simpler loops. |
248 |
# in finish() below, we may have to discard several MB in the worst
|
249 |
# case. To avoid buffering that much, we read and discard by chunks
|
|
250 |
# instead. The underlying file is either a socket or a StringIO, so reading
|
|
251 |
# 8k chunks should be fine.
|
|
252 |
_discarded_buf_size = 8192 |
|
253 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
254 |
def begin(self): |
255 |
"""Begin to read the response from the server.
|
|
256 |
||
257 |
httplib assumes that some responses get no content and do
|
|
258 |
not even attempt to read the body in that case, leaving
|
|
259 |
the body in the socket, blocking the next request. Let's
|
|
260 |
try to workaround that.
|
|
261 |
"""
|
|
2004.1.2
by vila
Implements a BasicAuthManager. |
262 |
httplib.HTTPResponse.begin(self) |
2004.1.7
by vila
Better handling of passwords (user should be queried only once). |
263 |
if self.status in self._body_ignored_responses: |
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
264 |
if self.debuglevel >= 2: |
2004.1.16
by v.ladeuil+lp at free
Add tests against erroneous http status lines. |
265 |
print "For status: [%s]," % self.status, |
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
266 |
print "will ready body, length: %s" % self.length |
2004.1.16
by v.ladeuil+lp at free
Add tests against erroneous http status lines. |
267 |
if not (self.length is None or self.will_close): |
268 |
# In some cases, we just can't read the body not
|
|
269 |
# even try or we may encounter a 104, 'Connection
|
|
270 |
# reset by peer' error if there is indeed no body
|
|
271 |
# and the server closed the connection just after
|
|
272 |
# having issued the response headers (even if the
|
|
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
273 |
# headers indicate a Content-Type...)
|
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
274 |
body = self.read(self.length) |
275 |
if self.debuglevel >= 9: |
|
3024.2.3
by Vincent Ladeuil
Rewrite http_readv to allow several GET requests. Smoke tested against branch reported in the bug. |
276 |
# This one can be huge and is generally not interesting
|
2004.1.16
by v.ladeuil+lp at free
Add tests against erroneous http status lines. |
277 |
print "Consumed body: [%s]" % body |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
278 |
self.close() |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
279 |
elif self.status == 200: |
280 |
# Whatever the request is, it went ok, so we surely don't want to
|
|
281 |
# close the connection. Some cases are not correctly detected by
|
|
282 |
# httplib.HTTPConnection.getresponse (called by
|
|
283 |
# httplib.HTTPResponse.begin). The CONNECT response for the https
|
|
2955.2.1
by Vincent Ladeuil
Fix #160012 by leaving the http pipeline related exceptions raise. |
284 |
# through proxy case is one. Note: the 'will_close' below refers
|
285 |
# to the "true" socket between us and the server, whereas the
|
|
286 |
# 'close()' above refers to the copy of that socket created by
|
|
287 |
# httplib for the response itself. So, in the if above we close the
|
|
288 |
# socket to indicate that we are done with the response whereas
|
|
289 |
# below we keep the socket with the server opened.
|
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
290 |
self.will_close = False |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
291 |
|
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
292 |
def finish(self): |
3059.2.11
by Vincent Ladeuil
Fix typos mentioned by spiv. |
293 |
"""Finish reading the body.
|
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
294 |
|
3059.2.11
by Vincent Ladeuil
Fix typos mentioned by spiv. |
295 |
In some cases, the client may have left some bytes to read in the
|
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
296 |
body. That will block the next request to succeed if we use a
|
3059.2.11
by Vincent Ladeuil
Fix typos mentioned by spiv. |
297 |
persistent connection. If we don't use a persistent connection, well,
|
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
298 |
nothing will block the next request since a new connection will be
|
299 |
issued anyway.
|
|
3104.3.3
by Vincent Ladeuil
Jam's and Aaron feedback about bug #175886. |
300 |
|
301 |
:return: the number of bytes left on the socket (may be None)
|
|
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
302 |
"""
|
3104.3.3
by Vincent Ladeuil
Jam's and Aaron feedback about bug #175886. |
303 |
pending = None |
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
304 |
if not self.isclosed(): |
305 |
# Make sure nothing was left to be read on the socket
|
|
3104.3.1
by Vincent Ladeuil
Fix #175886 by reading remaining bytes by chunks. |
306 |
pending = 0 |
3146.3.2
by Vincent Ladeuil
Fix #179368 by keeping the current range hint on ShortReadvErrors. |
307 |
data = True |
3146.3.4
by Vincent Ladeuil
Review feedback, simpler loops. |
308 |
while data and self.length: |
309 |
# read() will update self.length
|
|
310 |
data = self.read(min(self.length, self._discarded_buf_size)) |
|
3104.3.1
by Vincent Ladeuil
Fix #175886 by reading remaining bytes by chunks. |
311 |
pending += len(data) |
312 |
if pending: |
|
3146.3.2
by Vincent Ladeuil
Fix #179368 by keeping the current range hint on ShortReadvErrors. |
313 |
trace.mutter("%s bytes left on the HTTP socket", pending) |
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
314 |
self.close() |
3104.3.3
by Vincent Ladeuil
Jam's and Aaron feedback about bug #175886. |
315 |
return pending |
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
316 |
|
2004.1.2
by vila
Implements a BasicAuthManager. |
317 |
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
318 |
# Not inheriting from 'object' because httplib.HTTPConnection doesn't.
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
319 |
class AbstractHTTPConnection: |
320 |
"""A custom HTTP(S) Connection, which can reset itself on a bad response"""
|
|
321 |
||
322 |
response_class = Response |
|
323 |
||
3104.3.3
by Vincent Ladeuil
Jam's and Aaron feedback about bug #175886. |
324 |
# When we detect a server responding with the whole file to range requests,
|
325 |
# we want to warn. But not below a given thresold.
|
|
326 |
_range_warning_thresold = 1024 * 1024 |
|
327 |
||
4776.2.1
by Vincent Ladeuil
Support no activity report on http sockets. |
328 |
def __init__(self, report_activity=None): |
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
329 |
self._response = None |
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
330 |
self._report_activity = report_activity |
3104.3.3
by Vincent Ladeuil
Jam's and Aaron feedback about bug #175886. |
331 |
self._ranges_received_whole_file = None |
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
332 |
|
333 |
def _mutter_connect(self): |
|
3104.3.4
by Vincent Ladeuil
Add test. |
334 |
netloc = '%s:%s' % (self.host, self.port) |
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
335 |
if self.proxied_host is not None: |
336 |
netloc += '(proxy for %s)' % self.proxied_host |
|
337 |
trace.mutter('* About to connect() to %s' % netloc) |
|
338 |
||
339 |
def getresponse(self): |
|
340 |
"""Capture the response to be able to cleanup"""
|
|
341 |
self._response = httplib.HTTPConnection.getresponse(self) |
|
342 |
return self._response |
|
343 |
||
3059.2.2
by Vincent Ladeuil
Read http responses on demand without buffering the whole body |
344 |
def cleanup_pipe(self): |
3111.1.24
by Vincent Ladeuil
Cleanups. |
345 |
"""Read the remaining bytes of the last response if any."""
|
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
346 |
if self._response is not None: |
5504.4.1
by Vincent Ladeuil
Fix http test spurious failures and get rid of some useless messages in log. |
347 |
try: |
348 |
pending = self._response.finish() |
|
349 |
# Warn the user (once)
|
|
350 |
if (self._ranges_received_whole_file is None |
|
351 |
and self._response.status == 200 |
|
352 |
and pending and pending > self._range_warning_thresold |
|
353 |
):
|
|
354 |
self._ranges_received_whole_file = True |
|
355 |
trace.warning( |
|
356 |
'Got a 200 response when asking for multiple ranges,'
|
|
357 |
' does your server at %s:%s support range requests?', |
|
358 |
self.host, self.port) |
|
359 |
except socket.error, e: |
|
360 |
# It's conceivable that the socket is in a bad state here
|
|
361 |
# (including some test cases) and in this case, it doesn't need
|
|
362 |
# cleaning anymore, so no need to fail, we just get rid of the
|
|
363 |
# socket and let callers reconnect
|
|
364 |
if (len(e.args) == 0 |
|
365 |
or e.args[0] not in (errno.ECONNRESET, errno.ECONNABORTED)): |
|
366 |
raise
|
|
367 |
self.close() |
|
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
368 |
self._response = None |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
369 |
# Preserve our preciousss
|
370 |
sock = self.sock |
|
371 |
self.sock = None |
|
3943.8.1
by Marius Kruger
remove all trailing whitespace from bzr source |
372 |
# Let httplib.HTTPConnection do its housekeeping
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
373 |
self.close() |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
374 |
# Restore our preciousss
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
375 |
self.sock = sock |
376 |
||
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
377 |
def _wrap_socket_for_reporting(self, sock): |
378 |
"""Wrap the socket before anybody use it."""
|
|
379 |
self.sock = _ReportingSocket(sock, self._report_activity) |
|
380 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
381 |
|
382 |
class HTTPConnection(AbstractHTTPConnection, httplib.HTTPConnection): |
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
383 |
|
384 |
# XXX: Needs refactoring at the caller level.
|
|
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
385 |
def __init__(self, host, port=None, proxied_host=None, |
6238.2.22
by Vincent Ladeuil
Create a specific test permutation for urllib https so we can inject our test ca certs. The wiring in _urllib2_wrappers is a bit hackish and it will need to use auth instead so different certs can be used for proxies and real servers but this could wait until authentication.conf is migrated to the config stacks. With this change in place, all https tests pass without the need to create a dedicated GlobalStore. |
386 |
report_activity=None, ca_certs=None): |
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
387 |
AbstractHTTPConnection.__init__(self, report_activity=report_activity) |
2929.3.9
by Vincent Ladeuil
Don't pretend we support HTTP/0.9 since we don't and do that correctly. |
388 |
# Use strict=True since we don't support HTTP/0.9
|
389 |
httplib.HTTPConnection.__init__(self, host, port, strict=True) |
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
390 |
self.proxied_host = proxied_host |
6238.2.22
by Vincent Ladeuil
Create a specific test permutation for urllib https so we can inject our test ca certs. The wiring in _urllib2_wrappers is a bit hackish and it will need to use auth instead so different certs can be used for proxies and real servers but this could wait until authentication.conf is migrated to the config stacks. With this change in place, all https tests pass without the need to create a dedicated GlobalStore. |
391 |
# ca_certs is ignored, it's only relevant for https
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
392 |
|
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
393 |
def connect(self): |
394 |
if 'http' in debug.debug_flags: |
|
395 |
self._mutter_connect() |
|
396 |
httplib.HTTPConnection.connect(self) |
|
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
397 |
self._wrap_socket_for_reporting(self.sock) |
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
398 |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
399 |
|
6238.2.4
by Jelmer Vernooij
Verify ssl certificates when using urllib. |
400 |
# These two methods were imported from Python 3.2's ssl module
|
401 |
||
402 |
def _dnsname_to_pat(dn): |
|
403 |
pats = [] |
|
404 |
for frag in dn.split(r'.'): |
|
405 |
if frag == '*': |
|
406 |
# When '*' is a fragment by itself, it matches a non-empty dotless
|
|
407 |
# fragment.
|
|
408 |
pats.append('[^.]+') |
|
409 |
else: |
|
410 |
# Otherwise, '*' matches any dotless fragment.
|
|
411 |
frag = re.escape(frag) |
|
412 |
pats.append(frag.replace(r'\*', '[^.]*')) |
|
413 |
return re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) |
|
414 |
||
415 |
||
416 |
def match_hostname(cert, hostname): |
|
417 |
"""Verify that *cert* (in decoded format as returned by
|
|
418 |
SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 rules
|
|
419 |
are mostly followed, but IP addresses are not accepted for *hostname*.
|
|
420 |
||
421 |
CertificateError is raised on failure. On success, the function
|
|
422 |
returns nothing.
|
|
423 |
"""
|
|
424 |
if not cert: |
|
425 |
raise ValueError("empty or no certificate") |
|
426 |
dnsnames = [] |
|
427 |
san = cert.get('subjectAltName', ()) |
|
428 |
for key, value in san: |
|
429 |
if key == 'DNS': |
|
430 |
if _dnsname_to_pat(value).match(hostname): |
|
431 |
return
|
|
432 |
dnsnames.append(value) |
|
433 |
if not san: |
|
434 |
# The subject is only checked when subjectAltName is empty
|
|
435 |
for sub in cert.get('subject', ()): |
|
436 |
for key, value in sub: |
|
437 |
# XXX according to RFC 2818, the most specific Common Name
|
|
438 |
# must be used.
|
|
439 |
if key == 'commonName': |
|
440 |
if _dnsname_to_pat(value).match(hostname): |
|
441 |
return
|
|
442 |
dnsnames.append(value) |
|
443 |
if len(dnsnames) > 1: |
|
6238.2.21
by Vincent Ladeuil
The https test server will now refuse connections if an ssl error occurs during the handshake. Ssl errors and certificate errors aborts requests without re-trying them. |
444 |
raise errors.CertificateError( |
445 |
"hostname %r doesn't match either of %s" |
|
6238.2.4
by Jelmer Vernooij
Verify ssl certificates when using urllib. |
446 |
% (hostname, ', '.join(map(repr, dnsnames)))) |
447 |
elif len(dnsnames) == 1: |
|
6238.2.21
by Vincent Ladeuil
The https test server will now refuse connections if an ssl error occurs during the handshake. Ssl errors and certificate errors aborts requests without re-trying them. |
448 |
raise errors.CertificateError("hostname %r doesn't match %r" % |
449 |
(hostname, dnsnames[0])) |
|
6238.2.4
by Jelmer Vernooij
Verify ssl certificates when using urllib. |
450 |
else: |
451 |
raise errors.CertificateError("no appropriate commonName or " |
|
452 |
"subjectAltName fields were found") |
|
453 |
||
454 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
455 |
class HTTPSConnection(AbstractHTTPConnection, httplib.HTTPSConnection): |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
456 |
|
457 |
def __init__(self, host, port=None, key_file=None, cert_file=None, |
|
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
458 |
proxied_host=None, |
6238.2.22
by Vincent Ladeuil
Create a specific test permutation for urllib https so we can inject our test ca certs. The wiring in _urllib2_wrappers is a bit hackish and it will need to use auth instead so different certs can be used for proxies and real servers but this could wait until authentication.conf is migrated to the config stacks. With this change in place, all https tests pass without the need to create a dedicated GlobalStore. |
459 |
report_activity=None, ca_certs=None): |
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
460 |
AbstractHTTPConnection.__init__(self, report_activity=report_activity) |
2929.3.9
by Vincent Ladeuil
Don't pretend we support HTTP/0.9 since we don't and do that correctly. |
461 |
# Use strict=True since we don't support HTTP/0.9
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
462 |
httplib.HTTPSConnection.__init__(self, host, port, |
2929.3.9
by Vincent Ladeuil
Don't pretend we support HTTP/0.9 since we don't and do that correctly. |
463 |
key_file, cert_file, strict=True) |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
464 |
self.proxied_host = proxied_host |
6238.2.22
by Vincent Ladeuil
Create a specific test permutation for urllib https so we can inject our test ca certs. The wiring in _urllib2_wrappers is a bit hackish and it will need to use auth instead so different certs can be used for proxies and real servers but this could wait until authentication.conf is migrated to the config stacks. With this change in place, all https tests pass without the need to create a dedicated GlobalStore. |
465 |
self.ca_certs = ca_certs |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
466 |
|
467 |
def connect(self): |
|
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
468 |
if 'http' in debug.debug_flags: |
469 |
self._mutter_connect() |
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
470 |
httplib.HTTPConnection.connect(self) |
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
471 |
self._wrap_socket_for_reporting(self.sock) |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
472 |
if self.proxied_host is None: |
473 |
self.connect_to_origin() |
|
474 |
||
475 |
def connect_to_origin(self): |
|
6238.2.7
by Jelmer Vernooij
Add ssl.cert_reqs. |
476 |
# FIXME JRV 2011-12-18: Use location config here?
|
477 |
config_stack = config.GlobalStack() |
|
478 |
cert_reqs = config_stack.get('ssl.cert_reqs') |
|
6238.2.13
by Jelmer Vernooij
add notes on using -Ossl.cert_reqs=none. |
479 |
if cert_reqs == ssl.CERT_NONE: |
6437.25.6
by Vincent Ladeuil
Feedback from mgz. |
480 |
trace.warning("Not checking SSL certificate for %s: %d", |
6238.2.7
by Jelmer Vernooij
Add ssl.cert_reqs. |
481 |
self.host, self.port) |
6437.25.1
by Vincent Ladeuil
Provide default paths for ca certs for supported platforms |
482 |
ca_certs = None |
6238.2.13
by Jelmer Vernooij
add notes on using -Ossl.cert_reqs=none. |
483 |
else: |
6437.25.1
by Vincent Ladeuil
Provide default paths for ca certs for supported platforms |
484 |
if self.ca_certs is None: |
485 |
ca_certs = config_stack.get('ssl.ca_certs') |
|
486 |
else: |
|
487 |
ca_certs = self.ca_certs |
|
6238.2.14
by Jelmer Vernooij
Urgh - only warn if there actually isn't a ca certs file set. |
488 |
if ca_certs is None: |
489 |
trace.warning( |
|
6437.25.6
by Vincent Ladeuil
Feedback from mgz. |
490 |
"No valid trusted SSL CA certificates file set. See "
|
6238.2.14
by Jelmer Vernooij
Urgh - only warn if there actually isn't a ca certs file set. |
491 |
"'bzr help ssl.ca_certs' for more information on setting "
|
6437.25.6
by Vincent Ladeuil
Feedback from mgz. |
492 |
"trusted CAs.") |
6238.2.13
by Jelmer Vernooij
add notes on using -Ossl.cert_reqs=none. |
493 |
try: |
494 |
ssl_sock = ssl.wrap_socket(self.sock, self.key_file, self.cert_file, |
|
495 |
cert_reqs=cert_reqs, ca_certs=ca_certs) |
|
496 |
except ssl.SSLError, e: |
|
6437.25.7
by Vincent Ladeuil
We need a clearer understanding of ssl errors. |
497 |
trace.note( |
498 |
"\n" |
|
499 |
"See `bzr help ssl.ca_certs` for how to specify trusted CA"
|
|
500 |
"certificates.\n" |
|
501 |
"Pass -Ossl.cert_reqs=none to disable certificate "
|
|
502 |
"verification entirely.\n") |
|
6238.2.13
by Jelmer Vernooij
add notes on using -Ossl.cert_reqs=none. |
503 |
raise
|
6437.25.4
by Vincent Ladeuil
Get rid of 'optional' for ssl.ca_reqs to simplify the model. |
504 |
if cert_reqs == ssl.CERT_REQUIRED: |
505 |
peer_cert = ssl_sock.getpeercert() |
|
6238.2.7
by Jelmer Vernooij
Add ssl.cert_reqs. |
506 |
match_hostname(peer_cert, self.host) |
6238.2.4
by Jelmer Vernooij
Verify ssl certificates when using urllib. |
507 |
|
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
508 |
# Wrap the ssl socket before anybody use it
|
509 |
self._wrap_socket_for_reporting(ssl_sock) |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
510 |
|
511 |
||
512 |
class Request(urllib2.Request): |
|
513 |
"""A custom Request object.
|
|
514 |
||
515 |
urllib2 determines the request method heuristically (based on
|
|
516 |
the presence or absence of data). We set the method
|
|
517 |
statically.
|
|
518 |
||
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
519 |
The Request object tracks:
|
520 |
- the connection the request will be made on.
|
|
521 |
- the authentication parameters needed to preventively set
|
|
522 |
the authentication header once a first authentication have
|
|
523 |
been made.
|
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
524 |
"""
|
525 |
||
526 |
def __init__(self, method, url, data=None, headers={}, |
|
527 |
origin_req_host=None, unverifiable=False, |
|
2520.2.2
by Vincent Ladeuil
Fix #115209 by issuing a single range request on 400: Bad Request |
528 |
connection=None, parent=None, |
4795.4.2
by Vincent Ladeuil
Revert auth reuse. |
529 |
accepted_errors=None): |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
530 |
urllib2.Request.__init__(self, url, data, headers, |
531 |
origin_req_host, unverifiable) |
|
532 |
self.method = method |
|
533 |
self.connection = connection |
|
2520.2.2
by Vincent Ladeuil
Fix #115209 by issuing a single range request on 400: Bad Request |
534 |
self.accepted_errors = accepted_errors |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
535 |
# To handle redirections
|
536 |
self.parent = parent |
|
537 |
self.redirected_to = None |
|
2164.2.15
by Vincent Ladeuil
Http redirections are not followed by default. Do not use hints |
538 |
# Unless told otherwise, redirections are not followed
|
539 |
self.follow_redirections = False |
|
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
540 |
# auth and proxy_auth are dicts containing, at least
|
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
541 |
# (scheme, host, port, realm, user, password, protocol, path).
|
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
542 |
# The dict entries are mostly handled by the AuthHandler.
|
543 |
# Some authentication schemes may add more entries.
|
|
4795.4.2
by Vincent Ladeuil
Revert auth reuse. |
544 |
self.auth = {} |
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
545 |
self.proxy_auth = {} |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
546 |
self.proxied_host = None |
2420.1.3
by Vincent Ladeuil
Implement http proxy basic authentication. |
547 |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
548 |
def get_method(self): |
549 |
return self.method |
|
550 |
||
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
551 |
def set_proxy(self, proxy, type): |
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
552 |
"""Set the proxy and remember the proxied host."""
|
4776.2.7
by Vincent Ladeuil
Fix proxy CONNECT for non-default ports. |
553 |
host, port = urllib.splitport(self.get_host()) |
554 |
if port is None: |
|
555 |
# We need to set the default port ourselves way before it gets set
|
|
556 |
# in the HTTP[S]Connection object at build time.
|
|
557 |
if self.type == 'https': |
|
558 |
conn_class = HTTPSConnection |
|
559 |
else: |
|
560 |
conn_class = HTTPConnection |
|
561 |
port = conn_class.default_port |
|
562 |
self.proxied_host = '%s:%s' % (host, port) |
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
563 |
urllib2.Request.set_proxy(self, proxy, type) |
4797.75.1
by Andrew Bennetts
Backport fix for bug 558343 from lp:bzr r5220. |
564 |
# When urllib2 makes a https request with our wrapper code and a proxy,
|
565 |
# it sets Host to the https proxy, not the host we want to talk to.
|
|
566 |
# I'm fairly sure this is our fault, but what is the cause is an open
|
|
567 |
# question. -- Robert Collins May 8 2010.
|
|
568 |
self.add_unredirected_header('Host', self.proxied_host) |
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
569 |
|
570 |
||
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
571 |
class _ConnectRequest(Request): |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
572 |
|
573 |
def __init__(self, request): |
|
574 |
"""Constructor
|
|
3943.8.1
by Marius Kruger
remove all trailing whitespace from bzr source |
575 |
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
576 |
:param request: the first request sent to the proxied host, already
|
577 |
processed by the opener (i.e. proxied_host is already set).
|
|
578 |
"""
|
|
579 |
# We give a fake url and redefine get_selector or urllib2 will be
|
|
580 |
# confused
|
|
581 |
Request.__init__(self, 'CONNECT', request.get_full_url(), |
|
582 |
connection=request.connection) |
|
3376.2.4
by Martin Pool
Remove every assert statement from bzrlib! |
583 |
if request.proxied_host is None: |
584 |
raise AssertionError() |
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
585 |
self.proxied_host = request.proxied_host |
586 |
||
587 |
def get_selector(self): |
|
588 |
return self.proxied_host |
|
589 |
||
590 |
def set_proxy(self, proxy, type): |
|
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
591 |
"""Set the proxy without remembering the proxied host.
|
592 |
||
593 |
We already know the proxied host by definition, the CONNECT request
|
|
594 |
occurs only when the connection goes through a proxy. The usual
|
|
595 |
processing (masquerade the request so that the connection is done to
|
|
596 |
the proxy while the request is targeted at another host) does not apply
|
|
597 |
here. In fact, the connection is already established with proxy and we
|
|
598 |
just want to enable the SSL tunneling.
|
|
599 |
"""
|
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
600 |
urllib2.Request.set_proxy(self, proxy, type) |
601 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
602 |
|
603 |
class ConnectionHandler(urllib2.BaseHandler): |
|
604 |
"""Provides connection-sharing by pre-processing requests.
|
|
605 |
||
606 |
urllib2 provides no way to access the HTTPConnection object
|
|
607 |
internally used. But we need it in order to achieve
|
|
608 |
connection sharing. So, we add it to the request just before
|
|
609 |
it is processed, and then we override the do_open method for
|
|
2363.4.7
by Vincent Ladeuil
Deeper tests, prepare the auth setting that will avoid the |
610 |
http[s] requests in AbstractHTTPHandler.
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
611 |
"""
|
612 |
||
613 |
handler_order = 1000 # after all pre-processings |
|
614 |
||
6238.2.22
by Vincent Ladeuil
Create a specific test permutation for urllib https so we can inject our test ca certs. The wiring in _urllib2_wrappers is a bit hackish and it will need to use auth instead so different certs can be used for proxies and real servers but this could wait until authentication.conf is migrated to the config stacks. With this change in place, all https tests pass without the need to create a dedicated GlobalStore. |
615 |
def __init__(self, report_activity=None, ca_certs=None): |
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
616 |
self._report_activity = report_activity |
6238.2.22
by Vincent Ladeuil
Create a specific test permutation for urllib https so we can inject our test ca certs. The wiring in _urllib2_wrappers is a bit hackish and it will need to use auth instead so different certs can be used for proxies and real servers but this could wait until authentication.conf is migrated to the config stacks. With this change in place, all https tests pass without the need to create a dedicated GlobalStore. |
617 |
self.ca_certs = ca_certs |
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
618 |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
619 |
def create_connection(self, request, http_connection_class): |
620 |
host = request.get_host() |
|
621 |
if not host: |
|
2004.1.15
by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests. |
622 |
# Just a bit of paranoia here, this should have been
|
623 |
# handled in the higher levels
|
|
2004.1.27
by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message. |
624 |
raise errors.InvalidURL(request.get_full_url(), 'no host given.') |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
625 |
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
626 |
# We create a connection (but it will not connect until the first
|
627 |
# request is made)
|
|
2004.1.42
by v.ladeuil+lp at free
Fix #70803 by catching the httplib exception. |
628 |
try: |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
629 |
connection = http_connection_class( |
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
630 |
host, proxied_host=request.proxied_host, |
6238.2.22
by Vincent Ladeuil
Create a specific test permutation for urllib https so we can inject our test ca certs. The wiring in _urllib2_wrappers is a bit hackish and it will need to use auth instead so different certs can be used for proxies and real servers but this could wait until authentication.conf is migrated to the config stacks. With this change in place, all https tests pass without the need to create a dedicated GlobalStore. |
631 |
report_activity=self._report_activity, |
632 |
ca_certs=self.ca_certs) |
|
2004.1.42
by v.ladeuil+lp at free
Fix #70803 by catching the httplib exception. |
633 |
except httplib.InvalidURL, exception: |
634 |
# There is only one occurrence of InvalidURL in httplib
|
|
635 |
raise errors.InvalidURL(request.get_full_url(), |
|
636 |
extra='nonnumeric port') |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
637 |
|
638 |
return connection |
|
639 |
||
640 |
def capture_connection(self, request, http_connection_class): |
|
641 |
"""Capture or inject the request connection.
|
|
642 |
||
643 |
Two cases:
|
|
644 |
- the request have no connection: create a new one,
|
|
645 |
||
646 |
- the request have a connection: this one have been used
|
|
647 |
already, let's capture it, so that we can give it to
|
|
648 |
another transport to be reused. We don't do that
|
|
649 |
ourselves: the Transport object get the connection from
|
|
650 |
a first request and then propagate it, from request to
|
|
651 |
request or to cloned transports.
|
|
652 |
"""
|
|
653 |
connection = request.connection |
|
654 |
if connection is None: |
|
655 |
# Create a new one
|
|
656 |
connection = self.create_connection(request, http_connection_class) |
|
657 |
request.connection = connection |
|
658 |
||
659 |
# All connections will pass here, propagate debug level
|
|
660 |
connection.set_debuglevel(DEBUG) |
|
661 |
return request |
|
662 |
||
663 |
def http_request(self, request): |
|
664 |
return self.capture_connection(request, HTTPConnection) |
|
665 |
||
666 |
def https_request(self, request): |
|
667 |
return self.capture_connection(request, HTTPSConnection) |
|
668 |
||
669 |
||
670 |
class AbstractHTTPHandler(urllib2.AbstractHTTPHandler): |
|
671 |
"""A custom handler for HTTP(S) requests.
|
|
672 |
||
673 |
We overrive urllib2.AbstractHTTPHandler to get a better
|
|
674 |
control of the connection, the ability to implement new
|
|
675 |
request types and return a response able to cope with
|
|
676 |
persistent connections.
|
|
677 |
"""
|
|
678 |
||
679 |
# We change our order to be before urllib2 HTTP[S]Handlers
|
|
2004.3.1
by vila
Test ConnectionError exceptions. |
680 |
# and be chosen instead of them (the first http_open called
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
681 |
# wins).
|
682 |
handler_order = 400 |
|
683 |
||
684 |
_default_headers = {'Pragma': 'no-cache', |
|
685 |
'Cache-control': 'max-age=0', |
|
686 |
'Connection': 'Keep-Alive', |
|
2004.1.15
by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests. |
687 |
'User-agent': 'bzr/%s (urllib)' % bzrlib_version, |
2004.3.3
by vila
Better (but still incomplete) design for bogus servers. |
688 |
'Accept': '*/*', |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
689 |
}
|
690 |
||
691 |
def __init__(self): |
|
2004.1.16
by v.ladeuil+lp at free
Add tests against erroneous http status lines. |
692 |
urllib2.AbstractHTTPHandler.__init__(self, debuglevel=DEBUG) |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
693 |
|
2004.1.15
by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests. |
694 |
def http_request(self, request): |
695 |
"""Common headers setting"""
|
|
696 |
||
697 |
request.headers.update(self._default_headers.copy()) |
|
698 |
# FIXME: We may have to add the Content-Length header if
|
|
699 |
# we have data to send.
|
|
700 |
return request |
|
701 |
||
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
702 |
def retry_or_raise(self, http_class, request, first_try): |
703 |
"""Retry the request (once) or raise the exception.
|
|
2004.3.1
by vila
Test ConnectionError exceptions. |
704 |
|
705 |
urllib2 raises exception of application level kind, we
|
|
706 |
just have to translate them.
|
|
707 |
||
708 |
httplib can raise exceptions of transport level (badly
|
|
709 |
formatted dialog, loss of connexion or socket level
|
|
710 |
problems). In that case we should issue the request again
|
|
711 |
(httplib will close and reopen a new connection if
|
|
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
712 |
needed).
|
713 |
"""
|
|
714 |
# When an exception occurs, we give back the original
|
|
715 |
# Traceback or the bugs are hard to diagnose.
|
|
716 |
exc_type, exc_val, exc_tb = sys.exc_info() |
|
717 |
if exc_type == socket.gaierror: |
|
718 |
# No need to retry, that will not help
|
|
719 |
raise errors.ConnectionError("Couldn't resolve host '%s'" |
|
720 |
% request.get_origin_req_host(), |
|
721 |
orig_error=exc_val) |
|
2955.2.1
by Vincent Ladeuil
Fix #160012 by leaving the http pipeline related exceptions raise. |
722 |
elif isinstance(exc_val, httplib.ImproperConnectionState): |
723 |
# The httplib pipeline is in incorrect state, it's a bug in our
|
|
724 |
# implementation.
|
|
725 |
raise exc_type, exc_val, exc_tb |
|
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
726 |
else: |
727 |
if first_try: |
|
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
728 |
if self._debuglevel >= 2: |
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
729 |
print 'Received exception: [%r]' % exc_val |
730 |
print ' On connection: [%r]' % request.connection |
|
731 |
method = request.get_method() |
|
732 |
url = request.get_full_url() |
|
733 |
print ' Will retry, %s %r' % (method, url) |
|
734 |
request.connection.close() |
|
735 |
response = self.do_open(http_class, request, False) |
|
736 |
else: |
|
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
737 |
if self._debuglevel >= 2: |
2004.1.39
by v.ladeuil+lp at free
Fix a race condition that make selftest fail once in a while. |
738 |
print 'Received second exception: [%r]' % exc_val |
739 |
print ' On connection: [%r]' % request.connection |
|
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
740 |
if exc_type in (httplib.BadStatusLine, httplib.UnknownProtocol): |
741 |
# httplib.BadStatusLine and
|
|
742 |
# httplib.UnknownProtocol indicates that a
|
|
743 |
# bogus server was encountered or a bad
|
|
744 |
# connection (i.e. transient errors) is
|
|
745 |
# experimented, we have already retried once
|
|
746 |
# for that request so we raise the exception.
|
|
747 |
my_exception = errors.InvalidHttpResponse( |
|
748 |
request.get_full_url(), |
|
749 |
'Bad status line received', |
|
750 |
orig_error=exc_val) |
|
4628.1.2
by Vincent Ladeuil
More complete fix. |
751 |
elif (isinstance(exc_val, socket.error) and len(exc_val.args) |
5599.3.3
by John Arbash Meinel
Treate WSAECONNABORTED the same as WSAECONNRESET in the http _urllib2 code. Bug #686587 |
752 |
and exc_val.args[0] in (errno.ECONNRESET, 10053, 10054)): |
753 |
# 10053 == WSAECONNABORTED
|
|
754 |
# 10054 == WSAECONNRESET
|
|
4628.1.2
by Vincent Ladeuil
More complete fix. |
755 |
raise errors.ConnectionReset( |
756 |
"Connection lost while sending request.") |
|
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
757 |
else: |
758 |
# All other exception are considered connection related.
|
|
759 |
||
760 |
# socket errors generally occurs for reasons
|
|
761 |
# far outside our scope, so closing the
|
|
762 |
# connection and retrying is the best we can
|
|
763 |
# do.
|
|
764 |
||
765 |
my_exception = errors.ConnectionError( |
|
766 |
msg= 'while sending %s %s:' % (request.get_method(), |
|
767 |
request.get_selector()), |
|
768 |
orig_error=exc_val) |
|
769 |
||
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
770 |
if self._debuglevel >= 2: |
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
771 |
print 'On connection: [%r]' % request.connection |
772 |
method = request.get_method() |
|
773 |
url = request.get_full_url() |
|
774 |
print ' Failed again, %s %r' % (method, url) |
|
775 |
print ' Will raise: [%r]' % my_exception |
|
776 |
raise my_exception, None, exc_tb |
|
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
777 |
return response |
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
778 |
|
779 |
def do_open(self, http_class, request, first_try=True): |
|
780 |
"""See urllib2.AbstractHTTPHandler.do_open for the general idea.
|
|
781 |
||
782 |
The request will be retried once if it fails.
|
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
783 |
"""
|
784 |
connection = request.connection |
|
3376.2.4
by Martin Pool
Remove every assert statement from bzrlib! |
785 |
if connection is None: |
786 |
raise AssertionError( |
|
787 |
'Cannot process a request without a connection') |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
788 |
|
2004.1.19
by v.ladeuil+lp at free
Test protocol version in http responses. |
789 |
# Get all the headers
|
2004.1.15
by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests. |
790 |
headers = {} |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
791 |
headers.update(request.header_items()) |
792 |
headers.update(request.unredirected_hdrs) |
|
3430.1.1
by Vincent Ladeuil
Fix bug #229076 by fixing header names before sending the request. |
793 |
# Some servers or proxies will choke on headers not properly
|
794 |
# cased. httplib/urllib/urllib2 all use capitalize to get canonical
|
|
795 |
# header names, but only python2.5 urllib2 use title() to fix them just
|
|
796 |
# before sending the request. And not all versions of python 2.5 do
|
|
797 |
# that. Since we replace urllib2.AbstractHTTPHandler.do_open we do it
|
|
798 |
# ourself below.
|
|
3430.1.2
by Vincent Ladeuil
Fixed as per Matt Nordhoff review. |
799 |
headers = dict((name.title(), val) for name, val in headers.iteritems()) |
2004.3.1
by vila
Test ConnectionError exceptions. |
800 |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
801 |
try: |
3052.3.3
by Vincent Ladeuil
Add -Dhttp support. |
802 |
method = request.get_method() |
803 |
url = request.get_selector() |
|
804 |
connection._send_request(method, url, |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
805 |
# FIXME: implements 100-continue
|
806 |
#None, # We don't send the body yet
|
|
807 |
request.get_data(), |
|
808 |
headers) |
|
3052.3.3
by Vincent Ladeuil
Add -Dhttp support. |
809 |
if 'http' in debug.debug_flags: |
810 |
trace.mutter('> %s %s' % (method, url)) |
|
5957.2.3
by Vincent Ladeuil
Mask credentials in the -Dhttp logging |
811 |
hdrs = [] |
812 |
for k,v in headers.iteritems(): |
|
813 |
# People are often told to paste -Dhttp output to help
|
|
814 |
# debug. Don't compromise credentials.
|
|
815 |
if k in ('Authorization', 'Proxy-Authorization'): |
|
816 |
v = '<masked>' |
|
817 |
hdrs.append('%s: %s' % (k, v)) |
|
3052.3.3
by Vincent Ladeuil
Add -Dhttp support. |
818 |
trace.mutter('> ' + '\n> '.join(hdrs) + '\n') |
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
819 |
if self._debuglevel >= 1: |
820 |
print 'Request sent: [%r] from (%s)' \ |
|
821 |
% (request, request.connection.sock.getsockname()) |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
822 |
response = connection.getresponse() |
823 |
convert_to_addinfourl = True |
|
6238.2.21
by Vincent Ladeuil
The https test server will now refuse connections if an ssl error occurs during the handshake. Ssl errors and certificate errors aborts requests without re-trying them. |
824 |
except (ssl.SSLError, errors.CertificateError): |
825 |
# Something is wrong with either the certificate or the hostname,
|
|
826 |
# re-trying won't help
|
|
827 |
raise
|
|
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
828 |
except (socket.gaierror, httplib.BadStatusLine, httplib.UnknownProtocol, |
829 |
socket.error, httplib.HTTPException): |
|
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
830 |
response = self.retry_or_raise(http_class, request, first_try) |
831 |
convert_to_addinfourl = False |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
832 |
|
833 |
# FIXME: HTTPConnection does not fully support 100-continue (the
|
|
834 |
# server responses are just ignored)
|
|
835 |
||
836 |
# if code == 100:
|
|
837 |
# mutter('Will send the body')
|
|
838 |
# # We can send the body now
|
|
839 |
# body = request.get_data()
|
|
840 |
# if body is None:
|
|
841 |
# raise URLError("No data given")
|
|
842 |
# connection.send(body)
|
|
843 |
# response = connection.getresponse()
|
|
844 |
||
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
845 |
if self._debuglevel >= 2: |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
846 |
print 'Receives response: %r' % response |
847 |
print ' For: %r(%r)' % (request.get_method(), |
|
848 |
request.get_full_url()) |
|
849 |
||
850 |
if convert_to_addinfourl: |
|
851 |
# Shamelessly copied from urllib2
|
|
852 |
req = request |
|
853 |
r = response |
|
854 |
r.recv = r.read |
|
3287.3.2
by Andrew Bennetts
Buffer 64k, rather than just 8k. |
855 |
fp = socket._fileobject(r, bufsize=65536) |
5393.6.1
by Toshio Kuratomi
Fix branching from lp: repositories under python-2.7 |
856 |
resp = addinfourl(fp, r.msg, req.get_full_url()) |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
857 |
resp.code = r.status |
858 |
resp.msg = r.reason |
|
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
859 |
resp.version = r.version |
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
860 |
if self._debuglevel >= 2: |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
861 |
print 'Create addinfourl: %r' % resp |
862 |
print ' For: %r(%r)' % (request.get_method(), |
|
863 |
request.get_full_url()) |
|
3059.2.5
by Vincent Ladeuil
DAMN^64, the http test server is 1.0 not 1.1 :( Better pipe cleaning and less readv caching (since that's the point of the whole fix). |
864 |
if 'http' in debug.debug_flags: |
865 |
version = 'HTTP/%d.%d' |
|
866 |
try: |
|
867 |
version = version % (resp.version / 10, |
|
868 |
resp.version % 10) |
|
869 |
except: |
|
870 |
version = 'HTTP/%r' % resp.version |
|
871 |
trace.mutter('< %s %s %s' % (version, resp.code, |
|
872 |
resp.msg)) |
|
873 |
# Use the raw header lines instead of treating resp.info() as a
|
|
874 |
# dict since we may miss duplicated headers otherwise.
|
|
875 |
hdrs = [h.rstrip('\r\n') for h in resp.info().headers] |
|
876 |
trace.mutter('< ' + '\n< '.join(hdrs) + '\n') |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
877 |
else: |
878 |
resp = response |
|
879 |
return resp |
|
880 |
||
881 |
||
882 |
class HTTPHandler(AbstractHTTPHandler): |
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
883 |
"""A custom handler that just thunks into HTTPConnection"""
|
884 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
885 |
def http_open(self, request): |
886 |
return self.do_open(HTTPConnection, request) |
|
887 |
||
888 |
||
889 |
class HTTPSHandler(AbstractHTTPHandler): |
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
890 |
"""A custom handler that just thunks into HTTPSConnection"""
|
891 |
||
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
892 |
https_request = AbstractHTTPHandler.http_request |
893 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
894 |
def https_open(self, request): |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
895 |
connection = request.connection |
896 |
if connection.sock is None and \ |
|
897 |
connection.proxied_host is not None and \ |
|
898 |
request.get_method() != 'CONNECT' : # Don't loop |
|
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
899 |
# FIXME: We need a gazillion connection tests here, but we still
|
900 |
# miss a https server :-( :
|
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
901 |
# - with and without proxy
|
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
902 |
# - with and without certificate
|
903 |
# - with self-signed certificate
|
|
904 |
# - with and without authentication
|
|
2929.3.9
by Vincent Ladeuil
Don't pretend we support HTTP/0.9 since we don't and do that correctly. |
905 |
# - with good and bad credentials (especially the proxy auth around
|
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
906 |
# CONNECT)
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
907 |
# - with basic and digest schemes
|
908 |
# - reconnection on errors
|
|
909 |
# - connection persistence behaviour (including reconnection)
|
|
910 |
||
911 |
# We are about to connect for the first time via a proxy, we must
|
|
912 |
# issue a CONNECT request first to establish the encrypted link
|
|
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
913 |
connect = _ConnectRequest(request) |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
914 |
response = self.parent.open(connect) |
915 |
if response.code != 200: |
|
4797.75.1
by Andrew Bennetts
Backport fix for bug 558343 from lp:bzr r5220. |
916 |
raise errors.ConnectionError("Can't connect to %s via proxy %s" % ( |
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
917 |
connect.proxied_host, self.host)) |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
918 |
# Housekeeping
|
3059.2.2
by Vincent Ladeuil
Read http responses on demand without buffering the whole body |
919 |
connection.cleanup_pipe() |
3943.8.1
by Marius Kruger
remove all trailing whitespace from bzr source |
920 |
# Establish the connection encryption
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
921 |
connection.connect_to_origin() |
922 |
# Propagate the connection to the original request
|
|
923 |
request.connection = connection |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
924 |
return self.do_open(HTTPSConnection, request) |
925 |
||
926 |
class HTTPRedirectHandler(urllib2.HTTPRedirectHandler): |
|
927 |
"""Handles redirect requests.
|
|
928 |
||
929 |
We have to implement our own scheme because we use a specific
|
|
930 |
Request object and because we want to implement a specific
|
|
931 |
policy.
|
|
932 |
"""
|
|
933 |
_debuglevel = DEBUG |
|
934 |
# RFC2616 says that only read requests should be redirected
|
|
935 |
# without interacting with the user. But bzr use some
|
|
936 |
# shortcuts to optimize against roundtrips which can leads to
|
|
937 |
# write requests being issued before read requests of
|
|
938 |
# containing dirs can be redirected. So we redirect write
|
|
939 |
# requests in the same way which seems to respect the spirit
|
|
940 |
# of the RFC if not its letter.
|
|
941 |
||
942 |
def redirect_request(self, req, fp, code, msg, headers, newurl): |
|
943 |
"""See urllib2.HTTPRedirectHandler.redirect_request"""
|
|
944 |
# We would have preferred to update the request instead
|
|
945 |
# of creating a new one, but the urllib2.Request object
|
|
946 |
# has a too complicated creation process to provide a
|
|
947 |
# simple enough equivalent update process. Instead, when
|
|
2164.2.29
by Vincent Ladeuil
Test the http redirection at the request level even if it's not |
948 |
# redirecting, we only update the following request in
|
949 |
# the redirect chain with a reference to the parent
|
|
950 |
# request .
|
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
951 |
|
2164.2.1
by v.ladeuil+lp at free
First rough http branch redirection implementation. |
952 |
# Some codes make no sense in our context and are treated
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
953 |
# as errors:
|
954 |
||
955 |
# 300: Multiple choices for different representations of
|
|
956 |
# the URI. Using that mechanisn with bzr will violate the
|
|
957 |
# protocol neutrality of Transport.
|
|
958 |
||
959 |
# 304: Not modified (SHOULD only occurs with conditional
|
|
960 |
# GETs which are not used by our implementation)
|
|
961 |
||
962 |
# 305: Use proxy. I can't imagine this one occurring in
|
|
963 |
# our context-- vila/20060909
|
|
964 |
||
965 |
# 306: Unused (if the RFC says so...)
|
|
966 |
||
2164.2.1
by v.ladeuil+lp at free
First rough http branch redirection implementation. |
967 |
# If the code is 302 and the request is HEAD, some may
|
968 |
# think that it is a sufficent hint that the file exists
|
|
969 |
# and that we MAY avoid following the redirections. But
|
|
970 |
# if we want to be sure, we MUST follow them.
|
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
971 |
|
972 |
if code in (301, 302, 303, 307): |
|
973 |
return Request(req.get_method(),newurl, |
|
974 |
headers = req.headers, |
|
975 |
origin_req_host = req.get_origin_req_host(), |
|
976 |
unverifiable = True, |
|
977 |
# TODO: It will be nice to be able to
|
|
978 |
# detect virtual hosts sharing the same
|
|
979 |
# IP address, that will allow us to
|
|
980 |
# share the same connection...
|
|
981 |
connection = None, |
|
982 |
parent = req, |
|
983 |
)
|
|
984 |
else: |
|
985 |
raise urllib2.HTTPError(req.get_full_url(), code, msg, headers, fp) |
|
986 |
||
2164.2.29
by Vincent Ladeuil
Test the http redirection at the request level even if it's not |
987 |
def http_error_302(self, req, fp, code, msg, headers): |
2004.3.1
by vila
Test ConnectionError exceptions. |
988 |
"""Requests the redirected to URI.
|
989 |
||
3059.2.2
by Vincent Ladeuil
Read http responses on demand without buffering the whole body |
990 |
Copied from urllib2 to be able to clean the pipe of the associated
|
991 |
connection, *before* issuing the redirected request but *after* having
|
|
992 |
eventually raised an error.
|
|
2004.3.1
by vila
Test ConnectionError exceptions. |
993 |
"""
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
994 |
# Some servers (incorrectly) return multiple Location headers
|
995 |
# (so probably same goes for URI). Use first header.
|
|
996 |
||
997 |
# TODO: Once we get rid of addinfourl objects, the
|
|
998 |
# following will need to be updated to use correct case
|
|
999 |
# for headers.
|
|
1000 |
if 'location' in headers: |
|
1001 |
newurl = headers.getheaders('location')[0] |
|
1002 |
elif 'uri' in headers: |
|
1003 |
newurl = headers.getheaders('uri')[0] |
|
1004 |
else: |
|
1005 |
return
|
|
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
1006 |
if self._debuglevel >= 1: |
2164.2.1
by v.ladeuil+lp at free
First rough http branch redirection implementation. |
1007 |
print 'Redirected to: %s (followed: %r)' % (newurl, |
1008 |
req.follow_redirections) |
|
1009 |
if req.follow_redirections is False: |
|
1010 |
req.redirected_to = newurl |
|
1011 |
return fp |
|
1012 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1013 |
newurl = urlparse.urljoin(req.get_full_url(), newurl) |
1014 |
||
1015 |
# This call succeeds or raise an error. urllib2 returns
|
|
1016 |
# if redirect_request returns None, but our
|
|
1017 |
# redirect_request never returns None.
|
|
1018 |
redirected_req = self.redirect_request(req, fp, code, msg, headers, |
|
1019 |
newurl) |
|
1020 |
||
1021 |
# loop detection
|
|
1022 |
# .redirect_dict has a key url if url was previously visited.
|
|
1023 |
if hasattr(req, 'redirect_dict'): |
|
1024 |
visited = redirected_req.redirect_dict = req.redirect_dict |
|
1025 |
if (visited.get(newurl, 0) >= self.max_repeats or |
|
1026 |
len(visited) >= self.max_redirections): |
|
1027 |
raise urllib2.HTTPError(req.get_full_url(), code, |
|
1028 |
self.inf_msg + msg, headers, fp) |
|
1029 |
else: |
|
1030 |
visited = redirected_req.redirect_dict = req.redirect_dict = {} |
|
1031 |
visited[newurl] = visited.get(newurl, 0) + 1 |
|
1032 |
||
1033 |
# We can close the fp now that we are sure that we won't
|
|
1034 |
# use it with HTTPError.
|
|
1035 |
fp.close() |
|
1036 |
# We have all we need already in the response
|
|
3059.2.2
by Vincent Ladeuil
Read http responses on demand without buffering the whole body |
1037 |
req.connection.cleanup_pipe() |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1038 |
|
1039 |
return self.parent.open(redirected_req) |
|
1040 |
||
2164.2.29
by Vincent Ladeuil
Test the http redirection at the request level even if it's not |
1041 |
http_error_301 = http_error_303 = http_error_307 = http_error_302 |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1042 |
|
1043 |
||
2167.3.1
by v.ladeuil+lp at free
Fix bug #74759. |
1044 |
class ProxyHandler(urllib2.ProxyHandler): |
1045 |
"""Handles proxy setting.
|
|
1046 |
||
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
1047 |
Copied and modified from urllib2 to be able to modify the request during
|
1048 |
the request pre-processing instead of modifying it at _open time. As we
|
|
1049 |
capture (or create) the connection object during request processing, _open
|
|
1050 |
time was too late.
|
|
1051 |
||
1052 |
The main task is to modify the request so that the connection is done to
|
|
1053 |
the proxy while the request still refers to the destination host.
|
|
1054 |
||
1055 |
Note: the proxy handling *may* modify the protocol used; the request may be
|
|
1056 |
against an https server proxied through an http proxy. So, https_request
|
|
1057 |
will be called, but later it's really http_open that will be called. This
|
|
2540.2.3
by Vincent Ladeuil
Take Aaron's comments into account. |
1058 |
explains why we don't have to call self.parent.open as the urllib2 did.
|
2167.3.1
by v.ladeuil+lp at free
Fix bug #74759. |
1059 |
"""
|
1060 |
||
1061 |
# Proxies must be in front
|
|
1062 |
handler_order = 100 |
|
2167.3.3
by v.ladeuil+lp at free
* bzrlib/transport/http/_urllib2_wrappers.py: |
1063 |
_debuglevel = DEBUG |
2167.3.1
by v.ladeuil+lp at free
Fix bug #74759. |
1064 |
|
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1065 |
def __init__(self, proxies=None): |
2167.3.1
by v.ladeuil+lp at free
Fix bug #74759. |
1066 |
urllib2.ProxyHandler.__init__(self, proxies) |
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
1067 |
# First, let's get rid of urllib2 implementation
|
2167.3.1
by v.ladeuil+lp at free
Fix bug #74759. |
1068 |
for type, proxy in self.proxies.items(): |
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
1069 |
if self._debuglevel >= 3: |
2167.3.3
by v.ladeuil+lp at free
* bzrlib/transport/http/_urllib2_wrappers.py: |
1070 |
print 'Will unbind %s_open for %r' % (type, proxy) |
2167.3.1
by v.ladeuil+lp at free
Fix bug #74759. |
1071 |
delattr(self, '%s_open' % type) |
1072 |
||
4797.75.1
by Andrew Bennetts
Backport fix for bug 558343 from lp:bzr r5220. |
1073 |
def bind_scheme_request(proxy, scheme): |
1074 |
if proxy is None: |
|
1075 |
return
|
|
1076 |
scheme_request = scheme + '_request' |
|
1077 |
if self._debuglevel >= 3: |
|
1078 |
print 'Will bind %s for %r' % (scheme_request, proxy) |
|
1079 |
setattr(self, scheme_request, |
|
1080 |
lambda request: self.set_proxy(request, scheme)) |
|
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
1081 |
# We are interested only by the http[s] proxies
|
2167.3.6
by v.ladeuil+lp at free
Take John's comments into account and add more tests. |
1082 |
http_proxy = self.get_proxy_env_var('http') |
4797.75.1
by Andrew Bennetts
Backport fix for bug 558343 from lp:bzr r5220. |
1083 |
bind_scheme_request(http_proxy, 'http') |
2167.3.6
by v.ladeuil+lp at free
Take John's comments into account and add more tests. |
1084 |
https_proxy = self.get_proxy_env_var('https') |
4797.75.1
by Andrew Bennetts
Backport fix for bug 558343 from lp:bzr r5220. |
1085 |
bind_scheme_request(https_proxy, 'https') |
2167.3.3
by v.ladeuil+lp at free
* bzrlib/transport/http/_urllib2_wrappers.py: |
1086 |
|
2167.3.6
by v.ladeuil+lp at free
Take John's comments into account and add more tests. |
1087 |
def get_proxy_env_var(self, name, default_to='all'): |
1088 |
"""Get a proxy env var.
|
|
1089 |
||
2182.1.1
by Aaron Bentley
Respect proxy environment settings (Vincent Ladeuil, #74759) |
1090 |
Note that we indirectly rely on
|
2167.3.6
by v.ladeuil+lp at free
Take John's comments into account and add more tests. |
1091 |
urllib.getproxies_environment taking into account the
|
1092 |
uppercased values for proxy variables.
|
|
1093 |
"""
|
|
2167.3.3
by v.ladeuil+lp at free
* bzrlib/transport/http/_urllib2_wrappers.py: |
1094 |
try: |
1095 |
return self.proxies[name.lower()] |
|
1096 |
except KeyError: |
|
2167.3.6
by v.ladeuil+lp at free
Take John's comments into account and add more tests. |
1097 |
if default_to is not None: |
1098 |
# Try to get the alternate environment variable
|
|
1099 |
try: |
|
1100 |
return self.proxies[default_to] |
|
1101 |
except KeyError: |
|
1102 |
pass
|
|
1103 |
return None |
|
2167.3.3
by v.ladeuil+lp at free
* bzrlib/transport/http/_urllib2_wrappers.py: |
1104 |
|
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
1105 |
def proxy_bypass(self, host): |
5639.2.2
by Vincent Ladeuil
Add tests and comments to clarify the feature. |
1106 |
"""Check if host should be proxied or not.
|
1107 |
||
1108 |
:returns: True to skip the proxy, False otherwise.
|
|
1109 |
"""
|
|
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1110 |
no_proxy = self.get_proxy_env_var('no', default_to=None) |
5639.2.2
by Vincent Ladeuil
Add tests and comments to clarify the feature. |
1111 |
bypass = self.evaluate_proxy_bypass(host, no_proxy) |
1112 |
if bypass is None: |
|
5639.2.1
by Martin Pool
Empty entries in the ``NO_PROXY`` variable are no longer treated as matching every host. |
1113 |
# Nevertheless, there are platform-specific ways to
|
1114 |
# ignore proxies...
|
|
1115 |
return urllib.proxy_bypass(host) |
|
1116 |
else: |
|
5639.2.2
by Vincent Ladeuil
Add tests and comments to clarify the feature. |
1117 |
return bypass |
5639.2.1
by Martin Pool
Empty entries in the ``NO_PROXY`` variable are no longer treated as matching every host. |
1118 |
|
1119 |
def evaluate_proxy_bypass(self, host, no_proxy): |
|
5639.2.2
by Vincent Ladeuil
Add tests and comments to clarify the feature. |
1120 |
"""Check the host against a comma-separated no_proxy list as a string.
|
1121 |
||
1122 |
:param host: ``host:port`` being requested
|
|
1123 |
||
5639.2.1
by Martin Pool
Empty entries in the ``NO_PROXY`` variable are no longer treated as matching every host. |
1124 |
:param no_proxy: comma-separated list of hosts to access directly.
|
5639.2.2
by Vincent Ladeuil
Add tests and comments to clarify the feature. |
1125 |
|
5639.2.1
by Martin Pool
Empty entries in the ``NO_PROXY`` variable are no longer treated as matching every host. |
1126 |
:returns: True to skip the proxy, False not to, or None to
|
1127 |
leave it to urllib.
|
|
1128 |
"""
|
|
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
1129 |
if no_proxy is None: |
5639.2.2
by Vincent Ladeuil
Add tests and comments to clarify the feature. |
1130 |
# All hosts are proxied
|
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
1131 |
return False |
1132 |
hhost, hport = urllib.splitport(host) |
|
2182.1.1
by Aaron Bentley
Respect proxy environment settings (Vincent Ladeuil, #74759) |
1133 |
# Does host match any of the domains mentioned in
|
1134 |
# no_proxy ? The rules about what is authorized in no_proxy
|
|
1135 |
# are fuzzy (to say the least). We try to allow most
|
|
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
1136 |
# commonly seen values.
|
1137 |
for domain in no_proxy.split(','): |
|
5639.2.1
by Martin Pool
Empty entries in the ``NO_PROXY`` variable are no longer treated as matching every host. |
1138 |
domain = domain.strip() |
1139 |
if domain == '': |
|
1140 |
continue
|
|
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
1141 |
dhost, dport = urllib.splitport(domain) |
2167.3.5
by v.ladeuil+lp at free
Tests for proxies, covering #74759. |
1142 |
if hport == dport or dport is None: |
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
1143 |
# Protect glob chars
|
1144 |
dhost = dhost.replace(".", r"\.") |
|
1145 |
dhost = dhost.replace("*", r".*") |
|
1146 |
dhost = dhost.replace("?", r".") |
|
2167.3.5
by v.ladeuil+lp at free
Tests for proxies, covering #74759. |
1147 |
if re.match(dhost, hhost, re.IGNORECASE): |
5639.2.2
by Vincent Ladeuil
Add tests and comments to clarify the feature. |
1148 |
return True |
1149 |
# Nothing explicitly avoid the host
|
|
1150 |
return None |
|
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
1151 |
|
2167.3.3
by v.ladeuil+lp at free
* bzrlib/transport/http/_urllib2_wrappers.py: |
1152 |
def set_proxy(self, request, type): |
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
1153 |
if self.proxy_bypass(request.get_host()): |
1154 |
return request |
|
1155 |
||
2167.3.6
by v.ladeuil+lp at free
Take John's comments into account and add more tests. |
1156 |
proxy = self.get_proxy_env_var(type) |
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
1157 |
if self._debuglevel >= 3: |
2167.3.3
by v.ladeuil+lp at free
* bzrlib/transport/http/_urllib2_wrappers.py: |
1158 |
print 'set_proxy %s_request for %r' % (type, proxy) |
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
1159 |
# FIXME: python 2.5 urlparse provides a better _parse_proxy which can
|
1160 |
# grok user:password@host:port as well as
|
|
1161 |
# http://user:password@host:port
|
|
1162 |
||
6055.2.1
by Jelmer Vernooij
Add UnparsedUrl. |
1163 |
parsed_url = transport.ConnectedTransport._split_url(proxy) |
1164 |
if not parsed_url.host: |
|
4294.2.9
by Robert Collins
Fixup tests broken by cleaning up the layering. |
1165 |
raise errors.InvalidURL(proxy, 'No host component') |
2900.2.15
by Vincent Ladeuil
AuthenticationConfig can be queried for logins too (first step). |
1166 |
|
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1167 |
if request.proxy_auth == {}: |
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1168 |
# No proxy auth parameter are available, we are handling the first
|
1169 |
# proxied request, intialize. scheme (the authentication scheme)
|
|
1170 |
# and realm will be set by the AuthHandler
|
|
1171 |
request.proxy_auth = { |
|
6055.2.1
by Jelmer Vernooij
Add UnparsedUrl. |
1172 |
'host': parsed_url.host, |
1173 |
'port': parsed_url.port, |
|
1174 |
'user': parsed_url.user, |
|
1175 |
'password': parsed_url.password, |
|
1176 |
'protocol': parsed_url.scheme, |
|
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1177 |
# We ignore path since we connect to a proxy
|
1178 |
'path': None} |
|
6055.2.1
by Jelmer Vernooij
Add UnparsedUrl. |
1179 |
if parsed_url.port is None: |
1180 |
phost = parsed_url.host |
|
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1181 |
else: |
6055.2.1
by Jelmer Vernooij
Add UnparsedUrl. |
1182 |
phost = parsed_url.host + ':%d' % parsed_url.port |
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1183 |
request.set_proxy(phost, type) |
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
1184 |
if self._debuglevel >= 3: |
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1185 |
print 'set_proxy: proxy set to %s://%s' % (type, phost) |
2167.3.1
by v.ladeuil+lp at free
Fix bug #74759. |
1186 |
return request |
1187 |
||
1188 |
||
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1189 |
class AbstractAuthHandler(urllib2.BaseHandler): |
1190 |
"""A custom abstract authentication handler for all http authentications.
|
|
1191 |
||
1192 |
Provides the meat to handle authentication errors and
|
|
1193 |
preventively set authentication headers after the first
|
|
1194 |
successful authentication.
|
|
1195 |
||
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1196 |
This can be used for http and proxy, as well as for basic, negotiate and
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1197 |
digest authentications.
|
1198 |
||
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
1199 |
This provides an unified interface for all authentication handlers
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1200 |
(urllib2 provides far too many with different policies).
|
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
1201 |
|
1202 |
The interaction between this handler and the urllib2
|
|
1203 |
framework is not obvious, it works as follow:
|
|
1204 |
||
1205 |
opener.open(request) is called:
|
|
1206 |
||
1207 |
- that may trigger http_request which will add an authentication header
|
|
1208 |
(self.build_header) if enough info is available.
|
|
1209 |
||
1210 |
- the request is sent to the server,
|
|
1211 |
||
1212 |
- if an authentication error is received self.auth_required is called,
|
|
1213 |
we acquire the authentication info in the error headers and call
|
|
1214 |
self.auth_match to check that we are able to try the
|
|
1215 |
authentication and complete the authentication parameters,
|
|
1216 |
||
1217 |
- we call parent.open(request), that may trigger http_request
|
|
1218 |
and will add a header (self.build_header), but here we have
|
|
1219 |
all the required info (keep in mind that the request and
|
|
1220 |
authentication used in the recursive calls are really (and must be)
|
|
1221 |
the *same* objects).
|
|
1222 |
||
1223 |
- if the call returns a response, the authentication have been
|
|
1224 |
successful and the request authentication parameters have been updated.
|
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1225 |
"""
|
1226 |
||
4307.4.3
by Vincent Ladeuil
Tighten multiple auth schemes handling. |
1227 |
scheme = None |
1228 |
"""The scheme as it appears in the server header (lower cased)"""
|
|
1229 |
||
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1230 |
_max_retry = 3 |
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
1231 |
"""We don't want to retry authenticating endlessly"""
|
1232 |
||
4050.2.3
by Vincent Ladeuil
Slight cosmetic tweaks. |
1233 |
requires_username = True |
1234 |
"""Whether the auth mechanism requires a username."""
|
|
1235 |
||
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1236 |
# The following attributes should be defined by daughter
|
1237 |
# classes:
|
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1238 |
# - auth_required_header: the header received from the server
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1239 |
# - auth_header: the header sent in the request
|
1240 |
||
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1241 |
def __init__(self): |
1242 |
# We want to know when we enter into an try/fail cycle of
|
|
1243 |
# authentications so we initialize to None to indicate that we aren't
|
|
1244 |
# in such a cycle by default.
|
|
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
1245 |
self._retry_count = None |
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1246 |
|
4050.2.2
by Vincent Ladeuil
Ensures all auth handlers correctly parse all auth headers. |
1247 |
def _parse_auth_header(self, server_header): |
1248 |
"""Parse the authentication header.
|
|
1249 |
||
1250 |
:param server_header: The value of the header sent by the server
|
|
1251 |
describing the authenticaion request.
|
|
1252 |
||
1253 |
:return: A tuple (scheme, remainder) scheme being the first word in the
|
|
1254 |
given header (lower cased), remainder may be None.
|
|
1255 |
"""
|
|
1256 |
try: |
|
1257 |
scheme, remainder = server_header.split(None, 1) |
|
1258 |
except ValueError: |
|
1259 |
scheme = server_header |
|
1260 |
remainder = None |
|
1261 |
return (scheme.lower(), remainder) |
|
1262 |
||
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
1263 |
def update_auth(self, auth, key, value): |
1264 |
"""Update a value in auth marking the auth as modified if needed"""
|
|
1265 |
old_value = auth.get(key, None) |
|
1266 |
if old_value != value: |
|
1267 |
auth[key] = value |
|
1268 |
auth['modified'] = True |
|
1269 |
||
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1270 |
def auth_required(self, request, headers): |
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1271 |
"""Retry the request if the auth scheme is ours.
|
1272 |
||
1273 |
:param request: The request needing authentication.
|
|
1274 |
:param headers: The headers for the authentication error response.
|
|
1275 |
:return: None or the response for the authenticated request.
|
|
1276 |
"""
|
|
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
1277 |
# Don't try to authenticate endlessly
|
1278 |
if self._retry_count is None: |
|
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1279 |
# The retry being recusrsive calls, None identify the first retry
|
1280 |
self._retry_count = 1 |
|
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
1281 |
else: |
1282 |
self._retry_count += 1 |
|
1283 |
if self._retry_count > self._max_retry: |
|
1284 |
# Let's be ready for next round
|
|
1285 |
self._retry_count = None |
|
1286 |
return None |
|
4307.4.2
by Vincent Ladeuil
Handle servers proposing several authentication schemes. |
1287 |
server_headers = headers.getheaders(self.auth_required_header) |
1288 |
if not server_headers: |
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1289 |
# The http error MUST have the associated
|
1290 |
# header. This must never happen in production code.
|
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1291 |
raise KeyError('%s not found' % self.auth_required_header) |
1292 |
||
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
1293 |
auth = self.get_auth(request) |
1294 |
auth['modified'] = False |
|
4795.4.5
by Vincent Ladeuil
Make sure all redirection code paths can handle authentication. |
1295 |
# Put some common info in auth if the caller didn't
|
1296 |
if auth.get('path', None) is None: |
|
6055.2.7
by Jelmer Vernooij
Change parse_url to URL.from_string. |
1297 |
parsed_url = urlutils.URL.from_string(request.get_full_url()) |
6055.2.1
by Jelmer Vernooij
Add UnparsedUrl. |
1298 |
self.update_auth(auth, 'protocol', parsed_url.scheme) |
1299 |
self.update_auth(auth, 'host', parsed_url.host) |
|
1300 |
self.update_auth(auth, 'port', parsed_url.port) |
|
1301 |
self.update_auth(auth, 'path', parsed_url.path) |
|
4307.4.2
by Vincent Ladeuil
Handle servers proposing several authentication schemes. |
1302 |
# FIXME: the auth handler should be selected at a single place instead
|
4307.4.3
by Vincent Ladeuil
Tighten multiple auth schemes handling. |
1303 |
# of letting all handlers try to match all headers, but the current
|
1304 |
# design doesn't allow a simple implementation.
|
|
4307.4.2
by Vincent Ladeuil
Handle servers proposing several authentication schemes. |
1305 |
for server_header in server_headers: |
1306 |
# Several schemes can be proposed by the server, try to match each
|
|
1307 |
# one in turn
|
|
1308 |
matching_handler = self.auth_match(server_header, auth) |
|
1309 |
if matching_handler: |
|
1310 |
# auth_match may have modified auth (by adding the
|
|
1311 |
# password or changing the realm, for example)
|
|
1312 |
if (request.get_header(self.auth_header, None) is not None |
|
1313 |
and not auth['modified']): |
|
1314 |
# We already tried that, give up
|
|
1315 |
return None |
|
1316 |
||
4307.4.3
by Vincent Ladeuil
Tighten multiple auth schemes handling. |
1317 |
# Only the most secure scheme proposed by the server should be
|
1318 |
# used, since the handlers use 'handler_order' to describe that
|
|
1319 |
# property, the first handler tried takes precedence, the
|
|
1320 |
# others should not attempt to authenticate if the best one
|
|
1321 |
# failed.
|
|
1322 |
best_scheme = auth.get('best_scheme', None) |
|
1323 |
if best_scheme is None: |
|
1324 |
# At that point, if current handler should doesn't succeed
|
|
1325 |
# the credentials are wrong (or incomplete), but we know
|
|
1326 |
# that the associated scheme should be used.
|
|
1327 |
best_scheme = auth['best_scheme'] = self.scheme |
|
1328 |
if best_scheme != self.scheme: |
|
1329 |
continue
|
|
1330 |
||
4307.4.2
by Vincent Ladeuil
Handle servers proposing several authentication schemes. |
1331 |
if self.requires_username and auth.get('user', None) is None: |
1332 |
# Without a known user, we can't authenticate
|
|
1333 |
return None |
|
1334 |
||
1335 |
# Housekeeping
|
|
1336 |
request.connection.cleanup_pipe() |
|
1337 |
# Retry the request with an authentication header added
|
|
1338 |
response = self.parent.open(request) |
|
1339 |
if response: |
|
1340 |
self.auth_successful(request, response) |
|
1341 |
return response |
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1342 |
# We are not qualified to handle the authentication.
|
1343 |
# Note: the authentication error handling will try all
|
|
1344 |
# available handlers. If one of them authenticates
|
|
1345 |
# successfully, a response will be returned. If none of
|
|
1346 |
# them succeeds, None will be returned and the error
|
|
1347 |
# handler will raise the 401 'Unauthorized' or the 407
|
|
1348 |
# 'Proxy Authentication Required' error.
|
|
1349 |
return None |
|
1350 |
||
1351 |
def add_auth_header(self, request, header): |
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1352 |
"""Add the authentication header to the request"""
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1353 |
request.add_unredirected_header(self.auth_header, header) |
1354 |
||
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1355 |
def auth_match(self, header, auth): |
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1356 |
"""Check that we are able to handle that authentication scheme.
|
1357 |
||
1358 |
The request authentication parameters may need to be
|
|
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
1359 |
updated with info from the server. Some of these
|
1360 |
parameters, when combined, are considered to be the
|
|
1361 |
authentication key, if one of them change the
|
|
1362 |
authentication result may change. 'user' and 'password'
|
|
1363 |
are exampls, but some auth schemes may have others
|
|
1364 |
(digest's nonce is an example, digest's nonce_count is a
|
|
1365 |
*counter-example*). Such parameters must be updated by
|
|
1366 |
using the update_auth() method.
|
|
3943.8.1
by Marius Kruger
remove all trailing whitespace from bzr source |
1367 |
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1368 |
:param header: The authentication header sent by the server.
|
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1369 |
:param auth: The auth parameters already known. They may be
|
1370 |
updated.
|
|
1371 |
:returns: True if we can try to handle the authentication.
|
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1372 |
"""
|
1373 |
raise NotImplementedError(self.auth_match) |
|
1374 |
||
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1375 |
def build_auth_header(self, auth, request): |
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1376 |
"""Build the value of the header used to authenticate.
|
1377 |
||
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1378 |
:param auth: The auth parameters needed to build the header.
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1379 |
:param request: The request needing authentication.
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1380 |
|
1381 |
:return: None or header.
|
|
1382 |
"""
|
|
1383 |
raise NotImplementedError(self.build_auth_header) |
|
1384 |
||
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
1385 |
def auth_successful(self, request, response): |
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1386 |
"""The authentification was successful for the request.
|
1387 |
||
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
1388 |
Additional infos may be available in the response.
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1389 |
|
1390 |
:param request: The succesfully authenticated request.
|
|
2420.1.9
by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth. |
1391 |
:param response: The server response (may contain auth info).
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1392 |
"""
|
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
1393 |
# It may happen that we need to reconnect later, let's be ready
|
1394 |
self._retry_count = None |
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1395 |
|
2900.2.20
by Vincent Ladeuil
http can query AuthenticationConfig for logins too. |
1396 |
def get_user_password(self, auth): |
3910.2.3
by Ben Jansen
Made tweaks requested by John Arbash Meinel. |
1397 |
"""Ask user for a password if none is already available.
|
1398 |
||
3910.2.4
by Vincent Ladeuil
Fixed as per John's review. |
1399 |
:param auth: authentication info gathered so far (from the initial url
|
1400 |
and then during dialog with the server).
|
|
3910.2.3
by Ben Jansen
Made tweaks requested by John Arbash Meinel. |
1401 |
"""
|
2900.2.20
by Vincent Ladeuil
http can query AuthenticationConfig for logins too. |
1402 |
auth_conf = config.AuthenticationConfig() |
4795.4.4
by Vincent Ladeuil
Protect more access to 'user' and 'password' auth attributes. |
1403 |
user = auth.get('user', None) |
1404 |
password = auth.get('password', None) |
|
2900.2.20
by Vincent Ladeuil
http can query AuthenticationConfig for logins too. |
1405 |
realm = auth['realm'] |
5484.2.2
by Martin Pool
Cope gracefully if urllib2 doesn't tell us the port number in the authentication callback |
1406 |
port = auth.get('port', None) |
2900.2.20
by Vincent Ladeuil
http can query AuthenticationConfig for logins too. |
1407 |
|
1408 |
if user is None: |
|
3910.2.1
by Ben Jansen
Changed HTTP transport auth so that URLs no longer need to include the username for HTTP Auth to work. Now, if bzr gets a 401 HTTP response, it looks in the authentication config for an appropriate username and password. If it doesn't find a username, it defaults to the local user. If it doesn't find a password, it prompts. |
1409 |
user = auth_conf.get_user(auth['protocol'], auth['host'], |
5484.2.2
by Martin Pool
Cope gracefully if urllib2 doesn't tell us the port number in the authentication callback |
1410 |
port=port, path=auth['path'], |
4222.3.12
by Jelmer Vernooij
Check that the HTTP transport prompts for usernames. |
1411 |
realm=realm, ask=True, |
1412 |
prompt=self.build_username_prompt(auth)) |
|
3910.2.2
by Vincent Ladeuil
Fix bug #300347 by allowing querying authentication.conf if no |
1413 |
if user is not None and password is None: |
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1414 |
password = auth_conf.get_password( |
5484.2.2
by Martin Pool
Cope gracefully if urllib2 doesn't tell us the port number in the authentication callback |
1415 |
auth['protocol'], auth['host'], user, |
1416 |
port=port, |
|
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1417 |
path=auth['path'], realm=realm, |
2900.2.19
by Vincent Ladeuil
Mention proxy and https in the password prompts, with tests. |
1418 |
prompt=self.build_password_prompt(auth)) |
2900.2.20
by Vincent Ladeuil
http can query AuthenticationConfig for logins too. |
1419 |
|
1420 |
return user, password |
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1421 |
|
2900.2.19
by Vincent Ladeuil
Mention proxy and https in the password prompts, with tests. |
1422 |
def _build_password_prompt(self, auth): |
1423 |
"""Build a prompt taking the protocol used into account.
|
|
1424 |
||
1425 |
The AuthHandler is used by http and https, we want that information in
|
|
1426 |
the prompt, so we build the prompt from the authentication dict which
|
|
1427 |
contains all the needed parts.
|
|
1428 |
||
3133.1.3
by Vincent Ladeuil
Fix typo (hi John ;). |
1429 |
Also, http and proxy AuthHandlers present different prompts to the
|
3133.1.2
by Vincent Ladeuil
Fix #177643 by making pycurl handle url-embedded credentials again. |
1430 |
user. The daughter classes should implements a public
|
2900.2.19
by Vincent Ladeuil
Mention proxy and https in the password prompts, with tests. |
1431 |
build_password_prompt using this method.
|
1432 |
"""
|
|
5923.1.2
by Vincent Ladeuil
Fix some more prompts to be unicode. |
1433 |
prompt = u'%s' % auth['protocol'].upper() + u' %(user)s@%(host)s' |
2900.2.19
by Vincent Ladeuil
Mention proxy and https in the password prompts, with tests. |
1434 |
realm = auth['realm'] |
1435 |
if realm is not None: |
|
5923.1.2
by Vincent Ladeuil
Fix some more prompts to be unicode. |
1436 |
prompt += u", Realm: '%s'" % realm.decode('utf8') |
1437 |
prompt += u' password' |
|
2900.2.19
by Vincent Ladeuil
Mention proxy and https in the password prompts, with tests. |
1438 |
return prompt |
1439 |
||
4222.3.12
by Jelmer Vernooij
Check that the HTTP transport prompts for usernames. |
1440 |
def _build_username_prompt(self, auth): |
1441 |
"""Build a prompt taking the protocol used into account.
|
|
1442 |
||
1443 |
The AuthHandler is used by http and https, we want that information in
|
|
1444 |
the prompt, so we build the prompt from the authentication dict which
|
|
1445 |
contains all the needed parts.
|
|
1446 |
||
1447 |
Also, http and proxy AuthHandlers present different prompts to the
|
|
1448 |
user. The daughter classes should implements a public
|
|
1449 |
build_username_prompt using this method.
|
|
1450 |
"""
|
|
5923.1.2
by Vincent Ladeuil
Fix some more prompts to be unicode. |
1451 |
prompt = u'%s' % auth['protocol'].upper() + u' %(host)s' |
4222.3.12
by Jelmer Vernooij
Check that the HTTP transport prompts for usernames. |
1452 |
realm = auth['realm'] |
1453 |
if realm is not None: |
|
5923.1.2
by Vincent Ladeuil
Fix some more prompts to be unicode. |
1454 |
prompt += u", Realm: '%s'" % realm.decode('utf8') |
1455 |
prompt += u' username' |
|
4222.3.12
by Jelmer Vernooij
Check that the HTTP transport prompts for usernames. |
1456 |
return prompt |
1457 |
||
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1458 |
def http_request(self, request): |
1459 |
"""Insert an authentication header if information is available"""
|
|
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1460 |
auth = self.get_auth(request) |
1461 |
if self.auth_params_reusable(auth): |
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1462 |
self.add_auth_header(request, self.build_auth_header(auth, request)) |
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1463 |
return request |
1464 |
||
1465 |
https_request = http_request # FIXME: Need test |
|
1466 |
||
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1467 |
|
1468 |
class NegotiateAuthHandler(AbstractAuthHandler): |
|
1469 |
"""A authentication handler that handles WWW-Authenticate: Negotiate.
|
|
1470 |
||
4032.1.4
by John Arbash Meinel
Found 2 more files with trailing whitespace. |
1471 |
At the moment this handler supports just Kerberos. In the future,
|
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1472 |
NTLM support may also be added.
|
1473 |
"""
|
|
1474 |
||
4307.4.3
by Vincent Ladeuil
Tighten multiple auth schemes handling. |
1475 |
scheme = 'negotiate' |
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1476 |
handler_order = 480 |
4017.5.1
by Jelmer Vernooij
Allow HTTP authentication handlers (such as the NegotiateAuthHandler) to |
1477 |
requires_username = False |
1478 |
||
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1479 |
def auth_match(self, header, auth): |
4050.2.2
by Vincent Ladeuil
Ensures all auth handlers correctly parse all auth headers. |
1480 |
scheme, raw_auth = self._parse_auth_header(header) |
4307.4.3
by Vincent Ladeuil
Tighten multiple auth schemes handling. |
1481 |
if scheme != self.scheme: |
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1482 |
return False |
1483 |
self.update_auth(auth, 'scheme', scheme) |
|
4011.3.2
by Jelmer Vernooij
Only attempt GSSAPI authentication when the kerberos module is present. |
1484 |
resp = self._auth_match_kerberos(auth) |
1485 |
if resp is None: |
|
1486 |
return False |
|
1487 |
# Optionally should try to authenticate using NTLM here
|
|
1488 |
self.update_auth(auth, 'negotiate_response', resp) |
|
1489 |
return True |
|
1490 |
||
1491 |
def _auth_match_kerberos(self, auth): |
|
1492 |
"""Try to create a GSSAPI response for authenticating against a host."""
|
|
6238.1.2
by John Arbash Meinel, Jelmer Vernooij
Only attempt to import kerberos module once. |
1493 |
global kerberos, checked_kerberos |
1494 |
if kerberos is None and not checked_kerberos: |
|
1495 |
try: |
|
1496 |
import kerberos |
|
1497 |
except ImportError: |
|
1498 |
kerberos = None |
|
1499 |
checked_kerberos = True |
|
1500 |
if kerberos is None: |
|
4011.3.2
by Jelmer Vernooij
Only attempt GSSAPI authentication when the kerberos module is present. |
1501 |
return None |
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1502 |
ret, vc = kerberos.authGSSClientInit("HTTP@%(host)s" % auth) |
1503 |
if ret < 1: |
|
4011.3.5
by Jelmer Vernooij
Move import next to other system libs, fix format. |
1504 |
trace.warning('Unable to create GSSAPI context for %s: %d', |
1505 |
auth['host'], ret) |
|
4011.3.2
by Jelmer Vernooij
Only attempt GSSAPI authentication when the kerberos module is present. |
1506 |
return None |
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1507 |
ret = kerberos.authGSSClientStep(vc, "") |
1508 |
if ret < 0: |
|
1509 |
trace.mutter('authGSSClientStep failed: %d', ret) |
|
4011.3.2
by Jelmer Vernooij
Only attempt GSSAPI authentication when the kerberos module is present. |
1510 |
return None |
1511 |
return kerberos.authGSSClientResponse(vc) |
|
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1512 |
|
1513 |
def build_auth_header(self, auth, request): |
|
4011.3.2
by Jelmer Vernooij
Only attempt GSSAPI authentication when the kerberos module is present. |
1514 |
return "Negotiate %s" % auth['negotiate_response'] |
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1515 |
|
1516 |
def auth_params_reusable(self, auth): |
|
1517 |
# If the auth scheme is known, it means a previous
|
|
1518 |
# authentication was successful, all information is
|
|
1519 |
# available, no further checks are needed.
|
|
4032.1.4
by John Arbash Meinel
Found 2 more files with trailing whitespace. |
1520 |
return (auth.get('scheme', None) == 'negotiate' and |
4011.3.4
by Jelmer Vernooij
review from vila: mention HTTPS, clarify error a bit, move import to top-level. |
1521 |
auth.get('negotiate_response', None) is not None) |
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1522 |
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1523 |
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1524 |
class BasicAuthHandler(AbstractAuthHandler): |
1525 |
"""A custom basic authentication handler."""
|
|
1526 |
||
4307.4.3
by Vincent Ladeuil
Tighten multiple auth schemes handling. |
1527 |
scheme = 'basic' |
2545.2.1
by Vincent Ladeuil
Fix 121889 by working around urllib2 bug. |
1528 |
handler_order = 500 |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1529 |
auth_regexp = re.compile('realm="([^"]*)"', re.I) |
1530 |
||
1531 |
def build_auth_header(self, auth, request): |
|
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1532 |
raw = '%s:%s' % (auth['user'], auth['password']) |
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1533 |
auth_header = 'Basic ' + raw.encode('base64').strip() |
1534 |
return auth_header |
|
1535 |
||
4284.1.1
by Vincent Ladeuil
Fix wrong realm extraction in http basic authentication (reported |
1536 |
def extract_realm(self, header_value): |
1537 |
match = self.auth_regexp.search(header_value) |
|
1538 |
realm = None |
|
1539 |
if match: |
|
1540 |
realm = match.group(1) |
|
1541 |
return match, realm |
|
1542 |
||
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1543 |
def auth_match(self, header, auth): |
4050.2.2
by Vincent Ladeuil
Ensures all auth handlers correctly parse all auth headers. |
1544 |
scheme, raw_auth = self._parse_auth_header(header) |
4307.4.3
by Vincent Ladeuil
Tighten multiple auth schemes handling. |
1545 |
if scheme != self.scheme: |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1546 |
return False |
1547 |
||
4284.1.1
by Vincent Ladeuil
Fix wrong realm extraction in http basic authentication (reported |
1548 |
match, realm = self.extract_realm(raw_auth) |
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1549 |
if match: |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1550 |
# Put useful info into auth
|
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
1551 |
self.update_auth(auth, 'scheme', scheme) |
1552 |
self.update_auth(auth, 'realm', realm) |
|
4795.4.3
by Vincent Ladeuil
Protect access to 'user' and 'password' auth attributes. |
1553 |
if (auth.get('user', None) is None |
1554 |
or auth.get('password', None) is None): |
|
2900.2.20
by Vincent Ladeuil
http can query AuthenticationConfig for logins too. |
1555 |
user, password = self.get_user_password(auth) |
1556 |
self.update_auth(auth, 'user', user) |
|
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
1557 |
self.update_auth(auth, 'password', password) |
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1558 |
return match is not None |
1559 |
||
1560 |
def auth_params_reusable(self, auth): |
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1561 |
# If the auth scheme is known, it means a previous
|
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1562 |
# authentication was successful, all information is
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1563 |
# available, no further checks are needed.
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1564 |
return auth.get('scheme', None) == 'basic' |
1565 |
||
1566 |
||
1567 |
def get_digest_algorithm_impls(algorithm): |
|
1568 |
H = None |
|
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1569 |
KD = None |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1570 |
if algorithm == 'MD5': |
2929.3.1
by Vincent Ladeuil
Fix python2.6 deprecation warnings (still 4 failures 5 errors in test suite). |
1571 |
H = lambda x: osutils.md5(x).hexdigest() |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1572 |
elif algorithm == 'SHA': |
5849.1.3
by Jelmer Vernooij
Simplify call to sha_string. |
1573 |
H = osutils.sha_string |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1574 |
if H is not None: |
1575 |
KD = lambda secret, data: H("%s:%s" % (secret, data)) |
|
1576 |
return H, KD |
|
1577 |
||
1578 |
||
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1579 |
def get_new_cnonce(nonce, nonce_count): |
1580 |
raw = '%s:%d:%s:%s' % (nonce, nonce_count, time.ctime(), |
|
1581 |
urllib2.randombytes(8)) |
|
5849.1.1
by Jelmer Vernooij
Use osutils.sha_string() when possible. |
1582 |
return osutils.sha_string(raw)[:16] |
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1583 |
|
1584 |
||
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1585 |
class DigestAuthHandler(AbstractAuthHandler): |
1586 |
"""A custom digest authentication handler."""
|
|
1587 |
||
4307.4.3
by Vincent Ladeuil
Tighten multiple auth schemes handling. |
1588 |
scheme = 'digest' |
4050.2.3
by Vincent Ladeuil
Slight cosmetic tweaks. |
1589 |
# Before basic as digest is a bit more secure and should be preferred
|
2545.2.1
by Vincent Ladeuil
Fix 121889 by working around urllib2 bug. |
1590 |
handler_order = 490 |
1591 |
||
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1592 |
def auth_params_reusable(self, auth): |
1593 |
# If the auth scheme is known, it means a previous
|
|
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1594 |
# authentication was successful, all information is
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1595 |
# available, no further checks are needed.
|
1596 |
return auth.get('scheme', None) == 'digest' |
|
1597 |
||
1598 |
def auth_match(self, header, auth): |
|
4050.2.2
by Vincent Ladeuil
Ensures all auth handlers correctly parse all auth headers. |
1599 |
scheme, raw_auth = self._parse_auth_header(header) |
4307.4.3
by Vincent Ladeuil
Tighten multiple auth schemes handling. |
1600 |
if scheme != self.scheme: |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1601 |
return False |
1602 |
||
1603 |
# Put the requested authentication info into a dict
|
|
1604 |
req_auth = urllib2.parse_keqv_list(urllib2.parse_http_list(raw_auth)) |
|
1605 |
||
1606 |
# Check that we can handle that authentication
|
|
1607 |
qop = req_auth.get('qop', None) |
|
1608 |
if qop != 'auth': # No auth-int so far |
|
1609 |
return False |
|
1610 |
||
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1611 |
H, KD = get_digest_algorithm_impls(req_auth.get('algorithm', 'MD5')) |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1612 |
if H is None: |
1613 |
return False |
|
1614 |
||
1615 |
realm = req_auth.get('realm', None) |
|
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1616 |
# Put useful info into auth
|
1617 |
self.update_auth(auth, 'scheme', scheme) |
|
1618 |
self.update_auth(auth, 'realm', realm) |
|
4795.4.3
by Vincent Ladeuil
Protect access to 'user' and 'password' auth attributes. |
1619 |
if auth.get('user', None) is None or auth.get('password', None) is None: |
2900.2.20
by Vincent Ladeuil
http can query AuthenticationConfig for logins too. |
1620 |
user, password = self.get_user_password(auth) |
1621 |
self.update_auth(auth, 'user', user) |
|
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1622 |
self.update_auth(auth, 'password', password) |
1623 |
||
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1624 |
try: |
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1625 |
if req_auth.get('algorithm', None) is not None: |
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
1626 |
self.update_auth(auth, 'algorithm', req_auth.get('algorithm')) |
1627 |
nonce = req_auth['nonce'] |
|
1628 |
if auth.get('nonce', None) != nonce: |
|
1629 |
# A new nonce, never used
|
|
1630 |
self.update_auth(auth, 'nonce_count', 0) |
|
1631 |
self.update_auth(auth, 'nonce', nonce) |
|
1632 |
self.update_auth(auth, 'qop', qop) |
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1633 |
auth['opaque'] = req_auth.get('opaque', None) |
1634 |
except KeyError: |
|
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1635 |
# Some required field is not there
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1636 |
return False |
1637 |
||
1638 |
return True |
|
1639 |
||
1640 |
def build_auth_header(self, auth, request): |
|
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1641 |
url_scheme, url_selector = urllib.splittype(request.get_selector()) |
1642 |
sel_host, uri = urllib.splithost(url_selector) |
|
1643 |
||
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1644 |
A1 = '%s:%s:%s' % (auth['user'], auth['realm'], auth['password']) |
1645 |
A2 = '%s:%s' % (request.get_method(), uri) |
|
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1646 |
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1647 |
nonce = auth['nonce'] |
1648 |
qop = auth['qop'] |
|
1649 |
||
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
1650 |
nonce_count = auth['nonce_count'] + 1 |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1651 |
ncvalue = '%08x' % nonce_count |
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1652 |
cnonce = get_new_cnonce(nonce, nonce_count) |
1653 |
||
1654 |
H, KD = get_digest_algorithm_impls(auth.get('algorithm', 'MD5')) |
|
1655 |
nonce_data = '%s:%s:%s:%s:%s' % (nonce, ncvalue, cnonce, qop, H(A2)) |
|
1656 |
request_digest = KD(H(A1), nonce_data) |
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1657 |
|
1658 |
header = 'Digest ' |
|
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1659 |
header += 'username="%s", realm="%s", nonce="%s"' % (auth['user'], |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1660 |
auth['realm'], |
1661 |
nonce) |
|
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1662 |
header += ', uri="%s"' % uri |
1663 |
header += ', cnonce="%s", nc=%s' % (cnonce, ncvalue) |
|
1664 |
header += ', qop="%s"' % qop |
|
1665 |
header += ', response="%s"' % request_digest |
|
1666 |
# Append the optional fields
|
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1667 |
opaque = auth.get('opaque', None) |
1668 |
if opaque: |
|
1669 |
header += ', opaque="%s"' % opaque |
|
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1670 |
if auth.get('algorithm', None): |
1671 |
header += ', algorithm="%s"' % auth.get('algorithm') |
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1672 |
|
1673 |
# We have used the nonce once more, update the count
|
|
1674 |
auth['nonce_count'] = nonce_count |
|
1675 |
||
1676 |
return header |
|
1677 |
||
1678 |
||
1679 |
class HTTPAuthHandler(AbstractAuthHandler): |
|
1680 |
"""Custom http authentication handler.
|
|
2004.3.1
by vila
Test ConnectionError exceptions. |
1681 |
|
2363.4.12
by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better |
1682 |
Send the authentication preventively to avoid the roundtrip
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1683 |
associated with the 401 error and keep the revelant info in
|
1684 |
the auth request attribute.
|
|
2004.3.1
by vila
Test ConnectionError exceptions. |
1685 |
"""
|
1686 |
||
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1687 |
auth_required_header = 'www-authenticate' |
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1688 |
auth_header = 'Authorization' |
1689 |
||
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1690 |
def get_auth(self, request): |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1691 |
"""Get the auth params from the request"""
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1692 |
return request.auth |
1693 |
||
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1694 |
def set_auth(self, request, auth): |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1695 |
"""Set the auth params for the request"""
|
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1696 |
request.auth = auth |
2004.3.1
by vila
Test ConnectionError exceptions. |
1697 |
|
2900.2.19
by Vincent Ladeuil
Mention proxy and https in the password prompts, with tests. |
1698 |
def build_password_prompt(self, auth): |
1699 |
return self._build_password_prompt(auth) |
|
1700 |
||
4222.3.12
by Jelmer Vernooij
Check that the HTTP transport prompts for usernames. |
1701 |
def build_username_prompt(self, auth): |
1702 |
return self._build_username_prompt(auth) |
|
1703 |
||
2363.4.9
by Vincent Ladeuil
Catch first succesful authentification to avoid further 401 |
1704 |
def http_error_401(self, req, fp, code, msg, headers): |
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1705 |
return self.auth_required(req, headers) |
1706 |
||
1707 |
||
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1708 |
class ProxyAuthHandler(AbstractAuthHandler): |
1709 |
"""Custom proxy authentication handler.
|
|
2420.1.3
by Vincent Ladeuil
Implement http proxy basic authentication. |
1710 |
|
1711 |
Send the authentication preventively to avoid the roundtrip
|
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1712 |
associated with the 407 error and keep the revelant info in
|
1713 |
the proxy_auth request attribute..
|
|
2420.1.3
by Vincent Ladeuil
Implement http proxy basic authentication. |
1714 |
"""
|
1715 |
||
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1716 |
auth_required_header = 'proxy-authenticate' |
2420.1.7
by Vincent Ladeuil
Tested against squid-2.6.5 with basic authentication. |
1717 |
# FIXME: the correct capitalization is Proxy-Authorization,
|
2420.1.8
by Vincent Ladeuil
Interesting typo :-) A mix between capitalize, title and fuzzy may be... |
1718 |
# but python-2.4 urllib2.Request insist on using capitalize()
|
2420.1.7
by Vincent Ladeuil
Tested against squid-2.6.5 with basic authentication. |
1719 |
# instead of title().
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1720 |
auth_header = 'Proxy-authorization' |
1721 |
||
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1722 |
def get_auth(self, request): |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1723 |
"""Get the auth params from the request"""
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1724 |
return request.proxy_auth |
1725 |
||
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1726 |
def set_auth(self, request, auth): |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1727 |
"""Set the auth params for the request"""
|
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1728 |
request.proxy_auth = auth |
2420.1.3
by Vincent Ladeuil
Implement http proxy basic authentication. |
1729 |
|
2900.2.19
by Vincent Ladeuil
Mention proxy and https in the password prompts, with tests. |
1730 |
def build_password_prompt(self, auth): |
1731 |
prompt = self._build_password_prompt(auth) |
|
5923.1.2
by Vincent Ladeuil
Fix some more prompts to be unicode. |
1732 |
prompt = u'Proxy ' + prompt |
2900.2.19
by Vincent Ladeuil
Mention proxy and https in the password prompts, with tests. |
1733 |
return prompt |
1734 |
||
4222.3.12
by Jelmer Vernooij
Check that the HTTP transport prompts for usernames. |
1735 |
def build_username_prompt(self, auth): |
1736 |
prompt = self._build_username_prompt(auth) |
|
5923.1.2
by Vincent Ladeuil
Fix some more prompts to be unicode. |
1737 |
prompt = u'Proxy ' + prompt |
4222.3.12
by Jelmer Vernooij
Check that the HTTP transport prompts for usernames. |
1738 |
return prompt |
1739 |
||
2420.1.3
by Vincent Ladeuil
Implement http proxy basic authentication. |
1740 |
def http_error_407(self, req, fp, code, msg, headers): |
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1741 |
return self.auth_required(req, headers) |
2420.1.3
by Vincent Ladeuil
Implement http proxy basic authentication. |
1742 |
|
1743 |
||
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1744 |
class HTTPBasicAuthHandler(BasicAuthHandler, HTTPAuthHandler): |
1745 |
"""Custom http basic authentication handler"""
|
|
1746 |
||
1747 |
||
1748 |
class ProxyBasicAuthHandler(BasicAuthHandler, ProxyAuthHandler): |
|
1749 |
"""Custom proxy basic authentication handler"""
|
|
1750 |
||
1751 |
||
1752 |
class HTTPDigestAuthHandler(DigestAuthHandler, HTTPAuthHandler): |
|
1753 |
"""Custom http basic authentication handler"""
|
|
1754 |
||
1755 |
||
1756 |
class ProxyDigestAuthHandler(DigestAuthHandler, ProxyAuthHandler): |
|
1757 |
"""Custom proxy basic authentication handler"""
|
|
1758 |
||
2420.1.3
by Vincent Ladeuil
Implement http proxy basic authentication. |
1759 |
|
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1760 |
class HTTPNegotiateAuthHandler(NegotiateAuthHandler, HTTPAuthHandler): |
1761 |
"""Custom http negotiate authentication handler"""
|
|
1762 |
||
1763 |
||
1764 |
class ProxyNegotiateAuthHandler(NegotiateAuthHandler, ProxyAuthHandler): |
|
1765 |
"""Custom proxy negotiate authentication handler"""
|
|
1766 |
||
1767 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1768 |
class HTTPErrorProcessor(urllib2.HTTPErrorProcessor): |
1769 |
"""Process HTTP error responses.
|
|
1770 |
||
1771 |
We don't really process the errors, quite the contrary
|
|
1772 |
instead, we leave our Transport handle them.
|
|
1773 |
"""
|
|
1774 |
||
2520.2.2
by Vincent Ladeuil
Fix #115209 by issuing a single range request on 400: Bad Request |
1775 |
accepted_errors = [200, # Ok |
1776 |
206, # Partial content |
|
1777 |
404, # Not found |
|
1778 |
]
|
|
1779 |
"""The error codes the caller will handle.
|
|
1780 |
||
1781 |
This can be specialized in the request on a case-by case basis, but the
|
|
1782 |
common cases are covered here.
|
|
1783 |
"""
|
|
1784 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1785 |
def http_response(self, request, response): |
1786 |
code, msg, hdrs = response.code, response.msg, response.info() |
|
1787 |
||
2520.2.2
by Vincent Ladeuil
Fix #115209 by issuing a single range request on 400: Bad Request |
1788 |
accepted_errors = request.accepted_errors |
1789 |
if accepted_errors is None: |
|
1790 |
accepted_errors = self.accepted_errors |
|
1791 |
||
1792 |
if code not in accepted_errors: |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1793 |
response = self.parent.error('http', request, response, |
1794 |
code, msg, hdrs) |
|
1795 |
return response |
|
1796 |
||
1797 |
https_response = http_response |
|
1798 |
||
1799 |
||
1800 |
class HTTPDefaultErrorHandler(urllib2.HTTPDefaultErrorHandler): |
|
1801 |
"""Translate common errors into bzr Exceptions"""
|
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
1802 |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1803 |
def http_error_default(self, req, fp, code, msg, hdrs): |
2520.2.2
by Vincent Ladeuil
Fix #115209 by issuing a single range request on 400: Bad Request |
1804 |
if code == 403: |
3430.3.1
by Vincent Ladeuil
Fix #230223 by making both http implementations raise appropriate exceptions. |
1805 |
raise errors.TransportError( |
1806 |
'Server refuses to fulfill the request (403 Forbidden)'
|
|
1807 |
' for %s' % req.get_full_url()) |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1808 |
else: |
2004.1.27
by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message. |
1809 |
raise errors.InvalidHttpResponse(req.get_full_url(), |
1810 |
'Unable to handle http code %d: %s' |
|
1811 |
% (code, msg)) |
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
1812 |
|
2520.2.2
by Vincent Ladeuil
Fix #115209 by issuing a single range request on 400: Bad Request |
1813 |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1814 |
class Opener(object): |
1815 |
"""A wrapper around urllib2.build_opener
|
|
1816 |
||
1817 |
Daughter classes can override to build their own specific opener
|
|
1818 |
"""
|
|
2145.1.1
by mbp at sourcefrog
merge urllib keepalive etc |
1819 |
# TODO: Provides hooks for daughter classes.
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1820 |
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
1821 |
def __init__(self, |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1822 |
connection=ConnectionHandler, |
1823 |
redirect=HTTPRedirectHandler, |
|
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
1824 |
error=HTTPErrorProcessor, |
6238.2.22
by Vincent Ladeuil
Create a specific test permutation for urllib https so we can inject our test ca certs. The wiring in _urllib2_wrappers is a bit hackish and it will need to use auth instead so different certs can be used for proxies and real servers but this could wait until authentication.conf is migrated to the config stacks. With this change in place, all https tests pass without the need to create a dedicated GlobalStore. |
1825 |
report_activity=None, |
1826 |
ca_certs=None): |
|
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
1827 |
self._opener = urllib2.build_opener( |
6238.2.22
by Vincent Ladeuil
Create a specific test permutation for urllib https so we can inject our test ca certs. The wiring in _urllib2_wrappers is a bit hackish and it will need to use auth instead so different certs can be used for proxies and real servers but this could wait until authentication.conf is migrated to the config stacks. With this change in place, all https tests pass without the need to create a dedicated GlobalStore. |
1828 |
connection(report_activity=report_activity, ca_certs=ca_certs), |
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
1829 |
redirect, error, |
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1830 |
ProxyHandler(), |
1831 |
HTTPBasicAuthHandler(), |
|
1832 |
HTTPDigestAuthHandler(), |
|
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1833 |
HTTPNegotiateAuthHandler(), |
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1834 |
ProxyBasicAuthHandler(), |
1835 |
ProxyDigestAuthHandler(), |
|
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1836 |
ProxyNegotiateAuthHandler(), |
2004.1.2
by vila
Implements a BasicAuthManager. |
1837 |
HTTPHandler, |
1838 |
HTTPSHandler, |
|
1839 |
HTTPDefaultErrorHandler, |
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
1840 |
)
|
2520.2.2
by Vincent Ladeuil
Fix #115209 by issuing a single range request on 400: Bad Request |
1841 |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1842 |
self.open = self._opener.open |
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
1843 |
if DEBUG >= 9: |
2004.1.9
by vila
Takes jam's remarks into account when possible, add TODOs for the rest. |
1844 |
# When dealing with handler order, it's easy to mess
|
1845 |
# things up, the following will help understand which
|
|
1846 |
# handler is used, when and for what.
|
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1847 |
import pprint |
1848 |
pprint.pprint(self._opener.__dict__) |