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

  • Committer: Jelmer Vernooij
  • Date: 2020-07-18 15:20:23 UTC
  • mto: (7490.40.61 work)
  • mto: This revision was merged to the branch mainline in revision 7519.
  • Revision ID: jelmer@jelmer.uk-20200718152023-cabh92o24ke217te
Ignore missing revs.

Show diffs side-by-side

added added

removed removed

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