Mercurial > public > mercurial-scm > hg-stable
diff tests/badserverext.py @ 41480:4d5aae86c9bd
tests: log sendall() operations and port test-http-bad-server.t
Python 3's HTTP server layer buffers output and uses sendall()
instead of write(). In order to make test-http-bad-server.t pass
on Python 3, we needed to teach our socket proxy to log sendall()
events and to abort future sends if we reached our send limit.
The tests using `tail` were difficult to port with inline output
conditionals since the number of lines varied. So we now use
`#if py3` for these tests.
test-http-bad-server.t now passes on Python 3.6 and 3.7 on at
least Linux. However, it does not yet pass on Python 3.5 because
of low-level differences to how the HTTP server is implemented.
Differential Revision: https://phab.mercurial-scm.org/D5753
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Wed, 30 Jan 2019 12:12:25 -0800 |
parents | d343d9ac173e |
children | 2372284d9457 |
line wrap: on
line diff
--- a/tests/badserverext.py Tue Jan 29 14:06:46 2019 -0800 +++ b/tests/badserverext.py Wed Jan 30 12:12:25 2019 -0800 @@ -75,7 +75,7 @@ object.__setattr__(self, '_closeaftersendbytes', closeaftersendbytes) def __getattribute__(self, name): - if name in ('makefile',): + if name in ('makefile', 'sendall', '_writelog'): return object.__getattribute__(self, name) return getattr(object.__getattribute__(self, '_orig'), name) @@ -86,6 +86,13 @@ def __setattr__(self, name, value): setattr(object.__getattribute__(self, '_orig'), name, value) + def _writelog(self, msg): + msg = msg.replace(b'\r', b'\\r').replace(b'\n', b'\\n') + + object.__getattribute__(self, '_logfp').write(msg) + object.__getattribute__(self, '_logfp').write(b'\n') + object.__getattribute__(self, '_logfp').flush() + def makefile(self, mode, bufsize): f = object.__getattribute__(self, '_orig').makefile(mode, bufsize) @@ -99,6 +106,38 @@ closeafterrecvbytes=closeafterrecvbytes, closeaftersendbytes=closeaftersendbytes) + def sendall(self, data, flags=0): + remaining = object.__getattribute__(self, '_closeaftersendbytes') + + # No read limit. Call original function. + if not remaining: + result = object.__getattribute__(self, '_orig').sendall(data, flags) + self._writelog(b'sendall(%d) -> %s' % (len(data), data)) + return result + + if len(data) > remaining: + newdata = data[0:remaining] + else: + newdata = data + + remaining -= len(newdata) + + result = object.__getattribute__(self, '_orig').sendall(newdata, flags) + + self._writelog(b'sendall(%d from %d) -> (%d) %s' % ( + len(newdata), len(data), remaining, newdata)) + + object.__setattr__(self, '_closeaftersendbytes', remaining) + + if remaining <= 0: + self._writelog(b'write limit reached; closing socket') + object.__getattribute__(self, '_orig').shutdown(socket.SHUT_RDWR) + + raise Exception('connection closed after sending N bytes') + + return result + + # We can't adjust __class__ on socket._fileobject, so define a proxy. class fileobjectproxy(object): __slots__ = (