From patchwork Thu Aug 8 07:48:46 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [3,of,3,RFC] hgweb: handle obsolete changesets gracefully From: Dan Villiom Podlaski Christiansen X-Patchwork-Id: 2094 Message-Id: <5507e2af80f5e8026a05.1375948126@dookie.local> To: Mercurial Devel Date: Thu, 08 Aug 2013 09:48:46 +0200 # HG changeset patch # User Dan Villiom Podlaski Christiansen # Date 1375624663 -7200 # Sun Aug 04 15:57:43 2013 +0200 # Node ID 5507e2af80f5e8026a054ce440c0c8958134eefe # Parent b1f7ae28c3e371a70ee363a4fbe5d50063a224fc hgweb: handle obsolete changesets gracefully If the changeset has any successors, issue a 403 Moved Permanently; otherwise we issue a 410 Gone. Please note that this is slightly misleading for 'secret' changesets, as they may appear later on. diff --git a/mercurial/hgweb/common.py b/mercurial/hgweb/common.py --- a/mercurial/hgweb/common.py +++ b/mercurial/hgweb/common.py @@ -9,12 +9,14 @@ import errno, mimetypes, os HTTP_OK = 200 +HTTP_MOVED_PERMANENTLY = 301 HTTP_NOT_MODIFIED = 304 HTTP_BAD_REQUEST = 400 HTTP_UNAUTHORIZED = 401 HTTP_FORBIDDEN = 403 HTTP_NOT_FOUND = 404 HTTP_METHOD_NOT_ALLOWED = 405 +HTTP_GONE = 410 HTTP_SERVER_ERROR = 500 diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py +++ b/mercurial/hgweb/hgweb_mod.py @@ -8,11 +8,13 @@ import os from mercurial import ui, hg, hook, error, encoding, templater, util, repoview +from mercurial import obsolete +from mercurial.node import short from mercurial.templatefilters import websub from mercurial.i18n import _ from common import get_stat, ErrorResponse, permhooks, caching -from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST -from common import HTTP_NOT_FOUND, HTTP_SERVER_ERROR +from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST, HTTP_GONE +from common import HTTP_NOT_FOUND, HTTP_SERVER_ERROR, HTTP_MOVED_PERMANENTLY from request import wsgirequest import webcommands, protocol, webutil, re @@ -249,6 +251,33 @@ class hgweb(object): return content + except error.FilteredLookupError, err: + succsets = obsolete.successorssets(self.repo, err.rev) + + if not succsets: + req.respond(HTTP_GONE, ctype) + + return tmpl('error', error=err.message) + + elif len(succsets) != 1: + # TODO: changeset has divergent successors + req.respond(HTTP_SERVER_ERROR, ctype) + + return tmpl('error', error=err.message) + + location = [req.url.rstrip('/')] + location += req.form['cmd'] + + location.append(short(succsets[0][-1])) + + if 'file' in req.form: + location += req.form['file'] + + req.headers.extend([('Location', '/'.join(location))]) + req.respond(HTTP_MOVED_PERMANENTLY, ctype) + + return tmpl('error', error=err.message) + except (error.LookupError, error.RepoLookupError), err: req.respond(HTTP_NOT_FOUND, ctype) msg = str(err) diff --git a/mercurial/hgweb/webutil.py b/mercurial/hgweb/webutil.py --- a/mercurial/hgweb/webutil.py +++ b/mercurial/hgweb/webutil.py @@ -201,6 +201,9 @@ def cleanpath(repo, path): def changeidctx (repo, changeid): try: ctx = repo[changeid] + except error.FilteredLookupError: + raise + except error.RepoError: man = repo.manifest ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))] diff --git a/tests/test-hgweb-commands.t b/tests/test-hgweb-commands.t --- a/tests/test-hgweb-commands.t +++ b/tests/test-hgweb-commands.t @@ -1385,7 +1385,7 @@ proper status for filtered revision $ PATH_INFO=/rev/5; export PATH_INFO $ QUERY_STRING='style=raw' $ python hgweb.cgi #> search - Status: 404 Not Found\r (esc) + Status: 410 Gone\r (esc) ETag: *\r (glob) (esc) Content-Type: text/plain; charset=ascii\r (esc) \r (esc) @@ -1399,7 +1399,7 @@ proper status for filtered revision $ PATH_INFO=/rev/4; export PATH_INFO $ QUERY_STRING='style=raw' $ python hgweb.cgi #> search - Status: 404 Not Found\r (esc) + Status: 410 Gone\r (esc) ETag: *\r (glob) (esc) Content-Type: text/plain; charset=ascii\r (esc) \r (esc) @@ -1498,6 +1498,47 @@ filtered '0' changeset +Test obsolete redirection + + $ cat > ../obs.py << EOF + > import mercurial.obsolete + > mercurial.obsolete._enabled = True + > EOF + $ echo '[extensions]' >> $HGRCPATH + $ echo "rebase=" >> $HGRCPATH + $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH + $ hg up 12 + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ echo B > b; hg add b + $ hg ci -m 6 + +Use a rebase to obsolete r13, and test redirection + + $ hg rebase -r 13 -d 11 + $ PATH_INFO=/rev/13; export PATH_INFO + $ QUERY_STRING='style=raw' + $ python hgweb.cgi #> search + Status: 301 Moved Permanently\r (esc) + ETag: *\r (glob) (esc) + Location: /test/rev/0d601cf5c587\r (esc) + Content-Type: text/plain; charset=ascii\r (esc) + \r (esc) + + error: revision 13 is hidden + +Rebase r13 once more, testing divergent changesets + + $ hg --hidden rebase -r 13 -d 9 + $ PATH_INFO=/rev/13; export PATH_INFO + $ QUERY_STRING='style=raw' + $ python hgweb.cgi #> search + Status: 500 Internal Server Error\r (esc) + ETag: *\r (glob) (esc) + Content-Type: text/plain; charset=ascii\r (esc) + \r (esc) + + error: revision 13 is hidden + $ cd .. diff --git a/tests/test-obsolete.t b/tests/test-obsolete.t --- a/tests/test-obsolete.t +++ b/tests/test-obsolete.t @@ -747,7 +747,7 @@ check filelog view $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'rev/68' 200 Script output follows $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'rev/67' - 404 Not Found + 410 Gone [1] check that web.view config option: