[MERGE] Fix _get_line logic in smart protocol
Martin Pool
mbp at canonical.com
Fri Feb 29 15:43:43 GMT 2008
Martin Pool has voted tweak.
Status is now: Conditionally approved
Comment:
Good catch.
=== modified file 'bzrlib/smart/medium.py'
--- bzrlib/smart/medium.py 2008-02-06 03:52:25 +0000
+++ bzrlib/smart/medium.py 2008-02-27 07:02:52 +0000
@@ -62,6 +62,14 @@
# backing_transport could be passed to serve instead of
__init__
self.backing_transport = backing_transport
self.finished = False
+ self.push_back = None
+
+ def _push_back(self, bytes):
+ assert self.push_back is None, (
+ "_push_back called when self.push is %r" %
(self.push_back,))
+ if bytes == '':
+ return
+ self.push_back = bytes
Should be self.push, not self.push_back in the assertion message.
The variable should probably be underscored? Asking that reminds me
that
it's poor to have self.a and self._a with different meanings.
Please add a comment in the constructor about what it holds, and a
docstring on this method.
def serve(self):
"""Serve requests until the client disconnects."""
@@ -163,11 +173,18 @@
return
protocol.accept_bytes(bytes)
- self.push_back = protocol.excess_buffer
+ self._push_back(protocol.excess_buffer)
def _get_bytes(self, desired_count):
# We ignore the desired_count because on sockets it's more
efficient to
# read 4k at a time.
+ if self.push_back is not None:
+ assert self.push_back != '', (
+ 'self.push_back should never be the empty string, which
can be '
+ 'confused with EOF')
In general I would recommend saying
'%s.thing should blah blah' % (self, )
as it gives a bit more of a clue what went wrong, at least pointing you
to
the right class and sometimes more than that. For these objects we
probably will generally only have one so it's not a big deal.
+ bytes = self.push_back
+ self.push_back = None
+ return bytes
return self.socket.recv(4096)
def terminate_due_to_error(self):
@@ -217,6 +234,13 @@
protocol.accept_bytes(bytes)
def _get_bytes(self, desired_count):
+ if self.push_back is not None:
+ assert self.push_back != '', (
+ 'self.push_back should never be the empty string, which
can be '
+ 'confused with EOF')
+ bytes = self.push_back
+ self.push_back = None
+ return bytes
return self._in.read(desired_count)
There is a little duplicated code; maybe you should do
if self._push_back_buffer is not None:
return self._get_bytes_from_push_back(desired_count)
and maybe that function should return just the desired_count, since
that's
cheap to do? On the other hand some callers may be happy to get more,
like read_line.
def terminate_due_to_error(self):
=== modified file 'bzrlib/tests/test_smart_transport.py'
+ def test_socket_stream_incomplete_request(self):
+ """The medium should still construct the right protocol version
even if
+ the initial read only reads part of the request.
+
+ Specifically, it should correctly read the protocol version
line even
+ if the partial read doesn't end in a newline. An older, naive
+ implementation of _get_line in the server used to have a bug in
that
+ case.
+ """
+ incomplete_request_bytes = protocol.REQUEST_VERSION_TWO + 'hel'
+ rest_of_request_bytes = 'lo\n'
+ expected_response = (
+ protocol.RESPONSE_VERSION_TWO + 'success\nok\x012\n')
+ server_sock, client_sock = self.portable_socket_pair()
+ server = medium.SmartServerSocketStreamMedium(
+ server_sock, None)
+ client_sock.sendall(incomplete_request_bytes)
+ server_protocol = server._build_protocol()
+ client_sock.sendall(rest_of_request_bytes)
+ server._serve_one_request(server_protocol)
Without tracing through the code, I'm not totally sure that the two
writes
will be seen by the server as two reads. But I presume you ran this
before putting in the fix and so you know it works.
For details, see:
http://bundlebuggy.aaronbentley.com/request/%3C20080228150627.GG3192%40steerpike.home.puzzling.org%3E
More information about the bazaar
mailing list