Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/websdk/mercurial/url.py
diff options
context:
space:
mode:
Diffstat (limited to 'websdk/mercurial/url.py')
-rw-r--r--[l---------]websdk/mercurial/url.py474
1 files changed, 473 insertions, 1 deletions
diff --git a/websdk/mercurial/url.py b/websdk/mercurial/url.py
index e2784a9..2430d3a 120000..100644
--- a/websdk/mercurial/url.py
+++ b/websdk/mercurial/url.py
@@ -1 +1,473 @@
-/usr/share/pyshared/mercurial/url.py \ No newline at end of file
+# url.py - HTTP handling for mercurial
+#
+# Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
+# Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
+# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+import urllib, urllib2, httplib, os, socket, cStringIO
+from i18n import _
+import keepalive, util, sslutil
+import httpconnection as httpconnectionmod
+
+class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
+ def __init__(self, ui):
+ urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
+ self.ui = ui
+
+ def find_user_password(self, realm, authuri):
+ authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
+ self, realm, authuri)
+ user, passwd = authinfo
+ if user and passwd:
+ self._writedebug(user, passwd)
+ return (user, passwd)
+
+ if not user or not passwd:
+ res = httpconnectionmod.readauthforuri(self.ui, authuri, user)
+ if res:
+ group, auth = res
+ user, passwd = auth.get('username'), auth.get('password')
+ self.ui.debug("using auth.%s.* for authentication\n" % group)
+ if not user or not passwd:
+ if not self.ui.interactive():
+ raise util.Abort(_('http authorization required'))
+
+ self.ui.write(_("http authorization required\n"))
+ self.ui.write(_("realm: %s\n") % realm)
+ if user:
+ self.ui.write(_("user: %s\n") % user)
+ else:
+ user = self.ui.prompt(_("user:"), default=None)
+
+ if not passwd:
+ passwd = self.ui.getpass()
+
+ self.add_password(realm, authuri, user, passwd)
+ self._writedebug(user, passwd)
+ return (user, passwd)
+
+ def _writedebug(self, user, passwd):
+ msg = _('http auth: user %s, password %s\n')
+ self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set'))
+
+ def find_stored_password(self, authuri):
+ return urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
+ self, None, authuri)
+
+class proxyhandler(urllib2.ProxyHandler):
+ def __init__(self, ui):
+ proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
+ # XXX proxyauthinfo = None
+
+ if proxyurl:
+ # proxy can be proper url or host[:port]
+ if not (proxyurl.startswith('http:') or
+ proxyurl.startswith('https:')):
+ proxyurl = 'http://' + proxyurl + '/'
+ proxy = util.url(proxyurl)
+ if not proxy.user:
+ proxy.user = ui.config("http_proxy", "user")
+ proxy.passwd = ui.config("http_proxy", "passwd")
+
+ # see if we should use a proxy for this url
+ no_list = ["localhost", "127.0.0.1"]
+ no_list.extend([p.lower() for
+ p in ui.configlist("http_proxy", "no")])
+ no_list.extend([p.strip().lower() for
+ p in os.getenv("no_proxy", '').split(',')
+ if p.strip()])
+ # "http_proxy.always" config is for running tests on localhost
+ if ui.configbool("http_proxy", "always"):
+ self.no_list = []
+ else:
+ self.no_list = no_list
+
+ proxyurl = str(proxy)
+ proxies = {'http': proxyurl, 'https': proxyurl}
+ ui.debug('proxying through http://%s:%s\n' %
+ (proxy.host, proxy.port))
+ else:
+ proxies = {}
+
+ # urllib2 takes proxy values from the environment and those
+ # will take precedence if found. So, if there's a config entry
+ # defining a proxy, drop the environment ones
+ if ui.config("http_proxy", "host"):
+ for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
+ try:
+ if env in os.environ:
+ del os.environ[env]
+ except OSError:
+ pass
+
+ urllib2.ProxyHandler.__init__(self, proxies)
+ self.ui = ui
+
+ def proxy_open(self, req, proxy, type_):
+ host = req.get_host().split(':')[0]
+ if host in self.no_list:
+ return None
+
+ # work around a bug in Python < 2.4.2
+ # (it leaves a "\n" at the end of Proxy-authorization headers)
+ baseclass = req.__class__
+ class _request(baseclass):
+ def add_header(self, key, val):
+ if key.lower() == 'proxy-authorization':
+ val = val.strip()
+ return baseclass.add_header(self, key, val)
+ req.__class__ = _request
+
+ return urllib2.ProxyHandler.proxy_open(self, req, proxy, type_)
+
+def _gen_sendfile(orgsend):
+ def _sendfile(self, data):
+ # send a file
+ if isinstance(data, httpconnectionmod.httpsendfile):
+ # if auth required, some data sent twice, so rewind here
+ data.seek(0)
+ for chunk in util.filechunkiter(data):
+ orgsend(self, chunk)
+ else:
+ orgsend(self, data)
+ return _sendfile
+
+has_https = util.safehasattr(urllib2, 'HTTPSHandler')
+if has_https:
+ try:
+ _create_connection = socket.create_connection
+ except AttributeError:
+ _GLOBAL_DEFAULT_TIMEOUT = object()
+
+ def _create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
+ source_address=None):
+ # lifted from Python 2.6
+
+ msg = "getaddrinfo returns an empty list"
+ host, port = address
+ for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
+ af, socktype, proto, canonname, sa = res
+ sock = None
+ try:
+ sock = socket.socket(af, socktype, proto)
+ if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
+ sock.settimeout(timeout)
+ if source_address:
+ sock.bind(source_address)
+ sock.connect(sa)
+ return sock
+
+ except socket.error, msg:
+ if sock is not None:
+ sock.close()
+
+ raise socket.error, msg
+
+class httpconnection(keepalive.HTTPConnection):
+ # must be able to send big bundle as stream.
+ send = _gen_sendfile(keepalive.HTTPConnection.send)
+
+ def connect(self):
+ if has_https and self.realhostport: # use CONNECT proxy
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.sock.connect((self.host, self.port))
+ if _generic_proxytunnel(self):
+ # we do not support client x509 certificates
+ self.sock = sslutil.ssl_wrap_socket(self.sock, None, None)
+ else:
+ keepalive.HTTPConnection.connect(self)
+
+ def getresponse(self):
+ proxyres = getattr(self, 'proxyres', None)
+ if proxyres:
+ if proxyres.will_close:
+ self.close()
+ self.proxyres = None
+ return proxyres
+ return keepalive.HTTPConnection.getresponse(self)
+
+# general transaction handler to support different ways to handle
+# HTTPS proxying before and after Python 2.6.3.
+def _generic_start_transaction(handler, h, req):
+ tunnel_host = getattr(req, '_tunnel_host', None)
+ if tunnel_host:
+ if tunnel_host[:7] not in ['http://', 'https:/']:
+ tunnel_host = 'https://' + tunnel_host
+ new_tunnel = True
+ else:
+ tunnel_host = req.get_selector()
+ new_tunnel = False
+
+ if new_tunnel or tunnel_host == req.get_full_url(): # has proxy
+ u = util.url(tunnel_host)
+ if new_tunnel or u.scheme == 'https': # only use CONNECT for HTTPS
+ h.realhostport = ':'.join([u.host, (u.port or '443')])
+ h.headers = req.headers.copy()
+ h.headers.update(handler.parent.addheaders)
+ return
+
+ h.realhostport = None
+ h.headers = None
+
+def _generic_proxytunnel(self):
+ proxyheaders = dict(
+ [(x, self.headers[x]) for x in self.headers
+ if x.lower().startswith('proxy-')])
+ self._set_hostport(self.host, self.port)
+ self.send('CONNECT %s HTTP/1.0\r\n' % self.realhostport)
+ for header in proxyheaders.iteritems():
+ self.send('%s: %s\r\n' % header)
+ self.send('\r\n')
+
+ # majority of the following code is duplicated from
+ # httplib.HTTPConnection as there are no adequate places to
+ # override functions to provide the needed functionality
+ res = self.response_class(self.sock,
+ strict=self.strict,
+ method=self._method)
+
+ while True:
+ version, status, reason = res._read_status()
+ if status != httplib.CONTINUE:
+ break
+ while True:
+ skip = res.fp.readline().strip()
+ if not skip:
+ break
+ res.status = status
+ res.reason = reason.strip()
+
+ if res.status == 200:
+ while True:
+ line = res.fp.readline()
+ if line == '\r\n':
+ break
+ return True
+
+ if version == 'HTTP/1.0':
+ res.version = 10
+ elif version.startswith('HTTP/1.'):
+ res.version = 11
+ elif version == 'HTTP/0.9':
+ res.version = 9
+ else:
+ raise httplib.UnknownProtocol(version)
+
+ if res.version == 9:
+ res.length = None
+ res.chunked = 0
+ res.will_close = 1
+ res.msg = httplib.HTTPMessage(cStringIO.StringIO())
+ return False
+
+ res.msg = httplib.HTTPMessage(res.fp)
+ res.msg.fp = None
+
+ # are we using the chunked-style of transfer encoding?
+ trenc = res.msg.getheader('transfer-encoding')
+ if trenc and trenc.lower() == "chunked":
+ res.chunked = 1
+ res.chunk_left = None
+ else:
+ res.chunked = 0
+
+ # will the connection close at the end of the response?
+ res.will_close = res._check_close()
+
+ # do we have a Content-Length?
+ # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked"
+ length = res.msg.getheader('content-length')
+ if length and not res.chunked:
+ try:
+ res.length = int(length)
+ except ValueError:
+ res.length = None
+ else:
+ if res.length < 0: # ignore nonsensical negative lengths
+ res.length = None
+ else:
+ res.length = None
+
+ # does the body have a fixed length? (of zero)
+ if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or
+ 100 <= status < 200 or # 1xx codes
+ res._method == 'HEAD'):
+ res.length = 0
+
+ # if the connection remains open, and we aren't using chunked, and
+ # a content-length was not provided, then assume that the connection
+ # WILL close.
+ if (not res.will_close and
+ not res.chunked and
+ res.length is None):
+ res.will_close = 1
+
+ self.proxyres = res
+
+ return False
+
+class httphandler(keepalive.HTTPHandler):
+ def http_open(self, req):
+ return self.do_open(httpconnection, req)
+
+ def _start_transaction(self, h, req):
+ _generic_start_transaction(self, h, req)
+ return keepalive.HTTPHandler._start_transaction(self, h, req)
+
+if has_https:
+ class httpsconnection(httplib.HTTPSConnection):
+ response_class = keepalive.HTTPResponse
+ # must be able to send big bundle as stream.
+ send = _gen_sendfile(keepalive.safesend)
+ getresponse = keepalive.wrapgetresponse(httplib.HTTPSConnection)
+
+ def connect(self):
+ self.sock = _create_connection((self.host, self.port))
+
+ host = self.host
+ if self.realhostport: # use CONNECT proxy
+ _generic_proxytunnel(self)
+ host = self.realhostport.rsplit(':', 1)[0]
+ self.sock = sslutil.ssl_wrap_socket(
+ self.sock, self.key_file, self.cert_file,
+ **sslutil.sslkwargs(self.ui, host))
+ sslutil.validator(self.ui, host)(self.sock)
+
+ class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler):
+ def __init__(self, ui):
+ keepalive.KeepAliveHandler.__init__(self)
+ urllib2.HTTPSHandler.__init__(self)
+ self.ui = ui
+ self.pwmgr = passwordmgr(self.ui)
+
+ def _start_transaction(self, h, req):
+ _generic_start_transaction(self, h, req)
+ return keepalive.KeepAliveHandler._start_transaction(self, h, req)
+
+ def https_open(self, req):
+ # req.get_full_url() does not contain credentials and we may
+ # need them to match the certificates.
+ url = req.get_full_url()
+ user, password = self.pwmgr.find_stored_password(url)
+ res = httpconnectionmod.readauthforuri(self.ui, url, user)
+ if res:
+ group, auth = res
+ self.auth = auth
+ self.ui.debug("using auth.%s.* for authentication\n" % group)
+ else:
+ self.auth = None
+ return self.do_open(self._makeconnection, req)
+
+ def _makeconnection(self, host, port=None, *args, **kwargs):
+ keyfile = None
+ certfile = None
+
+ if len(args) >= 1: # key_file
+ keyfile = args[0]
+ if len(args) >= 2: # cert_file
+ certfile = args[1]
+ args = args[2:]
+
+ # if the user has specified different key/cert files in
+ # hgrc, we prefer these
+ if self.auth and 'key' in self.auth and 'cert' in self.auth:
+ keyfile = self.auth['key']
+ certfile = self.auth['cert']
+
+ conn = httpsconnection(host, port, keyfile, certfile, *args, **kwargs)
+ conn.ui = self.ui
+ return conn
+
+class httpdigestauthhandler(urllib2.HTTPDigestAuthHandler):
+ def __init__(self, *args, **kwargs):
+ urllib2.HTTPDigestAuthHandler.__init__(self, *args, **kwargs)
+ self.retried_req = None
+
+ def reset_retry_count(self):
+ # Python 2.6.5 will call this on 401 or 407 errors and thus loop
+ # forever. We disable reset_retry_count completely and reset in
+ # http_error_auth_reqed instead.
+ pass
+
+ def http_error_auth_reqed(self, auth_header, host, req, headers):
+ # Reset the retry counter once for each request.
+ if req is not self.retried_req:
+ self.retried_req = req
+ self.retried = 0
+ # In python < 2.5 AbstractDigestAuthHandler raises a ValueError if
+ # it doesn't know about the auth type requested. This can happen if
+ # somebody is using BasicAuth and types a bad password.
+ try:
+ return urllib2.HTTPDigestAuthHandler.http_error_auth_reqed(
+ self, auth_header, host, req, headers)
+ except ValueError, inst:
+ arg = inst.args[0]
+ if arg.startswith("AbstractDigestAuthHandler doesn't know "):
+ return
+ raise
+
+class httpbasicauthhandler(urllib2.HTTPBasicAuthHandler):
+ def __init__(self, *args, **kwargs):
+ urllib2.HTTPBasicAuthHandler.__init__(self, *args, **kwargs)
+ self.retried_req = None
+
+ def reset_retry_count(self):
+ # Python 2.6.5 will call this on 401 or 407 errors and thus loop
+ # forever. We disable reset_retry_count completely and reset in
+ # http_error_auth_reqed instead.
+ pass
+
+ def http_error_auth_reqed(self, auth_header, host, req, headers):
+ # Reset the retry counter once for each request.
+ if req is not self.retried_req:
+ self.retried_req = req
+ self.retried = 0
+ return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed(
+ self, auth_header, host, req, headers)
+
+handlerfuncs = []
+
+def opener(ui, authinfo=None):
+ '''
+ construct an opener suitable for urllib2
+ authinfo will be added to the password manager
+ '''
+ if ui.configbool('ui', 'usehttp2', False):
+ handlers = [httpconnectionmod.http2handler(ui, passwordmgr(ui))]
+ else:
+ handlers = [httphandler()]
+ if has_https:
+ handlers.append(httpshandler(ui))
+
+ handlers.append(proxyhandler(ui))
+
+ passmgr = passwordmgr(ui)
+ if authinfo is not None:
+ passmgr.add_password(*authinfo)
+ user, passwd = authinfo[2:4]
+ ui.debug('http auth: user %s, password %s\n' %
+ (user, passwd and '*' * len(passwd) or 'not set'))
+
+ handlers.extend((httpbasicauthhandler(passmgr),
+ httpdigestauthhandler(passmgr)))
+ handlers.extend([h(ui, passmgr) for h in handlerfuncs])
+ opener = urllib2.build_opener(*handlers)
+
+ # 1.0 here is the _protocol_ version
+ opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
+ opener.addheaders.append(('Accept', 'application/mercurial-0.1'))
+ return opener
+
+def open(ui, url_, data=None):
+ u = util.url(url_)
+ if u.scheme:
+ u.scheme = u.scheme.lower()
+ url_, authinfo = u.authinfo()
+ else:
+ path = util.normpath(os.path.abspath(url_))
+ url_ = 'file://' + urllib.pathname2url(path)
+ authinfo = None
+ return opener(ui, authinfo).open(url_, data)