hg serve: an option to use the first unused port above a given number
Roman Neuhauser
neuhauser at sigpipe.cz
Mon Sep 16 20:26:56 CDT 2013
# kbullock+mercurial at ringworld.org / 2013-09-16 13:20:59 -0500:
> On 16 Sep 2013, at 11:18 AM, Roman Neuhauser wrote:
>
> > hello,
> >
> > i need to run hg-serve in tests for an extension i'm writing.
> > it looks like hg-serve can only listen on a given port (defaults
> > to 8000). this makes the tests somewhat brittle: if there are
> > N concurrent runs, N-1 of them will fail.
> >
> > i'd like to have hg-serve optionally bind to the first available
> > port at-or-above a given number. looking at commands.serve and
> > hgweb.server.create_server, it shouldn't even be that hard.
>
> We do concurrent tests of hgweb in Mercurial's own test suite.
> Check test-hgweb-*.t.
>
> Do you have a strategy for avoiding race conditions in your proposed
> approach? If so then it might allow some cleanup of our own test
> suite, but we'd have to see a patch on -devel.
this is for hg-stable, for no particular reason.
it's not complete (no doc update), i want early feedback.
questions: how can i integrate the test into the mercurial suite?
is there anything besides mercurial/help/config.txt for docs i need
to update?
# HG changeset patch
# User Roman Neuhauser <neuhauser at sigpipe.cz>
# Date 1379378142 -7200
# Tue Sep 17 02:35:42 2013 +0200
# Branch stable
# Node ID 003f4cd01fe4bb109208d447b262ec5bb06ea4e0
# Parent 064f7d697852ad6de03b7b2cbf452b69f5de6bef
`hg serve` can use one of multiple ports
A new configuration option, `web.ports` defines the length
of the port range `hg serve` should try.
web.port=8000, web.ports=2 means "try to bind() to 8000 first,
and to 8001 if that fails". Default is 1.
setup:
$ export HGRCPATH="$PWD/hgrc"
$ cat > $HGRCPATH <<EOF
> [ui]
> interactive = False
> [web]
> port = 18000
> ports = 4
> EOF
$ hg init
web.ports = 4 means the fifth will fail:
$ hg serve -d --pid-file 18000.pid
listening at http://stick.suse.cz:18000/ (bound to *:18000)
$ hg serve -d --pid-file 18001.pid
listening at http://stick.suse.cz:18001/ (bound to *:18001)
$ hg serve -d --pid-file 18002.pid
listening at http://stick.suse.cz:18002/ (bound to *:18002)
$ hg serve -d --pid-file 18003.pid
listening at http://stick.suse.cz:18003/ (bound to *:18003)
$ hg serve -d --pid-file 1DEAD.pid
abort: cannot start server at ':18003': Address already in use
abort: child process failed to start
[255]
teardown:
$ kill $(cat *.pid)
diff --git a/mercurial/hgweb/server.py b/mercurial/hgweb/server.py
--- a/mercurial/hgweb/server.py
+++ b/mercurial/hgweb/server.py
@@ -322,9 +322,18 @@ def create_server(ui, app):
import mimetypes; mimetypes.init()
address = ui.config('web', 'address', '')
- port = util.getport(ui.config('web', 'port', 8000))
- try:
- return cls(ui, app, (address, port), handler)
- except socket.error, inst:
- raise util.Abort(_("cannot start server at '%s:%d': %s")
- % (address, port, inst.args[1]))
+ base = util.getport(ui.config('web', 'port', 8000))
+ ports = ui.configint('web', 'ports', 1)
+ last = base + ports
+ if ports <= 0:
+ raise util.Abort(_("web.ports must be positive"))
+
+ error = 'unknown error'
+ for port in range(base, last):
+ try:
+ return cls(ui, app, (address, port), handler)
+ except socket.error, inst:
+ error = inst.args[1]
+
+ raise util.Abort(_("cannot start server at '%s:%d': %s")
+ % (address, port, error))
--
roman
More information about the Mercurial-devel
mailing list