[PATCH 1 of 4 V2] serve: add support for Mercurial subrepositories

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
16 messages Options
Reply | Threaded
Open this post in threaded view
|

[PATCH 1 of 4 V2] serve: add support for Mercurial subrepositories

Matt Harbison-2
# HG changeset patch
# User Matt Harbison <[hidden email]>
# Date 1486875517 18000
#      Sat Feb 11 23:58:37 2017 -0500
# Node ID 4f2862487d789edc1f36b5687d828a2914e1dc32
# Parent  afaf3c2b129c8940387fd9928ae4fdc28259d13c
serve: add support for Mercurial subrepositories

I've been using `hg serve --web-conf ...` with a simple '/=projects/**' [paths]
configuration for awhile without issue.  Let's ditch the need for the manual
configuration in this case, and limit the repos served to the actual subrepos.

This doesn't attempt to handle the case where a new subrepo appears while the
server is running.  That could probably be handled with a hook if somebody wants
it.  But it's such a rare case, it probably doesn't matter for the temporary
serves.

Unfortunately, the root of the webserver when serving multiple repos is the html
index file.  This makes the URL different for `hg serve` vs `hg serve -S`,
because the top level repo then needs to be part of the path.  That can be
fixed later.

diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -2294,6 +2294,15 @@
         bad.extend(f for f in rejected if f in match.files())
     return bad
 
+def addwebdirpath(repo, serverpath, webconf):
+    webconf[serverpath] = repo.root
+    repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root))
+
+    for r in repo.revs('filelog("path:.hgsub")'):
+        ctx = repo[r]
+        for subpath in ctx.substate:
+            ctx.sub(subpath).addwebdirpath(serverpath, webconf)
+
 def forget(ui, repo, match, prefix, explicitonly):
     join = lambda f: os.path.join(prefix, f)
     bad = []
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -4607,7 +4607,8 @@
     ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
     ('', 'style', '', _('template style to use'), _('STYLE')),
     ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
-    ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
+    ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
+     + subrepoopts,
     _('[OPTION]...'),
     optionalrepo=True)
 def serve(ui, repo, **opts):
diff --git a/mercurial/help/subrepos.txt b/mercurial/help/subrepos.txt
--- a/mercurial/help/subrepos.txt
+++ b/mercurial/help/subrepos.txt
@@ -136,6 +136,11 @@
     subrepository changes are available when referenced by top-level
     repositories.  Push is a no-op for Subversion subrepositories.
 
+:serve: serve does not recurse into subrepositories unless
+    -S/--subrepos is specified.  Git and Subversion subrepositories
+    are currently silently ignored.  Mercurial subrepositories with a
+    URL source are also silently ignored.
+
 :status: status does not recurse into subrepositories unless
     -S/--subrepos is specified. Subrepository changes are displayed as
     regular Mercurial changes on the subrepository
diff --git a/mercurial/server.py b/mercurial/server.py
--- a/mercurial/server.py
+++ b/mercurial/server.py
@@ -16,6 +16,7 @@
 
 from . import (
     chgserver,
+    cmdutil,
     commandserver,
     error,
     hgweb,
@@ -135,11 +136,24 @@
         baseui = ui
     webconf = opts.get('web_conf') or opts.get('webdir_conf')
     if webconf:
+        if opts.get('subrepos'):
+            raise error.Abort(_('--web-conf cannot be used with --subrepos'))
+
         # load server settings (e.g. web.port) to "copied" ui, which allows
         # hgwebdir to reload webconf cleanly
         servui = ui.copy()
         servui.readconfig(webconf, sections=['web'])
         alluis.add(servui)
+    elif opts.get('subrepos'):
+        servui = ui.copy()
+        alluis.add(servui)
+
+        # If repo is None, hgweb.createapp() already raises a proper abort
+        # message as long as webconf is None.
+        if repo:
+            webconf = dict()
+            cmdutil.addwebdirpath(repo, repo.wvfs.basename(repo.root) + '/',
+                                  webconf)
     else:
         servui = ui
 
diff --git a/mercurial/subrepo.py b/mercurial/subrepo.py
--- a/mercurial/subrepo.py
+++ b/mercurial/subrepo.py
@@ -438,6 +438,15 @@
         self._ctx = ctx
         self._path = path
 
+    def addwebdirpath(self, serverpath, webconf):
+        """Add the hgwebdir entries for this subrepo, and any of its subrepos.
+
+        ``serverpath`` is the path component of the URL for this repo.
+
+        ``webconf`` is the dictionary of hgwebdir entries.
+        """
+        pass
+
     def storeclean(self, path):
         """
         returns true if the repository has not changed since it was last
@@ -645,6 +654,17 @@
         self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
         self._initrepo(r, state[0], create)
 
+    @annotatesubrepoerror
+    def addwebdirpath(self, serverpath, webconf):
+        # The URL request contains the subrepo source path, not the local
+        # subrepo path.  The distinction matters for 'foo = ../foo' type
+        # entries.  It isn't possible to serve up 'foo = http://..' type
+        # entries, because the server path is relative to this local server.
+        src = self._state[0]
+        if util.url(src).islocal():
+            path = util.normpath(serverpath + src)
+            cmdutil.addwebdirpath(self._repo, path + '/', webconf)
+
     def storeclean(self, path):
         with self._repo.lock():
             return self._storeclean(path)
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -179,6 +179,7 @@
   --repository
   --stdio
   --style
+  --subrepos
   --templates
   --time
   --traceback
@@ -189,6 +190,7 @@
   -A
   -E
   -R
+  -S
   -a
   -d
   -h
@@ -220,7 +222,7 @@
   pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
   push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
   remove: after, force, subrepos, include, exclude
-  serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate
+  serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, subrepos
   status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos, template
   summary: remote
   update: clean, check, date, rev, tool
diff --git a/tests/test-subrepo-deep-nested-change.t b/tests/test-subrepo-deep-nested-change.t
--- a/tests/test-subrepo-deep-nested-change.t
+++ b/tests/test-subrepo-deep-nested-change.t
@@ -73,6 +73,53 @@
   adding main/main (glob)
   $ hg commit -R main -m "main import"
 
+#if serve
+  $ hg serve -R main --debug -S -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log
+  adding main/ = $TESTTMP/main (glob)
+  adding sub1/ = $TESTTMP/main/sub1 (glob)
+  adding sub2/ = $TESTTMP/main/sub1/sub2 (glob)
+  listening at <a href="http://*:$HGPORT/">http://*:$HGPORT/ (bound to *:$HGPORT) (glob)
+  adding main/ = $TESTTMP/main (glob)
+  adding sub1/ = $TESTTMP/main/sub1 (glob)
+  adding sub2/ = $TESTTMP/main/sub1/sub2 (glob)
+  $ cat hg1.pid >> $DAEMON_PIDS
+
+  $ hg clone <a href="http://localhost:$HGPORT/main">http://localhost:$HGPORT/main httpclone --config progress.disable=True
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 3 changes to 3 files
+  updating to branch default
+  cloning subrepo sub1 from <a href="http://localhost:$HGPORT/sub1">http://localhost:$HGPORT/sub1
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 3 changes to 3 files
+  cloning subrepo sub1/sub2 from <a href="http://localhost:$HGPORT/sub2">http://localhost:$HGPORT/sub2 (glob)
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ cat access.log
+  * "GET /main?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * "GET /main?cmd=batch HTTP/1.1" 200 - * (glob)
+  * "GET /main?cmd=getbundle HTTP/1.1" 200 - * (glob)
+  * "GET /sub1?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * "GET /sub1?cmd=batch HTTP/1.1" 200 - * (glob)
+  * "GET /sub1?cmd=getbundle HTTP/1.1" 200 - * (glob)
+  * "GET /sub2?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * "GET /sub2?cmd=batch HTTP/1.1" 200 - * (glob)
+  * "GET /sub2?cmd=getbundle HTTP/1.1" 200 - * (glob)
+
+  $ killdaemons.py
+  $ rm hg1.pid error.log access.log
+#endif
+
 Cleaning both repositories, just as a clone -U
 
   $ hg up -C -R sub2 null
diff --git a/tests/test-subrepo-recursion.t b/tests/test-subrepo-recursion.t
--- a/tests/test-subrepo-recursion.t
+++ b/tests/test-subrepo-recursion.t
@@ -251,6 +251,60 @@
    z1
   +z2
 
+#if serve
+  $ cd ..
+  $ hg serve -R repo --debug -S -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log
+  adding repo/ = $TESTTMP/repo (glob)
+  adding repo/foo/ = $TESTTMP/repo/foo (glob)
+  adding repo/foo/bar/ = $TESTTMP/repo/foo/bar (glob)
+  listening at <a href="http://*:$HGPORT/">http://*:$HGPORT/ (bound to *:$HGPORT) (glob)
+  adding repo/ = $TESTTMP/repo (glob)
+  adding repo/foo/ = $TESTTMP/repo/foo (glob)
+  adding repo/foo/bar/ = $TESTTMP/repo/foo/bar (glob)
+  $ cat hg1.pid >> $DAEMON_PIDS
+
+  $ hg clone <a href="http://localhost:$HGPORT/repo">http://localhost:$HGPORT/repo clone  --config progress.disable=True
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 3 changesets with 5 changes to 3 files
+  updating to branch default
+  cloning subrepo foo from <a href="http://localhost:$HGPORT/repo/foo">http://localhost:$HGPORT/repo/foo
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 4 changesets with 7 changes to 3 files
+  cloning subrepo foo/bar from <a href="http://localhost:$HGPORT/repo/foo/bar">http://localhost:$HGPORT/repo/foo/bar (glob)
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 3 changesets with 3 changes to 1 files
+  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ cat clone/foo/bar/z.txt
+  z1
+  z2
+  z3
+
+  $ cat access.log
+  * "GET /repo?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * "GET /repo?cmd=batch HTTP/1.1" 200 - * (glob)
+  * "GET /repo?cmd=getbundle HTTP/1.1" 200 - * (glob)
+  * "GET /repo/foo?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * "GET /repo/foo?cmd=batch HTTP/1.1" 200 - * (glob)
+  * "GET /repo/foo?cmd=getbundle HTTP/1.1" 200 - * (glob)
+  * "GET /repo/foo/bar?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * "GET /repo/foo/bar?cmd=batch HTTP/1.1" 200 - * (glob)
+  * "GET /repo/foo/bar?cmd=getbundle HTTP/1.1" 200 - * (glob)
+
+  $ killdaemons.py
+  $ rm hg1.pid error.log access.log
+  $ cd repo
+#endif
+
 Enable progress extension for archive tests:
 
   $ cp $HGRCPATH $HGRCPATH.no-progress
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 2 of 4 V2] clone: use the HTTP 301 location to record the default path

Matt Harbison-2
# HG changeset patch
# User Matt Harbison <[hidden email]>
# Date 1487087965 18000
#      Tue Feb 14 10:59:25 2017 -0500
# Node ID 27a4bc77e8b8e50abc76c387f117082e5853c47e
# Parent  4f2862487d789edc1f36b5687d828a2914e1dc32
clone: use the HTTP 301 location to record the default path

This is needed to simplify making `hg serve -S` paths the same as `hg serve`.
Since 301 is permanent and cachable, this is arguably the proper behavior
anyway.  For example, once Firefox sees a 301, it automatically forwards,
without querying the original URL again.

Code hosting software may make use of redirects.  I only have access to SCM
Manager.  It uses 302 to access subrepos, so it isn't affected by this.  My
understanding of the python libraries being used is minimal, and this was
inspired by http://www.diveintopython.net/http_web_services/redirects.html.

Since `hg serve` doesn't issue redirects, the added tests aren't showing the new
redirect functionality yet.  But they set a baseline that won't change when
redirects are issued.  Check-commit complains about the methods with '_', but
those names are defined by python.

diff --git a/mercurial/hg.py b/mercurial/hg.py
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -621,6 +621,15 @@
 
         destrepo = destpeer.local()
         if destrepo:
+            # Update for 301 redirects
+            endsrc = srcpeer.url()
+            if not islocal(endsrc):
+                abspath = endsrc
+
+                # statichttprepository never sees the 'static-' prefix, so that
+                # need to be accounted for here.
+                if origsource.startswith('static-http'):
+                    abspath = 'static-' + abspath
             template = uimod.samplehgrcs['cloned']
             fp = destrepo.vfs("hgrc", "w", text=True)
             u = util.url(abspath)
diff --git a/mercurial/httppeer.py b/mercurial/httppeer.py
--- a/mercurial/httppeer.py
+++ b/mercurial/httppeer.py
@@ -88,12 +88,12 @@
                              (u.query or u.fragment))
 
         # urllib cannot handle URLs with embedded user or passwd
-        self._url, authinfo = u.authinfo()
+        self._url, self._authinfo = u.authinfo()
 
         self.ui = ui
         self.ui.debug('using %s\n' % self._url)
 
-        self.urlopener = url.opener(ui, authinfo)
+        self.urlopener = url.opener(ui, self._authinfo)
         self.requestbuilder = urlreq.request
 
     def __del__(self):
@@ -230,6 +230,16 @@
         if self._url.rstrip('/') != resp_url.rstrip('/'):
             if not self.ui.quiet:
                 self.ui.warn(_('real URL is %s\n') % resp_url)
+            if resp.status == httplib.MOVED_PERMANENTLY:
+                u = util.url(resp_url.rstrip('/'))
+
+                # The path has auth info on creation.  Restore that here.
+                creds = self._authinfo
+                if creds:
+                    u.user = creds[2]
+                    u.passwd = creds[3] or None
+                self.path = str(u)
+
         self._url = resp_url
         try:
             proto = resp.getheader('content-type')
diff --git a/mercurial/pycompat.py b/mercurial/pycompat.py
--- a/mercurial/pycompat.py
+++ b/mercurial/pycompat.py
@@ -227,6 +227,7 @@
         "HTTPDigestAuthHandler",
         "HTTPHandler",
         "HTTPPasswordMgrWithDefaultRealm",
+        "HTTPRedirectHandler",
         "HTTPSHandler",
         "install_opener",
         "ProxyHandler",
diff --git a/mercurial/url.py b/mercurial/url.py
--- a/mercurial/url.py
+++ b/mercurial/url.py
@@ -272,6 +272,30 @@
 
     return False
 
+class httpredirecthandler(urlreq.httpredirecthandler):
+    def __init__(self):
+        self._permmoved = True
+
+    def redirect_request(self, req, fp, code, msg, hdrs, newurl):
+        '''Called by all http_error_3xx() to monitor redirect types seen'''
+        # Avoid treating 302 -> 301 -> 200 or 301 -> 302 -> 200 as permanent
+        # redirects.
+        self._permmoved = self._permmoved and code == httplib.MOVED_PERMANENTLY
+
+        impl = urlreq.httpredirecthandler.redirect_request
+        return impl(self, req, fp, code, msg, hdrs, newurl)
+
+    def http_error_301(self, req, fp, code, msg, headers):
+        '''Capture the permanent redirect status for later access'''
+        impl = urlreq.httpredirecthandler.http_error_301
+        result = impl(self, req, fp, code, msg, headers)
+
+        # For an unbroken chain of 301, indicate 301 in the status.  Otherwise,
+        # keep the 200 status.
+        if self._permmoved:
+            result.status = code
+        return result
+
 class httphandler(keepalive.HTTPHandler):
     def http_open(self, req):
         return self.do_open(httpconnection, req)
@@ -437,6 +461,7 @@
             handlers.append(httpshandler(ui))
 
     handlers.append(proxyhandler(ui))
+    handlers.append(httpredirecthandler())
 
     passmgr = passwordmgr(ui, ui.httppasswordmgrdb)
     if authinfo is not None:
diff --git a/tests/test-subrepo-deep-nested-change.t b/tests/test-subrepo-deep-nested-change.t
--- a/tests/test-subrepo-deep-nested-change.t
+++ b/tests/test-subrepo-deep-nested-change.t
@@ -84,20 +84,20 @@
   adding sub2/ = $TESTTMP/main/sub1/sub2 (glob)
   $ cat hg1.pid >> $DAEMON_PIDS
 
-  $ hg clone <a href="http://localhost:$HGPORT/main">http://localhost:$HGPORT/main httpclone --config progress.disable=True
+  $ hg clone <a href="http://user@localhost:$HGPORT/main">http://user@localhost:$HGPORT/main httpclone --config progress.disable=True
   requesting all changes
   adding changesets
   adding manifests
   adding file changes
   added 1 changesets with 3 changes to 3 files
   updating to branch default
-  cloning subrepo sub1 from <a href="http://localhost:$HGPORT/sub1">http://localhost:$HGPORT/sub1
+  cloning subrepo sub1 from <a href="http://user@localhost:$HGPORT/sub1">http://user@localhost:$HGPORT/sub1
   requesting all changes
   adding changesets
   adding manifests
   adding file changes
   added 1 changesets with 3 changes to 3 files
-  cloning subrepo sub1/sub2 from <a href="http://localhost:$HGPORT/sub2">http://localhost:$HGPORT/sub2 (glob)
+  cloning subrepo sub1/sub2 from <a href="http://user@localhost:$HGPORT/sub2">http://user@localhost:$HGPORT/sub2 (glob)
   requesting all changes
   adding changesets
   adding manifests
@@ -105,6 +105,11 @@
   added 1 changesets with 1 changes to 1 files
   3 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
+  $ hg -R httpclone showconfig paths.default
+  <a href="http://user@localhost:$HGPORT/main">http://user@localhost:$HGPORT/main
+  $ hg -R httpclone/sub1 showconfig paths.default
+  <a href="http://user@localhost:$HGPORT/sub1">http://user@localhost:$HGPORT/sub1
+
   $ cat access.log
   * "GET /main?cmd=capabilities HTTP/1.1" 200 - (glob)
   * "GET /main?cmd=batch HTTP/1.1" 200 - * (glob)
diff --git a/tests/test-subrepo-recursion.t b/tests/test-subrepo-recursion.t
--- a/tests/test-subrepo-recursion.t
+++ b/tests/test-subrepo-recursion.t
@@ -263,20 +263,20 @@
   adding repo/foo/bar/ = $TESTTMP/repo/foo/bar (glob)
   $ cat hg1.pid >> $DAEMON_PIDS
 
-  $ hg clone <a href="http://localhost:$HGPORT/repo">http://localhost:$HGPORT/repo clone  --config progress.disable=True
+  $ hg clone <a href="http://user:pass@localhost:$HGPORT/repo">http://user:pass@localhost:$HGPORT/repo clone  --config progress.disable=True
   requesting all changes
   adding changesets
   adding manifests
   adding file changes
   added 3 changesets with 5 changes to 3 files
   updating to branch default
-  cloning subrepo foo from <a href="http://localhost:$HGPORT/repo/foo">http://localhost:$HGPORT/repo/foo
+  cloning subrepo foo from <a href="http://user@localhost:$HGPORT/repo/foo">http://user@localhost:$HGPORT/repo/foo
   requesting all changes
   adding changesets
   adding manifests
   adding file changes
   added 4 changesets with 7 changes to 3 files
-  cloning subrepo foo/bar from <a href="http://localhost:$HGPORT/repo/foo/bar">http://localhost:$HGPORT/repo/foo/bar (glob)
+  cloning subrepo foo/bar from <a href="http://user@localhost:$HGPORT/repo/foo/bar">http://user@localhost:$HGPORT/repo/foo/bar (glob)
   requesting all changes
   adding changesets
   adding manifests
@@ -284,6 +284,11 @@
   added 3 changesets with 3 changes to 1 files
   3 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
+  $ hg -R clone showconfig paths.default
+  <a href="http://user@localhost:$HGPORT/repo">http://user@localhost:$HGPORT/repo
+  $ hg -R clone/foo showconfig paths.default
+  <a href="http://user@localhost:$HGPORT/repo/foo">http://user@localhost:$HGPORT/repo/foo
+
   $ cat clone/foo/bar/z.txt
   z1
   z2
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 3 of 4 V2] serve: make the URL the same for `hg serve` and `hg serve -S`

Matt Harbison-2
In reply to this post by Matt Harbison-2
# HG changeset patch
# User Matt Harbison <[hidden email]>
# Date 1486877030 18000
#      Sun Feb 12 00:23:50 2017 -0500
# Node ID 38babd487181374325f3d27c5bc081dadf31b9b9
# Parent  27a4bc77e8b8e50abc76c387f117082e5853c47e
serve: make the URL the same for `hg serve` and `hg serve -S`

It's perfectly workable to serve up the parent repo without the -S for push and
pull, as long as there are no subrepo changes are in play.  Therefore, having a
different URL for the main repo based on the presence of this option seems like
it would get annoying.

The first attempt at this was simply to trim the outer repo name from the paths
when populating 'webconf', so that the root repository was actually hosted at
'/'.  That worked fine for `hg` operations, but had bad effects on the html,
such as making the 'changeset' link on the left side simply 'rev/tip'.  (The
source html shows the link was '//rev/tip', since {repo} was subbed in as an
empty string.)  There were also issues rendering the subrepo pages that ended
with 'tip', including missing icons.

By forwarding the `hg` request for the outer repo, all of the actual
repositories can remain in their normal places.  The forward needs to be
permanent, so that the actual address is stored in the hgrc file.  This is
crucial, because the .hgsub source paths are evaluated relative to the parent
repo's source.  This avoids having to figure out how to put in forwarding logic
for each subrepository as well.

diff --git a/mercurial/hgweb/__init__.py b/mercurial/hgweb/__init__.py
--- a/mercurial/hgweb/__init__.py
+++ b/mercurial/hgweb/__init__.py
@@ -85,9 +85,13 @@
     def run(self):
         self.httpd.serve_forever()
 
-def createapp(baseui, repo, webconf):
+def createapp(baseui, repo, webconf, subrepos=False):
     if webconf:
-        return hgwebdir_mod.hgwebdir(webconf, baseui=baseui)
+        rootrepo = None
+        if subrepos:
+            rootrepo = repo.wvfs.basename(repo.root)
+
+        return hgwebdir_mod.hgwebdir(webconf, baseui=baseui, rootrepo=rootrepo)
     else:
         if not repo:
             raise error.RepoError(_("there is no Mercurial repository"
diff --git a/mercurial/hgweb/common.py b/mercurial/hgweb/common.py
--- a/mercurial/hgweb/common.py
+++ b/mercurial/hgweb/common.py
@@ -23,6 +23,7 @@
 httpserver = util.httpserver
 
 HTTP_OK = 200
+HTTP_MOVED_PERMANENTLY = 301
 HTTP_NOT_MODIFIED = 304
 HTTP_BAD_REQUEST = 400
 HTTP_UNAUTHORIZED = 401
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
@@ -16,6 +16,7 @@
 
 from .common import (
     ErrorResponse,
+    HTTP_MOVED_PERMANENTLY,
     HTTP_NOT_FOUND,
     HTTP_OK,
     HTTP_SERVER_ERROR,
@@ -115,12 +116,13 @@
 
     Instances are typically used as WSGI applications.
     """
-    def __init__(self, conf, baseui=None):
+    def __init__(self, conf, baseui=None, rootrepo=None):
         self.conf = conf
         self.baseui = baseui
         self.ui = None
         self.lastrefresh = 0
         self.motd = None
+        self._rootrepo = rootrepo
         self.refresh()
 
     def refresh(self):
@@ -255,6 +257,15 @@
 
             # top-level index
             elif not virtual:
+                if self._rootrepo:
+                    # Redirect '/' to the main repo when -S is given.
+                    path = '/' + self._rootrepo
+                    if self.prefix:
+                        path = '/' + self.prefix + path
+                    req.headers.append(('Location', path))
+                    html = '<html>Moved <a href="' + path + '">here</a>.</html>'
+                    req.respond(HTTP_MOVED_PERMANENTLY, "text/html", body=html)
+                    return []
                 req.respond(HTTP_OK, ctype)
                 return self.makeindex(req, tmpl)
 
diff --git a/mercurial/server.py b/mercurial/server.py
--- a/mercurial/server.py
+++ b/mercurial/server.py
@@ -166,7 +166,7 @@
         for u in alluis:
             u.setconfig("web", o, val, 'serve')
 
-    app = hgweb.createapp(baseui, repo, webconf)
+    app = hgweb.createapp(baseui, repo, webconf, opts.get('subrepos'))
     return hgweb.httpservice(servui, app, opts)
 
 def createservice(ui, repo, opts):
diff --git a/tests/test-subrepo-deep-nested-change.t b/tests/test-subrepo-deep-nested-change.t
--- a/tests/test-subrepo-deep-nested-change.t
+++ b/tests/test-subrepo-deep-nested-change.t
@@ -84,7 +84,8 @@
   adding sub2/ = $TESTTMP/main/sub1/sub2 (glob)
   $ cat hg1.pid >> $DAEMON_PIDS
 
-  $ hg clone <a href="http://user@localhost:$HGPORT/main">http://user@localhost:$HGPORT/main httpclone --config progress.disable=True
+  $ hg clone <a href="http://user@localhost:$HGPORT">http://user@localhost:$HGPORT httpclone --config progress.disable=True
+  real URL is <a href="http://localhost:$HGPORT/main">http://localhost:$HGPORT/main
   requesting all changes
   adding changesets
   adding manifests
@@ -111,7 +112,10 @@
   <a href="http://user@localhost:$HGPORT/sub1">http://user@localhost:$HGPORT/sub1
 
   $ cat access.log
+  * "GET /?cmd=capabilities HTTP/1.1" 301 - (glob)
+  * "GET /main HTTP/1.1" 200 - (glob)
   * "GET /main?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * "GET /main?cmd=between HTTP/1.1" 200 - * (glob)
   * "GET /main?cmd=batch HTTP/1.1" 200 - * (glob)
   * "GET /main?cmd=getbundle HTTP/1.1" 200 - * (glob)
   * "GET /sub1?cmd=capabilities HTTP/1.1" 200 - (glob)
diff --git a/tests/test-subrepo-recursion.t b/tests/test-subrepo-recursion.t
--- a/tests/test-subrepo-recursion.t
+++ b/tests/test-subrepo-recursion.t
@@ -263,7 +263,8 @@
   adding repo/foo/bar/ = $TESTTMP/repo/foo/bar (glob)
   $ cat hg1.pid >> $DAEMON_PIDS
 
-  $ hg clone <a href="http://user:pass@localhost:$HGPORT/repo">http://user:pass@localhost:$HGPORT/repo clone  --config progress.disable=True
+  $ hg clone <a href="http://user:pass@localhost:$HGPORT">http://user:pass@localhost:$HGPORT clone  --config progress.disable=True
+  real URL is <a href="http://localhost:$HGPORT/repo">http://localhost:$HGPORT/repo
   requesting all changes
   adding changesets
   adding manifests
@@ -295,7 +296,10 @@
   z3
 
   $ cat access.log
+  * "GET /?cmd=capabilities HTTP/1.1" 301 - (glob)
+  * "GET /repo HTTP/1.1" 200 - (glob)
   * "GET /repo?cmd=capabilities HTTP/1.1" 200 - (glob)
+  * "GET /repo?cmd=between HTTP/1.1" 200 - * (glob)
   * "GET /repo?cmd=batch HTTP/1.1" 200 - * (glob)
   * "GET /repo?cmd=getbundle HTTP/1.1" 200 - * (glob)
   * "GET /repo/foo?cmd=capabilities HTTP/1.1" 200 - (glob)
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 4 of 4 V2] serve: restore the index file for `hg serve -S`

Matt Harbison-2
In reply to this post by Matt Harbison-2
# HG changeset patch
# User Matt Harbison <[hidden email]>
# Date 1487129917 18000
#      Tue Feb 14 22:38:37 2017 -0500
# Node ID 3a0c0f139204e6fa86af22c1d54d8c1dd7a4aa2b
# Parent  38babd487181374325f3d27c5bc081dadf31b9b9
serve: restore the index file for `hg serve -S`

There's currently no integration of subrepos into the browsable directory
listing for the parent repo, so this is a nice to have.  The redirect is only
necessary for a Mercurial client.  The comment in mercurial.url.opener() seems
to indicate that the 'Accept' header will always be around for BC.  Another
option to detect a Mercurial client is to check the Request URI for the
capabilities command.

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
@@ -255,10 +255,11 @@
                 staticfile(static, fname, req)
                 return []
 
-            # top-level index
+            # top-level index, or HTTP_MOVED_PERMANENTLY for a Mercurial client
+            # when the server is invoked with --subrepos.
             elif not virtual:
-                if self._rootrepo:
-                    # Redirect '/' to the main repo when -S is given.
+                accept = req.env.get('HTTP_ACCEPT', "")
+                if self._rootrepo and accept == 'application/mercurial-0.1':
                     path = '/' + self._rootrepo
                     if self.prefix:
                         path = '/' + self.prefix + path
diff --git a/tests/test-subrepo-recursion.t b/tests/test-subrepo-recursion.t
--- a/tests/test-subrepo-recursion.t
+++ b/tests/test-subrepo-recursion.t
@@ -295,6 +295,10 @@
   z2
   z3
 
+  $ wget -q <a href="http://localhost:$HGPORT">http://localhost:$HGPORT
+  $ cat index.html | grep '<title>'
+  <title>Mercurial repositories index</title>
+
   $ cat access.log
   * "GET /?cmd=capabilities HTTP/1.1" 301 - (glob)
   * "GET /repo HTTP/1.1" 200 - (glob)
@@ -308,9 +312,10 @@
   * "GET /repo/foo/bar?cmd=capabilities HTTP/1.1" 200 - (glob)
   * "GET /repo/foo/bar?cmd=batch HTTP/1.1" 200 - * (glob)
   * "GET /repo/foo/bar?cmd=getbundle HTTP/1.1" 200 - * (glob)
+  * "GET / HTTP/1.0" 200 - (glob)
 
   $ killdaemons.py
-  $ rm hg1.pid error.log access.log
+  $ rm hg1.pid error.log access.log index.html
   $ cd repo
 #endif
 
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 1 of 4 V2] serve: add support for Mercurial subrepositories

Yuya Nishihara
In reply to this post by Matt Harbison-2
On Thu, 16 Feb 2017 16:41:08 -0500, Matt Harbison wrote:

> # HG changeset patch
> # User Matt Harbison <[hidden email]>
> # Date 1486875517 18000
> #      Sat Feb 11 23:58:37 2017 -0500
> # Node ID 4f2862487d789edc1f36b5687d828a2914e1dc32
> # Parent  afaf3c2b129c8940387fd9928ae4fdc28259d13c
> serve: add support for Mercurial subrepositories
>
> I've been using `hg serve --web-conf ...` with a simple '/=projects/**' [paths]
> configuration for awhile without issue.  Let's ditch the need for the manual
> configuration in this case, and limit the repos served to the actual subrepos.
>
> This doesn't attempt to handle the case where a new subrepo appears while the
> server is running.  That could probably be handled with a hook if somebody wants
> it.  But it's such a rare case, it probably doesn't matter for the temporary
> serves.
>
> Unfortunately, the root of the webserver when serving multiple repos is the html
> index file.  This makes the URL different for `hg serve` vs `hg serve -S`,
> because the top level repo then needs to be part of the path.  That can be
> fixed later.

I'm puzzled by this new version which extensively relies on somewhat magical
HTTP redirection. Instead, can't we fix hgwebdir to be able to host hgweb at
'/' URL, and add explicit URL to show the index page hidden by it?

That will be useful without subrepos. Currently we can't see the index page
'/foo' if we have a repository '/foo' and '/foo/bar'.
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 2 of 4 V2] clone: use the HTTP 301 location to record the default path

Yuya Nishihara
In reply to this post by Matt Harbison-2
On Thu, 16 Feb 2017 16:41:09 -0500, Matt Harbison wrote:
> # HG changeset patch
> # User Matt Harbison <[hidden email]>
> # Date 1487087965 18000
> #      Tue Feb 14 10:59:25 2017 -0500
> # Node ID 27a4bc77e8b8e50abc76c387f117082e5853c47e
> # Parent  4f2862487d789edc1f36b5687d828a2914e1dc32
> clone: use the HTTP 301 location to record the default path

> @@ -230,6 +230,16 @@
>          if self._url.rstrip('/') != resp_url.rstrip('/'):
>              if not self.ui.quiet:
>                  self.ui.warn(_('real URL is %s\n') % resp_url)
> +            if resp.status == httplib.MOVED_PERMANENTLY:
> +                u = util.url(resp_url.rstrip('/'))
> +
> +                # The path has auth info on creation.  Restore that here.
> +                creds = self._authinfo
> +                if creds:
> +                    u.user = creds[2]
> +                    u.passwd = creds[3] or None
> +                self.path = str(u)

I didn't carefully review this patch, but is it safe to reuse the credential
for redirected server?
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 3 of 4 V2] serve: make the URL the same for `hg serve` and `hg serve -S`

Yuya Nishihara
In reply to this post by Matt Harbison-2
On Thu, 16 Feb 2017 16:41:10 -0500, Matt Harbison wrote:
> # HG changeset patch
> # User Matt Harbison <[hidden email]>
> # Date 1486877030 18000
> #      Sun Feb 12 00:23:50 2017 -0500
> # Node ID 38babd487181374325f3d27c5bc081dadf31b9b9
> # Parent  27a4bc77e8b8e50abc76c387f117082e5853c47e
> serve: make the URL the same for `hg serve` and `hg serve -S`

>              elif not virtual:
> +                if self._rootrepo:
> +                    # Redirect '/' to the main repo when -S is given.
> +                    path = '/' + self._rootrepo
> +                    if self.prefix:
> +                        path = '/' + self.prefix + path
> +                    req.headers.append(('Location', path))
> +                    html = '<html>Moved <a href="' + path + '">here</a>.</html>'
> +                    req.respond(HTTP_MOVED_PERMANENTLY, "text/html", body=html)
> +                    return []

Suppose "hg serve" is used for temporarily serving random repositories, 301
seems too strong.
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 2 of 4 V2] clone: use the HTTP 301 location to record the default path

Matt Harbison-2
In reply to this post by Yuya Nishihara
On Mon, 20 Feb 2017 09:33:05 -0500, Yuya Nishihara <[hidden email]> wrote:

> On Thu, 16 Feb 2017 16:41:09 -0500, Matt Harbison wrote:
>> # HG changeset patch
>> # User Matt Harbison <[hidden email]>
>> # Date 1487087965 18000
>> #      Tue Feb 14 10:59:25 2017 -0500
>> # Node ID 27a4bc77e8b8e50abc76c387f117082e5853c47e
>> # Parent  4f2862487d789edc1f36b5687d828a2914e1dc32
>> clone: use the HTTP 301 location to record the default path
>
>> @@ -230,6 +230,16 @@
>>          if self._url.rstrip('/') != resp_url.rstrip('/'):
>>              if not self.ui.quiet:
>>                  self.ui.warn(_('real URL is %s\n') % resp_url)
>> +            if resp.status == httplib.MOVED_PERMANENTLY:
>> +                u = util.url(resp_url.rstrip('/'))
>> +
>> +                # The path has auth info on creation.  Restore that  
>> here.
>> +                creds = self._authinfo
>> +                if creds:
>> +                    u.user = creds[2]
>> +                    u.passwd = creds[3] or None
>> +                self.path = str(u)
>
> I didn't carefully review this patch, but is it safe to reuse the  
> credential
> for redirected server?

Hmmm.. Maybe not.  At least for the clone path, it isn't even necessary,  
because the password is removed before writing out the hgrc file.  I  
didn't see that until after I added this, and then forgot to remove it.  
The username is stored in the hgrc file though.

It looks like url.passwordmgr stores the username/password, but I don't  
have a handy setup that will password prompt and redirect to see if that  
info can easily be obtained here.  I have no problem with dropping the 'if  
creds' (and related changes not shown here) bits.
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 1 of 4 V2] serve: add support for Mercurial subrepositories

Matt Harbison-2
In reply to this post by Yuya Nishihara
On Mon, 20 Feb 2017 09:27:44 -0500, Yuya Nishihara <[hidden email]> wrote:

> On Thu, 16 Feb 2017 16:41:08 -0500, Matt Harbison wrote:
>> # HG changeset patch
>> # User Matt Harbison <[hidden email]>
>> # Date 1486875517 18000
>> #      Sat Feb 11 23:58:37 2017 -0500
>> # Node ID 4f2862487d789edc1f36b5687d828a2914e1dc32
>> # Parent  afaf3c2b129c8940387fd9928ae4fdc28259d13c
>> serve: add support for Mercurial subrepositories
>>
>> I've been using `hg serve --web-conf ...` with a simple '/=projects/**'  
>> [paths]
>> configuration for awhile without issue.  Let's ditch the need for the  
>> manual
>> configuration in this case, and limit the repos served to the actual  
>> subrepos.
>>
>> This doesn't attempt to handle the case where a new subrepo appears  
>> while the
>> server is running.  That could probably be handled with a hook if  
>> somebody wants
>> it.  But it's such a rare case, it probably doesn't matter for the  
>> temporary
>> serves.
>>
>> Unfortunately, the root of the webserver when serving multiple repos is  
>> the html
>> index file.  This makes the URL different for `hg serve` vs `hg serve  
>> -S`,
>> because the top level repo then needs to be part of the path.  That can  
>> be
>> fixed later.
>
> I'm puzzled by this new version which extensively relies on somewhat  
> magical
> HTTP redirection. Instead, can't we fix hgwebdir to be able to host  
> hgweb at
> '/' URL, and add explicit URL to show the index page hidden by it?

This patch is the same as the first in V1, except that I dropped forcing  
the web.staticurl config, and I globbed away a bunch of the http  
parameters in the tests, since only the URI and status is of interest.

The first version did host the parent at '/', and the subrepos relative to  
it.  I switched from that, not because of the index hiding, but because of  
all the problems I ran into.

First were problems (I don't remember all of the details, but eventually  
managed to fix) with `hg` commands.  And then I noticed that the initial  
repo view wasn't rendering properly in the browser.  There was output from  
the server like this:

127.0.0.1 - - [20/Feb/2017 21:02:36] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [20/Feb/2017 21:02:42] "GET /favicon.ico HTTP/1.1" 404 -

That seemed to be fixed by the staticurl config.  I was also able to load  
"http://localhost:8000/subrepo" and get it to display properly, so I  
submitted it.  But I waved Augie off because then I noticed more problems  
in the browser:

- At http://localhost:8000, the links on the left side were corrupt (graph  
is simply 'graph/tip', tags is 'http://tags', etc.)  The pattern seemed to  
be that things ending in 'tip' are relative URLs, and the rest get an  
http: prefix.  It looks from the sources like it is just a matter of '/'  
not translating for the {repo} template.

- At http://localhost:8000/subrepo, all of the links on the left look  
proper.  But if you click on one that ends in 'tip' (like 'graph'), the  
page doesn't render properly (like when 'staticurl' wasn't set on the  
parent repo).  It does properly render pages that don't end in 'tip', like  
'branches'.

I also had a different failed attempt at hosting the parent at '/' a few  
years ago, which had the same rendering issues.  And I didn't check  
anything other than paper.  All in all, a week or so of nonstop problems  
convinced me that I was going about this the wrong way.

To me, the benefits of this series over the original is:

1) The repos aren't moved, and therefore don't incur these subtle bugs
2) These changes are totally isolated to subrepo serving, so there should  
be no risk to mainstream hgweb usage
3) This is completely compatible with existing serves with a simple  
'/=$projects_dir/**' config file, because the real repos are not anchored  
to '/'.

>
> That will be useful without subrepos. Currently we can't see the index  
> page '/foo' if we have a repository '/foo' and '/foo/bar'.

I actually wondered why there wasn't an explicit index.html handled where  
needed, and maybe a redirect to it.  I don't think doing that is  
incompatible with this series though.
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 3 of 4 V2] serve: make the URL the same for `hg serve` and `hg serve -S`

Matt Harbison-2
In reply to this post by Yuya Nishihara
On Mon, 20 Feb 2017 09:38:13 -0500, Yuya Nishihara <[hidden email]> wrote:

> On Thu, 16 Feb 2017 16:41:10 -0500, Matt Harbison wrote:
>> # HG changeset patch
>> # User Matt Harbison <[hidden email]>
>> # Date 1486877030 18000
>> #      Sun Feb 12 00:23:50 2017 -0500
>> # Node ID 38babd487181374325f3d27c5bc081dadf31b9b9
>> # Parent  27a4bc77e8b8e50abc76c387f117082e5853c47e
>> serve: make the URL the same for `hg serve` and `hg serve -S`
>
>>              elif not virtual:
>> +                if self._rootrepo:
>> +                    # Redirect '/' to the main repo when -S is given.
>> +                    path = '/' + self._rootrepo
>> +                    if self.prefix:
>> +                        path = '/' + self.prefix + path
>> +                    req.headers.append(('Location', path))
>> +                    html = '<html>Moved <a href="' + path +  
>> '">here</a>.</html>'
>> +                    req.respond(HTTP_MOVED_PERMANENTLY, "text/html",  
>> body=html)
>> +                    return []
>
> Suppose "hg serve" is used for temporarily serving random repositories,  
> 301
> seems too strong.

The temporary serve is the case that I'm interested in.  It may not be  
clear from this snippet, but the redirect is only reachable when using `hg  
serve -S`, not random repos listed in a --web-conf file.

I guess I didn't think it was too strong, because the repo never is at  
'/'.  Once the `hg serve` process is killed, everything is gone.  
Everything goes away eventually.  But until then, it's a permanent  
redirect.

The temporary/permanent distinction is important, because there needs to  
be a trigger to update paths.default in hgrc without doing so for  
temporary redirects.  The updated hgrc path is important, because the  
subrepo is cloned relative to the parent repo's paths.default value.  
(Keeping both the sub=sub and sub=../sub style paths working in the two  
new tests were one of the many headaches with this.)

Since nothing other than hg clients should see the redirect, do you have a  
specific concern?  (I haven't paid much attention to chg to know if it  
would somehow cache the URL and get directed wrong when `hg serve` is  
killed and something else is served.  But the aggressive caching done by  
web browsers is why I limited the redirect as narrowly as possible.)
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 3 of 4 V2] serve: make the URL the same for `hg serve` and `hg serve -S`

Yuya Nishihara
On Mon, 20 Feb 2017 22:25:21 -0500, Matt Harbison wrote:

> On Mon, 20 Feb 2017 09:38:13 -0500, Yuya Nishihara <[hidden email]> wrote:
> > On Thu, 16 Feb 2017 16:41:10 -0500, Matt Harbison wrote:
> >> # HG changeset patch
> >> # User Matt Harbison <[hidden email]>
> >> # Date 1486877030 18000
> >> #      Sun Feb 12 00:23:50 2017 -0500
> >> # Node ID 38babd487181374325f3d27c5bc081dadf31b9b9
> >> # Parent  27a4bc77e8b8e50abc76c387f117082e5853c47e
> >> serve: make the URL the same for `hg serve` and `hg serve -S`
> >
> >>              elif not virtual:
> >> +                if self._rootrepo:
> >> +                    # Redirect '/' to the main repo when -S is given.
> >> +                    path = '/' + self._rootrepo
> >> +                    if self.prefix:
> >> +                        path = '/' + self.prefix + path
> >> +                    req.headers.append(('Location', path))
> >> +                    html = '<html>Moved <a href="' + path +  
> >> '">here</a>.</html>'
> >> +                    req.respond(HTTP_MOVED_PERMANENTLY, "text/html",  
> >> body=html)
> >> +                    return []
> >
> > Suppose "hg serve" is used for temporarily serving random repositories,  
> > 301
> > seems too strong.
>
> The temporary serve is the case that I'm interested in.  It may not be  
> clear from this snippet, but the redirect is only reachable when using `hg  
> serve -S`, not random repos listed in a --web-conf file.
>
> I guess I didn't think it was too strong, because the repo never is at  
> '/'.  Once the `hg serve` process is killed, everything is gone.  
> Everything goes away eventually.  But until then, it's a permanent  
> redirect.
>
> The temporary/permanent distinction is important, because there needs to  
> be a trigger to update paths.default in hgrc without doing so for  
> temporary redirects.  The updated hgrc path is important, because the  
> subrepo is cloned relative to the parent repo's paths.default value.  
> (Keeping both the sub=sub and sub=../sub style paths working in the two  
> new tests were one of the many headaches with this.)
>
> Since nothing other than hg clients should see the redirect, do you have a  
> specific concern?  (I haven't paid much attention to chg to know if it  
> would somehow cache the URL and get directed wrong when `hg serve` is  
> killed and something else is served.  But the aggressive caching done by  
> web browsers is why I limited the redirect as narrowly as possible.)

Hmm, if no web browser is involved, it would be okay as hg client wouldn't have
a permanent URL cache. I don't like the idea of using redirection to compensate
the problem of hgweb, though.
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 1 of 4 V2] serve: add support for Mercurial subrepositories

Yuya Nishihara
In reply to this post by Matt Harbison-2
On Mon, 20 Feb 2017 22:00:20 -0500, Matt Harbison wrote:

> On Mon, 20 Feb 2017 09:27:44 -0500, Yuya Nishihara <[hidden email]> wrote:
> > On Thu, 16 Feb 2017 16:41:08 -0500, Matt Harbison wrote:
> >> # HG changeset patch
> >> # User Matt Harbison <[hidden email]>
> >> # Date 1486875517 18000
> >> #      Sat Feb 11 23:58:37 2017 -0500
> >> # Node ID 4f2862487d789edc1f36b5687d828a2914e1dc32
> >> # Parent  afaf3c2b129c8940387fd9928ae4fdc28259d13c
> >> serve: add support for Mercurial subrepositories
> >>
> >> I've been using `hg serve --web-conf ...` with a simple '/=projects/**'  
> >> [paths]
> >> configuration for awhile without issue.  Let's ditch the need for the  
> >> manual
> >> configuration in this case, and limit the repos served to the actual  
> >> subrepos.
> >>
> >> This doesn't attempt to handle the case where a new subrepo appears  
> >> while the
> >> server is running.  That could probably be handled with a hook if  
> >> somebody wants
> >> it.  But it's such a rare case, it probably doesn't matter for the  
> >> temporary
> >> serves.
> >>
> >> Unfortunately, the root of the webserver when serving multiple repos is  
> >> the html
> >> index file.  This makes the URL different for `hg serve` vs `hg serve  
> >> -S`,
> >> because the top level repo then needs to be part of the path.  That can  
> >> be
> >> fixed later.
> >
> > I'm puzzled by this new version which extensively relies on somewhat  
> > magical
> > HTTP redirection. Instead, can't we fix hgwebdir to be able to host  
> > hgweb at
> > '/' URL, and add explicit URL to show the index page hidden by it?
>
> This patch is the same as the first in V1, except that I dropped forcing  
> the web.staticurl config, and I globbed away a bunch of the http  
> parameters in the tests, since only the URI and status is of interest.
>
> The first version did host the parent at '/', and the subrepos relative to  
> it.

Yes, that seems intuitive way to serve a repo and its subrepos behind. I
thought there wasn't redirection magic as the parent repo was served at '/',
was there?

> - At http://localhost:8000, the links on the left side were corrupt (graph  
> is simply 'graph/tip', tags is 'http://tags', etc.)  The pattern seemed to  
> be that things ending in 'tip' are relative URLs, and the rest get an  
> http: prefix.  It looks from the sources like it is just a matter of '/'  
> not translating for the {repo} template.

[snip]

I saw some of these problems in V1. However, the standalone hgweb can host
a repository at '/', so I don't think it's quite difficult to host the subrepo
parent at '/'.

> To me, the benefits of this series over the original is:
>
> 1) The repos aren't moved, and therefore don't incur these subtle bugs
> 2) These changes are totally isolated to subrepo serving, so there should  
> be no risk to mainstream hgweb usage
> 3) This is completely compatible with existing serves with a simple  
> '/=$projects_dir/**' config file, because the real repos are not anchored  
> to '/'.

I think (2) and (3) are up to how we build a repository map. If a map has no
'/' entry, hgweb would serve nothing at '/' and is compatible with the current
behavior.
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 1 of 4 V2] serve: add support for Mercurial subrepositories

Matt Harbison-2
Sorry for the delay- I was swamped the last few days.

On Tue, 21 Feb 2017 09:33:58 -0500, Yuya Nishihara <[hidden email]> wrote:

> On Mon, 20 Feb 2017 22:00:20 -0500, Matt Harbison wrote:
>> On Mon, 20 Feb 2017 09:27:44 -0500, Yuya Nishihara <[hidden email]>  
>> wrote:
>> > On Thu, 16 Feb 2017 16:41:08 -0500, Matt Harbison wrote:
>> >> # HG changeset patch
>> >> # User Matt Harbison <[hidden email]>
>> >> # Date 1486875517 18000
>> >> #      Sat Feb 11 23:58:37 2017 -0500
>> >> # Node ID 4f2862487d789edc1f36b5687d828a2914e1dc32
>> >> # Parent  afaf3c2b129c8940387fd9928ae4fdc28259d13c
>> >> serve: add support for Mercurial subrepositories
>> >>
>> >> I've been using `hg serve --web-conf ...` with a simple  
>> '/=projects/**'
>> >> [paths]
>> >> configuration for awhile without issue.  Let's ditch the need for the
>> >> manual
>> >> configuration in this case, and limit the repos served to the actual
>> >> subrepos.
>> >>
>> >> This doesn't attempt to handle the case where a new subrepo appears
>> >> while the
>> >> server is running.  That could probably be handled with a hook if
>> >> somebody wants
>> >> it.  But it's such a rare case, it probably doesn't matter for the
>> >> temporary
>> >> serves.
>> >>
>> >> Unfortunately, the root of the webserver when serving multiple repos  
>> is
>> >> the html
>> >> index file.  This makes the URL different for `hg serve` vs `hg serve
>> >> -S`,
>> >> because the top level repo then needs to be part of the path.  That  
>> can
>> >> be
>> >> fixed later.
>> >
>> > I'm puzzled by this new version which extensively relies on somewhat
>> > magical
>> > HTTP redirection. Instead, can't we fix hgwebdir to be able to host
>> > hgweb at
>> > '/' URL, and add explicit URL to show the index page hidden by it?
>>
>> This patch is the same as the first in V1, except that I dropped forcing
>> the web.staticurl config, and I globbed away a bunch of the http
>> parameters in the tests, since only the URI and status is of interest.
>>
>> The first version did host the parent at '/', and the subrepos relative  
>> to
>> it.
>
> Yes, that seems intuitive way to serve a repo and its subrepos behind. I
> thought there wasn't redirection magic as the parent repo was served at  
> '/',
> was there?

Correct. Redirection was only introduced in V2.

>> - At http://localhost:8000, the links on the left side were corrupt  
>> (graph
>> is simply 'graph/tip', tags is 'http://tags', etc.)  The pattern seemed  
>> to
>> be that things ending in 'tip' are relative URLs, and the rest get an
>> http: prefix.  It looks from the sources like it is just a matter of '/'
>> not translating for the {repo} template.
>
> [snip]
>
> I saw some of these problems in V1. However, the standalone hgweb can  
> host
> a repository at '/', so I don't think it's quite difficult to host the  
> subrepo
> parent at '/'.

That's a good point, so now I'm really puzzled over the subtle  
differences.  It looks like when it finds a hosted repo, it just farms out  
to hgweb, like the single serve would do.

>> To me, the benefits of this series over the original is:
>>
>> 1) The repos aren't moved, and therefore don't incur these subtle bugs
>> 2) These changes are totally isolated to subrepo serving, so there  
>> should
>> be no risk to mainstream hgweb usage
>> 3) This is completely compatible with existing serves with a simple
>> '/=$projects_dir/**' config file, because the real repos are not  
>> anchored
>> to '/'.
>
> I think (2) and (3) are up to how we build a repository map.

What I mean for (2) is that in V2, none of the code changes are hit  
without -S.  That seems like a good thing, given the subtle breakage in V1.

What I mean by (3) is that the transition for current clones is to simply  
drop --web-conf=.. and start using -S on the server side.  Clients won't  
see a difference.

I assume that the hardcoded web.staticurl in V1 also means that it won't  
be able to serve a subrepo named 'static'?  (That's admittedly an edge  
case.)

I only see the two ways to build the repo map, so I'm not sure what you  
are saying here.

> If a map has no
> '/' entry, hgweb would serve nothing at '/' and is compatible with the  
> current
> behavior.
 
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 1 of 4 V2] serve: add support for Mercurial subrepositories

Yuya Nishihara
On Sat, 25 Feb 2017 15:25:30 -0500, Matt Harbison wrote:

> On Tue, 21 Feb 2017 09:33:58 -0500, Yuya Nishihara <[hidden email]> wrote:
> >> - At http://localhost:8000, the links on the left side were corrupt  
> >> (graph
> >> is simply 'graph/tip', tags is 'http://tags', etc.)  The pattern seemed  
> >> to
> >> be that things ending in 'tip' are relative URLs, and the rest get an
> >> http: prefix.  It looks from the sources like it is just a matter of '/'
> >> not translating for the {repo} template.
> >
> > [snip]
> >
> > I saw some of these problems in V1. However, the standalone hgweb can  
> > host
> > a repository at '/', so I don't think it's quite difficult to host the  
> > subrepo
> > parent at '/'.
>
> That's a good point, so now I'm really puzzled over the subtle  
> differences.  It looks like when it finds a hosted repo, it just farms out  
> to hgweb, like the single serve would do.

Yes. hgwebdir is just a dispatcher except for generating an index page.

This is a dirty hack to make hgwebdir serve a repo at '/'.

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
@@ -335,7 +335,7 @@ class hgweb(object):
         req.url = req.env['SCRIPT_NAME']
         if not req.url.endswith('/'):
             req.url += '/'
-        if 'REPO_NAME' in req.env:
+        if req.env.get('REPO_NAME'):
             req.url += req.env['REPO_NAME'] + '/'
 
         if 'PATH_INFO' in req.env:
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
@@ -254,18 +254,19 @@ class hgwebdir(object):
                 return []
 
             # top-level index
-            elif not virtual:
+            elif False:
                 req.respond(HTTP_OK, ctype)
                 return self.makeindex(req, tmpl)
 
             # nested indexes and hgwebs
 
             repos = dict(self.repos)
-            virtualrepo = virtual
-            while virtualrepo:
-                real = repos.get(virtualrepo)
+            # XXX dirty hack to handle ''. IIRC, we have a helper function
+            virtualrepo = '/' + virtual
+            while True:
+                real = repos.get(virtualrepo[1:])
                 if real:
-                    req.env['REPO_NAME'] = virtualrepo
+                    req.env['REPO_NAME'] = virtualrepo[1:]
                     try:
                         # ensure caller gets private copy of ui
                         repo = hg.repository(self.ui.copy(), real)

> >> To me, the benefits of this series over the original is:
> >>
> >> 1) The repos aren't moved, and therefore don't incur these subtle bugs
> >> 2) These changes are totally isolated to subrepo serving, so there  
> >> should
> >> be no risk to mainstream hgweb usage
> >> 3) This is completely compatible with existing serves with a simple
> >> '/=$projects_dir/**' config file, because the real repos are not  
> >> anchored
> >> to '/'.
> >
> > I think (2) and (3) are up to how we build a repository map.
>
> What I mean for (2) is that in V2, none of the code changes are hit  
> without -S.  That seems like a good thing, given the subtle breakage in V1.
>
> What I mean by (3) is that the transition for current clones is to simply  
> drop --web-conf=.. and start using -S on the server side.  Clients won't  
> see a difference.

No code change is nice, but I don't buy it much since the redirection thing
seemed unnecessarily complicated.

> I assume that the hardcoded web.staticurl in V1 also means that it won't  
> be able to serve a subrepo named 'static'?  (That's admittedly an edge  
> case.)

Yes. Also, you can't see /rev/ page if there's a subrepo named 'rev'.

> I only see the two ways to build the repo map, so I'm not sure what you  
> are saying here.

I meant we could delete the root repo from self.repos map on plain hgwebdir
server if that's considered a behavior change. (otherwise the root index
page would be replaced by the hgweb for the root repo.)
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 1 of 4 V2] serve: add support for Mercurial subrepositories

Matt Harbison-2
On Sun, 26 Feb 2017 08:28:01 -0500, Yuya Nishihara <[hidden email]> wrote:

> On Sat, 25 Feb 2017 15:25:30 -0500, Matt Harbison wrote:
>> On Tue, 21 Feb 2017 09:33:58 -0500, Yuya Nishihara <[hidden email]>  
>> wrote:
>> >> - At http://localhost:8000, the links on the left side were corrupt
>> >> (graph
>> >> is simply 'graph/tip', tags is 'http://tags', etc.)  The pattern  
>> seemed
>> >> to
>> >> be that things ending in 'tip' are relative URLs, and the rest get an
>> >> http: prefix.  It looks from the sources like it is just a matter of  
>> '/'
>> >> not translating for the {repo} template.
>> >
>> > [snip]
>> >
>> > I saw some of these problems in V1. However, the standalone hgweb can
>> > host
>> > a repository at '/', so I don't think it's quite difficult to host the
>> > subrepo
>> > parent at '/'.
>>
>> That's a good point, so now I'm really puzzled over the subtle
>> differences.  It looks like when it finds a hosted repo, it just farms  
>> out
>> to hgweb, like the single serve would do.
>
> Yes. hgwebdir is just a dispatcher except for generating an index page.
>
> This is a dirty hack to make hgwebdir serve a repo at '/'.
>
> 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
> @@ -335,7 +335,7 @@ class hgweb(object):
>          req.url = req.env['SCRIPT_NAME']
>          if not req.url.endswith('/'):
>              req.url += '/'
> -        if 'REPO_NAME' in req.env:
> +        if req.env.get('REPO_NAME'):
>              req.url += req.env['REPO_NAME'] + '/'
>         if 'PATH_INFO' in req.env:
> 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
> @@ -254,18 +254,19 @@ class hgwebdir(object):
>                  return []
>             # top-level index
> -            elif not virtual:
> +            elif False:
>                  req.respond(HTTP_OK, ctype)
>                  return self.makeindex(req, tmpl)
>             # nested indexes and hgwebs
>             repos = dict(self.repos)
> -            virtualrepo = virtual
> -            while virtualrepo:
> -                real = repos.get(virtualrepo)
> +            # XXX dirty hack to handle ''. IIRC, we have a helper  
> function
> +            virtualrepo = '/' + virtual
> +            while True:
> +                real = repos.get(virtualrepo[1:])
>                  if real:
> -                    req.env['REPO_NAME'] = virtualrepo
> +                    req.env['REPO_NAME'] = virtualrepo[1:]
>                      try:
>                          # ensure caller gets private copy of ui
>                          repo = hg.repository(self.ui.copy(), real)

Oh, nice! Thanks for this.

Can you clarify what you mean by the hack? It seems like adding '/' to  
paths isn't all that uncommon (especially in subrepo code), and the  
closest I could find to a helper for removing '/' is  
hgwebdir_mod.cleannames().  But that takes a list and also does a  
util.pconvert().

Just a couple lines above this, 'virtual' is cleaned up with  
string.strip('/').  (That's interesting, because then I can't see how the  
"virtual.startswith('static/')" tests can ever be true.  It looks like  
this was papered over in 74f65f44a9aa?)

>> >> To me, the benefits of this series over the original is:
>> >>
>> >> 1) The repos aren't moved, and therefore don't incur these subtle  
>> bugs
>> >> 2) These changes are totally isolated to subrepo serving, so there
>> >> should
>> >> be no risk to mainstream hgweb usage
>> >> 3) This is completely compatible with existing serves with a simple
>> >> '/=$projects_dir/**' config file, because the real repos are not
>> >> anchored
>> >> to '/'.
>> >
>> > I think (2) and (3) are up to how we build a repository map.
>>
>> What I mean for (2) is that in V2, none of the code changes are hit
>> without -S.  That seems like a good thing, given the subtle breakage in  
>> V1.
>>
>> What I mean by (3) is that the transition for current clones is to  
>> simply
>> drop --web-conf=.. and start using -S on the server side.  Clients won't
>> see a difference.
>
> No code change is nice, but I don't buy it much since the redirection  
> thing
> seemed unnecessarily complicated.
>
>> I assume that the hardcoded web.staticurl in V1 also means that it won't
>> be able to serve a subrepo named 'static'?  (That's admittedly an edge
>> case.)
>
> Yes. Also, you can't see /rev/ page if there's a subrepo named 'rev'.
>
>> I only see the two ways to build the repo map, so I'm not sure what you
>> are saying here.
>
> I meant we could delete the root repo from self.repos map on plain  
> hgwebdir
> server if that's considered a behavior change. (otherwise the root index
> page would be replaced by the hgweb for the root repo.)
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 1 of 4 V2] serve: add support for Mercurial subrepositories

Yuya Nishihara
On Sun, 26 Feb 2017 23:09:59 -0500, Matt Harbison wrote:

> On Sun, 26 Feb 2017 08:28:01 -0500, Yuya Nishihara <[hidden email]> wrote:
> > This is a dirty hack to make hgwebdir serve a repo at '/'.
> >
> > 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
> > @@ -335,7 +335,7 @@ class hgweb(object):
> >          req.url = req.env['SCRIPT_NAME']
> >          if not req.url.endswith('/'):
> >              req.url += '/'
> > -        if 'REPO_NAME' in req.env:
> > +        if req.env.get('REPO_NAME'):
> >              req.url += req.env['REPO_NAME'] + '/'
> >         if 'PATH_INFO' in req.env:
> > 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
> > @@ -254,18 +254,19 @@ class hgwebdir(object):
> >                  return []
> >             # top-level index
> > -            elif not virtual:
> > +            elif False:
> >                  req.respond(HTTP_OK, ctype)
> >                  return self.makeindex(req, tmpl)
> >             # nested indexes and hgwebs
> >             repos = dict(self.repos)
> > -            virtualrepo = virtual
> > -            while virtualrepo:
> > -                real = repos.get(virtualrepo)
> > +            # XXX dirty hack to handle ''. IIRC, we have a helper  
> > function
> > +            virtualrepo = '/' + virtual
> > +            while True:
> > +                real = repos.get(virtualrepo[1:])
> >                  if real:
> > -                    req.env['REPO_NAME'] = virtualrepo
> > +                    req.env['REPO_NAME'] = virtualrepo[1:]
> >                      try:
> >                          # ensure caller gets private copy of ui
> >                          repo = hg.repository(self.ui.copy(), real)
>
> Oh, nice! Thanks for this.
>
> Can you clarify what you mean by the hack?

I wrote that code without thinking an edge case nor what would be the best way.
That's all.

> It seems like adding '/' to  
> paths isn't all that uncommon (especially in subrepo code), and the  
> closest I could find to a helper for removing '/' is  
> hgwebdir_mod.cleannames().  But that takes a list and also does a  
> util.pconvert().

Ok, found it. What I had in mind was util.finddirs(), which requires the first
'/' though.

> Just a couple lines above this, 'virtual' is cleaned up with  
> string.strip('/').  (That's interesting, because then I can't see how the  
> "virtual.startswith('static/')" tests can ever be true.  It looks like  
> this was papered over in 74f65f44a9aa?)

'virtual/foo', maybe.
_______________________________________________
Mercurial-devel mailing list
[hidden email]
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel