/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 brzlib/tests/test_http_response.py

  • Committer: Jelmer Vernooij
  • Date: 2017-05-21 12:41:27 UTC
  • mto: This revision was merged to the branch mainline in revision 6623.
  • Revision ID: jelmer@jelmer.uk-20170521124127-iv8etg0vwymyai6y
s/bzr/brz/ in apport config.

Show diffs side-by-side

added added

removed removed

Lines of Context:
37
37
  InvalidHttpResponse.
38
38
"""
39
39
 
40
 
from io import BytesIO
41
 
 
42
 
import http.client as http_client
43
 
 
44
 
parse_headers = http_client.parse_headers
45
 
 
46
 
from .. import (
 
40
from cStringIO import StringIO
 
41
import httplib
 
42
 
 
43
from brzlib import (
47
44
    errors,
48
45
    tests,
49
46
    )
50
 
from ..transport.http import (
 
47
from brzlib.transport.http import (
51
48
    response,
52
 
    HTTPConnection,
 
49
    _urllib2_wrappers,
53
50
    )
54
 
from .file_utils import (
 
51
from brzlib.tests.file_utils import (
55
52
    FakeReadFile,
56
53
    )
57
54
 
60
57
    """A socket-like object that can be given a predefined content."""
61
58
 
62
59
    def __init__(self, data):
63
 
        self.readfile = BytesIO(data)
 
60
        self.readfile = StringIO(data)
64
61
 
65
62
    def makefile(self, mode='r', bufsize=None):
66
63
        return self.readfile
67
64
 
68
65
 
69
 
class FakeHTTPConnection(HTTPConnection):
 
66
class FakeHTTPConnection(_urllib2_wrappers.HTTPConnection):
70
67
 
71
68
    def __init__(self, sock):
72
 
        HTTPConnection.__init__(self, 'localhost')
 
69
        _urllib2_wrappers.HTTPConnection.__init__(self, 'localhost')
73
70
        # Set the socket to bypass the connection
74
71
        self.sock = sock
75
72
 
81
78
class TestResponseFileIter(tests.TestCase):
82
79
 
83
80
    def test_iter_empty(self):
84
 
        f = response.ResponseFile('empty', BytesIO())
 
81
        f = response.ResponseFile('empty', StringIO())
85
82
        self.assertEqual([], list(f))
86
83
 
87
84
    def test_iter_many(self):
88
 
        f = response.ResponseFile('many', BytesIO(b'0\n1\nboo!\n'))
89
 
        self.assertEqual([b'0\n', b'1\n', b'boo!\n'], list(f))
 
85
        f = response.ResponseFile('many', StringIO('0\n1\nboo!\n'))
 
86
        self.assertEqual(['0\n', '1\n', 'boo!\n'], list(f))
90
87
 
91
88
 
92
89
class TestHTTPConnection(tests.TestCase):
93
90
 
94
91
    def test_cleanup_pipe(self):
95
 
        sock = ReadSocket(b"""HTTP/1.1 200 OK\r
 
92
        sock = ReadSocket("""HTTP/1.1 200 OK\r
96
93
Content-Type: text/plain; charset=UTF-8\r
97
94
Content-Length: 18
98
95
\r
106
103
        # Now, get the response
107
104
        resp = conn.getresponse()
108
105
        # Read part of the response
109
 
        self.assertEqual(b'0123456789\n', resp.read(11))
 
106
        self.assertEqual('0123456789\n', resp.read(11))
110
107
        # Override the thresold to force the warning emission
111
 
        conn._range_warning_thresold = 6  # There are 7 bytes pending
 
108
        conn._range_warning_thresold = 6 # There are 7 bytes pending
112
109
        conn.cleanup_pipe()
113
110
        self.assertContainsRe(self.get_log(), 'Got a 200 response when asking')
114
111
 
120
117
    # which offsets are easy to calculate for test writers. It's used as a
121
118
    # building block with slight variations but basically 'a' is the first char
122
119
    # of the range and 'z' is the last.
123
 
    alpha = b'abcdefghijklmnopqrstuvwxyz'
 
120
    alpha = 'abcdefghijklmnopqrstuvwxyz'
124
121
 
125
122
    def test_can_read_at_first_access(self):
126
123
        """Test that the just created file can be read."""
132
129
        start = self.first_range_start
133
130
        # Before any use, tell() should be at the range start
134
131
        self.assertEqual(start, f.tell())
135
 
        cur = start  # For an overall offset assertion
 
132
        cur = start # For an overall offset assertion
136
133
        f.seek(start + 3)
137
134
        cur += 3
138
 
        self.assertEqual(b'def', f.read(3))
 
135
        self.assertEqual('def', f.read(3))
139
136
        cur += len('def')
140
137
        f.seek(4, 1)
141
138
        cur += 4
142
 
        self.assertEqual(b'klmn', f.read(4))
 
139
        self.assertEqual('klmn', f.read(4))
143
140
        cur += len('klmn')
144
141
        # read(0) in the middle of a range
145
 
        self.assertEqual(b'', f.read(0))
 
142
        self.assertEqual('', f.read(0))
146
143
        # seek in place
147
144
        here = f.tell()
148
145
        f.seek(0, 1)
151
148
 
152
149
    def test_read_zero(self):
153
150
        f = self._file
154
 
        self.assertEqual(b'', f.read(0))
 
151
        self.assertEqual('', f.read(0))
155
152
        f.seek(10, 1)
156
 
        self.assertEqual(b'', f.read(0))
 
153
        self.assertEqual('', f.read(0))
157
154
 
158
155
    def test_seek_at_range_end(self):
159
156
        f = self._file
163
160
        """Test read behaviour at range end."""
164
161
        f = self._file
165
162
        self.assertEqual(self.alpha, f.read())
166
 
        self.assertEqual(b'', f.read(0))
 
163
        self.assertEqual('', f.read(0))
167
164
        self.assertRaises(errors.InvalidRange, f.read, 1)
168
165
 
169
166
    def test_unbounded_read_after_seek(self):
170
167
        f = self._file
171
168
        f.seek(24, 1)
172
169
        # Should not cross ranges
173
 
        self.assertEqual(b'yz', f.read())
 
170
        self.assertEqual('yz', f.read())
174
171
 
175
172
    def test_seek_backwards(self):
176
173
        f = self._file
196
193
        self.assertRaises(errors.InvalidRange, f.read, 10)
197
194
 
198
195
    def test_seek_from_end(self):
199
 
        """Test seeking from the end of the file.
200
 
 
201
 
        The semantic is unclear in case of multiple ranges. Seeking from end
202
 
        exists only for the http transports, cannot be used if the file size is
203
 
        unknown and is not used in breezy itself. This test must be (and is)
204
 
        overridden by daughter classes.
205
 
 
206
 
        Reading from end makes sense only when a range has been requested from
207
 
        the end of the file (see HttpTransportBase._get() when using the
208
 
        'tail_amount' parameter). The HTTP response can only be a whole file or
209
 
        a single range.
210
 
        """
211
 
        f = self._file
212
 
        f.seek(-2, 2)
213
 
        self.assertEqual(b'yz', f.read())
 
196
       """Test seeking from the end of the file.
 
197
 
 
198
       The semantic is unclear in case of multiple ranges. Seeking from end
 
199
       exists only for the http transports, cannot be used if the file size is
 
200
       unknown and is not used in brzlib itself. This test must be (and is)
 
201
       overridden by daughter classes.
 
202
 
 
203
       Reading from end makes sense only when a range has been requested from
 
204
       the end of the file (see HttpTransportBase._get() when using the
 
205
       'tail_amount' parameter). The HTTP response can only be a whole file or
 
206
       a single range.
 
207
       """
 
208
       f = self._file
 
209
       f.seek(-2, 2)
 
210
       self.assertEqual('yz', f.read())
214
211
 
215
212
 
216
213
class TestRangeFileSizeUnknown(tests.TestCase, TestRangeFileMixin):
219
216
    def setUp(self):
220
217
        super(TestRangeFileSizeUnknown, self).setUp()
221
218
        self._file = response.RangeFile('Whole_file_size_known',
222
 
                                        BytesIO(self.alpha))
 
219
                                        StringIO(self.alpha))
223
220
        # We define no range, relying on RangeFile to provide default values
224
 
        self.first_range_start = 0  # It's the whole file
 
221
        self.first_range_start = 0 # It's the whole file
225
222
 
226
223
    def test_seek_from_end(self):
227
224
        """See TestRangeFileMixin.test_seek_from_end.
234
231
        """Test read behaviour at range end."""
235
232
        f = self._file
236
233
        self.assertEqual(self.alpha, f.read())
237
 
        self.assertEqual(b'', f.read(0))
238
 
        self.assertEqual(b'', f.read(1))
 
234
        self.assertEqual('', f.read(0))
 
235
        self.assertEqual('', f.read(1))
239
236
 
240
237
 
241
238
class TestRangeFileSizeKnown(tests.TestCase, TestRangeFileMixin):
244
241
    def setUp(self):
245
242
        super(TestRangeFileSizeKnown, self).setUp()
246
243
        self._file = response.RangeFile('Whole_file_size_known',
247
 
                                        BytesIO(self.alpha))
 
244
                                        StringIO(self.alpha))
248
245
        self._file.set_range(0, len(self.alpha))
249
 
        self.first_range_start = 0  # It's the whole file
 
246
        self.first_range_start = 0 # It's the whole file
250
247
 
251
248
 
252
249
class TestRangeFileSingleRange(tests.TestCase, TestRangeFileMixin):
255
252
    def setUp(self):
256
253
        super(TestRangeFileSingleRange, self).setUp()
257
254
        self._file = response.RangeFile('Single_range_file',
258
 
                                        BytesIO(self.alpha))
 
255
                                        StringIO(self.alpha))
259
256
        self.first_range_start = 15
260
257
        self._file.set_range(self.first_range_start, len(self.alpha))
261
258
 
 
259
 
262
260
    def test_read_before_range(self):
263
261
        # This can't occur under normal circumstances, we have to force it
264
262
        f = self._file
265
 
        f._pos = 0  # Force an invalid pos
 
263
        f._pos = 0 # Force an invalid pos
266
264
        self.assertRaises(errors.InvalidRange, f.read, 2)
267
265
 
268
266
 
283
281
    # in HTTP response headers and the boundary lines that separate
284
282
    # multipart content.
285
283
 
286
 
    boundary = b"separation"
 
284
    boundary = "separation"
287
285
 
288
286
    def setUp(self):
289
287
        super(TestRangeFileMultipleRanges, self).setUp()
290
288
 
291
289
        boundary = self.boundary
292
290
 
293
 
        content = b''
 
291
        content = ''
294
292
        self.first_range_start = 25
295
 
        file_size = 200  # big enough to encompass all ranges
 
293
        file_size = 200 # big enough to encompass all ranges
296
294
        for (start, part) in [(self.first_range_start, self.alpha),
297
295
                              # Two contiguous ranges
298
296
                              (100, self.alpha),
303
301
        content += self._boundary_line()
304
302
 
305
303
        self._file = response.RangeFile('Multiple_ranges_file',
306
 
                                        BytesIO(content))
 
304
                                        StringIO(content))
307
305
        self.set_file_boundary()
308
306
 
309
307
    def _boundary_line(self):
310
308
        """Helper to build the formatted boundary line."""
311
 
        return b'--' + self.boundary + b'\r\n'
 
309
        return '--' + self.boundary + '\r\n'
312
310
 
313
311
    def set_file_boundary(self):
314
312
        # Ranges are set by decoding the range headers, the RangeFile user is
317
315
        # which is part of the Content-Type header).
318
316
        self._file.set_boundary(self.boundary)
319
317
 
320
 
    def _multipart_byterange(self, data, offset, boundary, file_size=b'*'):
 
318
    def _multipart_byterange(self, data, offset, boundary, file_size='*'):
321
319
        """Encode a part of a file as a multipart/byterange MIME type.
322
320
 
323
321
        When a range request is issued, the HTTP response body can be
339
337
        # A range is described by a set of headers, but only 'Content-Range' is
340
338
        # required for our implementation (TestHandleResponse below will
341
339
        # exercise ranges with multiple or missing headers')
342
 
        if isinstance(file_size, int):
343
 
            file_size = b'%d' % file_size
344
 
        range += b'Content-Range: bytes %d-%d/%s\r\n' % (offset,
345
 
                                                         offset +
346
 
                                                         len(data) - 1,
347
 
                                                         file_size)
348
 
        range += b'\r\n'
 
340
        range += 'Content-Range: bytes %d-%d/%d\r\n' % (offset,
 
341
                                                        offset+len(data)-1,
 
342
                                                        file_size)
 
343
        range += '\r\n'
349
344
        # Finally the raw bytes
350
345
        range += data
351
346
        return range
352
347
 
353
348
    def test_read_all_ranges(self):
354
349
        f = self._file
355
 
        self.assertEqual(self.alpha, f.read())  # Read first range
356
 
        f.seek(100)  # Trigger the second range recognition
357
 
        self.assertEqual(self.alpha, f.read())  # Read second range
 
350
        self.assertEqual(self.alpha, f.read()) # Read first range
 
351
        f.seek(100) # Trigger the second range recognition
 
352
        self.assertEqual(self.alpha, f.read()) # Read second range
358
353
        self.assertEqual(126, f.tell())
359
 
        f.seek(126)  # Start of third range which is also the current pos !
360
 
        self.assertEqual(b'A', f.read(1))
 
354
        f.seek(126) # Start of third range which is also the current pos !
 
355
        self.assertEqual('A', f.read(1))
361
356
        f.seek(10, 1)
362
 
        self.assertEqual(b'LMN', f.read(3))
 
357
        self.assertEqual('LMN', f.read(3))
363
358
 
364
359
    def test_seek_from_end(self):
365
360
        """See TestRangeFileMixin.test_seek_from_end."""
369
364
        # behaviour.
370
365
        f = self._file
371
366
        f.seek(-2, 2)
372
 
        self.assertEqual(b'yz', f.read())
 
367
        self.assertEqual('yz', f.read())
373
368
        self.assertRaises(errors.InvalidRange, f.seek, -2, 2)
374
369
 
375
370
    def test_seek_into_void(self):
386
381
 
387
382
    def test_seek_across_ranges(self):
388
383
        f = self._file
389
 
        f.seek(126)  # skip the two first ranges
390
 
        self.assertEqual(b'AB', f.read(2))
 
384
        f.seek(126) # skip the two first ranges
 
385
        self.assertEqual('AB', f.read(2))
391
386
 
392
387
    def test_checked_read_dont_overflow_buffers(self):
393
388
        f = self._file
394
389
        # We force a very low value to exercise all code paths in _checked_read
395
390
        f._discarded_buf_size = 8
396
 
        f.seek(126)  # skip the two first ranges
397
 
        self.assertEqual(b'AB', f.read(2))
 
391
        f.seek(126) # skip the two first ranges
 
392
        self.assertEqual('AB', f.read(2))
398
393
 
399
394
    def test_seek_twice_between_ranges(self):
400
395
        f = self._file
401
396
        start = self.first_range_start
402
 
        f.seek(start + 40)  # Past the first range but before the second
 
397
        f.seek(start + 40) # Past the first range but before the second
403
398
        # Now the file is positioned at the second range start (100)
404
399
        self.assertRaises(errors.InvalidRange, f.seek, start + 41)
405
400
 
435
430
    """
436
431
    # The boundary as it appears in boundary lines
437
432
    # IIS 6 and 7 use this value
438
 
    _boundary_trimmed = b"q1w2e3r4t5y6u7i8o9p0zaxscdvfbgnhmjklkl"
439
 
    boundary = b'<' + _boundary_trimmed + b'>'
 
433
    _boundary_trimmed = "q1w2e3r4t5y6u7i8o9p0zaxscdvfbgnhmjklkl"
 
434
    boundary = '<' + _boundary_trimmed + '>'
440
435
 
441
436
    def set_file_boundary(self):
442
437
        # Emulate broken rfc822.unquote() here by removing angles
448
443
 
449
444
    def test_seek_whence(self):
450
445
        """Test the seek whence parameter values."""
451
 
        f = response.RangeFile('foo', BytesIO(b'abc'))
 
446
        f = response.RangeFile('foo', StringIO('abc'))
452
447
        f.set_range(0, 3)
453
448
        f.seek(0)
454
449
        f.seek(1, 1)
458
453
    def test_range_syntax(self):
459
454
        """Test the Content-Range scanning."""
460
455
 
461
 
        f = response.RangeFile('foo', BytesIO())
 
456
        f = response.RangeFile('foo', StringIO())
462
457
 
463
458
        def ok(expected, header_value):
464
459
            f.set_range_from_header(header_value)
470
465
        ok((12, 2), '\tbytes 12-13/*')
471
466
        ok((28, 1), '  bytes 28-28/*')
472
467
        ok((2123, 2120), 'bytes  2123-4242/12310')
473
 
        ok((1, 10), 'bytes 1-10/ttt')  # We don't check total (ttt)
 
468
        ok((1, 10), 'bytes 1-10/ttt') # We don't check total (ttt)
474
469
 
475
470
        def nok(header_value):
476
471
            self.assertRaises(errors.InvalidHttpRange,
485
480
 
486
481
 
487
482
# Taken from real request responses
488
 
_full_text_response = (200, b"""HTTP/1.1 200 OK\r
 
483
_full_text_response = (200, """HTTP/1.1 200 OK\r
489
484
Date: Tue, 11 Jul 2006 04:32:56 GMT\r
490
485
Server: Apache/2.0.54 (Fedora)\r
491
486
Last-Modified: Sun, 23 Apr 2006 19:35:20 GMT\r
495
490
Connection: close\r
496
491
Content-Type: text/plain; charset=UTF-8\r
497
492
\r
498
 
""", b"""Bazaar-NG meta directory, format 1
 
493
""", """Bazaar-NG meta directory, format 1
499
494
""")
500
495
 
501
496
 
502
 
_single_range_response = (206, b"""HTTP/1.1 206 Partial Content\r
 
497
_single_range_response = (206, """HTTP/1.1 206 Partial Content\r
503
498
Date: Tue, 11 Jul 2006 04:45:22 GMT\r
504
499
Server: Apache/2.0.54 (Fedora)\r
505
500
Last-Modified: Thu, 06 Jul 2006 20:22:05 GMT\r
510
505
Connection: close\r
511
506
Content-Type: text/plain; charset=UTF-8\r
512
507
\r
513
 
""", b"""mbp@sourcefrog.net-20050309040815-13242001617e4a06
 
508
""", """mbp@sourcefrog.net-20050309040815-13242001617e4a06
514
509
mbp@sourcefrog.net-20050309040929-eee0eb3e6d1e762""")
515
510
 
516
511
 
517
 
_single_range_no_content_type = (206, b"""HTTP/1.1 206 Partial Content\r
 
512
_single_range_no_content_type = (206, """HTTP/1.1 206 Partial Content\r
518
513
Date: Tue, 11 Jul 2006 04:45:22 GMT\r
519
514
Server: Apache/2.0.54 (Fedora)\r
520
515
Last-Modified: Thu, 06 Jul 2006 20:22:05 GMT\r
524
519
Content-Range: bytes 100-199/93890\r
525
520
Connection: close\r
526
521
\r
527
 
""", b"""mbp@sourcefrog.net-20050309040815-13242001617e4a06
 
522
""", """mbp@sourcefrog.net-20050309040815-13242001617e4a06
528
523
mbp@sourcefrog.net-20050309040929-eee0eb3e6d1e762""")
529
524
 
530
525
 
531
 
_multipart_range_response = (206, b"""HTTP/1.1 206 Partial Content\r
 
526
_multipart_range_response = (206, """HTTP/1.1 206 Partial Content\r
532
527
Date: Tue, 11 Jul 2006 04:49:48 GMT\r
533
528
Server: Apache/2.0.54 (Fedora)\r
534
529
Last-Modified: Thu, 06 Jul 2006 20:22:05 GMT\r
538
533
Connection: close\r
539
534
Content-Type: multipart/byteranges; boundary=418470f848b63279b\r
540
535
\r
541
 
\r""", b"""--418470f848b63279b\r
 
536
\r""", """--418470f848b63279b\r
542
537
Content-type: text/plain; charset=UTF-8\r
543
538
Content-range: bytes 0-254/93890\r
544
539
\r
578
573
""")
579
574
 
580
575
 
581
 
_multipart_squid_range_response = (206, b"""HTTP/1.0 206 Partial Content\r
 
576
_multipart_squid_range_response = (206, """HTTP/1.0 206 Partial Content\r
582
577
Date: Thu, 31 Aug 2006 21:16:22 GMT\r
583
578
Server: Apache/2.2.2 (Unix) DAV/2\r
584
579
Last-Modified: Thu, 31 Aug 2006 17:57:06 GMT\r
590
585
Proxy-Connection: keep-alive\r
591
586
\r
592
587
""",
593
 
                                   b"""\r
 
588
"""\r
594
589
--squid/2.5.STABLE12:C99323425AD4FE26F726261FA6C24196\r
595
590
Content-Type: text/plain\r
596
591
Content-Range: bytes 0-99/18672\r
611
606
 
612
607
 
613
608
# This is made up
614
 
_full_text_response_no_content_type = (200, b"""HTTP/1.1 200 OK\r
 
609
_full_text_response_no_content_type = (200, """HTTP/1.1 200 OK\r
615
610
Date: Tue, 11 Jul 2006 04:32:56 GMT\r
616
611
Server: Apache/2.0.54 (Fedora)\r
617
612
Last-Modified: Sun, 23 Apr 2006 19:35:20 GMT\r
620
615
Content-Length: 35\r
621
616
Connection: close\r
622
617
\r
623
 
""", b"""Bazaar-NG meta directory, format 1
 
618
""", """Bazaar-NG meta directory, format 1
624
619
""")
625
620
 
626
621
 
627
 
_full_text_response_no_content_length = (200, b"""HTTP/1.1 200 OK\r
 
622
_full_text_response_no_content_length = (200, """HTTP/1.1 200 OK\r
628
623
Date: Tue, 11 Jul 2006 04:32:56 GMT\r
629
624
Server: Apache/2.0.54 (Fedora)\r
630
625
Last-Modified: Sun, 23 Apr 2006 19:35:20 GMT\r
633
628
Connection: close\r
634
629
Content-Type: text/plain; charset=UTF-8\r
635
630
\r
636
 
""", b"""Bazaar-NG meta directory, format 1
 
631
""", """Bazaar-NG meta directory, format 1
637
632
""")
638
633
 
639
634
 
640
 
_single_range_no_content_range = (206, b"""HTTP/1.1 206 Partial Content\r
 
635
_single_range_no_content_range = (206, """HTTP/1.1 206 Partial Content\r
641
636
Date: Tue, 11 Jul 2006 04:45:22 GMT\r
642
637
Server: Apache/2.0.54 (Fedora)\r
643
638
Last-Modified: Thu, 06 Jul 2006 20:22:05 GMT\r
646
641
Content-Length: 100\r
647
642
Connection: close\r
648
643
\r
649
 
""", b"""mbp@sourcefrog.net-20050309040815-13242001617e4a06
 
644
""", """mbp@sourcefrog.net-20050309040815-13242001617e4a06
650
645
mbp@sourcefrog.net-20050309040929-eee0eb3e6d1e762""")
651
646
 
652
647
 
653
 
_single_range_response_truncated = (206, b"""HTTP/1.1 206 Partial Content\r
 
648
_single_range_response_truncated = (206, """HTTP/1.1 206 Partial Content\r
654
649
Date: Tue, 11 Jul 2006 04:45:22 GMT\r
655
650
Server: Apache/2.0.54 (Fedora)\r
656
651
Last-Modified: Thu, 06 Jul 2006 20:22:05 GMT\r
661
656
Connection: close\r
662
657
Content-Type: text/plain; charset=UTF-8\r
663
658
\r
664
 
""", b"""mbp@sourcefrog.net-20050309040815-13242001617e4a06""")
665
 
 
666
 
 
667
 
_invalid_response = (444, b"""HTTP/1.1 444 Bad Response\r
 
659
""", """mbp@sourcefrog.net-20050309040815-13242001617e4a06""")
 
660
 
 
661
 
 
662
_invalid_response = (444, """HTTP/1.1 444 Bad Response\r
668
663
Date: Tue, 11 Jul 2006 04:32:56 GMT\r
669
664
Connection: close\r
670
665
Content-Type: text/html; charset=iso-8859-1\r
671
666
\r
672
 
""", b"""<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
 
667
""", """<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
673
668
<html><head>
674
669
<title>404 Not Found</title>
675
670
</head><body>
680
675
""")
681
676
 
682
677
 
683
 
_multipart_no_content_range = (206, b"""HTTP/1.0 206 Partial Content\r
 
678
_multipart_no_content_range = (206, """HTTP/1.0 206 Partial Content\r
684
679
Content-Type: multipart/byteranges; boundary=THIS_SEPARATES\r
685
680
Content-Length: 598\r
686
681
\r
687
682
""",
688
 
                               b"""\r
 
683
"""\r
689
684
--THIS_SEPARATES\r
690
685
Content-Type: text/plain\r
691
686
\r
694
689
""")
695
690
 
696
691
 
697
 
_multipart_no_boundary = (206, b"""HTTP/1.0 206 Partial Content\r
 
692
_multipart_no_boundary = (206, """HTTP/1.0 206 Partial Content\r
698
693
Content-Type: multipart/byteranges; boundary=THIS_SEPARATES\r
699
694
Content-Length: 598\r
700
695
\r
701
696
""",
702
 
                          b"""\r
 
697
"""\r
703
698
--THIS_SEPARATES\r
704
699
Content-Type: text/plain\r
705
700
Content-Range: bytes 0-18/18672\r
714
709
class TestHandleResponse(tests.TestCase):
715
710
 
716
711
    def _build_HTTPMessage(self, raw_headers):
717
 
        status_and_headers = BytesIO(raw_headers)
 
712
        status_and_headers = StringIO(raw_headers)
718
713
        # Get rid of the status line
719
714
        status_and_headers.readline()
720
 
        msg = parse_headers(status_and_headers)
721
 
        return msg.get
 
715
        msg = httplib.HTTPMessage(status_and_headers)
 
716
        return msg
722
717
 
723
718
    def get_response(self, a_response):
724
719
        """Process a supplied response, and return the result."""
725
720
        code, raw_headers, body = a_response
726
 
        getheader = self._build_HTTPMessage(raw_headers)
727
 
        return response.handle_response(
728
 
            'http://foo', code, getheader, BytesIO(a_response[2]))
 
721
        msg = self._build_HTTPMessage(raw_headers)
 
722
        return response.handle_response('http://foo', code, msg,
 
723
                                        StringIO(a_response[2]))
729
724
 
730
725
    def test_full_text(self):
731
726
        out = self.get_response(_full_text_response)
732
 
        # It is a BytesIO from the original data
 
727
        # It is a StringIO from the original data
733
728
        self.assertEqual(_full_text_response[2], out.read())
734
729
 
735
730
    def test_single_range(self):
776
771
    def test_full_text_no_content_type(self):
777
772
        # We should not require Content-Type for a full response
778
773
        code, raw_headers, body = _full_text_response_no_content_type
779
 
        getheader = self._build_HTTPMessage(raw_headers)
780
 
        out = response.handle_response(
781
 
            'http://foo', code, getheader, BytesIO(body))
 
774
        msg = self._build_HTTPMessage(raw_headers)
 
775
        out = response.handle_response('http://foo', code, msg, StringIO(body))
782
776
        self.assertEqual(body, out.read())
783
777
 
784
778
    def test_full_text_no_content_length(self):
785
779
        code, raw_headers, body = _full_text_response_no_content_length
786
 
        getheader = self._build_HTTPMessage(raw_headers)
787
 
        out = response.handle_response(
788
 
            'http://foo', code, getheader, BytesIO(body))
 
780
        msg = self._build_HTTPMessage(raw_headers)
 
781
        out = response.handle_response('http://foo', code, msg, StringIO(body))
789
782
        self.assertEqual(body, out.read())
790
783
 
791
784
    def test_missing_content_range(self):
792
785
        code, raw_headers, body = _single_range_no_content_range
793
 
        getheader = self._build_HTTPMessage(raw_headers)
 
786
        msg = self._build_HTTPMessage(raw_headers)
794
787
        self.assertRaises(errors.InvalidHttpResponse,
795
788
                          response.handle_response,
796
 
                          'http://bogus', code, getheader, BytesIO(body))
 
789
                          'http://bogus', code, msg, StringIO(body))
797
790
 
798
791
    def test_multipart_no_content_range(self):
799
792
        code, raw_headers, body = _multipart_no_content_range
800
 
        getheader = self._build_HTTPMessage(raw_headers)
 
793
        msg = self._build_HTTPMessage(raw_headers)
801
794
        self.assertRaises(errors.InvalidHttpResponse,
802
795
                          response.handle_response,
803
 
                          'http://bogus', code, getheader, BytesIO(body))
 
796
                          'http://bogus', code, msg, StringIO(body))
804
797
 
805
798
    def test_multipart_no_boundary(self):
806
799
        out = self.get_response(_multipart_no_boundary)
818
811
        super(TestRangeFileSizeReadLimited, self).setUp()
819
812
        # create a test datablock larger than _max_read_size.
820
813
        chunk_size = response.RangeFile._max_read_size
821
 
        test_pattern = b'0123456789ABCDEF'
822
 
        self.test_data = test_pattern * (3 * chunk_size // len(test_pattern))
 
814
        test_pattern = '0123456789ABCDEF'
 
815
        self.test_data =  test_pattern * (3 * chunk_size / len(test_pattern))
823
816
        self.test_data_len = len(self.test_data)
824
817
 
825
818
    def test_max_read_size(self):
842
835
        if response_data != self.test_data:
843
836
            message = "Data not equal.  Expected %d bytes, received %d."
844
837
            self.fail(message % (len(response_data), self.test_data_len))
 
838