Mercurial > public > mercurial-scm > hg-stable
diff mercurial/httppeer.py @ 32002:bf855efe5664
httppeer: wrap HTTPResponse.read() globally
There were a handful of places in the code where HTTPResponse.read()
was called with no explicit error handling or with inconsistent
error handling. In order to eliminate this class of bug, we globally
swap out HTTPResponse.read() with a unified error handler.
I initially attempted to fix all call sites. However, after
going down that rabbit hole, I figured it was best to just change
read() to do what we want. This appears to be a worthwhile
change, as the tests demonstrate many of our uncaught exceptions
go away.
To better represent this class of failure, we introduce a new
error type. The main benefit over IOError is it can hold a hint.
I'm receptive to tweaking its name or inheritance.
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Fri, 14 Apr 2017 00:33:56 -0700 |
parents | 48dea083f66d |
children | 84569d2b3fb7 |
line wrap: on
line diff
--- a/mercurial/httppeer.py Thu Apr 13 22:19:28 2017 -0700 +++ b/mercurial/httppeer.py Fri Apr 14 00:33:56 2017 -0700 @@ -75,6 +75,41 @@ 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 @@ 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):