Comments
Patch
@@ -246,3 +246,6 @@ class UnsupportedBundleSpecification(Exc
class CorruptedState(Exception):
"""error raised when a command is not able to read its state from file"""
+
+class RichIOError(Abort):
+ """An IOError that can also have a hint attached."""
@@ -75,6 +75,41 @@ def encodevalueinheaders(value, header,
return result
+def _wraphttpresponse(resp):
+ """Wrap an HTTPResponse with common error handlers.
+
+ This ensures that any I/O from any consumer raises the appropriate
+ error and messaging.
+ """
+ origread = resp.read
+
+ class readerproxy(resp.__class__):
+ def read(self, size=None):
+ try:
+ return origread(size)
+ except httplib.IncompleteRead as e:
+ # e.expected is an integer if length known or None otherwise.
+ if e.expected:
+ msg = _('HTTP request error (incomplete response; '
+ 'expected %d bytes got %d)') % (e.expected,
+ len(e.partial))
+ else:
+ msg = _('HTTP request error (incomplete response)')
+
+ raise error.RichIOError(
+ msg,
+ hint=_('this may be an intermittent network failure; '
+ 'if the error persists, consider contacting the '
+ 'network or server operator'))
+ except httplib.HTTPException as e:
+ raise error.RichIOError(
+ _('HTTP request error (%s)') % e,
+ hint=_('this may be an intermittent failure; '
+ 'if the error persists, consider contacting the '
+ 'network or server operator'))
+
+ resp.__class__ = readerproxy
+
class httppeer(wireproto.wirepeer):
def __init__(self, ui, path):
self.path = path
@@ -223,6 +258,10 @@ class httppeer(wireproto.wirepeer):
self.ui.debug('http error while sending %s command\n' % cmd)
self.ui.traceback()
raise IOError(None, inst)
+
+ # Insert error handlers for common I/O failures.
+ _wraphttpresponse(resp)
+
# record the url we got redirected to
resp_url = resp.geturl()
if resp_url.endswith(qs):
@@ -267,10 +267,10 @@ Server sends an incomplete capabilities
$ hg --config badserver.closeaftersendbytes=180 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
$ cat hg.pid > $DAEMON_PIDS
-TODO client spews a stack due to uncaught httplib.IncompleteRead
-
- $ hg clone http://localhost:$HGPORT/ clone 2> /dev/null
- [1]
+ $ hg clone http://localhost:$HGPORT/ clone
+ abort: HTTP request error (incomplete response; expected 385 bytes got 20)
+ (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
+ [255]
$ killdaemons.py $DAEMON_PIDS
@@ -461,11 +461,11 @@ Server sends empty HTTP body for getbund
$ hg --config badserver.closeaftersendbytes=933 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
$ cat hg.pid > $DAEMON_PIDS
-TODO client spews a stack due to uncaught httplib.IncompleteRead
-
- $ hg clone http://localhost:$HGPORT/ clone 2> /dev/null
+ $ hg clone http://localhost:$HGPORT/ clone
requesting all changes
- [1]
+ abort: HTTP request error (incomplete response)
+ (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
+ [255]
$ killdaemons.py $DAEMON_PIDS
@@ -525,11 +525,11 @@ Server sends partial compression string
$ hg --config badserver.closeaftersendbytes=945 serve -p $HGPORT -d --pid-file=hg.pid -E error.log
$ cat hg.pid > $DAEMON_PIDS
-TODO client spews a stack due to uncaught httplib.IncompleteRead
-
- $ hg clone http://localhost:$HGPORT/ clone 2> /dev/null
+ $ hg clone http://localhost:$HGPORT/ clone
requesting all changes
- [1]
+ abort: HTTP request error (incomplete response; expected 1 bytes got 3)
+ (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
+ [255]
$ killdaemons.py $DAEMON_PIDS
@@ -593,7 +593,8 @@ Server sends partial bundle2 header magi
$ hg clone http://localhost:$HGPORT/ clone
requesting all changes
- abort: connection ended unexpectedly
+ abort: HTTP request error (incomplete response; expected 1 bytes got 3)
+ (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
[255]
$ killdaemons.py $DAEMON_PIDS
@@ -616,7 +617,8 @@ Server sends incomplete bundle2 stream p
$ hg clone http://localhost:$HGPORT/ clone
requesting all changes
- abort: connection ended unexpectedly
+ abort: HTTP request error (incomplete response; expected 1 bytes got 3)
+ (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
[255]
$ killdaemons.py $DAEMON_PIDS
@@ -640,7 +642,8 @@ Servers stops after bundle2 stream param
$ hg clone http://localhost:$HGPORT/ clone
requesting all changes
- abort: connection ended unexpectedly
+ abort: HTTP request error (incomplete response)
+ (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
[255]
$ killdaemons.py $DAEMON_PIDS
@@ -664,7 +667,8 @@ Server stops sending after bundle2 part
$ hg clone http://localhost:$HGPORT/ clone
requesting all changes
- abort: connection ended unexpectedly
+ abort: HTTP request error (incomplete response)
+ (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
[255]
$ killdaemons.py $DAEMON_PIDS
@@ -785,7 +789,8 @@ Server stops sending after 0 length payl
added 1 changesets with 1 changes to 1 files
transaction abort!
rollback completed
- abort: connection ended unexpectedly
+ abort: HTTP request error (incomplete response)
+ (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
[255]
$ killdaemons.py $DAEMON_PIDS