39
from bzrlib.tests.HttpServer import (
44
from bzrlib.tests.HTTPTestUtil import (
45
BadProtocolRequestHandler,
46
BadStatusRequestHandler,
47
ForbiddenRequestHandler,
50
HTTPServerRedirecting,
51
InvalidStatusRequestHandler,
52
LimitedRangeHTTPServer,
53
NoRangeRequestHandler,
55
ProxyDigestAuthServer,
57
SingleRangeRequestHandler,
58
SingleOnlyRangeRequestHandler,
59
TestCaseWithRedirectedWebserver,
60
TestCaseWithTwoWebservers,
61
TestCaseWithWebserver,
45
from bzrlib.tests import (
64
49
from bzrlib.transport import (
66
do_catching_redirections,
70
53
from bzrlib.transport.http import (
75
from bzrlib.transport.http._urllib import HttpTransport_urllib
76
from bzrlib.transport.http._urllib2_wrappers import (
60
from bzrlib.transport.http._pycurl import PyCurlTransport
62
except errors.DependencyNotPresent:
63
pycurl_present = False
66
class TransportAdapter(tests.TestScenarioApplier):
67
"""Generate the same test for each transport implementation."""
70
transport_scenarios = [
71
('urllib', dict(_transport=_urllib.HttpTransport_urllib,
72
_server=http_server.HttpServer_urllib,
73
_qualified_prefix='http+urllib',)),
76
transport_scenarios.append(
77
('pycurl', dict(_transport=PyCurlTransport,
78
_server=http_server.HttpServer_PyCurl,
79
_qualified_prefix='http+pycurl',)))
80
self.scenarios = transport_scenarios
83
class TransportProtocolAdapter(TransportAdapter):
84
"""Generate the same test for each protocol implementation.
86
In addition to the transport adaptatation that we inherit from.
90
super(TransportProtocolAdapter, self).__init__()
91
protocol_scenarios = [
92
('HTTP/1.0', dict(_protocol_version='HTTP/1.0')),
93
('HTTP/1.1', dict(_protocol_version='HTTP/1.1')),
95
self.scenarios = tests.multiply_scenarios(self.scenarios,
99
class TransportProtocolAuthenticationAdapter(TransportProtocolAdapter):
100
"""Generate the same test for each authentication scheme implementation.
102
In addition to the protocol adaptatation that we inherit from.
106
super(TransportProtocolAuthenticationAdapter, self).__init__()
107
auth_scheme_scenarios = [
108
('basic', dict(_auth_scheme='basic')),
109
('digest', dict(_auth_scheme='digest')),
112
self.scenarios = tests.multiply_scenarios(self.scenarios,
113
auth_scheme_scenarios)
115
def load_tests(standard_tests, module, loader):
116
"""Multiply tests for http clients and protocol versions."""
117
# one for each transport
118
t_adapter = TransportAdapter()
119
t_classes= (TestHttpTransportRegistration,
120
TestHttpTransportUrls,
122
is_testing_for_transports = tests.condition_isinstance(t_classes)
124
# multiplied by one for each protocol version
125
tp_adapter = TransportProtocolAdapter()
126
tp_classes= (SmartHTTPTunnellingTest,
127
TestDoCatchRedirections,
129
TestHTTPRedirections,
130
TestHTTPSilentRedirections,
131
TestLimitedRangeRequestServer,
135
TestSpecificRequestHandler,
137
is_also_testing_for_protocols = tests.condition_isinstance(tp_classes)
139
# multiplied by one for each authentication scheme
140
tpa_adapter = TransportProtocolAuthenticationAdapter()
141
tpa_classes = (TestAuth,
143
is_also_testing_for_authentication = tests.condition_isinstance(
146
result = loader.suiteClass()
147
for test_class in tests.iter_suite_tests(standard_tests):
148
# Each test class is either standalone or testing for some combination
149
# of transport, protocol version, authentication scheme. Use the right
150
# adpater (or none) depending on the class.
151
if is_testing_for_transports(test_class):
152
result.addTests(t_adapter.adapt(test_class))
153
elif is_also_testing_for_protocols(test_class):
154
result.addTests(tp_adapter.adapt(test_class))
155
elif is_also_testing_for_authentication(test_class):
156
result.addTests(tpa_adapter.adapt(test_class))
158
result.addTest(test_class)
82
162
class FakeManager(object):
228
class TestHTTPServer(tests.TestCase):
229
"""Test the HTTP servers implementations."""
231
def test_invalid_protocol(self):
232
class BogusRequestHandler(http_server.TestingHTTPRequestHandler):
234
protocol_version = 'HTTP/0.1'
236
server = http_server.HttpServer(BogusRequestHandler)
238
self.assertRaises(httplib.UnknownProtocol,server.setUp)
241
self.fail('HTTP Server creation did not raise UnknownProtocol')
243
def test_force_invalid_protocol(self):
244
server = http_server.HttpServer(protocol_version='HTTP/0.1')
246
self.assertRaises(httplib.UnknownProtocol,server.setUp)
249
self.fail('HTTP Server creation did not raise UnknownProtocol')
251
def test_server_start_and_stop(self):
252
server = http_server.HttpServer()
254
self.assertTrue(server._http_running)
256
self.assertFalse(server._http_running)
258
def test_create_http_server_one_zero(self):
259
class RequestHandlerOneZero(http_server.TestingHTTPRequestHandler):
261
protocol_version = 'HTTP/1.0'
263
server = http_server.HttpServer(RequestHandlerOneZero)
265
self.addCleanup(server.tearDown)
266
self.assertIsInstance(server._httpd, http_server.TestingHTTPServer)
268
def test_create_http_server_one_one(self):
269
class RequestHandlerOneOne(http_server.TestingHTTPRequestHandler):
271
protocol_version = 'HTTP/1.1'
273
server = http_server.HttpServer(RequestHandlerOneOne)
275
self.addCleanup(server.tearDown)
276
self.assertIsInstance(server._httpd,
277
http_server.TestingThreadingHTTPServer)
279
def test_create_http_server_force_one_one(self):
280
class RequestHandlerOneZero(http_server.TestingHTTPRequestHandler):
282
protocol_version = 'HTTP/1.0'
284
server = http_server.HttpServer(RequestHandlerOneZero,
285
protocol_version='HTTP/1.1')
287
self.addCleanup(server.tearDown)
288
self.assertIsInstance(server._httpd,
289
http_server.TestingThreadingHTTPServer)
291
def test_create_http_server_force_one_zero(self):
292
class RequestHandlerOneOne(http_server.TestingHTTPRequestHandler):
294
protocol_version = 'HTTP/1.1'
296
server = http_server.HttpServer(RequestHandlerOneOne,
297
protocol_version='HTTP/1.0')
299
self.addCleanup(server.tearDown)
300
self.assertIsInstance(server._httpd,
301
http_server.TestingHTTPServer)
148
304
class TestWithTransport_pycurl(object):
149
305
"""Test case to inherit from if pycurl is present"""
677
796
# The server should have issued 3 requests
678
797
self.assertEqual(3, server.GET_request_nb)
799
def test_complete_readv_leave_pipe_clean(self):
800
server = self.get_readonly_server()
801
t = self._transport(server.get_url())
802
# force transport to issue multiple requests
804
l = list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
805
# The server should have issued 3 requests
806
self.assertEqual(3, server.GET_request_nb)
807
self.assertEqual('0123456789', t.get_bytes('a'))
808
self.assertEqual(4, server.GET_request_nb)
810
def test_incomplete_readv_leave_pipe_clean(self):
811
server = self.get_readonly_server()
812
t = self._transport(server.get_url())
813
# force transport to issue multiple requests
815
# Don't collapse readv results into a list so that we leave unread
816
# bytes on the socket
817
ireadv = iter(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
818
self.assertEqual((0, '0'), ireadv.next())
819
# The server should have issued one request so far
820
self.assertEqual(1, server.GET_request_nb)
821
self.assertEqual('0123456789', t.get_bytes('a'))
822
# get_bytes issued an additional request, the readv pending ones are
824
self.assertEqual(2, server.GET_request_nb)
827
class SingleRangeRequestHandler(http_server.TestingHTTPRequestHandler):
828
"""Always reply to range request as if they were single.
830
Don't be explicit about it, just to annoy the clients.
833
def get_multiple_ranges(self, file, file_size, ranges):
834
"""Answer as if it was a single range request and ignores the rest"""
835
(start, end) = ranges[0]
836
return self.get_single_range(file, file_size, start, end)
681
839
class TestSingleRangeRequestServer(TestRangeRequestServer):
682
840
"""Test readv against a server which accept only single range requests"""
684
def create_transport_readonly_server(self):
685
return HttpServer(SingleRangeRequestHandler)
688
class TestSingleRangeRequestServer_urllib(TestSingleRangeRequestServer,
689
TestCaseWithWebserver):
690
"""Tests single range requests accepting server for urllib implementation"""
692
_transport = HttpTransport_urllib
695
class TestSingleRangeRequestServer_pycurl(TestWithTransport_pycurl,
696
TestSingleRangeRequestServer,
697
TestCaseWithWebserver):
698
"""Tests single range requests accepting server for pycurl implementation"""
842
_req_handler_class = SingleRangeRequestHandler
845
class SingleOnlyRangeRequestHandler(http_server.TestingHTTPRequestHandler):
846
"""Only reply to simple range requests, errors out on multiple"""
848
def get_multiple_ranges(self, file, file_size, ranges):
849
"""Refuses the multiple ranges request"""
852
self.send_error(416, "Requested range not satisfiable")
854
(start, end) = ranges[0]
855
return self.get_single_range(file, file_size, start, end)
701
858
class TestSingleOnlyRangeRequestServer(TestRangeRequestServer):
702
859
"""Test readv against a server which only accept single range requests"""
704
def create_transport_readonly_server(self):
705
return HttpServer(SingleOnlyRangeRequestHandler)
708
class TestSingleOnlyRangeRequestServer_urllib(TestSingleOnlyRangeRequestServer,
709
TestCaseWithWebserver):
710
"""Tests single range requests accepting server for urllib implementation"""
712
_transport = HttpTransport_urllib
715
class TestSingleOnlyRangeRequestServer_pycurl(TestWithTransport_pycurl,
716
TestSingleOnlyRangeRequestServer,
717
TestCaseWithWebserver):
718
"""Tests single range requests accepting server for pycurl implementation"""
861
_req_handler_class = SingleOnlyRangeRequestHandler
864
class NoRangeRequestHandler(http_server.TestingHTTPRequestHandler):
865
"""Ignore range requests without notice"""
868
# Update the statistics
869
self.server.test_case_server.GET_request_nb += 1
870
# Just bypass the range handling done by TestingHTTPRequestHandler
871
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
721
874
class TestNoRangeRequestServer(TestRangeRequestServer):
722
875
"""Test readv against a server which do not accept range requests"""
724
def create_transport_readonly_server(self):
725
return HttpServer(NoRangeRequestHandler)
728
class TestNoRangeRequestServer_urllib(TestNoRangeRequestServer,
729
TestCaseWithWebserver):
730
"""Tests range requests refusing server for urllib implementation"""
732
_transport = HttpTransport_urllib
735
class TestNoRangeRequestServer_pycurl(TestWithTransport_pycurl,
736
TestNoRangeRequestServer,
737
TestCaseWithWebserver):
738
"""Tests range requests refusing server for pycurl implementation"""
741
class TestLimitedRangeRequestServer(object):
742
"""Tests readv requests against server that errors out on too much ranges.
744
This MUST be used by daughter classes that also inherit from
745
TestCaseWithWebserver.
747
We can't inherit directly from TestCaseWithWebserver or the
748
test framework will try to create an instance which cannot
749
run, its implementation being incomplete.
877
_req_handler_class = NoRangeRequestHandler
880
class MultipleRangeWithoutContentLengthRequestHandler(
881
http_server.TestingHTTPRequestHandler):
882
"""Reply to multiple range requests without content length header."""
884
def get_multiple_ranges(self, file, file_size, ranges):
885
self.send_response(206)
886
self.send_header('Accept-Ranges', 'bytes')
887
boundary = "%d" % random.randint(0,0x7FFFFFFF)
888
self.send_header("Content-Type",
889
"multipart/byteranges; boundary=%s" % boundary)
891
for (start, end) in ranges:
892
self.wfile.write("--%s\r\n" % boundary)
893
self.send_header("Content-type", 'application/octet-stream')
894
self.send_header("Content-Range", "bytes %d-%d/%d" % (start,
898
self.send_range_content(file, start, end - start + 1)
900
self.wfile.write("--%s\r\n" % boundary)
903
class TestMultipleRangeWithoutContentLengthServer(TestRangeRequestServer):
905
_req_handler_class = MultipleRangeWithoutContentLengthRequestHandler
908
class TruncatedMultipleRangeRequestHandler(
909
http_server.TestingHTTPRequestHandler):
910
"""Reply to multiple range requests truncating the last ones.
912
This server generates responses whose Content-Length describes all the
913
ranges, but fail to include the last ones leading to client short reads.
914
This has been observed randomly with lighttpd (bug #179368).
917
_truncated_ranges = 2
919
def get_multiple_ranges(self, file, file_size, ranges):
920
self.send_response(206)
921
self.send_header('Accept-Ranges', 'bytes')
923
self.send_header('Content-Type',
924
'multipart/byteranges; boundary=%s' % boundary)
925
boundary_line = '--%s\r\n' % boundary
926
# Calculate the Content-Length
928
for (start, end) in ranges:
929
content_length += len(boundary_line)
930
content_length += self._header_line_length(
931
'Content-type', 'application/octet-stream')
932
content_length += self._header_line_length(
933
'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
934
content_length += len('\r\n') # end headers
935
content_length += end - start # + 1
936
content_length += len(boundary_line)
937
self.send_header('Content-length', content_length)
940
# Send the multipart body
942
for (start, end) in ranges:
943
self.wfile.write(boundary_line)
944
self.send_header('Content-type', 'application/octet-stream')
945
self.send_header('Content-Range', 'bytes %d-%d/%d'
946
% (start, end, file_size))
948
if cur + self._truncated_ranges >= len(ranges):
949
# Abruptly ends the response and close the connection
950
self.close_connection = 1
952
self.send_range_content(file, start, end - start + 1)
955
self.wfile.write(boundary_line)
958
class TestTruncatedMultipleRangeServer(TestSpecificRequestHandler):
960
_req_handler_class = TruncatedMultipleRangeRequestHandler
963
super(TestTruncatedMultipleRangeServer, self).setUp()
964
self.build_tree_contents([('a', '0123456789')],)
966
def test_readv_with_short_reads(self):
967
server = self.get_readonly_server()
968
t = self._transport(server.get_url())
969
# Force separate ranges for each offset
970
t._bytes_to_read_before_seek = 0
971
ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
972
self.assertEqual((0, '0'), ireadv.next())
973
self.assertEqual((2, '2'), ireadv.next())
974
if not self._testing_pycurl():
975
# Only one request have been issued so far (except for pycurl that
976
# try to read the whole response at once)
977
self.assertEqual(1, server.GET_request_nb)
978
self.assertEqual((4, '45'), ireadv.next())
979
self.assertEqual((9, '9'), ireadv.next())
980
# Both implementations issue 3 requests but:
981
# - urllib does two multiple (4 ranges, then 2 ranges) then a single
983
# - pycurl does two multiple (4 ranges, 4 ranges) then a single range
984
self.assertEqual(3, server.GET_request_nb)
985
# Finally the client have tried a single range request and stays in
987
self.assertEqual('single', t._range_hint)
989
class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
990
"""Errors out when range specifiers exceed the limit"""
992
def get_multiple_ranges(self, file, file_size, ranges):
993
"""Refuses the multiple ranges request"""
994
tcs = self.server.test_case_server
995
if tcs.range_limit is not None and len(ranges) > tcs.range_limit:
997
# Emulate apache behavior
998
self.send_error(400, "Bad Request")
1000
return http_server.TestingHTTPRequestHandler.get_multiple_ranges(
1001
self, file, file_size, ranges)
1004
class LimitedRangeHTTPServer(http_server.HttpServer):
1005
"""An HttpServer erroring out on requests with too much range specifiers"""
1007
def __init__(self, request_handler=LimitedRangeRequestHandler,
1008
protocol_version=None,
1010
http_server.HttpServer.__init__(self, request_handler,
1011
protocol_version=protocol_version)
1012
self.range_limit = range_limit
1015
class TestLimitedRangeRequestServer(http_utils.TestCaseWithWebserver):
1016
"""Tests readv requests against a server erroring out on too much ranges."""
1018
# Requests with more range specifiers will error out
754
1021
def create_transport_readonly_server(self):
755
# Requests with more range specifiers will error out
756
return LimitedRangeHTTPServer(range_limit=self.range_limit)
1022
return LimitedRangeHTTPServer(range_limit=self.range_limit,
1023
protocol_version=self._protocol_version)
758
1025
def get_transport(self):
759
1026
return self._transport(self.get_readonly_server().get_url())
761
1028
def setUp(self):
762
TestCaseWithWebserver.setUp(self)
1029
http_utils.TestCaseWithWebserver.setUp(self)
763
1030
# We need to manipulate ranges that correspond to real chunks in the
764
1031
# response, so we build a content appropriately.
765
1032
filler = ''.join(['abcdefghij' for x in range(102)])
930
1193
'NO_PROXY': self.no_proxy_host})
932
1195
def test_http_proxy_without_scheme(self):
933
self.assertRaises(errors.InvalidURL,
935
{'http_proxy': self.proxy_address})
938
class TestProxyHttpServer_urllib(TestProxyHttpServer,
939
TestCaseWithTwoWebservers):
940
"""Tests proxy server for urllib implementation"""
942
_transport = HttpTransport_urllib
945
class TestProxyHttpServer_pycurl(TestWithTransport_pycurl,
947
TestCaseWithTwoWebservers):
948
"""Tests proxy server for pycurl implementation"""
951
TestProxyHttpServer.setUp(self)
952
# Oh my ! pycurl does not check for the port as part of
953
# no_proxy :-( So we just test the host part
954
self.no_proxy_host = 'localhost'
956
def test_HTTP_PROXY(self):
957
# pycurl does not check HTTP_PROXY for security reasons
958
# (for use in a CGI context that we do not care
959
# about. Should we ?)
960
raise tests.TestNotApplicable(
961
'pycurl does not check HTTP_PROXY for security reasons')
963
def test_HTTP_PROXY_with_NO_PROXY(self):
964
raise tests.TestNotApplicable(
965
'pycurl does not check HTTP_PROXY for security reasons')
967
def test_http_proxy_without_scheme(self):
968
# pycurl *ignores* invalid proxy env variables. If that
969
# ever change in the future, this test will fail
970
# indicating that pycurl do not ignore anymore such
972
self.not_proxied_in_env({'http_proxy': self.proxy_address})
975
class TestRanges(object):
976
"""Test the Range header in GET methods..
978
This MUST be used by daughter classes that also inherit from
979
TestCaseWithWebserver.
981
We can't inherit directly from TestCaseWithWebserver or the
982
test framework will try to create an instance which cannot
983
run, its implementation being incomplete.
987
TestCaseWithWebserver.setUp(self)
1196
if self._testing_pycurl():
1197
# pycurl *ignores* invalid proxy env variables. If that ever change
1198
# in the future, this test will fail indicating that pycurl do not
1199
# ignore anymore such variables.
1200
self.not_proxied_in_env({'http_proxy': self.proxy_address})
1202
self.assertRaises(errors.InvalidURL,
1203
self.proxied_in_env,
1204
{'http_proxy': self.proxy_address})
1207
class TestRanges(http_utils.TestCaseWithWebserver):
1208
"""Test the Range header in GET methods."""
1211
http_utils.TestCaseWithWebserver.setUp(self)
988
1212
self.build_tree_contents([('a', '0123456789')],)
989
1213
server = self.get_readonly_server()
990
1214
self.transport = self._transport(server.get_url())
1216
def create_transport_readonly_server(self):
1217
return http_server.HttpServer(protocol_version=self._protocol_version)
992
1219
def _file_contents(self, relpath, ranges):
993
1220
offsets = [ (start, end - start + 1) for start, end in ranges]
994
1221
coalesce = self.transport._coalesce_offsets
1341
1560
# Only one 'Authentication Required' error should occur
1342
1561
self.assertEqual(1, self.server.auth_required_errors)
1346
class TestHTTPAuth(TestAuth):
1347
"""Test HTTP authentication schemes.
1349
Daughter classes MUST inherit from TestCaseWithWebserver too.
1352
_auth_header = 'Authorization'
1355
TestCaseWithWebserver.setUp(self)
1356
self.server = self.get_readonly_server()
1357
TestAuth.setUp(self)
1359
def get_user_transport(self, user=None, password=None):
1360
return self._transport(self.get_user_url(user, password))
1563
def test_changing_nonce(self):
1564
if self._auth_scheme != 'digest':
1565
raise tests.TestNotApplicable('HTTP auth digest only test')
1566
if self._testing_pycurl():
1567
raise tests.KnownFailure(
1568
'pycurl does not handle a nonce change')
1569
self.server.add_user('joe', 'foo')
1570
t = self.get_user_transport('joe', 'foo')
1571
self.assertEqual('contents of a\n', t.get('a').read())
1572
self.assertEqual('contents of b\n', t.get('b').read())
1573
# Only one 'Authentication Required' error should have
1575
self.assertEqual(1, self.server.auth_required_errors)
1576
# The server invalidates the current nonce
1577
self.server.auth_nonce = self.server.auth_nonce + '. No, now!'
1578
self.assertEqual('contents of a\n', t.get('a').read())
1579
# Two 'Authentication Required' errors should occur (the
1580
# initial 'who are you' and a second 'who are you' with the new nonce)
1581
self.assertEqual(2, self.server.auth_required_errors)
1363
1585
class TestProxyAuth(TestAuth):
1364
"""Test proxy authentication schemes.
1586
"""Test proxy authentication schemes."""
1366
Daughter classes MUST also inherit from TestCaseWithWebserver.
1368
1588
_auth_header = 'Proxy-authorization'
1369
_password_prompt_prefix = 'Proxy '
1589
_password_prompt_prefix='Proxy '
1372
1591
def setUp(self):
1373
TestCaseWithWebserver.setUp(self)
1374
self.server = self.get_readonly_server()
1592
super(TestProxyAuth, self).setUp()
1375
1593
self._old_env = {}
1376
1594
self.addCleanup(self._restore_env)
1377
TestAuth.setUp(self)
1378
1595
# Override the contents to avoid false positives
1379
1596
self.build_tree_contents([('a', 'not proxied contents of a\n'),
1380
1597
('b', 'not proxied contents of b\n'),
1394
1623
for name, value in self._old_env.iteritems():
1395
1624
osutils.set_or_unset_env(name, value)
1398
class TestHTTPBasicAuth(TestHTTPAuth, TestCaseWithWebserver):
1399
"""Test http basic authentication scheme"""
1401
_transport = HttpTransport_urllib
1403
def create_transport_readonly_server(self):
1404
return HTTPBasicAuthServer()
1407
class TestHTTPProxyBasicAuth(TestProxyAuth, TestCaseWithWebserver):
1408
"""Test proxy basic authentication scheme"""
1410
_transport = HttpTransport_urllib
1412
def create_transport_readonly_server(self):
1413
return ProxyBasicAuthServer()
1416
class TestDigestAuth(object):
1417
"""Digest Authentication specific tests"""
1419
def test_changing_nonce(self):
1420
self.server.add_user('joe', 'foo')
1421
t = self.get_user_transport('joe', 'foo')
1422
self.assertEqual('contents of a\n', t.get('a').read())
1423
self.assertEqual('contents of b\n', t.get('b').read())
1424
# Only one 'Authentication Required' error should have
1426
self.assertEqual(1, self.server.auth_required_errors)
1427
# The server invalidates the current nonce
1428
self.server.auth_nonce = self.server.auth_nonce + '. No, now!'
1429
self.assertEqual('contents of a\n', t.get('a').read())
1430
# Two 'Authentication Required' errors should occur (the
1431
# initial 'who are you' and a second 'who are you' with the new nonce)
1432
self.assertEqual(2, self.server.auth_required_errors)
1435
class TestHTTPDigestAuth(TestHTTPAuth, TestDigestAuth, TestCaseWithWebserver):
1436
"""Test http digest authentication scheme"""
1438
_transport = HttpTransport_urllib
1440
def create_transport_readonly_server(self):
1441
return HTTPDigestAuthServer()
1444
class TestHTTPProxyDigestAuth(TestProxyAuth, TestDigestAuth,
1445
TestCaseWithWebserver):
1446
"""Test proxy digest authentication scheme"""
1448
_transport = HttpTransport_urllib
1450
def create_transport_readonly_server(self):
1451
return ProxyDigestAuthServer()
1454
class TestAuth_pycurl(object):
1455
"Tests that can't be applied to pycurl."""
1457
def test_prompt_for_password(self):
1458
raise tests.TestNotApplicable(
1459
'pycurl cannot prompt, it handles auth by embedding'
1460
' user:pass in urls only')
1462
def test_no_prompt_for_password_when_using_auth_config(self):
1463
raise tests.TestNotApplicable(
1464
'pycurl does not support authentication.conf'
1465
' since it cannot prompt')
1468
class TestHTTPBasicAuth_pycurl(TestWithTransport_pycurl, TestAuth_pycurl,
1470
"""Test http basic authentication scheme for pycurl"""
1473
class TestHTTPProxyBasicAuth_pycurl(TestWithTransport_pycurl, TestAuth_pycurl,
1474
TestHTTPProxyBasicAuth):
1475
"""Test proxy basic authentication scheme for pycurl"""
1477
def test_empty_pass(self):
1478
raise tests.KnownFailure(
1479
'some versions of pycurl does not handle empty proxy passwords')
1482
class TestHTTPDigestAuth_pycurl(TestWithTransport_pycurl, TestAuth_pycurl,
1483
TestHTTPDigestAuth):
1484
"""Test http digest authentication scheme for pycurl"""
1486
def test_changing_nonce(self):
1487
raise tests.KnownFailure(
1488
'pycurl does not handle a nonce change')
1491
class TestHTTPProxyDigestAuth_pycurl(TestWithTransport_pycurl, TestAuth_pycurl,
1492
TestHTTPProxyDigestAuth):
1493
"""Test http digest authentication scheme for pycurl"""
1495
def test_empty_pass(self):
1496
raise tests.KnownFailure(
1497
'some versions of pycurl does not handle empty proxy passwords')
1499
def test_changing_nonce(self):
1500
raise tests.KnownFailure(
1501
'pycurl does not handle a nonce change')
1626
def test_empty_pass(self):
1627
if self._testing_pycurl():
1629
if pycurl.version_info()[1] < '7.16.0':
1630
raise tests.KnownFailure(
1631
'pycurl < 7.16.0 does not handle empty proxy passwords')
1632
super(TestProxyAuth, self).test_empty_pass()
1635
class SampleSocket(object):
1636
"""A socket-like object for use in testing the HTTP request handler."""
1638
def __init__(self, socket_read_content):
1639
"""Constructs a sample socket.
1641
:param socket_read_content: a byte sequence
1643
# Use plain python StringIO so we can monkey-patch the close method to
1644
# not discard the contents.
1645
from StringIO import StringIO
1646
self.readfile = StringIO(socket_read_content)
1647
self.writefile = StringIO()
1648
self.writefile.close = lambda: None
1650
def makefile(self, mode='r', bufsize=None):
1652
return self.readfile
1654
return self.writefile
1657
class SmartHTTPTunnellingTest(tests.TestCaseWithTransport):
1660
super(SmartHTTPTunnellingTest, self).setUp()
1661
# We use the VFS layer as part of HTTP tunnelling tests.
1662
self._captureVar('BZR_NO_SMART_VFS', None)
1663
self.transport_readonly_server = http_utils.HTTPServerWithSmarts
1665
def create_transport_readonly_server(self):
1666
return http_utils.HTTPServerWithSmarts(
1667
protocol_version=self._protocol_version)
1669
def test_bulk_data(self):
1670
# We should be able to send and receive bulk data in a single message.
1671
# The 'readv' command in the smart protocol both sends and receives
1672
# bulk data, so we use that.
1673
self.build_tree(['data-file'])
1674
http_server = self.get_readonly_server()
1675
http_transport = self._transport(http_server.get_url())
1676
medium = http_transport.get_smart_medium()
1677
# Since we provide the medium, the url below will be mostly ignored
1678
# during the test, as long as the path is '/'.
1679
remote_transport = remote.RemoteTransport('bzr://fake_host/',
1682
[(0, "c")], list(remote_transport.readv("data-file", [(0,1)])))
1684
def test_http_send_smart_request(self):
1686
post_body = 'hello\n'
1687
expected_reply_body = 'ok\x012\n'
1689
http_server = self.get_readonly_server()
1690
http_transport = self._transport(http_server.get_url())
1691
medium = http_transport.get_smart_medium()
1692
response = medium.send_http_smart_request(post_body)
1693
reply_body = response.read()
1694
self.assertEqual(expected_reply_body, reply_body)
1696
def test_smart_http_server_post_request_handler(self):
1697
httpd = self.get_readonly_server()._get_httpd()
1699
socket = SampleSocket(
1700
'POST /.bzr/smart %s \r\n' % self._protocol_version
1701
# HTTP/1.1 posts must have a Content-Length (but it doesn't hurt
1703
+ 'Content-Length: 6\r\n'
1706
# Beware: the ('localhost', 80) below is the
1707
# client_address parameter, but we don't have one because
1708
# we have defined a socket which is not bound to an
1709
# address. The test framework never uses this client
1710
# address, so far...
1711
request_handler = http_utils.SmartRequestHandler(socket,
1714
response = socket.writefile.getvalue()
1715
self.assertStartsWith(response, '%s 200 ' % self._protocol_version)
1716
# This includes the end of the HTTP headers, and all the body.
1717
expected_end_of_response = '\r\n\r\nok\x012\n'
1718
self.assertEndsWith(response, expected_end_of_response)