71
72
def vary_by_http_client_implementation():
72
73
"""Test the libraries we can use, currently just urllib."""
73
74
transport_scenarios = [
74
('urllib', dict(_transport=HttpTransport,
75
_server=http_server.HttpServer,
76
_url_protocol='http',)),
75
('urllib', dict(_transport=_urllib.HttpTransport_urllib,
76
_server=http_server.HttpServer_urllib,
77
_url_protocol='http+urllib',)),
78
79
return transport_scenarios
81
82
def vary_by_http_protocol_version():
82
83
"""Test on http/1.0 and 1.1"""
84
('HTTP/1.0', dict(_protocol_version='HTTP/1.0')),
85
('HTTP/1.1', dict(_protocol_version='HTTP/1.1')),
85
('HTTP/1.0', dict(_protocol_version='HTTP/1.0')),
86
('HTTP/1.1', dict(_protocol_version='HTTP/1.1')),
119
120
def vary_by_http_activity():
120
121
activity_scenarios = [
121
122
('urllib,http', dict(_activity_server=ActivityHTTPServer,
122
_transport=HttpTransport,)),
123
_transport=_urllib.HttpTransport_urllib,)),
124
125
if features.HTTPSServerFeature.available():
125
126
# FIXME: Until we have a better way to handle self-signed certificates
133
class HTTPS_transport(HttpTransport):
133
class HTTPS_urllib_transport(_urllib.HttpTransport_urllib):
135
135
def __init__(self, base, _from_transport=None):
136
super(HTTPS_transport, self).__init__(
136
super(HTTPS_urllib_transport, self).__init__(
137
137
base, _from_transport=_from_transport,
138
138
ca_certs=ssl_certs.build_path('ca.crt'))
140
140
activity_scenarios.append(
141
141
('urllib,https', dict(_activity_server=ActivityHTTPSServer,
142
_transport=HTTPS_transport,)),)
142
_transport=HTTPS_urllib_transport,)),)
143
143
return activity_scenarios
221
221
def parse_header(self, header, auth_handler_class=None):
222
222
if auth_handler_class is None:
223
auth_handler_class = http.AbstractAuthHandler
224
self.auth_handler = auth_handler_class()
223
auth_handler_class = _urllib2_wrappers.AbstractAuthHandler
224
self.auth_handler = auth_handler_class()
225
225
return self.auth_handler._parse_auth_header(header)
227
227
def test_empty_header(self):
253
253
def test_basic_extract_realm(self):
254
254
scheme, remainder = self.parse_header(
255
255
'Basic realm="Thou should not pass"',
256
http.BasicAuthHandler)
256
_urllib2_wrappers.BasicAuthHandler)
257
257
match, realm = self.auth_handler.extract_realm(remainder)
258
258
self.assertTrue(match is not None)
259
self.assertEqual(u'Thou should not pass', realm)
259
self.assertEqual('Thou should not pass', realm)
261
261
def test_digest_header(self):
262
262
scheme, remainder = self.parse_header(
271
271
super(TestHTTPRangeParsing, self).setUp()
272
272
# We focus on range parsing here and ignore everything else
274
273
class RequestHandler(http_server.TestingHTTPRequestHandler):
275
274
def setup(self): pass
277
275
def handle(self): pass
279
276
def finish(self): pass
281
278
self.req_handler = RequestHandler(None, None, None)
283
280
def assertRanges(self, ranges, header, file_size):
284
281
self.assertEqual(ranges,
285
self.req_handler._parse_ranges(header, file_size))
282
self.req_handler._parse_ranges(header, file_size))
287
284
def test_simple_range(self):
288
self.assertRanges([(0, 2)], 'bytes=0-2', 12)
285
self.assertRanges([(0,2)], 'bytes=0-2', 12)
290
287
def test_tail(self):
291
288
self.assertRanges([(8, 11)], 'bytes=-4', 12)
311
308
protocol_version = 'HTTP/0.1'
313
self.assertRaises(UnknownProtocol,
310
self.assertRaises(httplib.UnknownProtocol,
314
311
http_server.HttpServer, BogusRequestHandler)
316
313
def test_force_invalid_protocol(self):
317
self.assertRaises(UnknownProtocol,
314
self.assertRaises(httplib.UnknownProtocol,
318
315
http_server.HttpServer, protocol_version='HTTP/0.1')
320
317
def test_server_start_and_stop(self):
426
423
self.assertEqual(t.has('foo/bar'), True)
427
424
self.assertEqual(len(server.logs), 1)
428
425
self.assertContainsRe(server.logs[0],
429
r'"HEAD /foo/bar HTTP/1.." (200|302) - "-" "Breezy/')
426
r'"HEAD /foo/bar HTTP/1.." (200|302) - "-" "bzr/')
431
428
def test_http_has_not_found(self):
432
429
server = self.get_readonly_server()
433
430
t = self.get_readonly_transport()
434
431
self.assertEqual(t.has('not-found'), False)
435
432
self.assertContainsRe(server.logs[1],
436
r'"HEAD /not-found HTTP/1.." 404 - "-" "Breezy/')
433
r'"HEAD /not-found HTTP/1.." 404 - "-" "bzr/')
438
435
def test_http_get(self):
439
436
server = self.get_readonly_server()
484
481
def test_post_body_is_received(self):
485
server = RecordingServer(expect_body_tail=b'end-of-body',
482
server = RecordingServer(expect_body_tail='end-of-body',
486
483
scheme=self._url_protocol)
487
484
self.start_server(server)
488
485
url = server.get_url()
489
486
# FIXME: needs a cleanup -- vila 20100611
490
487
http_transport = transport.get_transport_from_url(url)
491
code, response = http_transport._post(b'abc def end-of-body')
493
server.received_bytes.startswith(b'POST /.bzr/smart HTTP/1.'))
495
b'content-length: 19\r' in server.received_bytes.lower())
496
self.assertTrue(b'content-type: application/octet-stream\r'
488
code, response = http_transport._post('abc def end-of-body')
490
server.received_bytes.startswith('POST /.bzr/smart HTTP/1.'))
491
self.assertTrue('content-length: 19\r' in server.received_bytes.lower())
492
self.assertTrue('content-type: application/octet-stream\r'
497
493
in server.received_bytes.lower())
498
494
# The transport should not be assuming that the server can accept
499
495
# chunked encoding the first time it connects, because HTTP/1.1, so we
500
496
# check for the literal string.
502
server.received_bytes.endswith(b'\r\n\r\nabc def end-of-body'))
498
server.received_bytes.endswith('\r\n\r\nabc def end-of-body'))
505
501
class TestRangeHeader(tests.TestCase):
506
502
"""Test range_header method"""
508
504
def check_header(self, value, ranges=[], tail=0):
509
offsets = [(start, end - start + 1) for start, end in ranges]
505
offsets = [ (start, end - start + 1) for start, end in ranges]
510
506
coalesce = transport.Transport._coalesce_offsets
511
507
coalesced = list(coalesce(offsets, limit=0, fudge_factor=0))
512
range_header = http.HttpTransport._range_header
508
range_header = http.HttpTransportBase._range_header
513
509
self.assertEqual(value, range_header(coalesced, tail))
515
511
def test_range_header_single(self):
516
self.check_header('0-9', ranges=[(0, 9)])
517
self.check_header('100-109', ranges=[(100, 109)])
512
self.check_header('0-9', ranges=[(0,9)])
513
self.check_header('100-109', ranges=[(100,109)])
519
515
def test_range_header_tail(self):
520
516
self.check_header('-10', tail=10)
523
519
def test_range_header_multi(self):
524
520
self.check_header('0-9,100-200,300-5000',
525
ranges=[(0, 9), (100, 200), (300, 5000)])
521
ranges=[(0,9), (100, 200), (300,5000)])
527
523
def test_range_header_mixed(self):
528
524
self.check_header('0-9,300-5000,-50',
529
ranges=[(0, 9), (300, 5000)],
525
ranges=[(0,9), (300,5000)],
628
624
def parse_request(self):
629
625
"""Fakes handling a single HTTP request, returns a bad status"""
630
626
ignored = http_server.TestingHTTPRequestHandler.parse_request(self)
631
self.wfile.write(b"Invalid status line\r\n")
627
self.wfile.write("Invalid status line\r\n")
632
628
# If we don't close the connection pycurl will hang. Since this is a
633
629
# stress test we don't *have* to respect the protocol, but we don't
634
630
# have to sabotage it too much either.
653
649
ignored = http_server.TestingHTTPRequestHandler.parse_request(self)
654
650
# Returns an invalid protocol version, but curl just
655
651
# ignores it and those cannot be tested.
656
self.wfile.write(b"%s %d %s\r\n" % (
657
b'HTTP/0.0', 404, b'Look at my protocol version'))
652
self.wfile.write("%s %d %s\r\n" % ('HTTP/0.0',
654
'Look at my protocol version'))
716
713
self.assertEqual(None, server.port)
718
715
def test_send_receive_bytes(self):
719
server = RecordingServer(expect_body_tail=b'c', scheme='http')
716
server = RecordingServer(expect_body_tail='c', scheme='http')
720
717
self.start_server(server)
721
718
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
722
719
sock.connect((server.host, server.port))
724
self.assertEqual(b'HTTP/1.1 200 OK\r\n',
721
self.assertEqual('HTTP/1.1 200 OK\r\n',
725
722
osutils.recv_all(sock, 4096))
726
self.assertEqual(b'abc', server.received_bytes)
723
self.assertEqual('abc', server.received_bytes)
729
726
class TestRangeRequestServer(TestSpecificRequestHandler):
736
733
super(TestRangeRequestServer, self).setUp()
737
self.build_tree_contents([('a', b'0123456789')],)
734
self.build_tree_contents([('a', '0123456789')],)
739
736
def test_readv(self):
740
737
t = self.get_readonly_transport()
741
738
l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
742
self.assertEqual(l[0], (0, b'0'))
743
self.assertEqual(l[1], (1, b'1'))
744
self.assertEqual(l[2], (3, b'34'))
745
self.assertEqual(l[3], (9, b'9'))
739
self.assertEqual(l[0], (0, '0'))
740
self.assertEqual(l[1], (1, '1'))
741
self.assertEqual(l[2], (3, '34'))
742
self.assertEqual(l[3], (9, '9'))
747
744
def test_readv_out_of_order(self):
748
745
t = self.get_readonly_transport()
749
746
l = list(t.readv('a', ((1, 1), (9, 1), (0, 1), (3, 2))))
750
self.assertEqual(l[0], (1, b'1'))
751
self.assertEqual(l[1], (9, b'9'))
752
self.assertEqual(l[2], (0, b'0'))
753
self.assertEqual(l[3], (3, b'34'))
747
self.assertEqual(l[0], (1, '1'))
748
self.assertEqual(l[1], (9, '9'))
749
self.assertEqual(l[2], (0, '0'))
750
self.assertEqual(l[3], (3, '34'))
755
752
def test_readv_invalid_ranges(self):
756
753
t = self.get_readonly_transport()
758
755
# This is intentionally reading off the end of the file
759
756
# since we are sure that it cannot get there
760
757
self.assertListRaises((errors.InvalidRange, errors.ShortReadvError,),
761
t.readv, 'a', [(1, 1), (8, 10)])
758
t.readv, 'a', [(1,1), (8,10)])
763
760
# This is trying to seek past the end of the file, it should
764
761
# also raise a special error
765
762
self.assertListRaises((errors.InvalidRange, errors.ShortReadvError,),
766
t.readv, 'a', [(12, 2)])
763
t.readv, 'a', [(12,2)])
768
765
def test_readv_multiple_get_requests(self):
769
766
server = self.get_readonly_server()
772
769
t._max_readv_combine = 1
773
770
t._max_get_ranges = 1
774
771
l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
775
self.assertEqual(l[0], (0, b'0'))
776
self.assertEqual(l[1], (1, b'1'))
777
self.assertEqual(l[2], (3, b'34'))
778
self.assertEqual(l[3], (9, b'9'))
772
self.assertEqual(l[0], (0, '0'))
773
self.assertEqual(l[1], (1, '1'))
774
self.assertEqual(l[2], (3, '34'))
775
self.assertEqual(l[3], (9, '9'))
779
776
# The server should have issued 4 requests
780
777
self.assertEqual(4, server.GET_request_nb)
787
784
# single range will keep its size even if bigger than the limit.
788
785
t._get_max_size = 2
789
786
l = list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
790
self.assertEqual(l[0], (0, b'0'))
791
self.assertEqual(l[1], (1, b'1'))
792
self.assertEqual(l[2], (2, b'2345'))
793
self.assertEqual(l[3], (6, b'6789'))
787
self.assertEqual(l[0], (0, '0'))
788
self.assertEqual(l[1], (1, '1'))
789
self.assertEqual(l[2], (2, '2345'))
790
self.assertEqual(l[3], (6, '6789'))
794
791
# The server should have issued 3 requests
795
792
self.assertEqual(3, server.GET_request_nb)
802
799
list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
803
800
# The server should have issued 3 requests
804
801
self.assertEqual(3, server.GET_request_nb)
805
self.assertEqual(b'0123456789', t.get_bytes('a'))
802
self.assertEqual('0123456789', t.get_bytes('a'))
806
803
self.assertEqual(4, server.GET_request_nb)
808
805
def test_incomplete_readv_leave_pipe_clean(self):
813
810
# Don't collapse readv results into a list so that we leave unread
814
811
# bytes on the socket
815
812
ireadv = iter(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
816
self.assertEqual((0, b'0'), next(ireadv))
813
self.assertEqual((0, '0'), next(ireadv))
817
814
# The server should have issued one request so far
818
815
self.assertEqual(1, server.GET_request_nb)
819
self.assertEqual(b'0123456789', t.get_bytes('a'))
816
self.assertEqual('0123456789', t.get_bytes('a'))
820
817
# get_bytes issued an additional request, the readv pending ones are
822
819
self.assertEqual(2, server.GET_request_nb)
884
881
self.send_header('Accept-Ranges', 'bytes')
885
882
# XXX: this is strange; the 'random' name below seems undefined and
886
883
# yet the tests pass -- mbp 2010-10-11 bug 658773
887
boundary = "%d" % random.randint(0, 0x7FFFFFFF)
884
boundary = "%d" % random.randint(0,0x7FFFFFFF)
888
885
self.send_header("Content-Type",
889
886
"multipart/byteranges; boundary=%s" % boundary)
890
887
self.end_headers()
891
888
for (start, end) in ranges:
892
self.wfile.write(b"--%s\r\n" % boundary.encode('ascii'))
889
self.wfile.write("--%s\r\n" % boundary)
893
890
self.send_header("Content-type", 'application/octet-stream')
894
891
self.send_header("Content-Range", "bytes %d-%d/%d" % (start,
931
928
'Content-type', 'application/octet-stream')
932
929
content_length += self._header_line_length(
933
930
'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
934
content_length += len('\r\n') # end headers
935
content_length += end - start # + 1
931
content_length += len('\r\n') # end headers
932
content_length += end - start # + 1
936
933
content_length += len(boundary_line)
937
934
self.send_header('Content-length', content_length)
938
935
self.end_headers()
969
966
# Force separate ranges for each offset
970
967
t._bytes_to_read_before_seek = 0
971
968
ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
972
self.assertEqual((0, b'0'), next(ireadv))
973
self.assertEqual((2, b'2'), next(ireadv))
969
self.assertEqual((0, '0'), next(ireadv))
970
self.assertEqual((2, '2'), next(ireadv))
974
971
# Only one request have been issued so far
975
972
self.assertEqual(1, server.GET_request_nb)
976
self.assertEqual((4, b'45'), next(ireadv))
977
self.assertEqual((9, b'9'), next(ireadv))
973
self.assertEqual((4, '45'), next(ireadv))
974
self.assertEqual((9, '9'), next(ireadv))
978
975
# We issue 3 requests: two multiple (4 ranges, then 2 ranges) then a
980
977
self.assertEqual(3, server.GET_request_nb)
1004
1001
'Content-type', 'application/octet-stream')
1005
1002
content_length += self._header_line_length(
1006
1003
'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
1007
content_length += len('\r\n') # end headers
1008
content_length += end - start # + 1
1004
content_length += len('\r\n') # end headers
1005
content_length += end - start # + 1
1009
1006
content_length += len(boundary_line)
1010
1007
self.send_header('Content-length', content_length)
1011
1008
self.end_headers()
1043
1040
# Force separate ranges for each offset
1044
1041
t._bytes_to_read_before_seek = 0
1045
1042
ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
1046
self.assertEqual((0, b'0'), next(ireadv))
1047
self.assertEqual((2, b'2'), next(ireadv))
1048
self.assertEqual((4, b'45'), next(ireadv))
1049
self.assertEqual((9, b'9'), next(ireadv))
1043
self.assertEqual((0, '0'), next(ireadv))
1044
self.assertEqual((2, '2'), next(ireadv))
1045
self.assertEqual((4, '45'), next(ireadv))
1046
self.assertEqual((9, '9'), next(ireadv))
1052
1049
class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
1094
1091
super(TestLimitedRangeRequestServer, self).setUp()
1095
1092
# We need to manipulate ranges that correspond to real chunks in the
1096
1093
# response, so we build a content appropriately.
1097
filler = b''.join([b'abcdefghij' for x in range(102)])
1098
content = b''.join([b'%04d' % v + filler for v in range(16)])
1094
filler = ''.join(['abcdefghij' for x in range(102)])
1095
content = ''.join(['%04d' % v + filler for v in range(16)])
1099
1096
self.build_tree_contents([('a', content)],)
1101
1098
def test_few_ranges(self):
1102
1099
t = self.get_readonly_transport()
1103
1100
l = list(t.readv('a', ((0, 4), (1024, 4), )))
1104
self.assertEqual(l[0], (0, b'0000'))
1105
self.assertEqual(l[1], (1024, b'0001'))
1101
self.assertEqual(l[0], (0, '0000'))
1102
self.assertEqual(l[1], (1024, '0001'))
1106
1103
self.assertEqual(1, self.get_readonly_server().GET_request_nb)
1108
1105
def test_more_ranges(self):
1109
1106
t = self.get_readonly_transport()
1110
1107
l = list(t.readv('a', ((0, 4), (1024, 4), (4096, 4), (8192, 4))))
1111
self.assertEqual(l[0], (0, b'0000'))
1112
self.assertEqual(l[1], (1024, b'0001'))
1113
self.assertEqual(l[2], (4096, b'0004'))
1114
self.assertEqual(l[3], (8192, b'0008'))
1108
self.assertEqual(l[0], (0, '0000'))
1109
self.assertEqual(l[1], (1024, '0001'))
1110
self.assertEqual(l[2], (4096, '0004'))
1111
self.assertEqual(l[3], (8192, '0008'))
1115
1112
# The server will refuse to serve the first request (too much ranges),
1116
1113
# a second request will succeed.
1117
1114
self.assertEqual(2, self.get_readonly_server().GET_request_nb)
1126
1123
def _proxied_request(self):
1127
handler = http.ProxyHandler()
1128
request = http.Request('GET', 'http://baz/buzzle')
1124
handler = _urllib2_wrappers.ProxyHandler()
1125
request = _urllib2_wrappers.Request('GET', 'http://baz/buzzle')
1129
1126
handler.set_proxy(request, 'http')
1132
1129
def assertEvaluateProxyBypass(self, expected, host, no_proxy):
1133
handler = http.ProxyHandler()
1130
handler = _urllib2_wrappers.ProxyHandler()
1134
1131
self.assertEqual(expected,
1135
handler.evaluate_proxy_bypass(host, no_proxy))
1132
handler.evaluate_proxy_bypass(host, no_proxy))
1137
1134
def test_empty_user(self):
1138
1135
self.overrideEnv('http_proxy', 'http://bar.com')
1191
1188
def setUp(self):
1192
1189
super(TestProxyHttpServer, self).setUp()
1193
1190
self.transport_secondary_server = http_utils.ProxyServer
1194
self.build_tree_contents([('foo', b'contents of foo\n'),
1195
('foo-proxied', b'proxied contents of foo\n')])
1191
self.build_tree_contents([('foo', 'contents of foo\n'),
1192
('foo-proxied', 'proxied contents of foo\n')])
1196
1193
# Let's setup some attributes for tests
1197
1194
server = self.get_readonly_server()
1198
1195
self.server_host_port = '%s:%d' % (server.host, server.port)
1203
1200
def assertProxied(self):
1204
1201
t = self.get_readonly_transport()
1205
self.assertEqual(b'proxied contents of foo\n', t.get('foo').read())
1202
self.assertEqual('proxied contents of foo\n', t.get('foo').read())
1207
1204
def assertNotProxied(self):
1208
1205
t = self.get_readonly_transport()
1209
self.assertEqual(b'contents of foo\n', t.get('foo').read())
1206
self.assertEqual('contents of foo\n', t.get('foo').read())
1211
1208
def test_http_proxy(self):
1212
1209
self.overrideEnv('http_proxy', self.proxy_url)
1260
1257
def setUp(self):
1261
1258
super(TestRanges, self).setUp()
1262
self.build_tree_contents([('a', b'0123456789')],)
1259
self.build_tree_contents([('a', '0123456789')],)
1264
1261
def create_transport_readonly_server(self):
1265
1262
return http_server.HttpServer(protocol_version=self._protocol_version)
1267
1264
def _file_contents(self, relpath, ranges):
1268
1265
t = self.get_readonly_transport()
1269
offsets = [(start, end - start + 1) for start, end in ranges]
1266
offsets = [ (start, end - start + 1) for start, end in ranges]
1270
1267
coalesce = t._coalesce_offsets
1271
1268
coalesced = list(coalesce(offsets, limit=0, fudge_factor=0))
1272
1269
code, data = t._get(relpath, coalesced)
1273
self.assertTrue(code in (200, 206), '_get returns: %d' % code)
1270
self.assertTrue(code in (200, 206),'_get returns: %d' % code)
1274
1271
for start, end in ranges:
1275
1272
data.seek(start)
1276
1273
yield data.read(end - start + 1)
1278
1275
def _file_tail(self, relpath, tail_amount):
1279
1276
t = self.get_readonly_transport()
1280
1277
code, data = t._get(relpath, [], tail_amount)
1281
self.assertTrue(code in (200, 206), '_get returns: %d' % code)
1278
self.assertTrue(code in (200, 206),'_get returns: %d' % code)
1282
1279
data.seek(-tail_amount, 2)
1283
1280
return data.read(tail_amount)
1285
1282
def test_range_header(self):
1287
1284
self.assertEqual(
1288
[b'0', b'234'], list(self._file_contents('a', [(0, 0), (2, 4)])))
1285
['0', '234'], list(self._file_contents('a', [(0,0), (2,4)])))
1290
1287
def test_range_header_tail(self):
1291
self.assertEqual(b'789', self._file_tail('a', 3))
1288
self.assertEqual('789', self._file_tail('a', 3))
1293
1290
def test_syntactically_invalid_range_header(self):
1294
1291
self.assertListRaises(errors.InvalidHttpRange,
1295
self._file_contents, 'a', [(4, 3)])
1292
self._file_contents, 'a', [(4, 3)])
1297
1294
def test_semantically_invalid_range_header(self):
1298
1295
self.assertListRaises(errors.InvalidHttpRange,
1299
self._file_contents, 'a', [(42, 128)])
1296
self._file_contents, 'a', [(42, 128)])
1302
1299
class TestHTTPRedirections(http_utils.TestCaseWithRedirectedWebserver):
1310
1307
def setUp(self):
1311
1308
super(TestHTTPRedirections, self).setUp()
1312
self.build_tree_contents([('a', b'0123456789'),
1309
self.build_tree_contents([('a', '0123456789'),
1314
b'# Bazaar revision bundle v0.9\n#\n')
1311
'# Bazaar revision bundle v0.9\n#\n')
1317
1314
def test_redirected(self):
1318
1315
self.assertRaises(errors.RedirectRequested,
1319
1316
self.get_old_transport().get, 'a')
1322
self.get_new_transport().get('a').read())
1325
class RedirectedRequest(http.Request):
1317
self.assertEqual('0123456789', self.get_new_transport().get('a').read())
1320
class RedirectedRequest(_urllib2_wrappers.Request):
1326
1321
"""Request following redirections. """
1328
init_orig = http.Request.__init__
1323
init_orig = _urllib2_wrappers.Request.__init__
1330
1325
def __init__(self, method, url, *args, **kwargs):
1331
1326
"""Constructor.
1334
1329
# Since the tests using this class will replace
1335
# http.Request, we can't just call the base class __init__
1330
# _urllib2_wrappers.Request, we can't just call the base class __init__
1336
1331
# or we'll loop.
1337
1332
RedirectedRequest.init_orig(self, method, url, *args, **kwargs)
1338
1333
self.follow_redirections = True
1341
1336
def install_redirected_request(test):
1342
test.overrideAttr(http, 'Request', RedirectedRequest)
1337
test.overrideAttr(_urllib2_wrappers, 'Request', RedirectedRequest)
1345
1340
def cleanup_http_redirection_connections(test):
1346
1341
# Some sockets are opened but never seen by _urllib, so we trap them at
1347
# the http level to be able to clean them up.
1342
# the _urllib2_wrappers level to be able to clean them up.
1348
1343
def socket_disconnect(sock):
1350
1345
sock.shutdown(socket.SHUT_RDWR)
1352
1347
except socket.error:
1355
1349
def connect(connection):
1356
1350
test.http_connect_orig(connection)
1357
1351
test.addCleanup(socket_disconnect, connection.sock)
1358
1352
test.http_connect_orig = test.overrideAttr(
1359
http.HTTPConnection, 'connect', connect)
1353
_urllib2_wrappers.HTTPConnection, 'connect', connect)
1361
1354
def connect(connection):
1362
1355
test.https_connect_orig(connection)
1363
1356
test.addCleanup(socket_disconnect, connection.sock)
1364
1357
test.https_connect_orig = test.overrideAttr(
1365
http.HTTPSConnection, 'connect', connect)
1358
_urllib2_wrappers.HTTPSConnection, 'connect', connect)
1368
1361
class TestHTTPSilentRedirections(http_utils.TestCaseWithRedirectedWebserver):
1383
1376
super(TestHTTPSilentRedirections, self).setUp()
1384
1377
install_redirected_request(self)
1385
1378
cleanup_http_redirection_connections(self)
1386
self.build_tree_contents([('a', b'a'),
1379
self.build_tree_contents([('a','a'),
1388
('1/a', b'redirected once'),
1381
('1/a', 'redirected once'),
1390
('2/a', b'redirected twice'),
1383
('2/a', 'redirected twice'),
1392
('3/a', b'redirected thrice'),
1385
('3/a', 'redirected thrice'),
1394
('4/a', b'redirected 4 times'),
1387
('4/a', 'redirected 4 times'),
1396
('5/a', b'redirected 5 times'),
1389
('5/a', 'redirected 5 times'),
1399
1392
def test_one_redirection(self):
1400
1393
t = self.get_old_transport()
1394
req = RedirectedRequest('GET', t._remote_path('a'))
1401
1395
new_prefix = 'http://%s:%s' % (self.new_server.host,
1402
1396
self.new_server.port)
1403
1397
self.old_server.redirections = \
1404
[('(.*)', r'%s/1\1' % (new_prefix), 301), ]
1407
t.request('GET', t._remote_path('a'), retries=1).read())
1398
[('(.*)', r'%s/1\1' % (new_prefix), 301),]
1399
self.assertEqual('redirected once', t._perform(req).read())
1409
1401
def test_five_redirections(self):
1410
1402
t = self.get_old_transport()
1403
req = RedirectedRequest('GET', t._remote_path('a'))
1411
1404
old_prefix = 'http://%s:%s' % (self.old_server.host,
1412
1405
self.old_server.port)
1413
1406
new_prefix = 'http://%s:%s' % (self.new_server.host,
1419
1412
('/4(.*)', r'%s/5\1' % (new_prefix), 301),
1420
1413
('(/[^/]+)', r'%s/1\1' % (old_prefix), 301),
1423
b'redirected 5 times',
1424
t.request('GET', t._remote_path('a'), retries=6).read())
1415
self.assertEqual('redirected 5 times', t._perform(req).read())
1427
1418
class TestDoCatchRedirections(http_utils.TestCaseWithRedirectedWebserver):
1435
1426
def setUp(self):
1436
1427
super(TestDoCatchRedirections, self).setUp()
1437
self.build_tree_contents([('a', b'0123456789'), ],)
1428
self.build_tree_contents([('a', '0123456789'),],)
1438
1429
cleanup_http_redirection_connections(self)
1440
1431
self.old_transport = self.get_old_transport()
1446
1437
t = self.get_new_transport()
1448
1439
# We use None for redirected so that we fail if redirected
1449
self.assertEqual(b'0123456789',
1440
self.assertEqual('0123456789',
1450
1441
transport.do_catching_redirections(
1451
self.get_a, t, None).read())
1442
self.get_a, t, None).read())
1453
1444
def test_one_redirection(self):
1454
1445
self.redirections = 0
1458
1449
redirected_t = t._redirected_to(exception.source, exception.target)
1459
1450
return redirected_t
1461
self.assertEqual(b'0123456789',
1452
self.assertEqual('0123456789',
1462
1453
transport.do_catching_redirections(
1463
self.get_a, self.old_transport, redirected).read())
1454
self.get_a, self.old_transport, redirected).read())
1464
1455
self.assertEqual(1, self.redirections)
1466
1457
def test_redirection_loop(self):
1494
1485
password = 'foo'
1495
1486
_setup_authentication_config(scheme='http', host='localhost',
1496
1487
user=user, password=password)
1497
handler = http.HTTPAuthHandler()
1488
handler = _urllib2_wrappers.HTTPAuthHandler()
1498
1489
got_pass = handler.get_user_password(dict(
1500
1491
protocol='http',
1501
1492
host='localhost',
1505
1496
self.assertEqual((user, password), got_pass)
1517
1508
def setUp(self):
1518
1509
super(TestAuth, self).setUp()
1519
1510
self.server = self.get_readonly_server()
1520
self.build_tree_contents([('a', b'contents of a\n'),
1521
('b', b'contents of b\n'), ])
1511
self.build_tree_contents([('a', 'contents of a\n'),
1512
('b', 'contents of b\n'),])
1523
1514
def create_transport_readonly_server(self):
1524
1515
server = self._auth_server(protocol_version=self._protocol_version)
1551
1542
def test_empty_pass(self):
1552
1543
self.server.add_user('joe', '')
1553
1544
t = self.get_user_transport('joe', '')
1554
self.assertEqual(b'contents of a\n', t.get('a').read())
1545
self.assertEqual('contents of a\n', t.get('a').read())
1555
1546
# Only one 'Authentication Required' error should occur
1556
1547
self.assertEqual(1, self.server.auth_required_errors)
1558
1549
def test_user_pass(self):
1559
1550
self.server.add_user('joe', 'foo')
1560
1551
t = self.get_user_transport('joe', 'foo')
1561
self.assertEqual(b'contents of a\n', t.get('a').read())
1552
self.assertEqual('contents of a\n', t.get('a').read())
1562
1553
# Only one 'Authentication Required' error should occur
1563
1554
self.assertEqual(1, self.server.auth_required_errors)
1584
1575
t = self.get_user_transport(None, None)
1585
1576
ui.ui_factory = tests.TestUIFactory(stdin='joe\nfoo\n')
1586
1577
stdout, stderr = ui.ui_factory.stdout, ui.ui_factory.stderr
1587
self.assertEqual(b'contents of a\n', t.get('a').read())
1578
self.assertEqual('contents of a\n',t.get('a').read())
1588
1579
# stdin should be empty
1589
1580
self.assertEqual('', ui.ui_factory.stdin.readline())
1599
1590
t = self.get_user_transport('joe', None)
1600
1591
ui.ui_factory = tests.TestUIFactory(stdin='foo\n')
1601
1592
stdout, stderr = ui.ui_factory.stdout, ui.ui_factory.stderr
1602
self.assertEqual(b'contents of a\n', t.get('a').read())
1593
self.assertEqual('contents of a\n', t.get('a').read())
1603
1594
# stdin should be empty
1604
1595
self.assertEqual('', ui.ui_factory.stdin.readline())
1605
1596
self._check_password_prompt(t._unqualified_scheme, 'joe',
1607
1598
self.assertEqual('', stdout.getvalue())
1608
1599
# And we shouldn't prompt again for a different request
1609
1600
# against the same transport.
1610
self.assertEqual(b'contents of b\n', t.get('b').read())
1601
self.assertEqual('contents of b\n',t.get('b').read())
1612
1603
# And neither against a clone
1613
self.assertEqual(b'contents of b\n', t2.get('b').read())
1604
self.assertEqual('contents of b\n',t2.get('b').read())
1614
1605
# Only one 'Authentication Required' error should occur
1615
1606
self.assertEqual(1, self.server.auth_required_errors)
1625
1616
def _expected_username_prompt(self, scheme):
1626
1617
return (self._username_prompt_prefix
1627
1618
+ "%s %s:%d, Realm: '%s' username: " % (scheme.upper(),
1628
self.server.host, self.server.port,
1629
self.server.auth_realm))
1619
self.server.host, self.server.port,
1620
self.server.auth_realm))
1631
1622
def test_no_prompt_for_password_when_using_auth_config(self):
1633
1624
password = 'foo'
1634
1625
stdin_content = 'bar\n' # Not the right password
1635
1626
self.server.add_user(user, password)
1639
1630
_setup_authentication_config(scheme='http', port=self.server.port,
1640
1631
user=user, password=password)
1641
1632
# Issue a request to the server to connect
1642
with t.get('a') as f:
1643
self.assertEqual(b'contents of a\n', f.read())
1633
self.assertEqual('contents of a\n',t.get('a').read())
1644
1634
# stdin should have been left untouched
1645
1635
self.assertEqual(stdin_content, ui.ui_factory.stdin.readline())
1646
1636
# Only one 'Authentication Required' error should occur
1652
1642
raise tests.TestNotApplicable('HTTP/proxy auth digest only test')
1653
1643
self.server.add_user('joe', 'foo')
1654
1644
t = self.get_user_transport('joe', 'foo')
1655
with t.get('a') as f:
1656
self.assertEqual(b'contents of a\n', f.read())
1657
with t.get('b') as f:
1658
self.assertEqual(b'contents of b\n', f.read())
1645
self.assertEqual('contents of a\n', t.get('a').read())
1646
self.assertEqual('contents of b\n', t.get('b').read())
1659
1647
# Only one 'Authentication Required' error should have
1660
1648
# occured so far
1661
1649
self.assertEqual(1, self.server.auth_required_errors)
1662
1650
# The server invalidates the current nonce
1663
1651
self.server.auth_nonce = self.server.auth_nonce + '. No, now!'
1664
self.assertEqual(b'contents of a\n', t.get('a').read())
1652
self.assertEqual('contents of a\n', t.get('a').read())
1665
1653
# Two 'Authentication Required' errors should occur (the
1666
1654
# initial 'who are you' and a second 'who are you' with the new nonce)
1667
1655
self.assertEqual(2, self.server.auth_required_errors)
1674
1662
user=user, password=password)
1675
1663
t = self.get_user_transport(None, None)
1676
1664
# Issue a request to the server to connect
1677
with t.get('a') as f:
1678
self.assertEqual(b'contents of a\n', f.read())
1665
self.assertEqual('contents of a\n', t.get('a').read())
1679
1666
# Only one 'Authentication Required' error should occur
1680
1667
self.assertEqual(1, self.server.auth_required_errors)
1723
1709
def setUp(self):
1724
1710
super(TestProxyAuth, self).setUp()
1725
1711
# Override the contents to avoid false positives
1726
self.build_tree_contents([('a', b'not proxied contents of a\n'),
1727
('b', b'not proxied contents of b\n'),
1728
('a-proxied', b'contents of a\n'),
1729
('b-proxied', b'contents of b\n'),
1712
self.build_tree_contents([('a', 'not proxied contents of a\n'),
1713
('b', 'not proxied contents of b\n'),
1714
('a-proxied', 'contents of a\n'),
1715
('b-proxied', 'contents of b\n'),
1732
1718
def get_user_transport(self, user, password):
1805
1788
remote_transport = remote.RemoteTransport('bzr://fake_host/',
1807
1790
self.assertEqual(
1808
[(0, b"c")], list(remote_transport.readv("data-file", [(0, 1)])))
1791
[(0, "c")], list(remote_transport.readv("data-file", [(0,1)])))
1810
1793
def test_http_send_smart_request(self):
1812
post_body = b'hello\n'
1813
expected_reply_body = b'ok\x012\n'
1795
post_body = 'hello\n'
1796
expected_reply_body = 'ok\x012\n'
1815
1798
http_transport = transport.get_transport_from_url(
1816
1799
self.http_server.get_url())
1823
1806
httpd = self.http_server.server
1825
1808
socket = SampleSocket(
1826
b'POST /.bzr/smart %s \r\n' % self._protocol_version.encode('ascii') +
1809
'POST /.bzr/smart %s \r\n' % self._protocol_version
1827
1810
# HTTP/1.1 posts must have a Content-Length (but it doesn't hurt
1829
b'Content-Length: 6\r\n'
1812
+ 'Content-Length: 6\r\n'
1832
1815
# Beware: the ('localhost', 80) below is the
1833
1816
# client_address parameter, but we don't have one because
1834
1817
# we have defined a socket which is not bound to an
1838
1821
('localhost', 80),
1840
1823
response = socket.writefile.getvalue()
1841
self.assertStartsWith(
1843
b'%s 200 ' % self._protocol_version.encode('ascii'))
1824
self.assertStartsWith(response, '%s 200 ' % self._protocol_version)
1844
1825
# This includes the end of the HTTP headers, and all the body.
1845
expected_end_of_response = b'\r\n\r\nok\x012\n'
1826
expected_end_of_response = '\r\n\r\nok\x012\n'
1846
1827
self.assertEndsWith(response, expected_end_of_response)
1905
1886
'https://www.example.com/foo')
1906
1887
self.assertIsInstance(r, type(t))
1907
1888
self.assertEqual('https://www.example.com/foo/',
1910
1891
def test_redirected_to_same_host_different_protocol(self):
1911
1892
t = self._transport('http://www.example.com/foo')
1912
1893
r = t._redirected_to('http://www.example.com/foo',
1913
'bzr://www.example.com/foo')
1894
'ftp://www.example.com/foo')
1914
1895
self.assertNotEqual(type(r), type(t))
1915
self.assertEqual('bzr://www.example.com/foo/', r.external_url())
1896
self.assertEqual('ftp://www.example.com/foo/', r.external_url())
1917
1898
def test_redirected_to_same_host_specific_implementation(self):
1918
1899
t = self._transport('http://www.example.com/foo')
1944
1925
def _handle_one_request(self):
1945
1926
tcs = self.server.test_case_server
1946
1927
requestline = self.rfile.readline()
1947
headers = parse_headers(self.rfile)
1948
bytes_read = len(headers.as_bytes())
1949
bytes_read += headers.as_bytes().count(b'\n')
1950
bytes_read += len(requestline)
1951
if requestline.startswith(b'POST'):
1928
headers = self.MessageClass(self.rfile, 0)
1929
# We just read: the request, the headers, an empty line indicating the
1930
# end of the headers.
1931
bytes_read = len(requestline)
1932
for line in headers.headers:
1933
bytes_read += len(line)
1934
bytes_read += len('\r\n')
1935
if requestline.startswith('POST'):
1952
1936
# The body should be a single line (or we don't know where it ends
1953
1937
# and we don't want to issue a blocking read)
1954
1938
body = self.rfile.readline()
2000
1983
self.server = self._activity_server(self._protocol_version)
2001
1984
self.server.start_server()
2002
1985
self.addCleanup(self.server.stop_server)
2003
_activities = {} # Don't close over self and create a cycle
1986
_activities = {} # Don't close over self and create a cycle
2005
1987
def report_activity(t, bytes, direction):
2006
1988
count = _activities.get(direction, 0)
2037
2019
Bazaar-NG meta directory, format 1
2039
2021
t = self.get_transport()
2040
self.assertEqual(b'Bazaar-NG meta directory, format 1\n',
2022
self.assertEqual('Bazaar-NG meta directory, format 1\n',
2041
2023
t.get('foo/bar').read())
2042
2024
self.assertActivitiesMatch()
2044
2026
def test_has(self):
2045
self.server.canned_response = b'''HTTP/1.1 200 OK\r
2027
self.server.canned_response = '''HTTP/1.1 200 OK\r
2046
2028
Server: SimpleHTTP/0.6 Python/2.5.2\r
2047
2029
Date: Thu, 29 Jan 2009 20:21:47 GMT\r
2048
2030
Content-type: application/octet-stream\r
2108
2090
# Remember that the request is ignored and that the ranges below
2109
2091
# doesn't have to match the canned response.
2110
2092
l = list(t.readv('/foo/bar', ((0, 255), (1000, 1050))))
2111
# Force consumption of the last bytesrange boundary
2112
t._get_connection().cleanup_pipe()
2113
2093
self.assertEqual(2, len(l))
2114
2094
self.assertActivitiesMatch()
2116
2096
def test_post(self):
2117
self.server.canned_response = b'''HTTP/1.1 200 OK\r
2097
self.server.canned_response = '''HTTP/1.1 200 OK\r
2118
2098
Date: Tue, 11 Jul 2006 04:32:56 GMT\r
2119
2099
Server: Apache/2.0.54 (Fedora)\r
2120
2100
Last-Modified: Sun, 23 Apr 2006 19:35:20 GMT\r
2129
2109
t = self.get_transport()
2130
2110
# We must send a single line of body bytes, see
2131
2111
# PredefinedRequestHandler._handle_one_request
2132
code, f = t._post(b'abc def end-of-body\n')
2133
self.assertEqual(b'lalala whatever as long as itsssss\n', f.read())
2112
code, f = t._post('abc def end-of-body\n')
2113
self.assertEqual('lalala whatever as long as itsssss\n', f.read())
2134
2114
self.assertActivitiesMatch()
2175
2155
_password_prompt_prefix = ''
2176
2156
_username_prompt_prefix = ''
2177
2157
_auth_server = http_utils.HTTPBasicAuthServer
2178
_transport = HttpTransport
2158
_transport = _urllib.HttpTransport_urllib
2180
2160
def setUp(self):
2181
2161
super(TestAuthOnRedirected, self).setUp()
2182
self.build_tree_contents([('a', b'a'),
2162
self.build_tree_contents([('a','a'),
2184
('1/a', b'redirected once'),
2164
('1/a', 'redirected once'),
2186
2166
new_prefix = 'http://%s:%s' % (self.new_server.host,
2187
2167
self.new_server.port)
2188
2168
self.old_server.redirections = [
2189
('(.*)', r'%s/1\1' % (new_prefix), 301), ]
2169
('(.*)', r'%s/1\1' % (new_prefix), 301),]
2190
2170
self.old_transport = self.get_old_transport()
2191
2171
self.new_server.add_user('joe', 'foo')
2192
2172
cleanup_http_redirection_connections(self)
2209
2189
return redirected_t
2211
2191
ui.ui_factory = tests.TestUIFactory(stdin='joe\nfoo\n')
2212
self.assertEqual(b'redirected once',
2192
self.assertEqual('redirected once',
2213
2193
transport.do_catching_redirections(
2214
self.get_a, self.old_transport, redirected).read())
2194
self.get_a, self.old_transport, redirected).read())
2215
2195
self.assertEqual(1, self.redirections)
2216
2196
# stdin should be empty
2217
2197
self.assertEqual('', ui.ui_factory.stdin.readline())
2222
2202
self.new_server.add_user('joe', 'foo')
2223
2203
ui.ui_factory = tests.TestUIFactory(stdin='joe\nfoo\n')
2224
2204
t = self.old_transport
2205
req = RedirectedRequest('GET', t.abspath('a'))
2225
2206
new_prefix = 'http://%s:%s' % (self.new_server.host,
2226
2207
self.new_server.port)
2227
2208
self.old_server.redirections = [
2228
('(.*)', r'%s/1\1' % (new_prefix), 301), ]
2231
t.request('GET', t.abspath('a'), retries=3).read())
2209
('(.*)', r'%s/1\1' % (new_prefix), 301),]
2210
self.assertEqual('redirected once', t._perform(req).read())
2232
2211
# stdin should be empty
2233
2212
self.assertEqual('', ui.ui_factory.stdin.readline())
2234
2213
# stdout should be empty, stderr will contains the prompts
2235
2214
self.assertEqual('', ui.ui_factory.stdout.getvalue())