Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/url.py @ 14244:e7525a555a64
url: use new http support if requested by the user
The new http library is wired in via an extra module
(httpconnection.py), as it requires similar but different plumbing to
connect the library to Mercurial's internals and urllib2. Eventualy we
should be able to remove all of keepalive.py and its associated tangle
in url.py and replace it all with the code in httpconnection.py.
To use the new library, set 'ui.usehttp2' to true. The underlying http
library uses the logging module liberally, so if things break you can
use 'ui.http2debuglevel' to set the log level to INFO or DEBUG to get
that logging information (for example, ui.http2debuglevel=info.)
author | Augie Fackler <durin42@gmail.com> |
---|---|
date | Fri, 06 May 2011 10:22:08 -0500 |
parents | 5fa21960b2f4 |
children | 376c32a5ccdc 4a43e23b8c55 |
comparison
equal
deleted
inserted
replaced
14243:861f28212398 | 14244:e7525a555a64 |
---|---|
6 # | 6 # |
7 # This software may be used and distributed according to the terms of the | 7 # This software may be used and distributed according to the terms of the |
8 # GNU General Public License version 2 or any later version. | 8 # GNU General Public License version 2 or any later version. |
9 | 9 |
10 import urllib, urllib2, httplib, os, socket, cStringIO | 10 import urllib, urllib2, httplib, os, socket, cStringIO |
11 import __builtin__ | |
12 from i18n import _ | 11 from i18n import _ |
13 import keepalive, util, sslutil | 12 import keepalive, util, sslutil |
14 | 13 import httpconnection as httpconnectionmod |
15 def readauthforuri(ui, uri): | |
16 # Read configuration | |
17 config = dict() | |
18 for key, val in ui.configitems('auth'): | |
19 if '.' not in key: | |
20 ui.warn(_("ignoring invalid [auth] key '%s'\n") % key) | |
21 continue | |
22 group, setting = key.rsplit('.', 1) | |
23 gdict = config.setdefault(group, dict()) | |
24 if setting in ('username', 'cert', 'key'): | |
25 val = util.expandpath(val) | |
26 gdict[setting] = val | |
27 | |
28 # Find the best match | |
29 scheme, hostpath = uri.split('://', 1) | |
30 bestlen = 0 | |
31 bestauth = None | |
32 for group, auth in config.iteritems(): | |
33 prefix = auth.get('prefix') | |
34 if not prefix: | |
35 continue | |
36 p = prefix.split('://', 1) | |
37 if len(p) > 1: | |
38 schemes, prefix = [p[0]], p[1] | |
39 else: | |
40 schemes = (auth.get('schemes') or 'https').split() | |
41 if (prefix == '*' or hostpath.startswith(prefix)) and \ | |
42 len(prefix) > bestlen and scheme in schemes: | |
43 bestlen = len(prefix) | |
44 bestauth = group, auth | |
45 return bestauth | |
46 | 14 |
47 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm): | 15 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm): |
48 def __init__(self, ui): | 16 def __init__(self, ui): |
49 urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self) | 17 urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self) |
50 self.ui = ui | 18 self.ui = ui |
56 if user and passwd: | 24 if user and passwd: |
57 self._writedebug(user, passwd) | 25 self._writedebug(user, passwd) |
58 return (user, passwd) | 26 return (user, passwd) |
59 | 27 |
60 if not user: | 28 if not user: |
61 res = readauthforuri(self.ui, authuri) | 29 res = httpconnectionmod.readauthforuri(self.ui, authuri) |
62 if res: | 30 if res: |
63 group, auth = res | 31 group, auth = res |
64 user, passwd = auth.get('username'), auth.get('password') | 32 user, passwd = auth.get('username'), auth.get('password') |
65 self.ui.debug("using auth.%s.* for authentication\n" % group) | 33 self.ui.debug("using auth.%s.* for authentication\n" % group) |
66 if not user or not passwd: | 34 if not user or not passwd: |
147 return baseclass.add_header(self, key, val) | 115 return baseclass.add_header(self, key, val) |
148 req.__class__ = _request | 116 req.__class__ = _request |
149 | 117 |
150 return urllib2.ProxyHandler.proxy_open(self, req, proxy, type_) | 118 return urllib2.ProxyHandler.proxy_open(self, req, proxy, type_) |
151 | 119 |
152 class httpsendfile(object): | |
153 """This is a wrapper around the objects returned by python's "open". | |
154 | |
155 Its purpose is to send file-like objects via HTTP and, to do so, it | |
156 defines a __len__ attribute to feed the Content-Length header. | |
157 """ | |
158 | |
159 def __init__(self, ui, *args, **kwargs): | |
160 # We can't just "self._data = open(*args, **kwargs)" here because there | |
161 # is an "open" function defined in this module that shadows the global | |
162 # one | |
163 self.ui = ui | |
164 self._data = __builtin__.open(*args, **kwargs) | |
165 self.seek = self._data.seek | |
166 self.close = self._data.close | |
167 self.write = self._data.write | |
168 self._len = os.fstat(self._data.fileno()).st_size | |
169 self._pos = 0 | |
170 self._total = len(self) / 1024 * 2 | |
171 | |
172 def read(self, *args, **kwargs): | |
173 try: | |
174 ret = self._data.read(*args, **kwargs) | |
175 except EOFError: | |
176 self.ui.progress(_('sending'), None) | |
177 self._pos += len(ret) | |
178 # We pass double the max for total because we currently have | |
179 # to send the bundle twice in the case of a server that | |
180 # requires authentication. Since we can't know until we try | |
181 # once whether authentication will be required, just lie to | |
182 # the user and maybe the push succeeds suddenly at 50%. | |
183 self.ui.progress(_('sending'), self._pos / 1024, | |
184 unit=_('kb'), total=self._total) | |
185 return ret | |
186 | |
187 def __len__(self): | |
188 return self._len | |
189 | |
190 def _gen_sendfile(orgsend): | 120 def _gen_sendfile(orgsend): |
191 def _sendfile(self, data): | 121 def _sendfile(self, data): |
192 # send a file | 122 # send a file |
193 if isinstance(data, httpsendfile): | 123 if isinstance(data, httpconnectionmod.httpsendfile): |
194 # if auth required, some data sent twice, so rewind here | 124 # if auth required, some data sent twice, so rewind here |
195 data.seek(0) | 125 data.seek(0) |
196 for chunk in util.filechunkiter(data): | 126 for chunk in util.filechunkiter(data): |
197 orgsend(self, chunk) | 127 orgsend(self, chunk) |
198 else: | 128 else: |
410 def _start_transaction(self, h, req): | 340 def _start_transaction(self, h, req): |
411 _generic_start_transaction(self, h, req) | 341 _generic_start_transaction(self, h, req) |
412 return keepalive.KeepAliveHandler._start_transaction(self, h, req) | 342 return keepalive.KeepAliveHandler._start_transaction(self, h, req) |
413 | 343 |
414 def https_open(self, req): | 344 def https_open(self, req): |
415 res = readauthforuri(self.ui, req.get_full_url()) | 345 res = httpconnectionmod.readauthforuri(self.ui, req.get_full_url()) |
416 if res: | 346 if res: |
417 group, auth = res | 347 group, auth = res |
418 self.auth = auth | 348 self.auth = auth |
419 self.ui.debug("using auth.%s.* for authentication\n" % group) | 349 self.ui.debug("using auth.%s.* for authentication\n" % group) |
420 else: | 350 else: |
493 def opener(ui, authinfo=None): | 423 def opener(ui, authinfo=None): |
494 ''' | 424 ''' |
495 construct an opener suitable for urllib2 | 425 construct an opener suitable for urllib2 |
496 authinfo will be added to the password manager | 426 authinfo will be added to the password manager |
497 ''' | 427 ''' |
498 handlers = [httphandler()] | 428 if ui.configbool('ui', 'usehttp2', False): |
499 if has_https: | 429 handlers = [httpconnectionmod.http2handler(ui, passwordmgr(ui))] |
500 handlers.append(httpshandler(ui)) | 430 else: |
431 handlers = [httphandler()] | |
432 if has_https: | |
433 handlers.append(httpshandler(ui)) | |
501 | 434 |
502 handlers.append(proxyhandler(ui)) | 435 handlers.append(proxyhandler(ui)) |
503 | 436 |
504 passmgr = passwordmgr(ui) | 437 passmgr = passwordmgr(ui) |
505 if authinfo is not None: | 438 if authinfo is not None: |