Patchwork [4,of,6] serve: support singlethreaded for debugging purposes

login
register
mail settings
Submitter timeless@mozdev.org
Date Jan. 12, 2016, 6:38 p.m.
Message ID <104e7b46c453161871f7.1452623887@waste.org>
Download mbox | patch
Permalink /patch/12700/
State Changes Requested
Delegated to: Yuya Nishihara
Headers show

Comments

timeless@mozdev.org - Jan. 12, 2016, 6:38 p.m.
# HG changeset patch
# User timeless <timeless@mozdev.org>
# Date 1452622106 0
#      Tue Jan 12 18:08:26 2016 +0000
# Node ID 104e7b46c453161871f7e9c4e3d60b9aaff13d02
# Parent  198238d09e72b4d83f212120af8175a67e99dde2
serve: support singlethreaded for debugging purposes
Yuya Nishihara - Jan. 15, 2016, 2:56 p.m.
On Tue, 12 Jan 2016 12:38:07 -0600, timeless wrote:
> # HG changeset patch
> # User timeless <timeless@mozdev.org>
> # Date 1452622106 0
> #      Tue Jan 12 18:08:26 2016 +0000
> # Node ID 104e7b46c453161871f7e9c4e3d60b9aaff13d02
> # Parent  198238d09e72b4d83f212120af8175a67e99dde2
> serve: support singlethreaded for debugging purposes
> 
> diff --git a/mercurial/commands.py b/mercurial/commands.py
> --- a/mercurial/commands.py
> +++ b/mercurial/commands.py
> @@ -6191,7 +6191,9 @@
>      ('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')),
> +    ('', 'singlethreaded', None, _('Single threaded (for debugging)')),

I like the idea, but I don't think it should be exported as a command option
because it is for debugging. Can it be an internal config knob?

> -try:
> -    import threading
> -    threading.activeCount() # silence pyflakes and bypass demandimport
> -    _mixin = SocketServer.ThreadingMixIn
> -except ImportError:
> -    if util.safehasattr(os, "fork"):
> -        _mixin = SocketServer.ForkingMixIn
> -    else:
> -        class _mixin(object):
> -            pass
> +class _mixin(object):
> +    pass

This change looks unnecessary as you've changed static HTTPServer class to
factory of class.

> +def getMercurialHTTPServer(_mixin):
> +    class MercurialHTTPServer(_mixin, BaseHTTPServer.HTTPServer):

New function should follow our naming convention.

Patch

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -6191,7 +6191,9 @@ 
     ('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')),
+    ('', 'singlethreaded', None, _('Single threaded (for debugging)')),
+    ],
     _('[OPTION]...'),
     optionalrepo=True)
 def serve(ui, repo, **opts):
diff --git a/mercurial/hgweb/__init__.py b/mercurial/hgweb/__init__.py
--- a/mercurial/hgweb/__init__.py
+++ b/mercurial/hgweb/__init__.py
@@ -51,7 +51,7 @@ 
 
     def init(self):
         util.setsignalhandler()
-        self.httpd = server.create_server(self.ui, self.app)
+        self.httpd = server.create_server(self.ui, self.app, self.opts)
 
         if self.opts['port'] and not self.ui.verbose:
             return
diff --git a/mercurial/hgweb/server.py b/mercurial/hgweb/server.py
--- a/mercurial/hgweb/server.py
+++ b/mercurial/hgweb/server.py
@@ -235,66 +235,72 @@ 
         self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
         self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
 
-try:
-    import threading
-    threading.activeCount() # silence pyflakes and bypass demandimport
-    _mixin = SocketServer.ThreadingMixIn
-except ImportError:
-    if util.safehasattr(os, "fork"):
-        _mixin = SocketServer.ForkingMixIn
-    else:
-        class _mixin(object):
-            pass
+class _mixin(object):
+    pass
 
 def openlog(opt, default):
     if opt and opt != '-':
         return open(opt, 'a')
     return default
 
-class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
+def getMercurialHTTPServer(_mixin):
+    class MercurialHTTPServer(_mixin, BaseHTTPServer.HTTPServer):
 
-    # SO_REUSEADDR has broken semantics on windows
-    if os.name == 'nt':
-        allow_reuse_address = 0
+        # SO_REUSEADDR has broken semantics on windows
+        if os.name == 'nt':
+            allow_reuse_address = 0
 
-    def __init__(self, ui, app, addr, handler, **kwargs):
-        BaseHTTPServer.HTTPServer.__init__(self, addr, handler, **kwargs)
-        self.daemon_threads = True
-        self.application = app
+        def __init__(self, ui, app, addr, handler, **kwargs):
+            BaseHTTPServer.HTTPServer.__init__(self, addr, handler, **kwargs)
+            self.daemon_threads = True
+            self.application = app
 
-        handler.preparehttpserver(self, ui.config('web', 'certificate'))
+            handler.preparehttpserver(self, ui.config('web', 'certificate'))
 
-        prefix = ui.config('web', 'prefix', '')
-        if prefix:
-            prefix = '/' + prefix.strip('/')
-        self.prefix = prefix
+            prefix = ui.config('web', 'prefix', '')
+            if prefix:
+                prefix = '/' + prefix.strip('/')
+            self.prefix = prefix
 
-        alog = openlog(ui.config('web', 'accesslog', '-'), sys.stdout)
-        elog = openlog(ui.config('web', 'errorlog', '-'), sys.stderr)
-        self.accesslog = alog
-        self.errorlog = elog
+            alog = openlog(ui.config('web', 'accesslog', '-'), sys.stdout)
+            elog = openlog(ui.config('web', 'errorlog', '-'), sys.stderr)
+            self.accesslog = alog
+            self.errorlog = elog
 
-        self.addr, self.port = self.socket.getsockname()[0:2]
-        self.fqaddr = socket.getfqdn(addr[0])
+            self.addr, self.port = self.socket.getsockname()[0:2]
+            self.fqaddr = socket.getfqdn(addr[0])
+    return MercurialHTTPServer
 
-class IPv6HTTPServer(MercurialHTTPServer):
-    address_family = getattr(socket, 'AF_INET6', None)
-    def __init__(self, *args, **kwargs):
-        if self.address_family is None:
-            raise error.RepoError(_('IPv6 is not available on this system'))
-        super(IPv6HTTPServer, self).__init__(*args, **kwargs)
+def getIPv6HTTPServer(MercurialHTTPServer):
+    class IPv6HTTPServer(MercurialHTTPServer):
+        address_family = getattr(socket, 'AF_INET6', None)
+        def __init__(self, *args, **kwargs):
+            if self.address_family is None:
+                raise error.RepoError(_('IPv6 is not available on this system'))
+            super(IPv6HTTPServer, self).__init__(*args, **kwargs)
+    return IPv6HTTPServer
 
-def create_server(ui, app):
+def create_server(ui, app, opts=None):
+    global _mixin
+    if not opts.get('singlethreaded'):
+        try:
+            import threading
+            threading.activeCount() # silence pyflakes and bypass demandimport
+            _mixin = SocketServer.ThreadingMixIn
+        except ImportError:
+            if util.safehasattr(os, "fork"):
+                _mixin = SocketServer.ForkingMixIn
 
     if ui.config('web', 'certificate'):
         handler = _httprequesthandlerssl
     else:
         handler = _httprequesthandler
 
+    mercurialserver = getMercurialHTTPServer(_mixin)
     if ui.configbool('web', 'ipv6'):
-        cls = IPv6HTTPServer
+        cls = getIPv6HTTPServer(mercurialserver)
     else:
-        cls = MercurialHTTPServer
+        cls = mercurialserver
 
     # ugly hack due to python issue5853 (for threaded use)
     try:
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -175,6 +175,7 @@ 
   --profile
   --quiet
   --repository
+  --singlethreaded
   --stdio
   --style
   --templates
@@ -218,7 +219,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-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate
+  serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, singlethreaded
   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-hgweb-locale.t b/tests/test-hgweb-locale.t
new file mode 100644
--- /dev/null
+++ b/tests/test-hgweb-locale.t
@@ -0,0 +1,15 @@ 
+#require serve
+
+Some tests for hgweb. Tests static files, plain files and different 404's.
+
+  $ hg init test
+  $ cd test
+  $ LANGUAGE=
+  $ LC_ALL=
+  $ hg serve --singlethreaded -n help -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
+  $ cat hg.pid >> $DAEMON_PIDS
+
+stop
+
+  $ killdaemons.py
+  $ cd ..