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