Bug 2739 - Error when cloning with http auth and Python <= 2.4.2
Summary: Error when cloning with http auth and Python <= 2.4.2
Status: RESOLVED WONTFIX
Alias: None
Product: Mercurial
Classification: Unclassified
Component: Mercurial (show other bugs)
Version: unspecified
Hardware: All All
: normal bug
Assignee: Thomas Arendsen Hein
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-03-29 08:37 UTC by John Ament
Modified: 2013-07-26 16:45 UTC (History)
6 users (show)

See Also:
Python Version: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description John Ament 2011-03-29 08:37 UTC
The following error occurs when I attempt to clone over http.
The client machine is a SLES 10 64bit VM.
The server is a SLES 10 64 bit physical machine.

hg clone http://twebapp2:8110/hg/src/HG/Cardbalance/
** unknown exception encountered, please report by visiting
**  http://mercurial.selenic.com/wiki/BugTracker
** Python 2.4.2 (#1, Dec  2 2008, 00:09:07) [GCC 4.1.2 20070115 (SUSE Linux)]
** Mercurial Distributed SCM (version 1.8.1)
** Extensions loaded: 
Traceback (most recent call last):
  File "/usr/bin/hg", line 38, in ?
    mercurial.dispatch.run()
  File "/usr/lib64/python2.4/site-packages/mercurial/dispatch.py", line 16,
in run
    sys.exit(dispatch(sys.argv[1:]))
  File "/usr/lib64/python2.4/site-packages/mercurial/dispatch.py", line 36,
in dispatch
    return _runcatch(u, args)
  File "/usr/lib64/python2.4/site-packages/mercurial/dispatch.py", line 58,
in _runcatch
    return _dispatch(ui, args)
  File "/usr/lib64/python2.4/site-packages/mercurial/dispatch.py", line 601,
in _dispatch
    cmdpats, cmdoptions)
  File "/usr/lib64/python2.4/site-packages/mercurial/dispatch.py", line 406,
in runcommand
    ret = _runcommand(ui, options, cmd, d)
  File "/usr/lib64/python2.4/site-packages/mercurial/dispatch.py", line 655,
in _runcommand
    return checkargs()
  File "/usr/lib64/python2.4/site-packages/mercurial/dispatch.py", line 609,
in checkargs
    return cmdfunc()
  File "/usr/lib64/python2.4/site-packages/mercurial/dispatch.py", line 598,
in <lambda>
    d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
  File "/usr/lib64/python2.4/site-packages/mercurial/util.py", line 433, in
check
    return func(*args, **kwargs)
  File "/usr/lib64/python2.4/site-packages/mercurial/commands.py", line 825,
in clone
    branch=opts.get('branch'))
  File "/usr/lib64/python2.4/site-packages/mercurial/hg.py", line 221, in clone
    src_repo = repository(ui, source)
  File "/usr/lib64/python2.4/site-packages/mercurial/hg.py", line 94, in
repository
    repo = _lookup(path).instance(ui, path, create)
  File "/usr/lib64/python2.4/site-packages/mercurial/httprepo.py", line 199,
in instance
    inst.between([(nullid, nullid)])
  File "/usr/lib64/python2.4/site-packages/mercurial/wireproto.py", line 68,
in between
    d = self._call("between", pairs=n)
  File "/usr/lib64/python2.4/site-packages/mercurial/httprepo.py", line 137,
in _call
    fp = self._callstream(cmd, **args)
  File "/usr/lib64/python2.4/site-packages/mercurial/httprepo.py", line 88,
in _callstream
    resp = self.urlopener.open(req)
  File "/usr/lib64/python2.4/urllib2.py", line 364, in open
    response = meth(req, response)
  File "/usr/lib64/python2.4/urllib2.py", line 471, in http_response
    response = self.parent.error(
  File "/usr/lib64/python2.4/urllib2.py", line 396, in error
    result = self._call_chain(*args)
  File "/usr/lib64/python2.4/urllib2.py", line 337, in _call_chain
    result = func(*args)
  File "/usr/lib64/python2.4/urllib2.py", line 741, in http_error_401
    host, req, headers)
  File "/usr/lib64/python2.4/site-packages/mercurial/url.py", line 691, in
http_error_auth_reqed
    return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed(
  File "/usr/lib64/python2.4/urllib2.py", line 720, in http_error_auth_reqed
    return self.retry_http_basic_auth(host, req, realm)
  File "/usr/lib64/python2.4/urllib2.py", line 723, in retry_http_basic_auth
    user,pw = self.passwd.find_user_password(realm, host)
  File "/usr/lib64/python2.4/site-packages/mercurial/url.py", line 158, in
find_user_password
    res = readauthforuri(self.ui, authuri)
  File "/usr/lib64/python2.4/site-packages/mercurial/url.py", line 88, in
readauthforuri
    scheme, hostpath = uri.split('://', 1)
ValueError: need more than 1 value to unpack
Comment 1 kiilerix 2011-03-29 09:50 UTC
Mercurial shouldn't crash, but it crashes because it somehow ended up with
an invalid url when the server requested authentication.

How is your server setup? Are you using simple basic authentication, or do
you have some redirects or rewrites or evil firewalls?

Does it work if you clone directly from the server?
Comment 2 John Ament 2011-03-29 09:57 UTC
I wonder if for some reason this is a python issue, more than a mercurial
issue.  I just noticed on the server i try it on, mercurial 1.8.1 is
installed and python 2.4.  On my laptop, I have mercurial 1.8.1 and python 2.6.

The URL does not do a redirect.  The only redirect I think that might be
possible is appending our internal suffix.  If I clone using SSH and the
file system path, it does work.  The server is a simple hgwebdir
implementation, using basic authentication over LDAP at the apache level. 
There are no redirects at the apache level either.  This is my apache
config, slightly obfuscated:

<Location "/hg">
  AuthType Basic
  AuthName "Mercurial"
  AuthBasicProvider ldap
  AuthzLDAPAuthoritative on
  AuthLDAPURL "my-valid-ldap-url-goes-here"
  Require valid-user

  PythonPath "sys.path + [ '/apps/bin/mercurial' ]"
  #PythonDebug On #Uncomment this ligne if you got a problem and need debug
information
  SetHandler mod_python
  PythonHandler modpython_gateway::handler
  PythonOption SCRIPT_NAME /hg
  PythonOption wsgi.application hgwebdir::test
</Location>

and my python script that serves it

def make_web_app():
            return hgwebdir("/apps/bin/mercurial/hgweb.config")

def test(environ, start_response):
            toto = wsgiapplication(make_web_app)
            return toto (environ, start_response)
Comment 3 kiilerix 2011-03-29 10:17 UTC
Does it work from your laptop with 2.6?

Basic auth works fine for me with 2.4.3 on the client side.

I would try to add a 'print uri' in url.py before/at line 88 and see if that
gave a hint.
Comment 4 John Ament 2011-03-29 11:29 UTC
I did that, I get the hostname:port without protocol, which is likely why
the code is failing.

There's something else weird.  I think in this environment in particular,
this code is doing something different.  I applied the change to my local
environment as well, and it didn't execute that code.

        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:
            res = readauthforuri(self.ui, authuri)
            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:

it skipped by the call for readauthforuri.
Comment 5 Matt Mackall 2011-10-11 22:44 UTC
related: issue2070
Comment 6 Thomas Arendsen Hein 2011-10-16 07:56 UTC
Still occurs with 2.0-rc on python 2.4.1 (Debian sarge) with any host
requiring authentication, with clone, pull or id:

hg id http://foo.example.com/restricted
** unknown exception encountered, please report by visiting
**  http://mercurial.selenic.com/wiki/BugTracker
** Python 2.4.1 (#2, Oct 18 2006, 20:58:01) [GCC 3.3.5 (Debian 1:3.3.5-13)]
** Mercurial Distributed SCM (version 89c80c3dc584)
** Extensions loaded: foo, mq, patchbomb, convert, transplant, fetch, churn,
keyword, nearest, inout, rebase, relink
Traceback (most recent call last):
  File "./hg", line 38, in ?
    mercurial.dispatch.run()
  File "/home/thomas/hg/repos/tah/mercurial/dispatch.py", line 27, in run
    sys.exit(dispatch(request(sys.argv[1:])))
  File "/home/thomas/hg/repos/tah/mercurial/dispatch.py", line 64, in dispatch
    return _runcatch(req)
  File "/home/thomas/hg/repos/tah/mercurial/dispatch.py", line 87, in _runcatch
    return _dispatch(req)
  File "/home/thomas/hg/repos/tah/mercurial/dispatch.py", line 684, in _dispatch
    cmdpats, cmdoptions)
  File "/home/thomas/hg/repos/tah/mercurial/dispatch.py", line 466, in
runcommand
    ret = _runcommand(ui, options, cmd, d)
  File "/home/thomas/hg/repos/tah/mercurial/dispatch.py", line 738, in
_runcommand
    return checkargs()
  File "/home/thomas/hg/repos/tah/mercurial/dispatch.py", line 692, in checkargs
    return cmdfunc()
  File "/home/thomas/hg/repos/tah/mercurial/dispatch.py", line 681, in <lambda>
    d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
  File "/home/thomas/hg/repos/tah/mercurial/util.py", line 444, in check
    return func(*args, **kwargs)
  File "/home/thomas/hg/repos/tah/mercurial/extensions.py", line 138, in wrap
    return util.checksignature(wrapper)(
  File "/home/thomas/hg/repos/tah/mercurial/util.py", line 444, in check
    return func(*args, **kwargs)
  File "/home/thomas/hg/repos/tah/hgext/mq.py", line 3231, in mqcommand
    return orig(ui, repo, *args, **kwargs)
  File "/home/thomas/hg/repos/tah/mercurial/util.py", line 444, in check
    return func(*args, **kwargs)
  File "/home/thomas/hg/repos/tah/mercurial/commands.py", line 3259, in identify
    repo = hg.peer(ui, {}, source)
  File "/home/thomas/hg/repos/tah/mercurial/hg.py", line 104, in peer
    return repository(rui, path, create)
  File "/home/thomas/hg/repos/tah/mercurial/hg.py", line 93, in repository
    repo = _peerlookup(path).instance(ui, path, create)
  File "/home/thomas/hg/repos/tah/mercurial/httprepo.py", line 236, in instance
    inst._fetchcaps()
  File "/home/thomas/hg/repos/tah/mercurial/httprepo.py", line 57, in _fetchcaps
    self.caps = set(self._call('capabilities').split())
  File "/home/thomas/hg/repos/tah/mercurial/httprepo.py", line 169, in _call
    fp = self._callstream(cmd, **args)
  File "/home/thomas/hg/repos/tah/mercurial/httprepo.py", line 117, in
_callstream
    resp = self.urlopener.open(req)
  File "/usr/lib/python2.4/urllib2.py", line 364, in open
    response = meth(req, response)
  File "/usr/lib/python2.4/urllib2.py", line 471, in http_response
    response = self.parent.error(
  File "/usr/lib/python2.4/urllib2.py", line 396, in error
    result = self._call_chain(*args)
  File "/usr/lib/python2.4/urllib2.py", line 337, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.4/urllib2.py", line 741, in http_error_401
    host, req, headers)
  File "/home/thomas/hg/repos/tah/mercurial/url.py", line 430, in
http_error_auth_reqed
    return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed(
  File "/usr/lib/python2.4/urllib2.py", line 720, in http_error_auth_reqed
    return self.retry_http_basic_auth(host, req, realm)
  File "/usr/lib/python2.4/urllib2.py", line 723, in retry_http_basic_auth
    user,pw = self.passwd.find_user_password(realm, host)
  File "/home/thomas/hg/repos/tah/mercurial/url.py", line 30, in
find_user_password
    res = httpconnectionmod.readauthforuri(self.ui, authuri, user)
  File "/home/thomas/hg/repos/tah/mercurial/httpconnection.py", line 73, in
readauthforuri
    scheme, hostpath = uri.split('://', 1)
ValueError: need more than 1 value to unpack

uri in readauthforuri is 'foo.example.com'

The parameters of httpbasicauthhandler.http_error_auth_reqed() are:
'www-authenticate', 'foo.example.com', <mercurial.url._request instance at
0xb7acb66c>, <httplib.HTTPMessage instance at 0xb79f5c4c>
So it already is the hostname instead of the URI at this point.

With Python 2.6 uri is 'http://foo.example.com/restricted?cmd=capabilities'
and I get asked for username/password.

test-http.t.err detects this error since f32a2989ff58.
Comment 7 Matt Mackall 2011-10-16 10:18 UTC
** Python 2.4.1 (#2, Oct 18 2006, 20:58:01) [GCC 3.3.5 (Debian 1:3.3.5-13)]
** Mercurial Distributed SCM (version 89c80c3dc584)

That's hg build is from 2009. 2.0-rc actually doesn't run on Python 2.4 at
all due to a try/except/finally sneaking. Once that compile issue is fixed,
test-http.t passes with Python 2.4.3.
Comment 8 Thomas Arendsen Hein 2011-10-16 13:13 UTC
It was 2.0-rc with the try/except/finally fixed (018608160299) as
mercurial/httpconnection.py does not exist in 89c80c3dc584.
I just forgot another 'make local' after checking that the bug already was
present in 89c80c3dc584 and was just not catched by the tests.
Comment 9 kiilerix 2011-10-16 16:31 UTC
So apparently it fails with both 2.4.1 and 2.4.2 but works with 2.4.3.

Has it worked with these Python versions before? Is it a regression? What
changeset broke it in the first place?
Comment 10 Matt Mackall 2011-10-16 20:18 UTC
It's probably this changeset in 2.4.3:

http://hg.python.org/cpython/rev/ac02f655b31e

Found with:

$ hg log -vpr '::v2.4.3 -::v2.4.1 and keyword(urllib2)'

on the cpython repo.
Comment 11 Thomas Arendsen Hein 2011-10-17 06:52 UTC
Mads, 89c80c3dc584 introduced the uri.split and exposes this problem.
With its parent, 3e544c074459, it works fine.

You can test with the repository http://hg.intevation.org/tmp/login/
(user and password are "test")
Comment 12 kiilerix 2011-10-17 08:27 UTC
From one point of view: It broke 2½ years ago for the oldest and (probably)
least used Python versions. That makes it a regression, but I would say
"cbb". The risk of introducing a fix/workaround is probably higher than the
benefit.

From another point of view: Python acknowledged that it was a Python bug and
they fixed it. We could perhaps work around it, but the right place to fix
it would be Python. Python could be updated to 2.4.3 or the fix could be
backported.
Comment 13 Thomas Arendsen Hein 2011-10-17 08:40 UTC
* Mads Kiilerich <bugs@mercurial.selenic.com> [20111017 16:26]:
> >From one point of view: It broke 2½ years ago for the oldest and (probably)
> least used Python versions. That makes it a regression, but I would say
> "cbb". The risk of introducing a fix/workaround is probably higher than the
> benefit.
> 
> >From another point of view: Python acknowledged that it was a Python bug and
> they fixed it. We could perhaps work around it, but the right place to fix
> it would be Python. Python could be updated to 2.4.3 or the fix could be
> backported.

What about catching the exception and producing a better message
than this long traceback? This should reduce the risk and the
required work/testing.
Comment 14 Matt Mackall 2011-10-17 09:09 UTC
How about something as simple as this:

diff -r ebeac9c41456 mercurial/httpconnection.py
--- a/mercurial/httpconnection.py	Sun Oct 16 22:15:43 2011 -0500
+++ b/mercurial/httpconnection.py	Mon Oct 17 10:07:34 2011 -0500
@@ -70,7 +70,11 @@
         gdict[setting] = val
 
     # Find the best match
-    scheme, hostpath = uri.split('://', 1)
+    if '://' in uri:
+        scheme, hostpath = uri.split('://', 1)
+    else:
+        # py2.4.1 doesn't provide the full URI
+        scheme, hostpath = 'http', uri
     bestuser = None
     bestlen = 0
     bestauth = None
Comment 15 HG Bot 2011-10-17 14:00 UTC
Fixed by http://selenic.com/repo/hg/rev/b3083042bdda
Matt Mackall <mpm@selenic.com>
auth: fix realm handling with Python < 2.4.3 (issue2739)

(please test the fix)
Comment 16 Thomas Arendsen Hein 2011-10-19 08:12 UTC
The path will not be available, so when using the example in the hgrc docs:
  foo.prefix = hg.intevation.org/mercurial
a user of python2.4 can only use
  foo.prefix = hg.intevation.org

But since the alternative is to not being able to access the repo at all the
fix seems good enough. I'll test it.
Comment 17 Thomas Arendsen Hein 2011-10-21 05:11 UTC
test-http.t still fails with 1ae824142c01 on python 2.4.1, but in a less
scary way:

--- /tmp/thomas/tah/tests/test-http.t
+++ /tmp/thomas/tah/tests/test-http.t.err
@@ -136,7 +136,8 @@
   abort: http authorization required
   [255]
   $ hg id http://user:pass@localhost:$HGPORT2/
-  5fed3813f7f5
+  abort: http authorization required
+  [255]
   $ echo '[auth]' >> .hg/hgrc
   $ echo 'l.schemes=http' >> .hg/hgrc
   $ echo 'l.prefix=*' >> .hg/hgrc
@@ -151,11 +152,9 @@
   $ hg id http://user:pass@localhost:$HGPORT2/
   5fed3813f7f5
   $ hg id http://user2@localhost:$HGPORT2/
-  abort: http authorization required
-  [255]
+  5fed3813f7f5
   $ hg id http://user:pass2@localhost:$HGPORT2/
-  abort: HTTP Error 403: no
-  [255]
+  5fed3813f7f5
   
   $ cd ..
Comment 18 Thomas Arendsen Hein 2011-10-24 09:19 UTC
The test fails, because with b3083042bdda the client always sends what is
configured in hgrc, not what is in the requested URL.
Comment 19 Bugzilla 2012-05-12 09:18 UTC

--- Bug imported by bugzilla@serpentine.com 2012-05-12 09:18 EDT  ---

This bug was previously known as _bug_ 2739 at http://mercurial.selenic.com/bts/issue2739
Comment 20 Matt Mackall 2013-07-26 16:45 UTC
Stale, closing.