3825.1.2
by Martin Pool
Merge 1.9final and its sftp fixes |
1 |
# Copyright (C) 2006, 2007, 2008 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 |
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
17 |
"""Implementaion of urllib2 tailored to bzr needs
|
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 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
39 |
DEBUG = 0 |
40 |
||
41 |
# FIXME: Oversimplifying, two kind of exceptions should be
|
|
42 |
# raised, once a request is issued: URLError before we have been
|
|
43 |
# able to process the response, HTTPError after that. Process the
|
|
44 |
# response means we are able to leave the socket clean, so if we
|
|
45 |
# are not able to do that, we should close the connection. The
|
|
46 |
# 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. |
47 |
# ensure that.
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
48 |
|
4628.1.2
by Vincent Ladeuil
More complete fix. |
49 |
import errno |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
50 |
import httplib |
4011.3.5
by Jelmer Vernooij
Move import next to other system libs, fix format. |
51 |
try: |
52 |
import kerberos |
|
53 |
except ImportError: |
|
54 |
have_kerberos = False |
|
55 |
else: |
|
56 |
have_kerberos = True |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
57 |
import socket |
58 |
import urllib |
|
59 |
import urllib2 |
|
60 |
import urlparse |
|
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
61 |
import re |
2004.1.16
by v.ladeuil+lp at free
Add tests against erroneous http status lines. |
62 |
import sys |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
63 |
import time |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
64 |
|
2004.1.25
by v.ladeuil+lp at free
Shuffle http related test code. Hopefully it ends up at the right place :) |
65 |
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. |
66 |
from bzrlib import ( |
2900.2.6
by Vincent Ladeuil
Make http aware of authentication config. |
67 |
config, |
3052.3.3
by Vincent Ladeuil
Add -Dhttp support. |
68 |
debug, |
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
69 |
errors, |
2929.3.1
by Vincent Ladeuil
Fix python2.6 deprecation warnings (still 4 failures 5 errors in test suite). |
70 |
osutils, |
3052.3.3
by Vincent Ladeuil
Add -Dhttp support. |
71 |
trace, |
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
72 |
transport, |
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
73 |
ui, |
74 |
)
|
|
75 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
76 |
|
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
77 |
class _ReportingFileSocket(object): |
78 |
||
79 |
def __init__(self, filesock, report_activity=None): |
|
80 |
self.filesock = filesock |
|
81 |
self._report_activity = report_activity |
|
82 |
||
83 |
||
84 |
def read(self, size=1): |
|
85 |
s = self.filesock.read(size) |
|
86 |
self._report_activity(len(s), 'read') |
|
87 |
return s |
|
88 |
||
3988.2.1
by Vincent Ladeuil
Workaround SSLFile wrong readline prototype and fix bogus tests. |
89 |
def readline(self): |
90 |
# This should be readline(self, size=-1), but httplib in python 2.4 and
|
|
91 |
# 2.5 defines a SSLFile wrapper whose readline method lacks the size
|
|
92 |
# parameter. So until we drop support for 2.4 and 2.5 and since we
|
|
93 |
# don't *need* the size parameter we'll stay with readline(self)
|
|
94 |
# -- vila 20090209
|
|
95 |
s = self.filesock.readline() |
|
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
96 |
self._report_activity(len(s), 'read') |
97 |
return s |
|
98 |
||
99 |
def __getattr__(self, name): |
|
100 |
return getattr(self.filesock, name) |
|
101 |
||
102 |
||
103 |
class _ReportingSocket(object): |
|
104 |
||
105 |
def __init__(self, sock, report_activity=None): |
|
3287.3.3
by Andrew Bennetts
A slightly neater hack for forcing buffering, thanks to John. |
106 |
self.sock = sock |
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
107 |
self._report_activity = report_activity |
108 |
||
109 |
def sendall(self, s, *args): |
|
4105.1.1
by Andrew Bennetts
Clean-up _ReportingSocket.send/sendall slightly. |
110 |
self.sock.sendall(s, *args) |
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
111 |
self._report_activity(len(s), 'write') |
112 |
||
113 |
def recv(self, *args): |
|
114 |
s = self.sock.recv(*args) |
|
115 |
self._report_activity(len(s), 'read') |
|
116 |
return s |
|
3287.3.3
by Andrew Bennetts
A slightly neater hack for forcing buffering, thanks to John. |
117 |
|
118 |
def makefile(self, mode='r', bufsize=-1): |
|
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
119 |
# httplib creates a fileobject that doesn't do buffering, which
|
120 |
# makes fp.readline() very expensive because it only reads one byte
|
|
121 |
# at a time. So we wrap the socket in an object that forces
|
|
122 |
# sock.makefile to make a buffered file.
|
|
123 |
fsock = self.sock.makefile(mode, 65536) |
|
124 |
# And wrap that into a reporting kind of fileobject
|
|
125 |
return _ReportingFileSocket(fsock, self._report_activity) |
|
3287.3.3
by Andrew Bennetts
A slightly neater hack for forcing buffering, thanks to John. |
126 |
|
127 |
def __getattr__(self, name): |
|
128 |
return getattr(self.sock, name) |
|
129 |
||
130 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
131 |
# We define our own Response class to keep our httplib pipe clean
|
132 |
class Response(httplib.HTTPResponse): |
|
2004.1.16
by v.ladeuil+lp at free
Add tests against erroneous http status lines. |
133 |
"""Custom HTTPResponse, to avoid the need to decorate.
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
134 |
|
135 |
httplib prefers to decorate the returned objects, rather
|
|
136 |
than using a custom object.
|
|
137 |
"""
|
|
138 |
||
2004.1.7
by vila
Better handling of passwords (user should be queried only once). |
139 |
# Some responses have bodies in which we have no interest
|
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
140 |
_body_ignored_responses = [301,302, 303, 307, 401, 403, 404] |
2004.1.7
by vila
Better handling of passwords (user should be queried only once). |
141 |
|
3146.3.4
by Vincent Ladeuil
Review feedback, simpler loops. |
142 |
# in finish() below, we may have to discard several MB in the worst
|
143 |
# case. To avoid buffering that much, we read and discard by chunks
|
|
144 |
# instead. The underlying file is either a socket or a StringIO, so reading
|
|
145 |
# 8k chunks should be fine.
|
|
146 |
_discarded_buf_size = 8192 |
|
147 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
148 |
def begin(self): |
149 |
"""Begin to read the response from the server.
|
|
150 |
||
151 |
httplib assumes that some responses get no content and do
|
|
152 |
not even attempt to read the body in that case, leaving
|
|
153 |
the body in the socket, blocking the next request. Let's
|
|
154 |
try to workaround that.
|
|
155 |
"""
|
|
2004.1.2
by vila
Implements a BasicAuthManager. |
156 |
httplib.HTTPResponse.begin(self) |
2004.1.7
by vila
Better handling of passwords (user should be queried only once). |
157 |
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. |
158 |
if self.debuglevel >= 2: |
2004.1.16
by v.ladeuil+lp at free
Add tests against erroneous http status lines. |
159 |
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. |
160 |
print "will ready body, length: %s" % self.length |
2004.1.16
by v.ladeuil+lp at free
Add tests against erroneous http status lines. |
161 |
if not (self.length is None or self.will_close): |
162 |
# In some cases, we just can't read the body not
|
|
163 |
# even try or we may encounter a 104, 'Connection
|
|
164 |
# reset by peer' error if there is indeed no body
|
|
165 |
# and the server closed the connection just after
|
|
166 |
# having issued the response headers (even if the
|
|
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
167 |
# headers indicate a Content-Type...)
|
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
168 |
body = self.read(self.length) |
169 |
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. |
170 |
# 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. |
171 |
print "Consumed body: [%s]" % body |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
172 |
self.close() |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
173 |
elif self.status == 200: |
174 |
# Whatever the request is, it went ok, so we surely don't want to
|
|
175 |
# close the connection. Some cases are not correctly detected by
|
|
176 |
# httplib.HTTPConnection.getresponse (called by
|
|
177 |
# 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. |
178 |
# through proxy case is one. Note: the 'will_close' below refers
|
179 |
# to the "true" socket between us and the server, whereas the
|
|
180 |
# 'close()' above refers to the copy of that socket created by
|
|
181 |
# httplib for the response itself. So, in the if above we close the
|
|
182 |
# socket to indicate that we are done with the response whereas
|
|
183 |
# 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 |
184 |
self.will_close = False |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
185 |
|
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). |
186 |
def finish(self): |
3059.2.11
by Vincent Ladeuil
Fix typos mentioned by spiv. |
187 |
"""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). |
188 |
|
3059.2.11
by Vincent Ladeuil
Fix typos mentioned by spiv. |
189 |
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). |
190 |
body. That will block the next request to succeed if we use a
|
3059.2.11
by Vincent Ladeuil
Fix typos mentioned by spiv. |
191 |
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). |
192 |
nothing will block the next request since a new connection will be
|
193 |
issued anyway.
|
|
3104.3.3
by Vincent Ladeuil
Jam's and Aaron feedback about bug #175886. |
194 |
|
195 |
: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). |
196 |
"""
|
3104.3.3
by Vincent Ladeuil
Jam's and Aaron feedback about bug #175886. |
197 |
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). |
198 |
if not self.isclosed(): |
199 |
# 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. |
200 |
pending = 0 |
3146.3.2
by Vincent Ladeuil
Fix #179368 by keeping the current range hint on ShortReadvErrors. |
201 |
data = True |
3146.3.4
by Vincent Ladeuil
Review feedback, simpler loops. |
202 |
while data and self.length: |
203 |
# read() will update self.length
|
|
204 |
data = self.read(min(self.length, self._discarded_buf_size)) |
|
3104.3.1
by Vincent Ladeuil
Fix #175886 by reading remaining bytes by chunks. |
205 |
pending += len(data) |
206 |
if pending: |
|
3146.3.2
by Vincent Ladeuil
Fix #179368 by keeping the current range hint on ShortReadvErrors. |
207 |
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). |
208 |
self.close() |
3104.3.3
by Vincent Ladeuil
Jam's and Aaron feedback about bug #175886. |
209 |
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). |
210 |
|
2004.1.2
by vila
Implements a BasicAuthManager. |
211 |
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
212 |
# Not inheriting from 'object' because httplib.HTTPConnection doesn't.
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
213 |
class AbstractHTTPConnection: |
214 |
"""A custom HTTP(S) Connection, which can reset itself on a bad response"""
|
|
215 |
||
216 |
response_class = Response |
|
217 |
||
3104.3.3
by Vincent Ladeuil
Jam's and Aaron feedback about bug #175886. |
218 |
# When we detect a server responding with the whole file to range requests,
|
219 |
# we want to warn. But not below a given thresold.
|
|
220 |
_range_warning_thresold = 1024 * 1024 |
|
221 |
||
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
222 |
def __init__(self, |
223 |
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). |
224 |
self._response = None |
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
225 |
self._report_activity = report_activity |
3104.3.3
by Vincent Ladeuil
Jam's and Aaron feedback about bug #175886. |
226 |
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). |
227 |
|
228 |
def _mutter_connect(self): |
|
3104.3.4
by Vincent Ladeuil
Add test. |
229 |
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). |
230 |
if self.proxied_host is not None: |
231 |
netloc += '(proxy for %s)' % self.proxied_host |
|
232 |
trace.mutter('* About to connect() to %s' % netloc) |
|
233 |
||
234 |
def getresponse(self): |
|
235 |
"""Capture the response to be able to cleanup"""
|
|
236 |
self._response = httplib.HTTPConnection.getresponse(self) |
|
237 |
return self._response |
|
238 |
||
3059.2.2
by Vincent Ladeuil
Read http responses on demand without buffering the whole body |
239 |
def cleanup_pipe(self): |
3111.1.24
by Vincent Ladeuil
Cleanups. |
240 |
"""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). |
241 |
if self._response is not None: |
3104.3.3
by Vincent Ladeuil
Jam's and Aaron feedback about bug #175886. |
242 |
pending = self._response.finish() |
3104.3.5
by Vincent Ladeuil
Fix typo. |
243 |
# Warn the user (once)
|
3104.3.3
by Vincent Ladeuil
Jam's and Aaron feedback about bug #175886. |
244 |
if (self._ranges_received_whole_file is None |
245 |
and self._response.status == 200 |
|
246 |
and pending and pending > self._range_warning_thresold |
|
247 |
):
|
|
248 |
self._ranges_received_whole_file = True |
|
249 |
trace.warning( |
|
250 |
'Got a 200 response when asking for multiple ranges,'
|
|
251 |
' does your server at %s:%s support range requests?', |
|
252 |
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). |
253 |
self._response = None |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
254 |
# Preserve our preciousss
|
255 |
sock = self.sock |
|
256 |
self.sock = None |
|
3943.8.1
by Marius Kruger
remove all trailing whitespace from bzr source |
257 |
# Let httplib.HTTPConnection do its housekeeping
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
258 |
self.close() |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
259 |
# Restore our preciousss
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
260 |
self.sock = sock |
261 |
||
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
262 |
def _wrap_socket_for_reporting(self, sock): |
263 |
"""Wrap the socket before anybody use it."""
|
|
264 |
self.sock = _ReportingSocket(sock, self._report_activity) |
|
265 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
266 |
|
267 |
class HTTPConnection(AbstractHTTPConnection, httplib.HTTPConnection): |
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
268 |
|
269 |
# XXX: Needs refactoring at the caller level.
|
|
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
270 |
def __init__(self, host, port=None, proxied_host=None, |
271 |
report_activity=None): |
|
272 |
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. |
273 |
# Use strict=True since we don't support HTTP/0.9
|
274 |
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 |
275 |
self.proxied_host = proxied_host |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
276 |
|
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). |
277 |
def connect(self): |
278 |
if 'http' in debug.debug_flags: |
|
279 |
self._mutter_connect() |
|
280 |
httplib.HTTPConnection.connect(self) |
|
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
281 |
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). |
282 |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
283 |
|
3815.2.3
by Martin Pool
merge fix for #293054, ssl on python2.6 |
284 |
# Build the appropriate socket wrapper for ssl
|
285 |
try: |
|
3823.1.3
by Vincent Ladeuil
Fixed as per John's review. |
286 |
# python 2.6 introduced a better ssl package
|
287 |
import ssl |
|
3815.2.3
by Martin Pool
merge fix for #293054, ssl on python2.6 |
288 |
_ssl_wrap_socket = ssl.wrap_socket |
289 |
except ImportError: |
|
3823.1.3
by Vincent Ladeuil
Fixed as per John's review. |
290 |
# python versions prior to 2.6 don't have ssl and ssl.wrap_socket instead
|
291 |
# they use httplib.FakeSocket
|
|
3815.2.3
by Martin Pool
merge fix for #293054, ssl on python2.6 |
292 |
def _ssl_wrap_socket(sock, key_file, cert_file): |
293 |
ssl_sock = socket.ssl(sock, key_file, cert_file) |
|
294 |
return httplib.FakeSocket(sock, ssl_sock) |
|
295 |
||
296 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
297 |
class HTTPSConnection(AbstractHTTPConnection, httplib.HTTPSConnection): |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
298 |
|
299 |
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. |
300 |
proxied_host=None, |
301 |
report_activity=None): |
|
302 |
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. |
303 |
# 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 |
304 |
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. |
305 |
key_file, cert_file, strict=True) |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
306 |
self.proxied_host = proxied_host |
307 |
||
308 |
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). |
309 |
if 'http' in debug.debug_flags: |
310 |
self._mutter_connect() |
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
311 |
httplib.HTTPConnection.connect(self) |
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
312 |
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 |
313 |
if self.proxied_host is None: |
314 |
self.connect_to_origin() |
|
315 |
||
316 |
def connect_to_origin(self): |
|
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
317 |
ssl_sock = _ssl_wrap_socket(self.sock, self.key_file, self.cert_file) |
318 |
# Wrap the ssl socket before anybody use it
|
|
319 |
self._wrap_socket_for_reporting(ssl_sock) |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
320 |
|
321 |
||
322 |
class Request(urllib2.Request): |
|
323 |
"""A custom Request object.
|
|
324 |
||
325 |
urllib2 determines the request method heuristically (based on
|
|
326 |
the presence or absence of data). We set the method
|
|
327 |
statically.
|
|
328 |
||
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
329 |
The Request object tracks:
|
330 |
- the connection the request will be made on.
|
|
331 |
- the authentication parameters needed to preventively set
|
|
332 |
the authentication header once a first authentication have
|
|
333 |
been made.
|
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
334 |
"""
|
335 |
||
336 |
def __init__(self, method, url, data=None, headers={}, |
|
337 |
origin_req_host=None, unverifiable=False, |
|
2520.2.2
by Vincent Ladeuil
Fix #115209 by issuing a single range request on 400: Bad Request |
338 |
connection=None, parent=None, |
339 |
accepted_errors=None): |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
340 |
urllib2.Request.__init__(self, url, data, headers, |
341 |
origin_req_host, unverifiable) |
|
342 |
self.method = method |
|
343 |
self.connection = connection |
|
2520.2.2
by Vincent Ladeuil
Fix #115209 by issuing a single range request on 400: Bad Request |
344 |
self.accepted_errors = accepted_errors |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
345 |
# To handle redirections
|
346 |
self.parent = parent |
|
347 |
self.redirected_to = None |
|
2164.2.15
by Vincent Ladeuil
Http redirections are not followed by default. Do not use hints |
348 |
# Unless told otherwise, redirections are not followed
|
349 |
self.follow_redirections = False |
|
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
350 |
# auth and proxy_auth are dicts containing, at least
|
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
351 |
# (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 |
352 |
# The dict entries are mostly handled by the AuthHandler.
|
353 |
# Some authentication schemes may add more entries.
|
|
354 |
self.auth = {} |
|
355 |
self.proxy_auth = {} |
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
356 |
self.proxied_host = None |
2420.1.3
by Vincent Ladeuil
Implement http proxy basic authentication. |
357 |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
358 |
def get_method(self): |
359 |
return self.method |
|
360 |
||
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
361 |
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. |
362 |
"""Set the proxy and remember the proxied host."""
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
363 |
self.proxied_host = self.get_host() |
364 |
urllib2.Request.set_proxy(self, proxy, type) |
|
365 |
||
366 |
||
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
367 |
class _ConnectRequest(Request): |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
368 |
|
369 |
def __init__(self, request): |
|
370 |
"""Constructor
|
|
3943.8.1
by Marius Kruger
remove all trailing whitespace from bzr source |
371 |
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
372 |
:param request: the first request sent to the proxied host, already
|
373 |
processed by the opener (i.e. proxied_host is already set).
|
|
374 |
"""
|
|
375 |
# We give a fake url and redefine get_selector or urllib2 will be
|
|
376 |
# confused
|
|
377 |
Request.__init__(self, 'CONNECT', request.get_full_url(), |
|
378 |
connection=request.connection) |
|
3376.2.4
by Martin Pool
Remove every assert statement from bzrlib! |
379 |
if request.proxied_host is None: |
380 |
raise AssertionError() |
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
381 |
self.proxied_host = request.proxied_host |
382 |
||
383 |
def get_selector(self): |
|
384 |
return self.proxied_host |
|
385 |
||
386 |
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. |
387 |
"""Set the proxy without remembering the proxied host.
|
388 |
||
389 |
We already know the proxied host by definition, the CONNECT request
|
|
390 |
occurs only when the connection goes through a proxy. The usual
|
|
391 |
processing (masquerade the request so that the connection is done to
|
|
392 |
the proxy while the request is targeted at another host) does not apply
|
|
393 |
here. In fact, the connection is already established with proxy and we
|
|
394 |
just want to enable the SSL tunneling.
|
|
395 |
"""
|
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
396 |
urllib2.Request.set_proxy(self, proxy, type) |
397 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
398 |
|
399 |
class ConnectionHandler(urllib2.BaseHandler): |
|
400 |
"""Provides connection-sharing by pre-processing requests.
|
|
401 |
||
402 |
urllib2 provides no way to access the HTTPConnection object
|
|
403 |
internally used. But we need it in order to achieve
|
|
404 |
connection sharing. So, we add it to the request just before
|
|
405 |
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 |
406 |
http[s] requests in AbstractHTTPHandler.
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
407 |
"""
|
408 |
||
409 |
handler_order = 1000 # after all pre-processings |
|
410 |
||
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
411 |
def __init__(self, report_activity=None): |
412 |
self._report_activity = report_activity |
|
413 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
414 |
def create_connection(self, request, http_connection_class): |
415 |
host = request.get_host() |
|
416 |
if not host: |
|
2004.1.15
by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests. |
417 |
# Just a bit of paranoia here, this should have been
|
418 |
# handled in the higher levels
|
|
2004.1.27
by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message. |
419 |
raise errors.InvalidURL(request.get_full_url(), 'no host given.') |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
420 |
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
421 |
# We create a connection (but it will not connect until the first
|
422 |
# request is made)
|
|
2004.1.42
by v.ladeuil+lp at free
Fix #70803 by catching the httplib exception. |
423 |
try: |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
424 |
connection = http_connection_class( |
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
425 |
host, proxied_host=request.proxied_host, |
426 |
report_activity=self._report_activity) |
|
2004.1.42
by v.ladeuil+lp at free
Fix #70803 by catching the httplib exception. |
427 |
except httplib.InvalidURL, exception: |
428 |
# There is only one occurrence of InvalidURL in httplib
|
|
429 |
raise errors.InvalidURL(request.get_full_url(), |
|
430 |
extra='nonnumeric port') |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
431 |
|
432 |
return connection |
|
433 |
||
434 |
def capture_connection(self, request, http_connection_class): |
|
435 |
"""Capture or inject the request connection.
|
|
436 |
||
437 |
Two cases:
|
|
438 |
- the request have no connection: create a new one,
|
|
439 |
||
440 |
- the request have a connection: this one have been used
|
|
441 |
already, let's capture it, so that we can give it to
|
|
442 |
another transport to be reused. We don't do that
|
|
443 |
ourselves: the Transport object get the connection from
|
|
444 |
a first request and then propagate it, from request to
|
|
445 |
request or to cloned transports.
|
|
446 |
"""
|
|
447 |
connection = request.connection |
|
448 |
if connection is None: |
|
449 |
# Create a new one
|
|
450 |
connection = self.create_connection(request, http_connection_class) |
|
451 |
request.connection = connection |
|
452 |
||
453 |
# All connections will pass here, propagate debug level
|
|
454 |
connection.set_debuglevel(DEBUG) |
|
455 |
return request |
|
456 |
||
457 |
def http_request(self, request): |
|
458 |
return self.capture_connection(request, HTTPConnection) |
|
459 |
||
460 |
def https_request(self, request): |
|
461 |
return self.capture_connection(request, HTTPSConnection) |
|
462 |
||
463 |
||
464 |
class AbstractHTTPHandler(urllib2.AbstractHTTPHandler): |
|
465 |
"""A custom handler for HTTP(S) requests.
|
|
466 |
||
467 |
We overrive urllib2.AbstractHTTPHandler to get a better
|
|
468 |
control of the connection, the ability to implement new
|
|
469 |
request types and return a response able to cope with
|
|
470 |
persistent connections.
|
|
471 |
"""
|
|
472 |
||
473 |
# We change our order to be before urllib2 HTTP[S]Handlers
|
|
2004.3.1
by vila
Test ConnectionError exceptions. |
474 |
# and be chosen instead of them (the first http_open called
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
475 |
# wins).
|
476 |
handler_order = 400 |
|
477 |
||
478 |
_default_headers = {'Pragma': 'no-cache', |
|
479 |
'Cache-control': 'max-age=0', |
|
480 |
'Connection': 'Keep-Alive', |
|
2004.1.15
by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests. |
481 |
'User-agent': 'bzr/%s (urllib)' % bzrlib_version, |
2004.3.3
by vila
Better (but still incomplete) design for bogus servers. |
482 |
'Accept': '*/*', |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
483 |
}
|
484 |
||
485 |
def __init__(self): |
|
2004.1.16
by v.ladeuil+lp at free
Add tests against erroneous http status lines. |
486 |
urllib2.AbstractHTTPHandler.__init__(self, debuglevel=DEBUG) |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
487 |
|
2004.1.15
by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests. |
488 |
def http_request(self, request): |
489 |
"""Common headers setting"""
|
|
490 |
||
491 |
request.headers.update(self._default_headers.copy()) |
|
492 |
# FIXME: We may have to add the Content-Length header if
|
|
493 |
# we have data to send.
|
|
494 |
return request |
|
495 |
||
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
496 |
def retry_or_raise(self, http_class, request, first_try): |
497 |
"""Retry the request (once) or raise the exception.
|
|
2004.3.1
by vila
Test ConnectionError exceptions. |
498 |
|
499 |
urllib2 raises exception of application level kind, we
|
|
500 |
just have to translate them.
|
|
501 |
||
502 |
httplib can raise exceptions of transport level (badly
|
|
503 |
formatted dialog, loss of connexion or socket level
|
|
504 |
problems). In that case we should issue the request again
|
|
505 |
(httplib will close and reopen a new connection if
|
|
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
506 |
needed).
|
507 |
"""
|
|
508 |
# When an exception occurs, we give back the original
|
|
509 |
# Traceback or the bugs are hard to diagnose.
|
|
510 |
exc_type, exc_val, exc_tb = sys.exc_info() |
|
511 |
if exc_type == socket.gaierror: |
|
512 |
# No need to retry, that will not help
|
|
513 |
raise errors.ConnectionError("Couldn't resolve host '%s'" |
|
514 |
% request.get_origin_req_host(), |
|
515 |
orig_error=exc_val) |
|
2955.2.1
by Vincent Ladeuil
Fix #160012 by leaving the http pipeline related exceptions raise. |
516 |
elif isinstance(exc_val, httplib.ImproperConnectionState): |
517 |
# The httplib pipeline is in incorrect state, it's a bug in our
|
|
518 |
# implementation.
|
|
519 |
raise exc_type, exc_val, exc_tb |
|
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
520 |
else: |
521 |
if first_try: |
|
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
522 |
if self._debuglevel >= 2: |
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
523 |
print 'Received exception: [%r]' % exc_val |
524 |
print ' On connection: [%r]' % request.connection |
|
525 |
method = request.get_method() |
|
526 |
url = request.get_full_url() |
|
527 |
print ' Will retry, %s %r' % (method, url) |
|
528 |
request.connection.close() |
|
529 |
response = self.do_open(http_class, request, False) |
|
530 |
else: |
|
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
531 |
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. |
532 |
print 'Received second exception: [%r]' % exc_val |
533 |
print ' On connection: [%r]' % request.connection |
|
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
534 |
if exc_type in (httplib.BadStatusLine, httplib.UnknownProtocol): |
535 |
# httplib.BadStatusLine and
|
|
536 |
# httplib.UnknownProtocol indicates that a
|
|
537 |
# bogus server was encountered or a bad
|
|
538 |
# connection (i.e. transient errors) is
|
|
539 |
# experimented, we have already retried once
|
|
540 |
# for that request so we raise the exception.
|
|
541 |
my_exception = errors.InvalidHttpResponse( |
|
542 |
request.get_full_url(), |
|
543 |
'Bad status line received', |
|
544 |
orig_error=exc_val) |
|
4628.1.2
by Vincent Ladeuil
More complete fix. |
545 |
elif (isinstance(exc_val, socket.error) and len(exc_val.args) |
546 |
and exc_val.args[0] in (errno.ECONNRESET, 10054)): |
|
547 |
raise errors.ConnectionReset( |
|
548 |
"Connection lost while sending request.") |
|
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
549 |
else: |
550 |
# All other exception are considered connection related.
|
|
551 |
||
552 |
# socket errors generally occurs for reasons
|
|
553 |
# far outside our scope, so closing the
|
|
554 |
# connection and retrying is the best we can
|
|
555 |
# do.
|
|
556 |
||
557 |
my_exception = errors.ConnectionError( |
|
558 |
msg= 'while sending %s %s:' % (request.get_method(), |
|
559 |
request.get_selector()), |
|
560 |
orig_error=exc_val) |
|
561 |
||
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
562 |
if self._debuglevel >= 2: |
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
563 |
print 'On connection: [%r]' % request.connection |
564 |
method = request.get_method() |
|
565 |
url = request.get_full_url() |
|
566 |
print ' Failed again, %s %r' % (method, url) |
|
567 |
print ' Will raise: [%r]' % my_exception |
|
568 |
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). |
569 |
return response |
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
570 |
|
571 |
def do_open(self, http_class, request, first_try=True): |
|
572 |
"""See urllib2.AbstractHTTPHandler.do_open for the general idea.
|
|
573 |
||
574 |
The request will be retried once if it fails.
|
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
575 |
"""
|
576 |
connection = request.connection |
|
3376.2.4
by Martin Pool
Remove every assert statement from bzrlib! |
577 |
if connection is None: |
578 |
raise AssertionError( |
|
579 |
'Cannot process a request without a connection') |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
580 |
|
2004.1.19
by v.ladeuil+lp at free
Test protocol version in http responses. |
581 |
# Get all the headers
|
2004.1.15
by v.ladeuil+lp at free
Better design for bogus servers. Both urllib and pycurl pass tests. |
582 |
headers = {} |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
583 |
headers.update(request.header_items()) |
584 |
headers.update(request.unredirected_hdrs) |
|
3430.1.1
by Vincent Ladeuil
Fix bug #229076 by fixing header names before sending the request. |
585 |
# Some servers or proxies will choke on headers not properly
|
586 |
# cased. httplib/urllib/urllib2 all use capitalize to get canonical
|
|
587 |
# header names, but only python2.5 urllib2 use title() to fix them just
|
|
588 |
# before sending the request. And not all versions of python 2.5 do
|
|
589 |
# that. Since we replace urllib2.AbstractHTTPHandler.do_open we do it
|
|
590 |
# ourself below.
|
|
3430.1.2
by Vincent Ladeuil
Fixed as per Matt Nordhoff review. |
591 |
headers = dict((name.title(), val) for name, val in headers.iteritems()) |
2004.3.1
by vila
Test ConnectionError exceptions. |
592 |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
593 |
try: |
3052.3.3
by Vincent Ladeuil
Add -Dhttp support. |
594 |
method = request.get_method() |
595 |
url = request.get_selector() |
|
596 |
connection._send_request(method, url, |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
597 |
# FIXME: implements 100-continue
|
598 |
#None, # We don't send the body yet
|
|
599 |
request.get_data(), |
|
600 |
headers) |
|
3052.3.3
by Vincent Ladeuil
Add -Dhttp support. |
601 |
if 'http' in debug.debug_flags: |
602 |
trace.mutter('> %s %s' % (method, url)) |
|
603 |
hdrs = ['%s: %s' % (k, v) for k,v in headers.items()] |
|
604 |
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. |
605 |
if self._debuglevel >= 1: |
606 |
print 'Request sent: [%r] from (%s)' \ |
|
607 |
% (request, request.connection.sock.getsockname()) |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
608 |
response = connection.getresponse() |
609 |
convert_to_addinfourl = True |
|
2004.1.37
by v.ladeuil+lp at free
Small refactoring. |
610 |
except (socket.gaierror, httplib.BadStatusLine, httplib.UnknownProtocol, |
611 |
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). |
612 |
response = self.retry_or_raise(http_class, request, first_try) |
613 |
convert_to_addinfourl = False |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
614 |
|
615 |
# FIXME: HTTPConnection does not fully support 100-continue (the
|
|
616 |
# server responses are just ignored)
|
|
617 |
||
618 |
# if code == 100:
|
|
619 |
# mutter('Will send the body')
|
|
620 |
# # We can send the body now
|
|
621 |
# body = request.get_data()
|
|
622 |
# if body is None:
|
|
623 |
# raise URLError("No data given")
|
|
624 |
# connection.send(body)
|
|
625 |
# response = connection.getresponse()
|
|
626 |
||
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
627 |
if self._debuglevel >= 2: |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
628 |
print 'Receives response: %r' % response |
629 |
print ' For: %r(%r)' % (request.get_method(), |
|
630 |
request.get_full_url()) |
|
631 |
||
632 |
if convert_to_addinfourl: |
|
633 |
# Shamelessly copied from urllib2
|
|
634 |
req = request |
|
635 |
r = response |
|
636 |
r.recv = r.read |
|
3287.3.2
by Andrew Bennetts
Buffer 64k, rather than just 8k. |
637 |
fp = socket._fileobject(r, bufsize=65536) |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
638 |
resp = urllib2.addinfourl(fp, r.msg, req.get_full_url()) |
639 |
resp.code = r.status |
|
640 |
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). |
641 |
resp.version = r.version |
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
642 |
if self._debuglevel >= 2: |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
643 |
print 'Create addinfourl: %r' % resp |
644 |
print ' For: %r(%r)' % (request.get_method(), |
|
645 |
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). |
646 |
if 'http' in debug.debug_flags: |
647 |
version = 'HTTP/%d.%d' |
|
648 |
try: |
|
649 |
version = version % (resp.version / 10, |
|
650 |
resp.version % 10) |
|
651 |
except: |
|
652 |
version = 'HTTP/%r' % resp.version |
|
653 |
trace.mutter('< %s %s %s' % (version, resp.code, |
|
654 |
resp.msg)) |
|
655 |
# Use the raw header lines instead of treating resp.info() as a
|
|
656 |
# dict since we may miss duplicated headers otherwise.
|
|
657 |
hdrs = [h.rstrip('\r\n') for h in resp.info().headers] |
|
658 |
trace.mutter('< ' + '\n< '.join(hdrs) + '\n') |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
659 |
else: |
660 |
resp = response |
|
661 |
return resp |
|
662 |
||
663 |
||
664 |
class HTTPHandler(AbstractHTTPHandler): |
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
665 |
"""A custom handler that just thunks into HTTPConnection"""
|
666 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
667 |
def http_open(self, request): |
668 |
return self.do_open(HTTPConnection, request) |
|
669 |
||
670 |
||
671 |
class HTTPSHandler(AbstractHTTPHandler): |
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
672 |
"""A custom handler that just thunks into HTTPSConnection"""
|
673 |
||
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
674 |
https_request = AbstractHTTPHandler.http_request |
675 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
676 |
def https_open(self, request): |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
677 |
connection = request.connection |
678 |
if connection.sock is None and \ |
|
679 |
connection.proxied_host is not None and \ |
|
680 |
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. |
681 |
# FIXME: We need a gazillion connection tests here, but we still
|
682 |
# miss a https server :-( :
|
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
683 |
# - with and without proxy
|
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
684 |
# - with and without certificate
|
685 |
# - with self-signed certificate
|
|
686 |
# - 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. |
687 |
# - 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. |
688 |
# CONNECT)
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
689 |
# - with basic and digest schemes
|
690 |
# - reconnection on errors
|
|
691 |
# - connection persistence behaviour (including reconnection)
|
|
692 |
||
693 |
# We are about to connect for the first time via a proxy, we must
|
|
694 |
# 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. |
695 |
connect = _ConnectRequest(request) |
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
696 |
response = self.parent.open(connect) |
697 |
if response.code != 200: |
|
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
698 |
raise ConnectionError("Can't connect to %s via proxy %s" % ( |
699 |
connect.proxied_host, self.host)) |
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
700 |
# Housekeeping
|
3059.2.2
by Vincent Ladeuil
Read http responses on demand without buffering the whole body |
701 |
connection.cleanup_pipe() |
3943.8.1
by Marius Kruger
remove all trailing whitespace from bzr source |
702 |
# Establish the connection encryption
|
2540.2.1
by Vincent Ladeuil
Rough, working, tested against squid+apache in basic auth fix for #120678 |
703 |
connection.connect_to_origin() |
704 |
# Propagate the connection to the original request
|
|
705 |
request.connection = connection |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
706 |
return self.do_open(HTTPSConnection, request) |
707 |
||
708 |
class HTTPRedirectHandler(urllib2.HTTPRedirectHandler): |
|
709 |
"""Handles redirect requests.
|
|
710 |
||
711 |
We have to implement our own scheme because we use a specific
|
|
712 |
Request object and because we want to implement a specific
|
|
713 |
policy.
|
|
714 |
"""
|
|
715 |
_debuglevel = DEBUG |
|
716 |
# RFC2616 says that only read requests should be redirected
|
|
717 |
# without interacting with the user. But bzr use some
|
|
718 |
# shortcuts to optimize against roundtrips which can leads to
|
|
719 |
# write requests being issued before read requests of
|
|
720 |
# containing dirs can be redirected. So we redirect write
|
|
721 |
# requests in the same way which seems to respect the spirit
|
|
722 |
# of the RFC if not its letter.
|
|
723 |
||
724 |
def redirect_request(self, req, fp, code, msg, headers, newurl): |
|
725 |
"""See urllib2.HTTPRedirectHandler.redirect_request"""
|
|
726 |
# We would have preferred to update the request instead
|
|
727 |
# of creating a new one, but the urllib2.Request object
|
|
728 |
# has a too complicated creation process to provide a
|
|
729 |
# 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 |
730 |
# redirecting, we only update the following request in
|
731 |
# the redirect chain with a reference to the parent
|
|
732 |
# request .
|
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
733 |
|
2164.2.1
by v.ladeuil+lp at free
First rough http branch redirection implementation. |
734 |
# Some codes make no sense in our context and are treated
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
735 |
# as errors:
|
736 |
||
737 |
# 300: Multiple choices for different representations of
|
|
738 |
# the URI. Using that mechanisn with bzr will violate the
|
|
739 |
# protocol neutrality of Transport.
|
|
740 |
||
741 |
# 304: Not modified (SHOULD only occurs with conditional
|
|
742 |
# GETs which are not used by our implementation)
|
|
743 |
||
744 |
# 305: Use proxy. I can't imagine this one occurring in
|
|
745 |
# our context-- vila/20060909
|
|
746 |
||
747 |
# 306: Unused (if the RFC says so...)
|
|
748 |
||
2164.2.1
by v.ladeuil+lp at free
First rough http branch redirection implementation. |
749 |
# If the code is 302 and the request is HEAD, some may
|
750 |
# think that it is a sufficent hint that the file exists
|
|
751 |
# and that we MAY avoid following the redirections. But
|
|
752 |
# if we want to be sure, we MUST follow them.
|
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
753 |
|
754 |
if code in (301, 302, 303, 307): |
|
755 |
return Request(req.get_method(),newurl, |
|
756 |
headers = req.headers, |
|
757 |
origin_req_host = req.get_origin_req_host(), |
|
758 |
unverifiable = True, |
|
759 |
# TODO: It will be nice to be able to
|
|
760 |
# detect virtual hosts sharing the same
|
|
761 |
# IP address, that will allow us to
|
|
762 |
# share the same connection...
|
|
763 |
connection = None, |
|
764 |
parent = req, |
|
765 |
)
|
|
766 |
else: |
|
767 |
raise urllib2.HTTPError(req.get_full_url(), code, msg, headers, fp) |
|
768 |
||
2164.2.29
by Vincent Ladeuil
Test the http redirection at the request level even if it's not |
769 |
def http_error_302(self, req, fp, code, msg, headers): |
2004.3.1
by vila
Test ConnectionError exceptions. |
770 |
"""Requests the redirected to URI.
|
771 |
||
3059.2.2
by Vincent Ladeuil
Read http responses on demand without buffering the whole body |
772 |
Copied from urllib2 to be able to clean the pipe of the associated
|
773 |
connection, *before* issuing the redirected request but *after* having
|
|
774 |
eventually raised an error.
|
|
2004.3.1
by vila
Test ConnectionError exceptions. |
775 |
"""
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
776 |
# Some servers (incorrectly) return multiple Location headers
|
777 |
# (so probably same goes for URI). Use first header.
|
|
778 |
||
779 |
# TODO: Once we get rid of addinfourl objects, the
|
|
780 |
# following will need to be updated to use correct case
|
|
781 |
# for headers.
|
|
782 |
if 'location' in headers: |
|
783 |
newurl = headers.getheaders('location')[0] |
|
784 |
elif 'uri' in headers: |
|
785 |
newurl = headers.getheaders('uri')[0] |
|
786 |
else: |
|
787 |
return
|
|
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
788 |
if self._debuglevel >= 1: |
2164.2.1
by v.ladeuil+lp at free
First rough http branch redirection implementation. |
789 |
print 'Redirected to: %s (followed: %r)' % (newurl, |
790 |
req.follow_redirections) |
|
791 |
if req.follow_redirections is False: |
|
792 |
req.redirected_to = newurl |
|
793 |
return fp |
|
794 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
795 |
newurl = urlparse.urljoin(req.get_full_url(), newurl) |
796 |
||
797 |
# This call succeeds or raise an error. urllib2 returns
|
|
798 |
# if redirect_request returns None, but our
|
|
799 |
# redirect_request never returns None.
|
|
800 |
redirected_req = self.redirect_request(req, fp, code, msg, headers, |
|
801 |
newurl) |
|
802 |
||
803 |
# loop detection
|
|
804 |
# .redirect_dict has a key url if url was previously visited.
|
|
805 |
if hasattr(req, 'redirect_dict'): |
|
806 |
visited = redirected_req.redirect_dict = req.redirect_dict |
|
807 |
if (visited.get(newurl, 0) >= self.max_repeats or |
|
808 |
len(visited) >= self.max_redirections): |
|
809 |
raise urllib2.HTTPError(req.get_full_url(), code, |
|
810 |
self.inf_msg + msg, headers, fp) |
|
811 |
else: |
|
812 |
visited = redirected_req.redirect_dict = req.redirect_dict = {} |
|
813 |
visited[newurl] = visited.get(newurl, 0) + 1 |
|
814 |
||
815 |
# We can close the fp now that we are sure that we won't
|
|
816 |
# use it with HTTPError.
|
|
817 |
fp.close() |
|
818 |
# 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 |
819 |
req.connection.cleanup_pipe() |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
820 |
|
821 |
return self.parent.open(redirected_req) |
|
822 |
||
2164.2.29
by Vincent Ladeuil
Test the http redirection at the request level even if it's not |
823 |
http_error_301 = http_error_303 = http_error_307 = http_error_302 |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
824 |
|
825 |
||
2167.3.1
by v.ladeuil+lp at free
Fix bug #74759. |
826 |
class ProxyHandler(urllib2.ProxyHandler): |
827 |
"""Handles proxy setting.
|
|
828 |
||
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
829 |
Copied and modified from urllib2 to be able to modify the request during
|
830 |
the request pre-processing instead of modifying it at _open time. As we
|
|
831 |
capture (or create) the connection object during request processing, _open
|
|
832 |
time was too late.
|
|
833 |
||
834 |
The main task is to modify the request so that the connection is done to
|
|
835 |
the proxy while the request still refers to the destination host.
|
|
836 |
||
837 |
Note: the proxy handling *may* modify the protocol used; the request may be
|
|
838 |
against an https server proxied through an http proxy. So, https_request
|
|
839 |
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. |
840 |
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. |
841 |
"""
|
842 |
||
843 |
# Proxies must be in front
|
|
844 |
handler_order = 100 |
|
2167.3.3
by v.ladeuil+lp at free
* bzrlib/transport/http/_urllib2_wrappers.py: |
845 |
_debuglevel = DEBUG |
2167.3.1
by v.ladeuil+lp at free
Fix bug #74759. |
846 |
|
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
847 |
def __init__(self, proxies=None): |
2167.3.1
by v.ladeuil+lp at free
Fix bug #74759. |
848 |
urllib2.ProxyHandler.__init__(self, proxies) |
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
849 |
# First, let's get rid of urllib2 implementation
|
2167.3.1
by v.ladeuil+lp at free
Fix bug #74759. |
850 |
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. |
851 |
if self._debuglevel >= 3: |
2167.3.3
by v.ladeuil+lp at free
* bzrlib/transport/http/_urllib2_wrappers.py: |
852 |
print 'Will unbind %s_open for %r' % (type, proxy) |
2167.3.1
by v.ladeuil+lp at free
Fix bug #74759. |
853 |
delattr(self, '%s_open' % type) |
854 |
||
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
855 |
# 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. |
856 |
http_proxy = self.get_proxy_env_var('http') |
857 |
https_proxy = self.get_proxy_env_var('https') |
|
2167.3.3
by v.ladeuil+lp at free
* bzrlib/transport/http/_urllib2_wrappers.py: |
858 |
|
859 |
if http_proxy is not None: |
|
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
860 |
if self._debuglevel >= 3: |
2167.3.3
by v.ladeuil+lp at free
* bzrlib/transport/http/_urllib2_wrappers.py: |
861 |
print 'Will bind http_request for %r' % http_proxy |
862 |
setattr(self, 'http_request', |
|
863 |
lambda request: self.set_proxy(request, 'http')) |
|
864 |
||
865 |
if https_proxy is not None: |
|
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
866 |
if self._debuglevel >= 3: |
2167.3.3
by v.ladeuil+lp at free
* bzrlib/transport/http/_urllib2_wrappers.py: |
867 |
print 'Will bind http_request for %r' % https_proxy |
868 |
setattr(self, 'https_request', |
|
869 |
lambda request: self.set_proxy(request, 'https')) |
|
870 |
||
2167.3.6
by v.ladeuil+lp at free
Take John's comments into account and add more tests. |
871 |
def get_proxy_env_var(self, name, default_to='all'): |
872 |
"""Get a proxy env var.
|
|
873 |
||
2182.1.1
by Aaron Bentley
Respect proxy environment settings (Vincent Ladeuil, #74759) |
874 |
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. |
875 |
urllib.getproxies_environment taking into account the
|
876 |
uppercased values for proxy variables.
|
|
877 |
"""
|
|
2167.3.3
by v.ladeuil+lp at free
* bzrlib/transport/http/_urllib2_wrappers.py: |
878 |
try: |
879 |
return self.proxies[name.lower()] |
|
880 |
except KeyError: |
|
2167.3.6
by v.ladeuil+lp at free
Take John's comments into account and add more tests. |
881 |
if default_to is not None: |
882 |
# Try to get the alternate environment variable
|
|
883 |
try: |
|
884 |
return self.proxies[default_to] |
|
885 |
except KeyError: |
|
886 |
pass
|
|
887 |
return None |
|
2167.3.3
by v.ladeuil+lp at free
* bzrlib/transport/http/_urllib2_wrappers.py: |
888 |
|
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
889 |
def proxy_bypass(self, host): |
890 |
"""Check if host should be proxied or not"""
|
|
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
891 |
no_proxy = self.get_proxy_env_var('no', default_to=None) |
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
892 |
if no_proxy is None: |
893 |
return False |
|
894 |
hhost, hport = urllib.splitport(host) |
|
2182.1.1
by Aaron Bentley
Respect proxy environment settings (Vincent Ladeuil, #74759) |
895 |
# Does host match any of the domains mentioned in
|
896 |
# no_proxy ? The rules about what is authorized in no_proxy
|
|
897 |
# 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. |
898 |
# commonly seen values.
|
899 |
for domain in no_proxy.split(','): |
|
900 |
dhost, dport = urllib.splitport(domain) |
|
2167.3.5
by v.ladeuil+lp at free
Tests for proxies, covering #74759. |
901 |
if hport == dport or dport is None: |
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
902 |
# Protect glob chars
|
903 |
dhost = dhost.replace(".", r"\.") |
|
904 |
dhost = dhost.replace("*", r".*") |
|
905 |
dhost = dhost.replace("?", r".") |
|
2167.3.5
by v.ladeuil+lp at free
Tests for proxies, covering #74759. |
906 |
if re.match(dhost, hhost, re.IGNORECASE): |
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
907 |
return True |
2182.1.1
by Aaron Bentley
Respect proxy environment settings (Vincent Ladeuil, #74759) |
908 |
# Nevertheless, there are platform-specific ways to
|
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
909 |
# ignore proxies...
|
910 |
return urllib.proxy_bypass(host) |
|
911 |
||
2167.3.3
by v.ladeuil+lp at free
* bzrlib/transport/http/_urllib2_wrappers.py: |
912 |
def set_proxy(self, request, type): |
2167.3.4
by v.ladeuil+lp at free
Better fix for #74759, but still not tests. |
913 |
if self.proxy_bypass(request.get_host()): |
914 |
return request |
|
915 |
||
2167.3.6
by v.ladeuil+lp at free
Take John's comments into account and add more tests. |
916 |
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. |
917 |
if self._debuglevel >= 3: |
2167.3.3
by v.ladeuil+lp at free
* bzrlib/transport/http/_urllib2_wrappers.py: |
918 |
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. |
919 |
# FIXME: python 2.5 urlparse provides a better _parse_proxy which can
|
920 |
# grok user:password@host:port as well as
|
|
921 |
# http://user:password@host:port
|
|
922 |
||
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
923 |
(scheme, user, password, |
924 |
host, port, path) = transport.ConnectedTransport._split_url(proxy) |
|
4294.2.9
by Robert Collins
Fixup tests broken by cleaning up the layering. |
925 |
if not host: |
926 |
raise errors.InvalidURL(proxy, 'No host component') |
|
2900.2.15
by Vincent Ladeuil
AuthenticationConfig can be queried for logins too (first step). |
927 |
|
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
928 |
if request.proxy_auth == {}: |
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
929 |
# No proxy auth parameter are available, we are handling the first
|
930 |
# proxied request, intialize. scheme (the authentication scheme)
|
|
931 |
# and realm will be set by the AuthHandler
|
|
932 |
request.proxy_auth = { |
|
933 |
'host': host, 'port': port, |
|
934 |
'user': user, 'password': password, |
|
935 |
'protocol': scheme, |
|
936 |
# We ignore path since we connect to a proxy
|
|
937 |
'path': None} |
|
938 |
if port is None: |
|
939 |
phost = host |
|
940 |
else: |
|
941 |
phost = host + ':%d' % port |
|
942 |
request.set_proxy(phost, type) |
|
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
943 |
if self._debuglevel >= 3: |
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
944 |
print 'set_proxy: proxy set to %s://%s' % (type, phost) |
2167.3.1
by v.ladeuil+lp at free
Fix bug #74759. |
945 |
return request |
946 |
||
947 |
||
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
948 |
class AbstractAuthHandler(urllib2.BaseHandler): |
949 |
"""A custom abstract authentication handler for all http authentications.
|
|
950 |
||
951 |
Provides the meat to handle authentication errors and
|
|
952 |
preventively set authentication headers after the first
|
|
953 |
successful authentication.
|
|
954 |
||
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
955 |
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. |
956 |
digest authentications.
|
957 |
||
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
958 |
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. |
959 |
(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. |
960 |
|
961 |
The interaction between this handler and the urllib2
|
|
962 |
framework is not obvious, it works as follow:
|
|
963 |
||
964 |
opener.open(request) is called:
|
|
965 |
||
966 |
- that may trigger http_request which will add an authentication header
|
|
967 |
(self.build_header) if enough info is available.
|
|
968 |
||
969 |
- the request is sent to the server,
|
|
970 |
||
971 |
- if an authentication error is received self.auth_required is called,
|
|
972 |
we acquire the authentication info in the error headers and call
|
|
973 |
self.auth_match to check that we are able to try the
|
|
974 |
authentication and complete the authentication parameters,
|
|
975 |
||
976 |
- we call parent.open(request), that may trigger http_request
|
|
977 |
and will add a header (self.build_header), but here we have
|
|
978 |
all the required info (keep in mind that the request and
|
|
979 |
authentication used in the recursive calls are really (and must be)
|
|
980 |
the *same* objects).
|
|
981 |
||
982 |
- if the call returns a response, the authentication have been
|
|
983 |
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. |
984 |
"""
|
985 |
||
4307.4.3
by Vincent Ladeuil
Tighten multiple auth schemes handling. |
986 |
scheme = None |
987 |
"""The scheme as it appears in the server header (lower cased)"""
|
|
988 |
||
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
989 |
_max_retry = 3 |
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
990 |
"""We don't want to retry authenticating endlessly"""
|
991 |
||
4050.2.3
by Vincent Ladeuil
Slight cosmetic tweaks. |
992 |
requires_username = True |
993 |
"""Whether the auth mechanism requires a username."""
|
|
994 |
||
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
995 |
# The following attributes should be defined by daughter
|
996 |
# classes:
|
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
997 |
# - 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. |
998 |
# - auth_header: the header sent in the request
|
999 |
||
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1000 |
def __init__(self): |
1001 |
# We want to know when we enter into an try/fail cycle of
|
|
1002 |
# authentications so we initialize to None to indicate that we aren't
|
|
1003 |
# 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. |
1004 |
self._retry_count = None |
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1005 |
|
4050.2.2
by Vincent Ladeuil
Ensures all auth handlers correctly parse all auth headers. |
1006 |
def _parse_auth_header(self, server_header): |
1007 |
"""Parse the authentication header.
|
|
1008 |
||
1009 |
:param server_header: The value of the header sent by the server
|
|
1010 |
describing the authenticaion request.
|
|
1011 |
||
1012 |
:return: A tuple (scheme, remainder) scheme being the first word in the
|
|
1013 |
given header (lower cased), remainder may be None.
|
|
1014 |
"""
|
|
1015 |
try: |
|
1016 |
scheme, remainder = server_header.split(None, 1) |
|
1017 |
except ValueError: |
|
1018 |
scheme = server_header |
|
1019 |
remainder = None |
|
1020 |
return (scheme.lower(), remainder) |
|
1021 |
||
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
1022 |
def update_auth(self, auth, key, value): |
1023 |
"""Update a value in auth marking the auth as modified if needed"""
|
|
1024 |
old_value = auth.get(key, None) |
|
1025 |
if old_value != value: |
|
1026 |
auth[key] = value |
|
1027 |
auth['modified'] = True |
|
1028 |
||
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1029 |
def auth_required(self, request, headers): |
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1030 |
"""Retry the request if the auth scheme is ours.
|
1031 |
||
1032 |
:param request: The request needing authentication.
|
|
1033 |
:param headers: The headers for the authentication error response.
|
|
1034 |
:return: None or the response for the authenticated request.
|
|
1035 |
"""
|
|
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
1036 |
# Don't try to authenticate endlessly
|
1037 |
if self._retry_count is None: |
|
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1038 |
# The retry being recusrsive calls, None identify the first retry
|
1039 |
self._retry_count = 1 |
|
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
1040 |
else: |
1041 |
self._retry_count += 1 |
|
1042 |
if self._retry_count > self._max_retry: |
|
1043 |
# Let's be ready for next round
|
|
1044 |
self._retry_count = None |
|
1045 |
return None |
|
4307.4.2
by Vincent Ladeuil
Handle servers proposing several authentication schemes. |
1046 |
server_headers = headers.getheaders(self.auth_required_header) |
1047 |
if not server_headers: |
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1048 |
# The http error MUST have the associated
|
1049 |
# 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. |
1050 |
raise KeyError('%s not found' % self.auth_required_header) |
1051 |
||
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
1052 |
auth = self.get_auth(request) |
1053 |
auth['modified'] = False |
|
4307.4.2
by Vincent Ladeuil
Handle servers proposing several authentication schemes. |
1054 |
# FIXME: the auth handler should be selected at a single place instead
|
4307.4.3
by Vincent Ladeuil
Tighten multiple auth schemes handling. |
1055 |
# of letting all handlers try to match all headers, but the current
|
1056 |
# design doesn't allow a simple implementation.
|
|
4307.4.2
by Vincent Ladeuil
Handle servers proposing several authentication schemes. |
1057 |
for server_header in server_headers: |
1058 |
# Several schemes can be proposed by the server, try to match each
|
|
1059 |
# one in turn
|
|
1060 |
matching_handler = self.auth_match(server_header, auth) |
|
1061 |
if matching_handler: |
|
1062 |
# auth_match may have modified auth (by adding the
|
|
1063 |
# password or changing the realm, for example)
|
|
1064 |
if (request.get_header(self.auth_header, None) is not None |
|
1065 |
and not auth['modified']): |
|
1066 |
# We already tried that, give up
|
|
1067 |
return None |
|
1068 |
||
4307.4.3
by Vincent Ladeuil
Tighten multiple auth schemes handling. |
1069 |
# Only the most secure scheme proposed by the server should be
|
1070 |
# used, since the handlers use 'handler_order' to describe that
|
|
1071 |
# property, the first handler tried takes precedence, the
|
|
1072 |
# others should not attempt to authenticate if the best one
|
|
1073 |
# failed.
|
|
1074 |
best_scheme = auth.get('best_scheme', None) |
|
1075 |
if best_scheme is None: |
|
1076 |
# At that point, if current handler should doesn't succeed
|
|
1077 |
# the credentials are wrong (or incomplete), but we know
|
|
1078 |
# that the associated scheme should be used.
|
|
1079 |
best_scheme = auth['best_scheme'] = self.scheme |
|
1080 |
if best_scheme != self.scheme: |
|
1081 |
continue
|
|
1082 |
||
4307.4.2
by Vincent Ladeuil
Handle servers proposing several authentication schemes. |
1083 |
if self.requires_username and auth.get('user', None) is None: |
1084 |
# Without a known user, we can't authenticate
|
|
1085 |
return None |
|
1086 |
||
1087 |
# Housekeeping
|
|
1088 |
request.connection.cleanup_pipe() |
|
1089 |
# Retry the request with an authentication header added
|
|
1090 |
response = self.parent.open(request) |
|
1091 |
if response: |
|
1092 |
self.auth_successful(request, response) |
|
1093 |
return response |
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1094 |
# We are not qualified to handle the authentication.
|
1095 |
# Note: the authentication error handling will try all
|
|
1096 |
# available handlers. If one of them authenticates
|
|
1097 |
# successfully, a response will be returned. If none of
|
|
1098 |
# them succeeds, None will be returned and the error
|
|
1099 |
# handler will raise the 401 'Unauthorized' or the 407
|
|
1100 |
# 'Proxy Authentication Required' error.
|
|
1101 |
return None |
|
1102 |
||
1103 |
def add_auth_header(self, request, header): |
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1104 |
"""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. |
1105 |
request.add_unredirected_header(self.auth_header, header) |
1106 |
||
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1107 |
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. |
1108 |
"""Check that we are able to handle that authentication scheme.
|
1109 |
||
1110 |
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. |
1111 |
updated with info from the server. Some of these
|
1112 |
parameters, when combined, are considered to be the
|
|
1113 |
authentication key, if one of them change the
|
|
1114 |
authentication result may change. 'user' and 'password'
|
|
1115 |
are exampls, but some auth schemes may have others
|
|
1116 |
(digest's nonce is an example, digest's nonce_count is a
|
|
1117 |
*counter-example*). Such parameters must be updated by
|
|
1118 |
using the update_auth() method.
|
|
3943.8.1
by Marius Kruger
remove all trailing whitespace from bzr source |
1119 |
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1120 |
: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 |
1121 |
:param auth: The auth parameters already known. They may be
|
1122 |
updated.
|
|
1123 |
: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. |
1124 |
"""
|
1125 |
raise NotImplementedError(self.auth_match) |
|
1126 |
||
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1127 |
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. |
1128 |
"""Build the value of the header used to authenticate.
|
1129 |
||
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1130 |
: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. |
1131 |
: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. |
1132 |
|
1133 |
:return: None or header.
|
|
1134 |
"""
|
|
1135 |
raise NotImplementedError(self.build_auth_header) |
|
1136 |
||
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
1137 |
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. |
1138 |
"""The authentification was successful for the request.
|
1139 |
||
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
1140 |
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. |
1141 |
|
1142 |
:param request: The succesfully authenticated request.
|
|
2420.1.9
by Vincent Ladeuil
Refactor proxy and auth test classes. Tests failing for digest auth. |
1143 |
: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. |
1144 |
"""
|
2540.2.2
by Vincent Ladeuil
Fix #120678 by issuing a CONNECT request when https is used via a proxy. |
1145 |
# It may happen that we need to reconnect later, let's be ready
|
1146 |
self._retry_count = None |
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1147 |
|
2900.2.20
by Vincent Ladeuil
http can query AuthenticationConfig for logins too. |
1148 |
def get_user_password(self, auth): |
3910.2.3
by Ben Jansen
Made tweaks requested by John Arbash Meinel. |
1149 |
"""Ask user for a password if none is already available.
|
1150 |
||
3910.2.4
by Vincent Ladeuil
Fixed as per John's review. |
1151 |
:param auth: authentication info gathered so far (from the initial url
|
1152 |
and then during dialog with the server).
|
|
3910.2.3
by Ben Jansen
Made tweaks requested by John Arbash Meinel. |
1153 |
"""
|
2900.2.20
by Vincent Ladeuil
http can query AuthenticationConfig for logins too. |
1154 |
auth_conf = config.AuthenticationConfig() |
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1155 |
user = auth['user'] |
1156 |
password = auth['password'] |
|
2900.2.20
by Vincent Ladeuil
http can query AuthenticationConfig for logins too. |
1157 |
realm = auth['realm'] |
1158 |
||
1159 |
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. |
1160 |
user = auth_conf.get_user(auth['protocol'], auth['host'], |
3910.2.2
by Vincent Ladeuil
Fix bug #300347 by allowing querying authentication.conf if no |
1161 |
port=auth['port'], path=auth['path'], |
4222.3.12
by Jelmer Vernooij
Check that the HTTP transport prompts for usernames. |
1162 |
realm=realm, ask=True, |
1163 |
prompt=self.build_username_prompt(auth)) |
|
3910.2.2
by Vincent Ladeuil
Fix bug #300347 by allowing querying authentication.conf if no |
1164 |
if user is not None and password is None: |
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1165 |
password = auth_conf.get_password( |
1166 |
auth['protocol'], auth['host'], user, port=auth['port'], |
|
1167 |
path=auth['path'], realm=realm, |
|
2900.2.19
by Vincent Ladeuil
Mention proxy and https in the password prompts, with tests. |
1168 |
prompt=self.build_password_prompt(auth)) |
2900.2.20
by Vincent Ladeuil
http can query AuthenticationConfig for logins too. |
1169 |
|
1170 |
return user, password |
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1171 |
|
2900.2.19
by Vincent Ladeuil
Mention proxy and https in the password prompts, with tests. |
1172 |
def _build_password_prompt(self, auth): |
1173 |
"""Build a prompt taking the protocol used into account.
|
|
1174 |
||
1175 |
The AuthHandler is used by http and https, we want that information in
|
|
1176 |
the prompt, so we build the prompt from the authentication dict which
|
|
1177 |
contains all the needed parts.
|
|
1178 |
||
3133.1.3
by Vincent Ladeuil
Fix typo (hi John ;). |
1179 |
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. |
1180 |
user. The daughter classes should implements a public
|
2900.2.19
by Vincent Ladeuil
Mention proxy and https in the password prompts, with tests. |
1181 |
build_password_prompt using this method.
|
1182 |
"""
|
|
1183 |
prompt = '%s' % auth['protocol'].upper() + ' %(user)s@%(host)s' |
|
1184 |
realm = auth['realm'] |
|
1185 |
if realm is not None: |
|
1186 |
prompt += ", Realm: '%s'" % realm |
|
1187 |
prompt += ' password' |
|
1188 |
return prompt |
|
1189 |
||
4222.3.12
by Jelmer Vernooij
Check that the HTTP transport prompts for usernames. |
1190 |
def _build_username_prompt(self, auth): |
1191 |
"""Build a prompt taking the protocol used into account.
|
|
1192 |
||
1193 |
The AuthHandler is used by http and https, we want that information in
|
|
1194 |
the prompt, so we build the prompt from the authentication dict which
|
|
1195 |
contains all the needed parts.
|
|
1196 |
||
1197 |
Also, http and proxy AuthHandlers present different prompts to the
|
|
1198 |
user. The daughter classes should implements a public
|
|
1199 |
build_username_prompt using this method.
|
|
1200 |
"""
|
|
1201 |
prompt = '%s' % auth['protocol'].upper() + ' %(host)s' |
|
1202 |
realm = auth['realm'] |
|
1203 |
if realm is not None: |
|
1204 |
prompt += ", Realm: '%s'" % realm |
|
1205 |
prompt += ' username' |
|
1206 |
return prompt |
|
1207 |
||
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1208 |
def http_request(self, request): |
1209 |
"""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 |
1210 |
auth = self.get_auth(request) |
1211 |
if self.auth_params_reusable(auth): |
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1212 |
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. |
1213 |
return request |
1214 |
||
1215 |
https_request = http_request # FIXME: Need test |
|
1216 |
||
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1217 |
|
1218 |
class NegotiateAuthHandler(AbstractAuthHandler): |
|
1219 |
"""A authentication handler that handles WWW-Authenticate: Negotiate.
|
|
1220 |
||
4032.1.4
by John Arbash Meinel
Found 2 more files with trailing whitespace. |
1221 |
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. |
1222 |
NTLM support may also be added.
|
1223 |
"""
|
|
1224 |
||
4307.4.3
by Vincent Ladeuil
Tighten multiple auth schemes handling. |
1225 |
scheme = 'negotiate' |
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1226 |
handler_order = 480 |
4017.5.1
by Jelmer Vernooij
Allow HTTP authentication handlers (such as the NegotiateAuthHandler) to |
1227 |
requires_username = False |
1228 |
||
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1229 |
def auth_match(self, header, auth): |
4050.2.2
by Vincent Ladeuil
Ensures all auth handlers correctly parse all auth headers. |
1230 |
scheme, raw_auth = self._parse_auth_header(header) |
4307.4.3
by Vincent Ladeuil
Tighten multiple auth schemes handling. |
1231 |
if scheme != self.scheme: |
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1232 |
return False |
1233 |
self.update_auth(auth, 'scheme', scheme) |
|
4011.3.2
by Jelmer Vernooij
Only attempt GSSAPI authentication when the kerberos module is present. |
1234 |
resp = self._auth_match_kerberos(auth) |
1235 |
if resp is None: |
|
1236 |
return False |
|
1237 |
# Optionally should try to authenticate using NTLM here
|
|
1238 |
self.update_auth(auth, 'negotiate_response', resp) |
|
1239 |
return True |
|
1240 |
||
1241 |
def _auth_match_kerberos(self, auth): |
|
1242 |
"""Try to create a GSSAPI response for authenticating against a host."""
|
|
4011.3.4
by Jelmer Vernooij
review from vila: mention HTTPS, clarify error a bit, move import to top-level. |
1243 |
if not have_kerberos: |
4011.3.2
by Jelmer Vernooij
Only attempt GSSAPI authentication when the kerberos module is present. |
1244 |
return None |
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1245 |
ret, vc = kerberos.authGSSClientInit("HTTP@%(host)s" % auth) |
1246 |
if ret < 1: |
|
4011.3.5
by Jelmer Vernooij
Move import next to other system libs, fix format. |
1247 |
trace.warning('Unable to create GSSAPI context for %s: %d', |
1248 |
auth['host'], ret) |
|
4011.3.2
by Jelmer Vernooij
Only attempt GSSAPI authentication when the kerberos module is present. |
1249 |
return None |
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1250 |
ret = kerberos.authGSSClientStep(vc, "") |
1251 |
if ret < 0: |
|
1252 |
trace.mutter('authGSSClientStep failed: %d', ret) |
|
4011.3.2
by Jelmer Vernooij
Only attempt GSSAPI authentication when the kerberos module is present. |
1253 |
return None |
1254 |
return kerberos.authGSSClientResponse(vc) |
|
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1255 |
|
1256 |
def build_auth_header(self, auth, request): |
|
4011.3.2
by Jelmer Vernooij
Only attempt GSSAPI authentication when the kerberos module is present. |
1257 |
return "Negotiate %s" % auth['negotiate_response'] |
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1258 |
|
1259 |
def auth_params_reusable(self, auth): |
|
1260 |
# If the auth scheme is known, it means a previous
|
|
1261 |
# authentication was successful, all information is
|
|
1262 |
# available, no further checks are needed.
|
|
4032.1.4
by John Arbash Meinel
Found 2 more files with trailing whitespace. |
1263 |
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. |
1264 |
auth.get('negotiate_response', None) is not None) |
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1265 |
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1266 |
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1267 |
class BasicAuthHandler(AbstractAuthHandler): |
1268 |
"""A custom basic authentication handler."""
|
|
1269 |
||
4307.4.3
by Vincent Ladeuil
Tighten multiple auth schemes handling. |
1270 |
scheme = 'basic' |
2545.2.1
by Vincent Ladeuil
Fix 121889 by working around urllib2 bug. |
1271 |
handler_order = 500 |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1272 |
auth_regexp = re.compile('realm="([^"]*)"', re.I) |
1273 |
||
1274 |
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 |
1275 |
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. |
1276 |
auth_header = 'Basic ' + raw.encode('base64').strip() |
1277 |
return auth_header |
|
1278 |
||
4284.1.1
by Vincent Ladeuil
Fix wrong realm extraction in http basic authentication (reported |
1279 |
def extract_realm(self, header_value): |
1280 |
match = self.auth_regexp.search(header_value) |
|
1281 |
realm = None |
|
1282 |
if match: |
|
1283 |
realm = match.group(1) |
|
1284 |
return match, realm |
|
1285 |
||
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1286 |
def auth_match(self, header, auth): |
4050.2.2
by Vincent Ladeuil
Ensures all auth handlers correctly parse all auth headers. |
1287 |
scheme, raw_auth = self._parse_auth_header(header) |
4307.4.3
by Vincent Ladeuil
Tighten multiple auth schemes handling. |
1288 |
if scheme != self.scheme: |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1289 |
return False |
1290 |
||
4284.1.1
by Vincent Ladeuil
Fix wrong realm extraction in http basic authentication (reported |
1291 |
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. |
1292 |
if match: |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1293 |
# Put useful info into auth
|
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
1294 |
self.update_auth(auth, 'scheme', scheme) |
1295 |
self.update_auth(auth, 'realm', realm) |
|
2900.2.20
by Vincent Ladeuil
http can query AuthenticationConfig for logins too. |
1296 |
if auth['user'] is None or auth['password'] is None: |
1297 |
user, password = self.get_user_password(auth) |
|
1298 |
self.update_auth(auth, 'user', user) |
|
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
1299 |
self.update_auth(auth, 'password', password) |
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1300 |
return match is not None |
1301 |
||
1302 |
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. |
1303 |
# 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. |
1304 |
# 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. |
1305 |
# available, no further checks are needed.
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1306 |
return auth.get('scheme', None) == 'basic' |
1307 |
||
1308 |
||
1309 |
def get_digest_algorithm_impls(algorithm): |
|
1310 |
H = None |
|
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1311 |
KD = None |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1312 |
if algorithm == 'MD5': |
2929.3.1
by Vincent Ladeuil
Fix python2.6 deprecation warnings (still 4 failures 5 errors in test suite). |
1313 |
H = lambda x: osutils.md5(x).hexdigest() |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1314 |
elif algorithm == 'SHA': |
2929.3.1
by Vincent Ladeuil
Fix python2.6 deprecation warnings (still 4 failures 5 errors in test suite). |
1315 |
H = lambda x: osutils.sha(x).hexdigest() |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1316 |
if H is not None: |
1317 |
KD = lambda secret, data: H("%s:%s" % (secret, data)) |
|
1318 |
return H, KD |
|
1319 |
||
1320 |
||
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1321 |
def get_new_cnonce(nonce, nonce_count): |
1322 |
raw = '%s:%d:%s:%s' % (nonce, nonce_count, time.ctime(), |
|
1323 |
urllib2.randombytes(8)) |
|
2929.3.1
by Vincent Ladeuil
Fix python2.6 deprecation warnings (still 4 failures 5 errors in test suite). |
1324 |
return osutils.sha(raw).hexdigest()[:16] |
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1325 |
|
1326 |
||
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1327 |
class DigestAuthHandler(AbstractAuthHandler): |
1328 |
"""A custom digest authentication handler."""
|
|
1329 |
||
4307.4.3
by Vincent Ladeuil
Tighten multiple auth schemes handling. |
1330 |
scheme = 'digest' |
4050.2.3
by Vincent Ladeuil
Slight cosmetic tweaks. |
1331 |
# 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. |
1332 |
handler_order = 490 |
1333 |
||
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1334 |
def auth_params_reusable(self, auth): |
1335 |
# 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. |
1336 |
# authentication was successful, all information is
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1337 |
# available, no further checks are needed.
|
1338 |
return auth.get('scheme', None) == 'digest' |
|
1339 |
||
1340 |
def auth_match(self, header, auth): |
|
4050.2.2
by Vincent Ladeuil
Ensures all auth handlers correctly parse all auth headers. |
1341 |
scheme, raw_auth = self._parse_auth_header(header) |
4307.4.3
by Vincent Ladeuil
Tighten multiple auth schemes handling. |
1342 |
if scheme != self.scheme: |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1343 |
return False |
1344 |
||
1345 |
# Put the requested authentication info into a dict
|
|
1346 |
req_auth = urllib2.parse_keqv_list(urllib2.parse_http_list(raw_auth)) |
|
1347 |
||
1348 |
# Check that we can handle that authentication
|
|
1349 |
qop = req_auth.get('qop', None) |
|
1350 |
if qop != 'auth': # No auth-int so far |
|
1351 |
return False |
|
1352 |
||
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1353 |
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. |
1354 |
if H is None: |
1355 |
return False |
|
1356 |
||
1357 |
realm = req_auth.get('realm', None) |
|
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1358 |
# Put useful info into auth
|
1359 |
self.update_auth(auth, 'scheme', scheme) |
|
1360 |
self.update_auth(auth, 'realm', realm) |
|
2900.2.20
by Vincent Ladeuil
http can query AuthenticationConfig for logins too. |
1361 |
if auth['user'] is None or auth['password'] is None: |
1362 |
user, password = self.get_user_password(auth) |
|
1363 |
self.update_auth(auth, 'user', user) |
|
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1364 |
self.update_auth(auth, 'password', password) |
1365 |
||
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1366 |
try: |
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1367 |
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. |
1368 |
self.update_auth(auth, 'algorithm', req_auth.get('algorithm')) |
1369 |
nonce = req_auth['nonce'] |
|
1370 |
if auth.get('nonce', None) != nonce: |
|
1371 |
# A new nonce, never used
|
|
1372 |
self.update_auth(auth, 'nonce_count', 0) |
|
1373 |
self.update_auth(auth, 'nonce', nonce) |
|
1374 |
self.update_auth(auth, 'qop', qop) |
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1375 |
auth['opaque'] = req_auth.get('opaque', None) |
1376 |
except KeyError: |
|
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1377 |
# Some required field is not there
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1378 |
return False |
1379 |
||
1380 |
return True |
|
1381 |
||
1382 |
def build_auth_header(self, auth, request): |
|
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1383 |
url_scheme, url_selector = urllib.splittype(request.get_selector()) |
1384 |
sel_host, uri = urllib.splithost(url_selector) |
|
1385 |
||
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1386 |
A1 = '%s:%s:%s' % (auth['user'], auth['realm'], auth['password']) |
1387 |
A2 = '%s:%s' % (request.get_method(), uri) |
|
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1388 |
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1389 |
nonce = auth['nonce'] |
1390 |
qop = auth['qop'] |
|
1391 |
||
2420.1.16
by Vincent Ladeuil
Handle nonce changes. Fix a nasty bug breaking the auth parameters sharing. |
1392 |
nonce_count = auth['nonce_count'] + 1 |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1393 |
ncvalue = '%08x' % nonce_count |
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1394 |
cnonce = get_new_cnonce(nonce, nonce_count) |
1395 |
||
1396 |
H, KD = get_digest_algorithm_impls(auth.get('algorithm', 'MD5')) |
|
1397 |
nonce_data = '%s:%s:%s:%s:%s' % (nonce, ncvalue, cnonce, qop, H(A2)) |
|
1398 |
request_digest = KD(H(A1), nonce_data) |
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1399 |
|
1400 |
header = 'Digest ' |
|
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1401 |
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. |
1402 |
auth['realm'], |
1403 |
nonce) |
|
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1404 |
header += ', uri="%s"' % uri |
1405 |
header += ', cnonce="%s", nc=%s' % (cnonce, ncvalue) |
|
1406 |
header += ', qop="%s"' % qop |
|
1407 |
header += ', response="%s"' % request_digest |
|
1408 |
# Append the optional fields
|
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1409 |
opaque = auth.get('opaque', None) |
1410 |
if opaque: |
|
1411 |
header += ', opaque="%s"' % opaque |
|
2420.1.14
by Vincent Ladeuil
Tested against squid-2.6.5 with digest authentication. |
1412 |
if auth.get('algorithm', None): |
1413 |
header += ', algorithm="%s"' % auth.get('algorithm') |
|
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1414 |
|
1415 |
# We have used the nonce once more, update the count
|
|
1416 |
auth['nonce_count'] = nonce_count |
|
1417 |
||
1418 |
return header |
|
1419 |
||
1420 |
||
1421 |
class HTTPAuthHandler(AbstractAuthHandler): |
|
1422 |
"""Custom http authentication handler.
|
|
2004.3.1
by vila
Test ConnectionError exceptions. |
1423 |
|
2363.4.12
by Vincent Ladeuil
Take jam's review comments into account. Fix typos, give better |
1424 |
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. |
1425 |
associated with the 401 error and keep the revelant info in
|
1426 |
the auth request attribute.
|
|
2004.3.1
by vila
Test ConnectionError exceptions. |
1427 |
"""
|
1428 |
||
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1429 |
auth_required_header = 'www-authenticate' |
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1430 |
auth_header = 'Authorization' |
1431 |
||
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1432 |
def get_auth(self, request): |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1433 |
"""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. |
1434 |
return request.auth |
1435 |
||
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1436 |
def set_auth(self, request, auth): |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1437 |
"""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 |
1438 |
request.auth = auth |
2004.3.1
by vila
Test ConnectionError exceptions. |
1439 |
|
2900.2.19
by Vincent Ladeuil
Mention proxy and https in the password prompts, with tests. |
1440 |
def build_password_prompt(self, auth): |
1441 |
return self._build_password_prompt(auth) |
|
1442 |
||
4222.3.12
by Jelmer Vernooij
Check that the HTTP transport prompts for usernames. |
1443 |
def build_username_prompt(self, auth): |
1444 |
return self._build_username_prompt(auth) |
|
1445 |
||
2363.4.9
by Vincent Ladeuil
Catch first succesful authentification to avoid further 401 |
1446 |
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. |
1447 |
return self.auth_required(req, headers) |
1448 |
||
1449 |
||
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1450 |
class ProxyAuthHandler(AbstractAuthHandler): |
1451 |
"""Custom proxy authentication handler.
|
|
2420.1.3
by Vincent Ladeuil
Implement http proxy basic authentication. |
1452 |
|
1453 |
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. |
1454 |
associated with the 407 error and keep the revelant info in
|
1455 |
the proxy_auth request attribute..
|
|
2420.1.3
by Vincent Ladeuil
Implement http proxy basic authentication. |
1456 |
"""
|
1457 |
||
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1458 |
auth_required_header = 'proxy-authenticate' |
2420.1.7
by Vincent Ladeuil
Tested against squid-2.6.5 with basic authentication. |
1459 |
# FIXME: the correct capitalization is Proxy-Authorization,
|
2420.1.8
by Vincent Ladeuil
Interesting typo :-) A mix between capitalize, title and fuzzy may be... |
1460 |
# 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. |
1461 |
# instead of title().
|
2420.1.5
by Vincent Ladeuil
Refactor http and proxy authentication. Tests passing. proxy password can be prompted too. |
1462 |
auth_header = 'Proxy-authorization' |
1463 |
||
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1464 |
def get_auth(self, request): |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1465 |
"""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. |
1466 |
return request.proxy_auth |
1467 |
||
2420.1.6
by Vincent Ladeuil
Update NEWS to explain the intent of the modification. Also, use dicts |
1468 |
def set_auth(self, request, auth): |
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1469 |
"""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 |
1470 |
request.proxy_auth = auth |
2420.1.3
by Vincent Ladeuil
Implement http proxy basic authentication. |
1471 |
|
2900.2.19
by Vincent Ladeuil
Mention proxy and https in the password prompts, with tests. |
1472 |
def build_password_prompt(self, auth): |
1473 |
prompt = self._build_password_prompt(auth) |
|
1474 |
prompt = 'Proxy ' + prompt |
|
1475 |
return prompt |
|
1476 |
||
4222.3.12
by Jelmer Vernooij
Check that the HTTP transport prompts for usernames. |
1477 |
def build_username_prompt(self, auth): |
1478 |
prompt = self._build_username_prompt(auth) |
|
1479 |
prompt = 'Proxy ' + prompt |
|
1480 |
return prompt |
|
1481 |
||
2420.1.3
by Vincent Ladeuil
Implement http proxy basic authentication. |
1482 |
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. |
1483 |
return self.auth_required(req, headers) |
2420.1.3
by Vincent Ladeuil
Implement http proxy basic authentication. |
1484 |
|
1485 |
||
2420.1.11
by Vincent Ladeuil
Implement digest authentication. Test suite passes. Tested against apache-2.x. |
1486 |
class HTTPBasicAuthHandler(BasicAuthHandler, HTTPAuthHandler): |
1487 |
"""Custom http basic authentication handler"""
|
|
1488 |
||
1489 |
||
1490 |
class ProxyBasicAuthHandler(BasicAuthHandler, ProxyAuthHandler): |
|
1491 |
"""Custom proxy basic authentication handler"""
|
|
1492 |
||
1493 |
||
1494 |
class HTTPDigestAuthHandler(DigestAuthHandler, HTTPAuthHandler): |
|
1495 |
"""Custom http basic authentication handler"""
|
|
1496 |
||
1497 |
||
1498 |
class ProxyDigestAuthHandler(DigestAuthHandler, ProxyAuthHandler): |
|
1499 |
"""Custom proxy basic authentication handler"""
|
|
1500 |
||
2420.1.3
by Vincent Ladeuil
Implement http proxy basic authentication. |
1501 |
|
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1502 |
class HTTPNegotiateAuthHandler(NegotiateAuthHandler, HTTPAuthHandler): |
1503 |
"""Custom http negotiate authentication handler"""
|
|
1504 |
||
1505 |
||
1506 |
class ProxyNegotiateAuthHandler(NegotiateAuthHandler, ProxyAuthHandler): |
|
1507 |
"""Custom proxy negotiate authentication handler"""
|
|
1508 |
||
1509 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1510 |
class HTTPErrorProcessor(urllib2.HTTPErrorProcessor): |
1511 |
"""Process HTTP error responses.
|
|
1512 |
||
1513 |
We don't really process the errors, quite the contrary
|
|
1514 |
instead, we leave our Transport handle them.
|
|
1515 |
"""
|
|
1516 |
||
2520.2.2
by Vincent Ladeuil
Fix #115209 by issuing a single range request on 400: Bad Request |
1517 |
accepted_errors = [200, # Ok |
1518 |
206, # Partial content |
|
1519 |
404, # Not found |
|
1520 |
]
|
|
1521 |
"""The error codes the caller will handle.
|
|
1522 |
||
1523 |
This can be specialized in the request on a case-by case basis, but the
|
|
1524 |
common cases are covered here.
|
|
1525 |
"""
|
|
1526 |
||
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1527 |
def http_response(self, request, response): |
1528 |
code, msg, hdrs = response.code, response.msg, response.info() |
|
1529 |
||
2520.2.2
by Vincent Ladeuil
Fix #115209 by issuing a single range request on 400: Bad Request |
1530 |
accepted_errors = request.accepted_errors |
1531 |
if accepted_errors is None: |
|
1532 |
accepted_errors = self.accepted_errors |
|
1533 |
||
1534 |
if code not in accepted_errors: |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1535 |
response = self.parent.error('http', request, response, |
1536 |
code, msg, hdrs) |
|
1537 |
return response |
|
1538 |
||
1539 |
https_response = http_response |
|
1540 |
||
1541 |
||
1542 |
class HTTPDefaultErrorHandler(urllib2.HTTPDefaultErrorHandler): |
|
1543 |
"""Translate common errors into bzr Exceptions"""
|
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
1544 |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1545 |
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 |
1546 |
if code == 403: |
3430.3.1
by Vincent Ladeuil
Fix #230223 by making both http implementations raise appropriate exceptions. |
1547 |
raise errors.TransportError( |
1548 |
'Server refuses to fulfill the request (403 Forbidden)'
|
|
1549 |
' for %s' % req.get_full_url()) |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1550 |
else: |
2004.1.27
by v.ladeuil+lp at free
Fix bug #57644 by issuing an explicit error message. |
1551 |
raise errors.InvalidHttpResponse(req.get_full_url(), |
1552 |
'Unable to handle http code %d: %s' |
|
1553 |
% (code, msg)) |
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
1554 |
|
2520.2.2
by Vincent Ladeuil
Fix #115209 by issuing a single range request on 400: Bad Request |
1555 |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1556 |
class Opener(object): |
1557 |
"""A wrapper around urllib2.build_opener
|
|
1558 |
||
1559 |
Daughter classes can override to build their own specific opener
|
|
1560 |
"""
|
|
2145.1.1
by mbp at sourcefrog
merge urllib keepalive etc |
1561 |
# TODO: Provides hooks for daughter classes.
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1562 |
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
1563 |
def __init__(self, |
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1564 |
connection=ConnectionHandler, |
1565 |
redirect=HTTPRedirectHandler, |
|
3945.1.5
by Vincent Ladeuil
Start implementing http activity reporting at socket level. |
1566 |
error=HTTPErrorProcessor, |
1567 |
report_activity=None): |
|
1568 |
self._opener = urllib2.build_opener( |
|
1569 |
connection(report_activity=report_activity), |
|
1570 |
redirect, error, |
|
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1571 |
ProxyHandler(), |
1572 |
HTTPBasicAuthHandler(), |
|
1573 |
HTTPDigestAuthHandler(), |
|
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1574 |
HTTPNegotiateAuthHandler(), |
2900.2.16
by Vincent Ladeuil
Make hhtp proxy aware of AuthenticationConfig (for password). |
1575 |
ProxyBasicAuthHandler(), |
1576 |
ProxyDigestAuthHandler(), |
|
4011.3.1
by Jelmer Vernooij
Add simple support for GSSAPI authentication over HTTP. |
1577 |
ProxyNegotiateAuthHandler(), |
2004.1.2
by vila
Implements a BasicAuthManager. |
1578 |
HTTPHandler, |
1579 |
HTTPSHandler, |
|
1580 |
HTTPDefaultErrorHandler, |
|
2004.2.1
by John Arbash Meinel
Cleanup of urllib functions |
1581 |
)
|
2520.2.2
by Vincent Ladeuil
Fix #115209 by issuing a single range request on 400: Bad Request |
1582 |
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1583 |
self.open = self._opener.open |
3111.1.20
by Vincent Ladeuil
Make all the test pass. Looks like we are HTTP/1.1 compliant. |
1584 |
if DEBUG >= 9: |
2004.1.9
by vila
Takes jam's remarks into account when possible, add TODOs for the rest. |
1585 |
# When dealing with handler order, it's easy to mess
|
1586 |
# things up, the following will help understand which
|
|
1587 |
# handler is used, when and for what.
|
|
2004.1.1
by vila
Connection sharing, with redirection. without authentification. |
1588 |
import pprint |
1589 |
pprint.pprint(self._opener.__dict__) |