[PATCH 1 of 2] cmdserver: write early exception to 'e' channel in 'unix' mode

Yuya Nishihara yuya at tcha.org
Sat Mar 12 09:01:11 EST 2016


# HG changeset patch
# User Yuya Nishihara <yuya at tcha.org>
# Date 1457787810 -32400
#      Sat Mar 12 22:03:30 2016 +0900
# Node ID 44c2880005610595f4a098dfc74672a68b93c147
# Parent  3c90090320ad1c9017a9f14019a19f05442f8866
cmdserver: write early exception to 'e' channel in 'unix' mode

In 'unix' mode, the server is typically detached from the console. Therefore
a client couldn't see the exception that occurred while instantiating the
server object.

This patch tries to catch the early error and send it to 'e' channel even if
the server isn't instantiated yet. This means the error may be sent before the
initial hello message. So it's up to the client implementation whether to
handle the early error message or error out as protocol violation.

The error handling code is also copied to chgserver.py. I'll factor out them
later if we manage to get chg passes the test suite.

diff --git a/hgext/chgserver.py b/hgext/chgserver.py
--- a/hgext/chgserver.py
+++ b/hgext/chgserver.py
@@ -525,9 +525,10 @@ class _requesthandler(SocketServer.Strea
         os.setpgid(0, 0)
         ui = self.server.ui
         repo = self.server.repo
-        sv = chgcmdserver(ui, repo, self.rfile, self.wfile, self.connection,
-                          self.server.hashstate, self.server.baseaddress)
+        sv = None
         try:
+            sv = chgcmdserver(ui, repo, self.rfile, self.wfile, self.connection,
+                              self.server.hashstate, self.server.baseaddress)
             try:
                 sv.serve()
             # handle exceptions that may be raised by command server. most of
@@ -544,7 +545,11 @@ class _requesthandler(SocketServer.Strea
         except: # re-raises
             # also write traceback to error channel. otherwise client cannot
             # see it because it is written to server's stderr by default.
-            traceback.print_exc(file=sv.cerr)
+            if sv:
+                cerr = sv.cerr
+            else:
+                cerr = commandserver.channeledoutput(self.wfile, 'e')
+            traceback.print_exc(file=cerr)
             raise
 
 def _tempaddress(address):
diff --git a/mercurial/commandserver.py b/mercurial/commandserver.py
--- a/mercurial/commandserver.py
+++ b/mercurial/commandserver.py
@@ -338,8 +338,9 @@ class _requesthandler(SocketServer.Strea
     def handle(self):
         ui = self.server.ui
         repo = self.server.repo
-        sv = server(ui, repo, self.rfile, self.wfile)
+        sv = None
         try:
+            sv = server(ui, repo, self.rfile, self.wfile)
             try:
                 sv.serve()
             # handle exceptions that may be raised by command server. most of
@@ -354,7 +355,11 @@ class _requesthandler(SocketServer.Strea
         except: # re-raises
             # also write traceback to error channel. otherwise client cannot
             # see it because it is written to server's stderr by default.
-            traceback.print_exc(file=sv.cerr)
+            if sv:
+                cerr = sv.cerr
+            else:
+                cerr = channeledoutput(self.wfile, 'e')
+            traceback.print_exc(file=cerr)
             raise
 
 class unixservice(object):
diff --git a/tests/test-commandserver.t b/tests/test-commandserver.t
--- a/tests/test-commandserver.t
+++ b/tests/test-commandserver.t
@@ -717,6 +717,35 @@ unix domain socket:
   listening at .hg/server.sock
   abort: unknown command unknowncommand
   killed!
+  $ rm .hg/server.log
+
+ if server crashed before hello, traceback will be sent to 'e' channel as
+ last ditch:
+
+  $ cat <<EOF >> .hg/hgrc
+  > [cmdserver]
+  > log = inexistent/path.log
+  > EOF
+  >>> from hgclient import unixserver, readchannel, check
+  >>> server = unixserver('.hg/server.sock', '.hg/server.log')
+  >>> def earlycrash(conn):
+  ...     while True:
+  ...         try:
+  ...             ch, data = readchannel(conn)
+  ...             if not data.startswith('  '):
+  ...                 print '%c, %r' % (ch, data)
+  ...         except EOFError:
+  ...             break
+  >>> check(earlycrash, server.connect)
+  e, 'Traceback (most recent call last):\n'
+  e, "IOError: *" (glob)
+  >>> server.shutdown()
+
+  $ cat .hg/server.log | grep -v '^  '
+  listening at .hg/server.sock
+  Traceback (most recent call last):
+  IOError: * (glob)
+  killed!
 #endif
 #if no-unix-socket
 


More information about the Mercurial-devel mailing list