62
59
def makefile(self, mode='r', bufsize=None):
63
60
return self.readfile
66
62
class FakeHTTPConnection(_urllib2_wrappers.HTTPConnection):
68
64
def __init__(self, sock):
225
221
self.assertEquals('', f.read(0))
226
222
self.assertEquals('', f.read(1))
229
224
class TestRangeFileSizeKnown(tests.TestCase, TestRangeFileMixin):
230
225
"""Test a RangeFile for a whole file whose size is known."""
254
249
f._pos = 0 # Force an invalid pos
255
250
self.assertRaises(errors.InvalidRange, f.read, 2)
258
class TestRangeFileMultipleRanges(tests.TestCase, TestRangeFileMixin):
252
class TestRangeFilMultipleRanges(tests.TestCase, TestRangeFileMixin):
259
253
"""Test a RangeFile for multiple ranges.
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.
271
# The following is used to represent the boundary paramter defined
272
# in HTTP response headers and the boundary lines that separate
275
boundary = "separation"
278
super(TestRangeFileMultipleRanges, self).setUp()
266
super(TestRangeFilMultipleRanges, self).setUp()
280
boundary = self.boundary
268
boundary = 'separation'
283
271
self.first_range_start = 25
289
277
content += self._multipart_byterange(part, start, boundary,
292
content += self._boundary_line()
280
content += self._boundary_line(boundary)
294
282
self._file = response.RangeFile('Multiple_ranges_file',
295
283
StringIO(content))
296
self.set_file_boundary()
298
def _boundary_line(self):
299
"""Helper to build the formatted boundary line."""
300
return '--' + self.boundary + '\r\n'
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)
290
def _boundary_line(self, boundary):
291
"""Helper to build the formatted boundary line."""
292
return '--' + boundary + '\r\n'
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.
325
bline = self._boundary_line()
310
bline = self._boundary_line(boundary)
326
311
# Each range begins with a boundary line
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))
379
def test_checked_read_dont_overflow_buffers(self):
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))
387
364
def test_seek_twice_between_ranges(self):
389
366
start = self.first_range_start
406
383
self.assertRaises(errors.InvalidHttpResponse, f.read, 1)
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)
415
This reveals a bug caused by
417
- The bad implementation of RFC 822 unquoting in Python (angles are not
418
quotes), coupled with
420
- The bad implementation of RFC 2046 in IIS (angles are not permitted chars
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 + '>'
429
def set_file_boundary(self):
430
# Emulate broken rfc822.unquote() here by removing angles
431
self._file.set_boundary(self._boundary_trimmed)
434
386
class TestRangeFileVarious(tests.TestCase):
435
387
"""Tests RangeFile aspects not covered elsewhere."""
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)
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.
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)
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.
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)
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)
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))