[PATCH 3 of 3 RFC] hgweb: handle obsolete changesets gracefully

Augie Fackler raf at durin42.com
Fri Aug 9 10:12:34 CDT 2013


On Thu, Aug 08, 2013 at 09:48:46AM +0200, Dan Villiom Podlaski Christiansen wrote:
> # 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;

I think you mean 301?

> otherwise we issue a 410 Gone. Please note that this is slightly
> misleading for 'secret' changesets, as they may appear later on.

I think you should handle these separately if possible, since secret
changesets probably want to be 404. Also, you should verify that the
destination of the 301 isn't a 404 before redirecting there, because
that /would/ potentially leak some secret changeset hash IDs.

>
> 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:
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel


More information about the Mercurial-devel mailing list