/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: Robert Collins
  • Date: 2010-05-11 08:36:16 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100511083616-b8fjb19zomwupid0
Make all lock methods return Result objects, rather than lock_read returning self, as per John's review.

Show diffs side-by-side

added added

removed removed

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