changeset 15017:f4522df38c65

wireproto: add out-of-band error class to allow remote repo to report errors Older clients will still print the provided error message and not much else: over ssh, this will be each line prefixed with 'remote: ' in addition to an "abort: unexpected response: '\n'"; over http, this will be the '---%<---' banners in addition to the 'does not appear to be a repository' message. Currently, clients with this patch will display 'abort: remote error:\n' and the provided error text, but it is trivial to style the error text however is deemed appropriate.
author Andrew Pritchard <andrewp@fogcreek.com>
date Tue, 02 Aug 2011 15:21:10 -0400
parents 871c77e78f5d
children 607f1434501d
files mercurial/dispatch.py mercurial/error.py mercurial/hgweb/protocol.py mercurial/httprepo.py mercurial/sshrepo.py mercurial/sshserver.py mercurial/wireproto.py
diffstat 7 files changed, 37 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/dispatch.py	Wed Aug 03 16:41:14 2011 -0500
+++ b/mercurial/dispatch.py	Tue Aug 02 15:21:10 2011 -0400
@@ -123,6 +123,9 @@
         else:
             ui.warn(_("hg: %s\n") % inst.args[1])
             commands.help_(ui, 'shortlist')
+    except error.OutOfBandError, inst:
+        ui.warn("abort: remote error:\n")
+        ui.warn(''.join(inst.args))
     except error.RepoError, inst:
         ui.warn(_("abort: %s!\n") % inst)
         if inst.hint:
--- a/mercurial/error.py	Wed Aug 03 16:41:14 2011 -0500
+++ b/mercurial/error.py	Tue Aug 02 15:21:10 2011 -0400
@@ -39,6 +39,9 @@
 class ConfigError(Abort):
     'Exception raised when parsing config files'
 
+class OutOfBandError(Exception):
+    'Exception raised when a remote repo reports failure'
+
 class ParseError(Exception):
     'Exception raised when parsing config files (msg[, pos])'
 
--- a/mercurial/hgweb/protocol.py	Wed Aug 03 16:41:14 2011 -0500
+++ b/mercurial/hgweb/protocol.py	Tue Aug 02 15:21:10 2011 -0400
@@ -10,6 +10,7 @@
 from common import HTTP_OK
 
 HGTYPE = 'application/mercurial-0.1'
+HGERRTYPE = 'application/hg-error'
 
 class webproto(object):
     def __init__(self, req, ui):
@@ -90,3 +91,7 @@
         rsp = '0\n%s\n' % rsp.res
         req.respond(HTTP_OK, HGTYPE, length=len(rsp))
         return [rsp]
+    elif isinstance(rsp, wireproto.ooberror):
+        rsp = rsp.message
+        req.respond(HTTP_OK, HGERRTYPE, length=len(rsp))
+        return [rsp]
--- a/mercurial/httprepo.py	Wed Aug 03 16:41:14 2011 -0500
+++ b/mercurial/httprepo.py	Tue Aug 02 15:21:10 2011 -0400
@@ -136,6 +136,8 @@
             proto = resp.headers.get('content-type', '')
 
         safeurl = util.hidepassword(self._url)
+        if proto.startswith('application/hg-error'):
+            raise error.OutOfBandError(resp.read())
         # accept old "text/plain" and "application/hg-changegroup" for now
         if not (proto.startswith('application/mercurial-') or
                 proto.startswith('text/plain') or
--- a/mercurial/sshrepo.py	Wed Aug 03 16:41:14 2011 -0500
+++ b/mercurial/sshrepo.py	Tue Aug 02 15:21:10 2011 -0400
@@ -164,6 +164,17 @@
 
     def _recv(self):
         l = self.pipei.readline()
+        if l == '\n':
+            err = []
+            while True:
+                line = self.pipee.readline()
+                if line == '-\n':
+                    break
+                err.extend([line])
+            if len(err) > 0:
+                # strip the trailing newline added to the last line server-side
+                err[-1] = err[-1][:-1]
+            self._abort(error.OutOfBandError(*err))
         self.readerr()
         try:
             l = int(l)
--- a/mercurial/sshserver.py	Wed Aug 03 16:41:14 2011 -0500
+++ b/mercurial/sshserver.py	Tue Aug 02 15:21:10 2011 -0400
@@ -82,6 +82,12 @@
     def sendpusherror(self, rsp):
         self.sendresponse(rsp.res)
 
+    def sendooberror(self, rsp):
+        self.ui.ferr.write('%s\n-\n' % rsp.message)
+        self.ui.ferr.flush()
+        self.fout.write('\n')
+        self.fout.flush()
+
     def serve_forever(self):
         try:
             while self.serve_one():
@@ -96,6 +102,7 @@
         wireproto.streamres: sendstream,
         wireproto.pushres: sendpushresponse,
         wireproto.pusherr: sendpusherror,
+        wireproto.ooberror: sendooberror,
     }
 
     def serve_one(self):
--- a/mercurial/wireproto.py	Wed Aug 03 16:41:14 2011 -0500
+++ b/mercurial/wireproto.py	Tue Aug 02 15:21:10 2011 -0400
@@ -335,6 +335,10 @@
     def __init__(self, res):
         self.res = res
 
+class ooberror(object):
+    def __init__(self, message):
+        self.message = message
+
 def dispatch(repo, proto, command):
     func, spec = commands[command]
     args = proto.getargs(spec)
@@ -376,6 +380,8 @@
             result = func(repo, proto, *[data[k] for k in keys])
         else:
             result = func(repo, proto)
+        if isinstance(result, ooberror):
+            return result
         res.append(escapearg(result))
     return ';'.join(res)