/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: Ian Clatworthy
  • Date: 2007-12-14 03:20:15 UTC
  • mto: (3147.1.1 ianc-integration)
  • mto: This revision was merged to the branch mainline in revision 3148.
  • Revision ID: ian.clatworthy@internode.on.net-20071214032015-m2bz4qyxnt44mser
include feedback from kiko

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
"""Tests from HTTP response parsing.
18
18
 
44
44
    errors,
45
45
    tests,
46
46
    )
47
 
from bzrlib.transport.http import (
48
 
    response,
49
 
    _urllib2_wrappers,
50
 
    )
51
 
from bzrlib.tests.file_utils import (
52
 
    FakeReadFile,
53
 
    )
54
 
 
55
 
 
56
 
class ReadSocket(object):
57
 
    """A socket-like object that can be given a predefined content."""
58
 
 
59
 
    def __init__(self, data):
60
 
        self.readfile = StringIO(data)
61
 
 
62
 
    def makefile(self, mode='r', bufsize=None):
63
 
        return self.readfile
64
 
 
65
 
 
66
 
class FakeHTTPConnection(_urllib2_wrappers.HTTPConnection):
67
 
 
68
 
    def __init__(self, sock):
69
 
        _urllib2_wrappers.HTTPConnection.__init__(self, 'localhost')
70
 
        # Set the socket to bypass the connection
71
 
        self.sock = sock
72
 
 
73
 
    def send(self, str):
74
 
        """Ignores the writes on the socket."""
75
 
        pass
76
 
 
77
 
 
78
 
class TestHTTPConnection(tests.TestCase):
79
 
 
80
 
    def test_cleanup_pipe(self):
81
 
        sock = ReadSocket("""HTTP/1.1 200 OK\r
82
 
Content-Type: text/plain; charset=UTF-8\r
83
 
Content-Length: 18
84
 
\r
85
 
0123456789
86
 
garbage""")
87
 
        conn = FakeHTTPConnection(sock)
88
 
        # Simulate the request sending so that the connection will be able to
89
 
        # read the response.
90
 
        conn.putrequest('GET', 'http://localhost/fictious')
91
 
        conn.endheaders()
92
 
        # Now, get the response
93
 
        resp = conn.getresponse()
94
 
        # Read part of the response
95
 
        self.assertEquals('0123456789\n', resp.read(11))
96
 
        # Override the thresold to force the warning emission
97
 
        conn._range_warning_thresold = 6 # There are 7 bytes pending
98
 
        conn.cleanup_pipe()
99
 
        self.assertContainsRe(self.get_log(), 'Got a 200 response when asking')
 
47
from bzrlib.transport.http import response
100
48
 
101
49
 
102
50
class TestRangeFileMixin(object):
224
172
        self.assertEquals('', f.read(0))
225
173
        self.assertEquals('', f.read(1))
226
174
 
227
 
 
228
175
class TestRangeFileSizeKnown(tests.TestCase, TestRangeFileMixin):
229
176
    """Test a RangeFile for a whole file whose size is known."""
230
177
 
253
200
        f._pos = 0 # Force an invalid pos
254
201
        self.assertRaises(errors.InvalidRange, f.read, 2)
255
202
 
256
 
 
257
 
class TestRangeFileMultipleRanges(tests.TestCase, TestRangeFileMixin):
 
203
class TestRangeFilMultipleRanges(tests.TestCase, TestRangeFileMixin):
258
204
    """Test a RangeFile for multiple ranges.
259
205
 
260
206
    The RangeFile used for the tests contains three ranges:
267
213
    fact) in real uses but may lead to hard to track bugs.
268
214
    """
269
215
 
270
 
    # The following is used to represent the boundary paramter defined
271
 
    # in HTTP response headers and the boundary lines that separate
272
 
    # multipart content.
273
 
 
274
 
    boundary = "separation"
275
 
 
276
216
    def setUp(self):
277
 
        super(TestRangeFileMultipleRanges, self).setUp()
 
217
        super(TestRangeFilMultipleRanges, self).setUp()
278
218
 
279
 
        boundary = self.boundary
 
219
        boundary = 'separation'
280
220
 
281
221
        content = ''
282
222
        self.first_range_start = 25
288
228
            content += self._multipart_byterange(part, start, boundary,
289
229
                                                 file_size)
290
230
        # Final boundary
291
 
        content += self._boundary_line()
 
231
        content += self._boundary_line(boundary)
292
232
 
293
233
        self._file = response.RangeFile('Multiple_ranges_file',
294
234
                                        StringIO(content))
295
 
        self.set_file_boundary()
296
 
 
297
 
    def _boundary_line(self):
298
 
        """Helper to build the formatted boundary line."""
299
 
        return '--' + self.boundary + '\r\n'
300
 
 
301
 
    def set_file_boundary(self):
302
235
        # Ranges are set by decoding the range headers, the RangeFile user is
303
236
        # supposed to call the following before using seek or read since it
304
237
        # requires knowing the *response* headers (in that case the boundary
305
238
        # which is part of the Content-Type header).
306
 
        self._file.set_boundary(self.boundary)
 
239
        self._file.set_boundary(boundary)
 
240
 
 
241
    def _boundary_line(self, boundary):
 
242
        """Helper to build the formatted boundary line."""
 
243
        return '--' + boundary + '\r\n'
307
244
 
308
245
    def _multipart_byterange(self, data, offset, boundary, file_size='*'):
309
246
        """Encode a part of a file as a multipart/byterange MIME type.
321
258
        :return: a string containing the data encoded as it will appear in the
322
259
            HTTP response body.
323
260
        """
324
 
        bline = self._boundary_line()
 
261
        bline = self._boundary_line(boundary)
325
262
        # Each range begins with a boundary line
326
263
        range = bline
327
264
        # A range is described by a set of headers, but only 'Content-Range' is
375
312
        f.seek(126) # skip the two first ranges
376
313
        self.assertEquals('AB', f.read(2))
377
314
 
378
 
    def test_checked_read_dont_overflow_buffers(self):
379
 
        f = self._file
380
 
        start = self.first_range_start
381
 
        # We force a very low value to exercise all code paths in _checked_read
382
 
        f._discarded_buf_size = 8
383
 
        f.seek(126) # skip the two first ranges
384
 
        self.assertEquals('AB', f.read(2))
385
 
 
386
315
    def test_seek_twice_between_ranges(self):
387
316
        f = self._file
388
317
        start = self.first_range_start
405
334
        self.assertRaises(errors.InvalidHttpResponse, f.read, 1)
406
335
 
407
336
 
408
 
class TestRangeFileMultipleRangesQuotedBoundaries(TestRangeFileMultipleRanges):
409
 
    """Perform the same tests as TestRangeFileMultipleRanges, but uses
410
 
    an angle-bracket quoted boundary string like IIS 6.0 and 7.0
411
 
    (but not IIS 5, which breaks the RFC in a different way
412
 
    by using square brackets, not angle brackets)
413
 
 
414
 
    This reveals a bug caused by
415
 
 
416
 
    - The bad implementation of RFC 822 unquoting in Python (angles are not
417
 
      quotes), coupled with
418
 
 
419
 
    - The bad implementation of RFC 2046 in IIS (angles are not permitted chars
420
 
      in boundary lines).
421
 
 
422
 
    """
423
 
    # The boundary as it appears in boundary lines
424
 
    # IIS 6 and 7 use this value
425
 
    _boundary_trimmed = "q1w2e3r4t5y6u7i8o9p0zaxscdvfbgnhmjklkl"
426
 
    boundary = '<' + _boundary_trimmed + '>'
427
 
 
428
 
    def set_file_boundary(self):
429
 
        # Emulate broken rfc822.unquote() here by removing angles
430
 
        self._file.set_boundary(self._boundary_trimmed)
431
 
 
432
 
 
433
337
class TestRangeFileVarious(tests.TestCase):
434
338
    """Tests RangeFile aspects not covered elsewhere."""
435
339
 
792
696
        out.read()  # Read the whole range
793
697
        # Fail to find the boundary line
794
698
        self.assertRaises(errors.InvalidHttpResponse, out.seek, 1, 1)
795
 
 
796
 
 
797
 
class TestRangeFileSizeReadLimited(tests.TestCase):
798
 
    """Test RangeFile _max_read_size functionality which limits the size of
799
 
    read blocks to prevent MemoryError messages in socket.recv.
800
 
    """
801
 
 
802
 
    def setUp(self):
803
 
        tests.TestCase.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