/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: Marius Kruger
  • Date: 2010-07-10 21:28:56 UTC
  • mto: (5384.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 5385.
  • Revision ID: marius.kruger@enerweb.co.za-20100710212856-uq4ji3go0u5se7hx
* Update documentation
* add NEWS

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