Rev 3494: Fix infinite busy-loop caused by connection loss during read of in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Wed Jun 11 13:04:01 BST 2008


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 3494
revision-id:pqm at pqm.ubuntu.com-20080611120348-tqhq37qvfz624jyb
parent: pqm at pqm.ubuntu.com-20080611042056-m5e074q47s9gwwjj
parent: andrew.bennetts at canonical.com-20080611110805-oluknyiukp111t13
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Wed 2008-06-11 13:03:48 +0100
message:
  Fix infinite busy-loop caused by connection loss during read of
  	response body in HPSS v1 and v2. (Andrew Bennetts)
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/smart/protocol.py       protocol.py-20061108035435-ot0lstk2590yqhzr-1
  bzrlib/tests/test_smart_transport.py test_ssh_transport.py-20060608202016-c25gvf1ob7ypbus6-2
    ------------------------------------------------------------
    revno: 3464.4.2
    revision-id:andrew.bennetts at canonical.com-20080611110805-oluknyiukp111t13
    parent: andrew.bennetts at canonical.com-20080602011217-91w8f210rc3adv6g
    parent: pqm at pqm.ubuntu.com-20080609211646-amc2rr2zi50omr8m
    committer: Andrew Bennetts <andrew.bennetts at canonical.com>
    branch nick: interrupted-connection-fix
    timestamp: Wed 2008-06-11 21:08:05 +1000
    message:
      Merge from bzr.dev.
    added:
      bzrlib/tests/blackbox/test_alias.py test_alias.py-20080425112253-fbt0yz1c1834jriz-1
      bzrlib/tests/blackbox/test_modified.py test_modified.py-20080424085848-nwqjenan4dq2vq3w-1
      bzrlib/tests/per_repository_reference/ repository_external_-20080220025549-nnm2s80it1lvcwnc-1
      bzrlib/tests/per_repository_reference/__init__.py __init__.py-20080220025549-nnm2s80it1lvcwnc-2
      bzrlib/tests/per_repository_reference/test_add_inventory.py test_add_inventory.p-20080220025549-nnm2s80it1lvcwnc-3
      bzrlib/tests/per_repository_reference/test_add_revision.py test_add_revision.py-20080220034108-ao1u8qgakqbo5a08-1
      bzrlib/tests/per_repository_reference/test_add_signature_text.py test_add_signature_t-20080220041905-1j2g4lyz3c6h34v4-1
      bzrlib/tests/per_repository_reference/test_all_revision_ids.py test_all_revision_id-20080220041905-1j2g4lyz3c6h34v4-2
      bzrlib/tests/per_repository_reference/test_break_lock.py test_break_lock.py-20080220042825-1f48qmpnuqqp5wg2-1
      bzrlib/tests/per_repository_reference/test_check.py test_check.py-20080220044229-sxxe747gzi6q8fyv-1
    renamed:
      doc/en/user-guide/revnos.txt => doc/en/user-guide/zen.txt revnos.txt-20080111231928-pbntxea0ynh9ww1t-1
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/__init__.py             __init__.py-20050309040759-33e65acf91bbcd5d
      bzrlib/branch.py               branch.py-20050309040759-e4baf4e0d046576e
      bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
      bzrlib/check.py                check.py-20050309040759-f3a679400c06bcc1
      bzrlib/config.py               config.py-20051011043216-070c74f4e9e338e8
      bzrlib/counted_lock.py         counted_lock.py-20070502135927-7dk86io3ok7ctx6k-1
      bzrlib/dirstate.py             dirstate.py-20060728012006-d6mvoihjb3je9peu-1
      bzrlib/errors.py               errors.py-20050309040759-20512168c4e14fbd
      bzrlib/knit.py                 knit.py-20051212171256-f056ac8f0fbe1bd9
      bzrlib/lockdir.py              lockdir.py-20060220222025-98258adf27fbdda3
      bzrlib/merge_directive.py      merge_directive.py-20070228184838-ja62280spt1g7f4x-1
      bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
      bzrlib/repository.py           rev_storage.py-20051111201905-119e9401e46257e3
      bzrlib/tag.py                  tag.py-20070212110532-91cw79inah2cfozx-1
      bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
      bzrlib/tests/blackbox/__init__.py __init__.py-20051128053524-eba30d8255e08dc3
      bzrlib/tests/blackbox/test_added.py test_added.py-20060119085008-6b8b90369d42a26c
      bzrlib/tests/blackbox/test_non_ascii.py test_non_ascii.py-20060105214030-68010be784a5d854
      bzrlib/tests/blackbox/test_uncommit.py test_uncommit.py-20051027212835-84944b63adae51be
      bzrlib/tests/blackbox/test_unknowns.py test_unknowns.py-20070905015344-74tg6s1synijo2oe-1
      bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
      bzrlib/tests/branch_implementations/test_pull.py test_pull.py-20060410103942-83c35b26657414fc
      bzrlib/tests/http_server.py    httpserver.py-20061012142527-m1yxdj1xazsf8d7s-1
      bzrlib/tests/repository_implementations/__init__.py __init__.py-20060131092037-9564957a7d4a841b
      bzrlib/tests/test_branch.py    test_branch.py-20060116013032-97819aa07b8ab3b5
      bzrlib/tests/test_config.py    testconfig.py-20051011041908-742d0c15d8d8c8eb
      bzrlib/tests/test_counted_lock.py test_counted_lock.py-20070502135927-7dk86io3ok7ctx6k-2
      bzrlib/tests/test_diff.py      testdiff.py-20050727164403-d1a3496ebb12e339
      bzrlib/tests/test_dirstate.py  test_dirstate.py-20060728012006-d6mvoihjb3je9peu-2
      bzrlib/tests/test_lockable_files.py test_lockable_files.py-20051225183927-365c7fd99591caf1
      bzrlib/tests/test_lockdir.py   test_lockdir.py-20060220222025-33d4221569a3d600
      bzrlib/tests/test_merge_directive.py test_merge_directive-20070228184838-ja62280spt1g7f4x-2
      bzrlib/tests/test_msgeditor.py test_msgeditor.py-20051202041359-920315ec6011ee51
      bzrlib/tests/test_selftest.py  test_selftest.py-20051202044319-c110a115d8c0456a
      bzrlib/tests/test_switch.py    test_switch.py-20071116011000-v5lnw7d2wkng9eux-2
      bzrlib/tests/test_transform.py test_transaction.py-20060105172520-b3ffb3946550e6c4
      bzrlib/tests/test_transport_implementations.py test_transport_implementations.py-20051227111451-f97c5c7d5c49fce7
      bzrlib/tests/test_versionedfile.py test_versionedfile.py-20060222045249-db45c9ed14a1c2e5
      bzrlib/tests/test_workingtree_4.py test_workingtree_4.p-20070223025758-531n3tznl3zacv2o-1
      bzrlib/tests/workingtree_implementations/test_parents.py test_set_parents.py-20060807231740-yicmnlci1mj8smu1-1
      bzrlib/transform.py            transform.py-20060105172343-dd99e54394d91687
      bzrlib/transport/__init__.py   transport.py-20050711165921-4978aa7ce1285ad5
      bzrlib/versionedfile.py        versionedfile.py-20060222045106-5039c71ee3b65490
      bzrlib/workingtree.py          workingtree.py-20050511021032-29b6ec0a681e02e3
      bzrlib/workingtree_4.py        workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
      contrib/bzr_access             bzr_access-20071210163004-c9lb1renhra2ncg0-1
      doc/en/user-guide/core_concepts.txt core_concepts.txt-20071114035000-q36a9h57ps06uvnl-2
      doc/en/user-guide/index.txt    index.txt-20060622101119-tgwtdci8z769bjb9-2
      doc/en/user-guide/zen.txt      revnos.txt-20080111231928-pbntxea0ynh9ww1t-1
    ------------------------------------------------------------
    revno: 3464.4.1
    revision-id:andrew.bennetts at canonical.com-20080602011217-91w8f210rc3adv6g
    parent: pqm at pqm.ubuntu.com-20080601233619-di6or8d3o26n917q
    committer: Andrew Bennetts <andrew.bennetts at canonical.com>
    branch nick: interrupted-connection-fix
    timestamp: Mon 2008-06-02 11:12:17 +1000
    message:
      Fix infinite busy-loop caused by connection loss during read of response body in HPSS v1 and v2.
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/smart/protocol.py       protocol.py-20061108035435-ot0lstk2590yqhzr-1
      bzrlib/tests/test_smart_transport.py test_ssh_transport.py-20060608202016-c25gvf1ob7ypbus6-2
=== modified file 'NEWS'
--- a/NEWS	2008-06-11 04:20:56 +0000
+++ b/NEWS	2008-06-11 12:03:48 +0000
@@ -49,6 +49,10 @@
       reading from the repository.)
       (Martin Pool, Andrew Bennetts, Robert Collins, #234748)
 
+    * Fix infinite loop consuming 100% CPU when a connection is lost while
+      reading a response body via the smart protocol v1 or v2.
+      (Andrew Bennetts)
+      
     * Inserting a bundle which changes the contents of a file with no trailing
       end of line, causing a knit snapshot in a 'knits' repository will no longer
       cause KnitCorrupt. (Robert Collins)

=== modified file 'bzrlib/smart/protocol.py'
--- a/bzrlib/smart/protocol.py	2008-05-21 02:49:58 +0000
+++ b/bzrlib/smart/protocol.py	2008-06-02 01:12:17 +0000
@@ -704,6 +704,10 @@
         while not _body_decoder.finished_reading:
             bytes_wanted = min(_body_decoder.next_read_size(), max_read)
             bytes = self._request.read_bytes(bytes_wanted)
+            if bytes == '':
+                # end of file encountered reading from server
+                raise errors.ConnectionReset(
+                    "Connection lost while reading response body.")
             _body_decoder.accept_bytes(bytes)
         self._request.finished_reading()
         self._body_buffer = StringIO(_body_decoder.read_pending_data())
@@ -805,6 +809,10 @@
         while not _body_decoder.finished_reading:
             bytes_wanted = min(_body_decoder.next_read_size(), max_read)
             bytes = self._request.read_bytes(bytes_wanted)
+            if bytes == '':
+                # end of file encountered reading from server
+                raise errors.ConnectionReset(
+                    "Connection lost while reading streamed body.")
             _body_decoder.accept_bytes(bytes)
             for body_bytes in iter(_body_decoder.read_next_chunk, None):
                 if 'hpss' in debug.debug_flags and type(body_bytes) is str:

=== modified file 'bzrlib/tests/test_smart_transport.py'
--- a/bzrlib/tests/test_smart_transport.py	2008-05-30 04:33:39 +0000
+++ b/bzrlib/tests/test_smart_transport.py	2008-06-02 01:12:17 +0000
@@ -1803,6 +1803,19 @@
         self.assertRaises(
             errors.ReadingCompleted, smart_protocol.read_body_bytes)
 
+    def test_client_read_body_bytes_interrupted_connection(self):
+        server_bytes = "ok\n999\nincomplete body"
+        input = StringIO(server_bytes)
+        output = StringIO()
+        client_medium = medium.SmartSimplePipesClientMedium(
+            input, output, 'base')
+        request = client_medium.get_request()
+        smart_protocol = self.client_protocol_class(request)
+        smart_protocol.call('foo')
+        smart_protocol.read_response_tuple(True)
+        self.assertRaises(
+            errors.ConnectionReset, smart_protocol.read_body_bytes)
+
 
 class TestVersionOneFeaturesInProtocolTwo(
     TestSmartProtocol, CommonSmartProtocolTestMixin):
@@ -2029,6 +2042,20 @@
         self.assertRaises(
             errors.ReadingCompleted, smart_protocol.read_body_bytes)
 
+    def test_client_read_body_bytes_interrupted_connection(self):
+        server_bytes = (self.response_marker +
+                        "success\nok\n999\nincomplete body")
+        input = StringIO(server_bytes)
+        output = StringIO()
+        client_medium = medium.SmartSimplePipesClientMedium(
+            input, output, 'base')
+        request = client_medium.get_request()
+        smart_protocol = self.client_protocol_class(request)
+        smart_protocol.call('foo')
+        smart_protocol.read_response_tuple(True)
+        self.assertRaises(
+            errors.ConnectionReset, smart_protocol.read_body_bytes)
+
 
 class TestSmartProtocolTwoSpecificsMixin(object):
 
@@ -2154,6 +2181,22 @@
         stream = smart_protocol.read_streamed_body()
         self.assertEqual(expected_chunks, list(stream))
 
+    def test_streamed_body_bytes_interrupted_connection(self):
+        body_header = 'chunked\n'
+        incomplete_body_chunk = "9999\nincomplete chunk"
+        server_bytes = (protocol.RESPONSE_VERSION_TWO +
+                        "success\nok\n" + body_header + incomplete_body_chunk)
+        input = StringIO(server_bytes)
+        output = StringIO()
+        client_medium = medium.SmartSimplePipesClientMedium(
+            input, output, 'base')
+        request = client_medium.get_request()
+        smart_protocol = protocol.SmartClientRequestProtocolTwo(request)
+        smart_protocol.call('foo')
+        smart_protocol.read_response_tuple(True)
+        stream = smart_protocol.read_streamed_body()
+        self.assertRaises(errors.ConnectionReset, stream.next)
+
     def test_client_read_response_tuple_sets_response_status(self):
         server_bytes = protocol.RESPONSE_VERSION_TWO + "success\nok\n"
         input = StringIO(server_bytes)
@@ -2338,16 +2381,7 @@
 
 class TestConventionalResponseHandler(tests.TestCase):
 
-    def test_interrupted_body_stream(self):
-        interrupted_body_stream = (
-            'oS' # successful response
-            's\0\0\0\x02le' # empty args
-            'b\0\0\0\x09chunk one' # first chunk
-            'b\0\0\0\x09chunk two' # second chunk
-            'oE' # error flag
-            's\0\0\0\x0el5:error3:abce' # bencoded error
-            'e' # message end
-            )
+    def make_response_handler(self, response_bytes):
         from bzrlib.smart.message import ConventionalResponseHandler
         response_handler = ConventionalResponseHandler()
         protocol_decoder = protocol.ProtocolThreeDecoder(response_handler)
@@ -2355,17 +2389,48 @@
         protocol_decoder.state_accept = protocol_decoder._state_accept_expecting_message_part
         output = StringIO()
         client_medium = medium.SmartSimplePipesClientMedium(
-            StringIO(interrupted_body_stream), output, 'base')
+            StringIO(response_bytes), output, 'base')
         medium_request = client_medium.get_request()
         medium_request.finished_writing()
         response_handler.setProtoAndMediumRequest(
             protocol_decoder, medium_request)
+        return response_handler
+
+    def test_body_stream_interrupted_by_error(self):
+        interrupted_body_stream = (
+            'oS' # successful response
+            's\0\0\0\x02le' # empty args
+            'b\0\0\0\x09chunk one' # first chunk
+            'b\0\0\0\x09chunk two' # second chunk
+            'oE' # error flag
+            's\0\0\0\x0el5:error3:abce' # bencoded error
+            'e' # message end
+            )
+        response_handler = self.make_response_handler(interrupted_body_stream)
         stream = response_handler.read_streamed_body()
         self.assertEqual('chunk one', stream.next())
         self.assertEqual('chunk two', stream.next())
         exc = self.assertRaises(errors.ErrorFromSmartServer, stream.next)
         self.assertEqual(('error', 'abc'), exc.error_tuple)
 
+    def test_body_stream_interrupted_by_connection_lost(self):
+        interrupted_body_stream = (
+            'oS' # successful response
+            's\0\0\0\x02le' # empty args
+            'b\0\0\xff\xffincomplete chunk')
+        response_handler = self.make_response_handler(interrupted_body_stream)
+        stream = response_handler.read_streamed_body()
+        self.assertRaises(errors.ConnectionReset, stream.next)
+
+    def test_read_body_bytes_interrupted_by_connection_lost(self):
+        interrupted_body_stream = (
+            'oS' # successful response
+            's\0\0\0\x02le' # empty args
+            'b\0\0\xff\xffincomplete chunk')
+        response_handler = self.make_response_handler(interrupted_body_stream)
+        self.assertRaises(
+            errors.ConnectionReset, response_handler.read_body_bytes)
+
 
 class TestMessageHandlerErrors(tests.TestCase):
     """Tests for v3 that unrecognised (but well-formed) requests/responses are




More information about the bazaar-commits mailing list