~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_http_response.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-12-20 16:16:34 UTC
  • mfrom: (3123.5.18 hardlinks)
  • Revision ID: pqm@pqm.ubuntu.com-20071220161634-2kcjb650o21ydko4
Accelerate build_tree using similar workingtrees (abentley)

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
 
    )
54
51
 
55
52
 
56
53
class ReadSocket(object):
62
59
    def makefile(self, mode='r', bufsize=None):
63
60
        return self.readfile
64
61
 
65
 
 
66
62
class FakeHTTPConnection(_urllib2_wrappers.HTTPConnection):
67
63
 
68
64
    def __init__(self, sock):
225
221
        self.assertEquals('', f.read(0))
226
222
        self.assertEquals('', f.read(1))
227
223
 
228
 
 
229
224
class TestRangeFileSizeKnown(tests.TestCase, TestRangeFileMixin):
230
225
    """Test a RangeFile for a whole file whose size is known."""
231
226
 
254
249
        f._pos = 0 # Force an invalid pos
255
250
        self.assertRaises(errors.InvalidRange, f.read, 2)
256
251
 
257
 
 
258
 
class TestRangeFileMultipleRanges(tests.TestCase, TestRangeFileMixin):
 
252
class TestRangeFilMultipleRanges(tests.TestCase, TestRangeFileMixin):
259
253
    """Test a RangeFile for multiple ranges.
260
254
 
261
255
    The RangeFile used for the tests contains three ranges:
268
262
    fact) in real uses but may lead to hard to track bugs.
269
263
    """
270
264
 
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
 
 
277
265
    def setUp(self):
278
 
        super(TestRangeFileMultipleRanges, self).setUp()
 
266
        super(TestRangeFilMultipleRanges, self).setUp()
279
267
 
280
 
        boundary = self.boundary
 
268
        boundary = 'separation'
281
269
 
282
270
        content = ''
283
271
        self.first_range_start = 25
289
277
            content += self._multipart_byterange(part, start, boundary,
290
278
                                                 file_size)
291
279
        # Final boundary
292
 
        content += self._boundary_line()
 
280
        content += self._boundary_line(boundary)
293
281
 
294
282
        self._file = response.RangeFile('Multiple_ranges_file',
295
283
                                        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):
303
284
        # Ranges are set by decoding the range headers, the RangeFile user is
304
285
        # supposed to call the following before using seek or read since it
305
286
        # requires knowing the *response* headers (in that case the boundary
306
287
        # which is part of the Content-Type header).
307
 
        self._file.set_boundary(self.boundary)
 
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'
308
293
 
309
294
    def _multipart_byterange(self, data, offset, boundary, file_size='*'):
310
295
        """Encode a part of a file as a multipart/byterange MIME type.
322
307
        :return: a string containing the data encoded as it will appear in the
323
308
            HTTP response body.
324
309
        """
325
 
        bline = self._boundary_line()
 
310
        bline = self._boundary_line(boundary)
326
311
        # Each range begins with a boundary line
327
312
        range = bline
328
313
        # A range is described by a set of headers, but only 'Content-Range' is
376
361
        f.seek(126) # skip the two first ranges
377
362
        self.assertEquals('AB', f.read(2))
378
363
 
379
 
    def test_checked_read_dont_overflow_buffers(self):
380
 
        f = self._file
381
 
        start = self.first_range_start
382
 
        # We force a very low value to exercise all code paths in _checked_read
383
 
        f._discarded_buf_size = 8
384
 
        f.seek(126) # skip the two first ranges
385
 
        self.assertEquals('AB', f.read(2))
386
 
 
387
364
    def test_seek_twice_between_ranges(self):
388
365
        f = self._file
389
366
        start = self.first_range_start
406
383
        self.assertRaises(errors.InvalidHttpResponse, f.read, 1)
407
384
 
408
385
 
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
 
 
434
386
class TestRangeFileVarious(tests.TestCase):
435
387
    """Tests RangeFile aspects not covered elsewhere."""
436
388
 
793
745
        out.read()  # Read the whole range
794
746
        # Fail to find the boundary line
795
747
        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