[PATCH 3 of 3 RFC] hgweb: handle obsolete changesets gracefully
Dan Villiom Podlaski Christiansen
danchr at gmail.com
Thu Aug 8 02:48:46 CDT 2013
# HG changeset patch
# User Dan Villiom Podlaski Christiansen <danchr at gmail.com>
# 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:
More information about the Mercurial-devel
mailing list