# HG changeset patch # User Gregory Szorc # Date 2018-04-24 20:55:25 # Node ID 877185de62cf6933142118c87eb702a77f0896fb # Parent 678ab0de7296c17317ceb6a3c34066e70ef41ee6 hgweb: reuse body file object when hgwebdir calls hgweb (issue5851) An unintended side-effect of f0a851542a05 was that the request body file object (which uses a util.cappedreader) was constructed twice when hgwebdir called into hgweb. Since we attempt to read all remaining data from this file object when Content-Length is defined and since there were two instances of this object and the client supplied no additional data to read, this resulted in deadlock. The fix implemented in this commit is to reuse the request body file object when it is passed from hgwebdir to hgweb. A test demonstrating `hg clone` and `hg push` via hgwebdir has been added. Without this patch, the test hangs when doing `hg clone`. Surprisingly, this must mean that we have effectively no test coverage of the wire protocol when run via hgwebdir. Differential Revision: https://phab.mercurial-scm.org/D3427 diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py --- a/mercurial/hgweb/hgwebdir_mod.py +++ b/mercurial/hgweb/hgwebdir_mod.py @@ -428,7 +428,10 @@ class hgwebdir(object): uenv.iteritems()} req = requestmod.parserequestfromenv( uenv, reponame=virtualrepo, - altbaseurl=self.ui.config('web', 'baseurl')) + altbaseurl=self.ui.config('web', 'baseurl'), + # Reuse wrapped body file object otherwise state + # tracking can get confused. + bodyfh=req.bodyfh) try: # ensure caller gets private copy of ui repo = hg.repository(self.ui.copy(), real) diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py --- a/mercurial/hgweb/request.py +++ b/mercurial/hgweb/request.py @@ -124,7 +124,7 @@ class parsedrequest(object): # WSGI environment dict, unmodified. rawenv = attr.ib() -def parserequestfromenv(env, reponame=None, altbaseurl=None): +def parserequestfromenv(env, reponame=None, altbaseurl=None, bodyfh=None): """Parse URL components from environment variables. WSGI defines request attributes via environment variables. This function @@ -144,6 +144,9 @@ def parserequestfromenv(env, reponame=No if the request were to ``http://myserver:9000/prefix/rev/@``. In other words, ``wsgi.url_scheme``, ``SERVER_NAME``, ``SERVER_PORT``, and ``SCRIPT_NAME`` are all effectively replaced by components from this URL. + + ``bodyfh`` can be used to specify a file object to read the request body + from. If not defined, ``wsgi.input`` from the environment dict is used. """ # PEP 3333 defines the WSGI spec and is a useful reference for this code. @@ -307,9 +310,10 @@ def parserequestfromenv(env, reponame=No if 'CONTENT_TYPE' in env and 'HTTP_CONTENT_TYPE' not in env: headers['Content-Type'] = env['CONTENT_TYPE'] - bodyfh = env['wsgi.input'] - if 'Content-Length' in headers: - bodyfh = util.cappedreader(bodyfh, int(headers['Content-Length'])) + if bodyfh is None: + bodyfh = env['wsgi.input'] + if 'Content-Length' in headers: + bodyfh = util.cappedreader(bodyfh, int(headers['Content-Length'])) return parsedrequest(method=env['REQUEST_METHOD'], url=fullurl, baseurl=baseurl, diff --git a/tests/test-push-http.t b/tests/test-push-http.t --- a/tests/test-push-http.t +++ b/tests/test-push-http.t @@ -380,3 +380,47 @@ Make phases updates work #endif $ cd .. + +Pushing via hgwebdir works + + $ hg init hgwebdir + $ cd hgwebdir + $ echo 0 > a + $ hg -q commit -A -m initial + $ cd .. + + $ cat > web.conf << EOF + > [paths] + > / = * + > [web] + > push_ssl = false + > allow_push = * + > EOF + + $ hg serve --web-conf web.conf -p $HGPORT -d --pid-file hg.pid + $ cat hg.pid > $DAEMON_PIDS + + $ hg clone http://localhost:$HGPORT/hgwebdir hgwebdir-local + requesting all changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + new changesets 98a3f8f02ba7 + updating to branch default + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd hgwebdir-local + $ echo commit > a + $ hg commit -m 'local commit' + + $ hg push + pushing to http://localhost:$HGPORT/hgwebdir + searching for changes + remote: adding changesets + remote: adding manifests + remote: adding file changes + remote: added 1 changesets with 1 changes to 1 files + + $ killdaemons.py + + $ cd ..