/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_http_response.py

  • Committer: Andrew Bennetts
  • Date: 2008-09-08 12:59:00 UTC
  • mfrom: (3695 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3756.
  • Revision ID: andrew.bennetts@canonical.com-20080908125900-8ywtsr7jqyyatjz0
Merge from bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
48
48
    response,
49
49
    _urllib2_wrappers,
50
50
    )
 
51
from bzrlib.tests.file_utils import (
 
52
    FakeReadFile,
 
53
    )
51
54
 
52
55
 
53
56
class ReadSocket(object):
59
62
    def makefile(self, mode='r', bufsize=None):
60
63
        return self.readfile
61
64
 
 
65
 
62
66
class FakeHTTPConnection(_urllib2_wrappers.HTTPConnection):
63
67
 
64
68
    def __init__(self, sock):
221
225
        self.assertEquals('', f.read(0))
222
226
        self.assertEquals('', f.read(1))
223
227
 
 
228
 
224
229
class TestRangeFileSizeKnown(tests.TestCase, TestRangeFileMixin):
225
230
    """Test a RangeFile for a whole file whose size is known."""
226
231
 
249
254
        f._pos = 0 # Force an invalid pos
250
255
        self.assertRaises(errors.InvalidRange, f.read, 2)
251
256
 
 
257
 
252
258
class TestRangeFileMultipleRanges(tests.TestCase, TestRangeFileMixin):
253
259
    """Test a RangeFile for multiple ranges.
254
260
 
262
268
    fact) in real uses but may lead to hard to track bugs.
263
269
    """
264
270
 
 
271
    # The following is used to represent the boundary paramter defined
 
272
    # in HTTP response headers and the boundary lines that separate
 
273
    # multipart content.
 
274
 
 
275
    boundary = "separation"
 
276
 
265
277
    def setUp(self):
266
278
        super(TestRangeFileMultipleRanges, self).setUp()
267
279
 
268
 
        boundary = 'separation'
 
280
        boundary = self.boundary
269
281
 
270
282
        content = ''
271
283
        self.first_range_start = 25
277
289
            content += self._multipart_byterange(part, start, boundary,
278
290
                                                 file_size)
279
291
        # Final boundary
280
 
        content += self._boundary_line(boundary)
 
292
        content += self._boundary_line()
281
293
 
282
294
        self._file = response.RangeFile('Multiple_ranges_file',
283
295
                                        StringIO(content))
 
296
        self.set_file_boundary()
 
297
 
 
298
    def _boundary_line(self):
 
299
        """Helper to build the formatted boundary line."""
 
300
        return '--' + self.boundary + '\r\n'
 
301
 
 
302
    def set_file_boundary(self):
284
303
        # Ranges are set by decoding the range headers, the RangeFile user is
285
304
        # supposed to call the following before using seek or read since it
286
305
        # requires knowing the *response* headers (in that case the boundary
287
306
        # which is part of the Content-Type header).
288
 
        self._file.set_boundary(boundary)
289
 
 
290
 
    def _boundary_line(self, boundary):
291
 
        """Helper to build the formatted boundary line."""
292
 
        return '--' + boundary + '\r\n'
 
307
        self._file.set_boundary(self.boundary)
293
308
 
294
309
    def _multipart_byterange(self, data, offset, boundary, file_size='*'):
295
310
        """Encode a part of a file as a multipart/byterange MIME type.
307
322
        :return: a string containing the data encoded as it will appear in the
308
323
            HTTP response body.
309
324
        """
310
 
        bline = self._boundary_line(boundary)
 
325
        bline = self._boundary_line()
311
326
        # Each range begins with a boundary line
312
327
        range = bline
313
328
        # A range is described by a set of headers, but only 'Content-Range' is
391
406
        self.assertRaises(errors.InvalidHttpResponse, f.read, 1)
392
407
 
393
408
 
 
409
class TestRangeFileMultipleRangesQuotedBoundaries(TestRangeFileMultipleRanges):
 
410
    """Perform the same tests as TestRangeFileMultipleRanges, but uses 
 
411
    an angle-bracket quoted boundary string like IIS 6.0 and 7.0
 
412
    (but not IIS 5, which breaks the RFC in a different way
 
413
    by using square brackets, not angle brackets)
 
414
    
 
415
    This reveals a bug caused by 
 
416
    
 
417
    - The bad implementation of RFC 822 unquoting in Python (angles are not 
 
418
      quotes), coupled with 
 
419
 
 
420
    - The bad implementation of RFC 2046 in IIS (angles are not permitted chars
 
421
      in boundary lines).
 
422
 
 
423
    """
 
424
    # The boundary as it appears in boundary lines
 
425
    # IIS 6 and 7 use this value
 
426
    _boundary_trimmed = "q1w2e3r4t5y6u7i8o9p0zaxscdvfbgnhmjklkl"
 
427
    boundary = '<' + _boundary_trimmed + '>'
 
428
 
 
429
    def set_file_boundary(self):
 
430
        # Emulate broken rfc822.unquote() here by removing angles
 
431
        self._file.set_boundary(self._boundary_trimmed)
 
432
 
 
433
 
394
434
class TestRangeFileVarious(tests.TestCase):
395
435
    """Tests RangeFile aspects not covered elsewhere."""
396
436
 
753
793
        out.read()  # Read the whole range
754
794
        # Fail to find the boundary line
755
795
        self.assertRaises(errors.InvalidHttpResponse, out.seek, 1, 1)
 
796
 
 
797
 
 
798
class TestRangeFileSizeReadLimited(tests.TestCase):
 
799
    """Test RangeFile _max_read_size functionality which limits the size of
 
800
    read blocks to prevent MemoryError messages in socket.recv.
 
801
    """
 
802
 
 
803
    def setUp(self):
 
804
        # create a test datablock larger than _max_read_size.
 
805
        chunk_size = response.RangeFile._max_read_size
 
806
        test_pattern = '0123456789ABCDEF'
 
807
        self.test_data =  test_pattern * (3 * chunk_size / len(test_pattern))
 
808
        self.test_data_len = len(self.test_data)
 
809
 
 
810
    def test_max_read_size(self):
 
811
        """Read data in blocks and verify that the reads are not larger than
 
812
           the maximum read size.
 
813
        """
 
814
        # retrieve data in large blocks from response.RangeFile object
 
815
        mock_read_file = FakeReadFile(self.test_data)
 
816
        range_file = response.RangeFile('test_max_read_size', mock_read_file)
 
817
        response_data = range_file.read(self.test_data_len)
 
818
 
 
819
        # verify read size was equal to the maximum read size
 
820
        self.assertTrue(mock_read_file.get_max_read_size() > 0)
 
821
        self.assertEqual(mock_read_file.get_max_read_size(),
 
822
                         response.RangeFile._max_read_size)
 
823
        self.assertEqual(mock_read_file.get_read_count(), 3)
 
824
 
 
825
        # report error if the data wasn't equal (we only report the size due
 
826
        # to the length of the data)
 
827
        if response_data != self.test_data:
 
828
            message = "Data not equal.  Expected %d bytes, received %d."
 
829
            self.fail(message % (len(response_data), self.test_data_len))
 
830