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"""
251
387
except ImportError:
252
388
raise tests.TestSkipped('pycurl not present')
253
# Now that we have pycurl imported, we can fake its version_info
254
# This was taken from a windows pycurl without SSL
256
pycurl.version_info = lambda : (2,
264
('ftp', 'gopher', 'telnet',
265
'dict', 'ldap', 'http', 'file'),
269
self.assertRaises(errors.DependencyNotPresent, self._transport,
270
'https://launchpad.net')
272
class TestHttpConnections(object):
273
"""Test the http connections.
275
This MUST be used by daughter classes that also inherit from
276
TestCaseWithWebserver.
278
We can't inherit directly from TestCaseWithWebserver or the
279
test framework will try to create an instance which cannot
280
run, its implementation being incomplete.
390
version_info_orig = pycurl.version_info
392
# Now that we have pycurl imported, we can fake its version_info
393
# This was taken from a windows pycurl without SSL
395
pycurl.version_info = lambda : (2,
403
('ftp', 'gopher', 'telnet',
404
'dict', 'ldap', 'http', 'file'),
408
self.assertRaises(errors.DependencyNotPresent, self._transport,
409
'https://launchpad.net')
411
# Restore the right function
412
pycurl.version_info = version_info_orig
415
class TestHTTPConnections(http_utils.TestCaseWithWebserver):
416
"""Test the http connections."""
284
TestCaseWithWebserver.setUp(self)
285
self.build_tree(['xxx', 'foo/', 'foo/bar'], line_endings='binary',
419
http_utils.TestCaseWithWebserver.setUp(self)
420
self.build_tree(['foo/', 'foo/bar'], line_endings='binary',
286
421
transport=self.get_transport())
288
423
def test_http_has(self):
334
469
socket.setdefaulttimeout(default_timeout)
337
class TestHttpConnections_urllib(TestHttpConnections, TestCaseWithWebserver):
338
"""Test http connections with urllib"""
340
_transport = HttpTransport_urllib
344
class TestHttpConnections_pycurl(TestWithTransport_pycurl,
346
TestCaseWithWebserver):
347
"""Test http connections with pycurl"""
350
472
class TestHttpTransportRegistration(tests.TestCase):
351
473
"""Test registrations of various http implementations"""
353
475
def test_http_registered(self):
354
# urlllib should always be present
355
t = get_transport('http+urllib://bzr.google.com/')
356
self.assertIsInstance(t, Transport)
357
self.assertIsInstance(t, HttpTransport_urllib)
360
class TestPost(object):
362
def _test_post_body_is_received(self, scheme):
476
t = transport.get_transport('%s://foo.com/' % self._qualified_prefix)
477
self.assertIsInstance(t, transport.Transport)
478
self.assertIsInstance(t, self._transport)
481
class TestPost(tests.TestCase):
483
def test_post_body_is_received(self):
363
484
server = RecordingServer(expect_body_tail='end-of-body')
365
486
self.addCleanup(server.tearDown)
487
scheme = self._qualified_prefix
366
488
url = '%s://%s:%s/' % (scheme, server.host, server.port)
368
http_transport = get_transport(url)
369
except errors.UnsupportedProtocol:
370
raise tests.TestSkipped('%s not available' % scheme)
489
http_transport = self._transport(url)
371
490
code, response = http_transport._post('abc def end-of-body')
373
492
server.received_bytes.startswith('POST /.bzr/smart HTTP/1.'))
494
617
Both implementations raises the same error as for a bad status.
497
def create_transport_readonly_server(self):
498
return HttpServer(InvalidStatusRequestHandler)
501
class TestInvalidStatusServer_urllib(TestInvalidStatusServer,
502
TestCaseWithWebserver):
503
"""Tests invalid status server for urllib implementation"""
505
_transport = HttpTransport_urllib
508
class TestInvalidStatusServer_pycurl(TestWithTransport_pycurl,
509
TestInvalidStatusServer,
510
TestCaseWithWebserver):
511
"""Tests invalid status server for pycurl implementation"""
514
class TestBadProtocolServer(object):
620
_req_handler_class = InvalidStatusRequestHandler
622
def test_http_has(self):
623
if self._testing_pycurl() and self._protocol_version == 'HTTP/1.1':
624
raise tests.KnownFailure(
625
'pycurl hangs if the server send back garbage')
626
super(TestInvalidStatusServer, self).test_http_has()
628
def test_http_get(self):
629
if self._testing_pycurl() and self._protocol_version == 'HTTP/1.1':
630
raise tests.KnownFailure(
631
'pycurl hangs if the server send back garbage')
632
super(TestInvalidStatusServer, self).test_http_get()
635
class BadProtocolRequestHandler(http_server.TestingHTTPRequestHandler):
636
"""Whatever request comes in, returns a bad protocol version"""
638
def parse_request(self):
639
"""Fakes handling a single HTTP request, returns a bad status"""
640
ignored = http_server.TestingHTTPRequestHandler.parse_request(self)
641
# Returns an invalid protocol version, but curl just
642
# ignores it and those cannot be tested.
643
self.wfile.write("%s %d %s\r\n" % ('HTTP/0.0',
645
'Look at my protocol version'))
649
class TestBadProtocolServer(TestSpecificRequestHandler):
515
650
"""Tests bad protocol from server."""
517
def create_transport_readonly_server(self):
518
return HttpServer(BadProtocolRequestHandler)
652
_req_handler_class = BadProtocolRequestHandler
655
if pycurl_present and self._transport == PyCurlTransport:
656
raise tests.TestNotApplicable(
657
"pycurl doesn't check the protocol version")
658
super(TestBadProtocolServer, self).setUp()
520
660
def test_http_has(self):
521
661
server = self.get_readonly_server()
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
907
class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
908
"""Errors out when range specifiers exceed the limit"""
910
def get_multiple_ranges(self, file, file_size, ranges):
911
"""Refuses the multiple ranges request"""
912
tcs = self.server.test_case_server
913
if tcs.range_limit is not None and len(ranges) > tcs.range_limit:
915
# Emulate apache behavior
916
self.send_error(400, "Bad Request")
918
return http_server.TestingHTTPRequestHandler.get_multiple_ranges(
919
self, file, file_size, ranges)
922
class LimitedRangeHTTPServer(http_server.HttpServer):
923
"""An HttpServer erroring out on requests with too much range specifiers"""
925
def __init__(self, request_handler=LimitedRangeRequestHandler,
926
protocol_version=None,
928
http_server.HttpServer.__init__(self, request_handler,
929
protocol_version=protocol_version)
930
self.range_limit = range_limit
933
class TestLimitedRangeRequestServer(http_utils.TestCaseWithWebserver):
934
"""Tests readv requests against a server erroring out on too much ranges."""
936
# Requests with more range specifiers will error out
754
939
def create_transport_readonly_server(self):
755
# Requests with more range specifiers will error out
756
return LimitedRangeHTTPServer(range_limit=self.range_limit)
940
return LimitedRangeHTTPServer(range_limit=self.range_limit,
941
protocol_version=self._protocol_version)
758
943
def get_transport(self):
759
944
return self._transport(self.get_readonly_server().get_url())
762
TestCaseWithWebserver.setUp(self)
947
http_utils.TestCaseWithWebserver.setUp(self)
763
948
# We need to manipulate ranges that correspond to real chunks in the
764
949
# response, so we build a content appropriately.
765
950
filler = ''.join(['abcdefghij' for x in range(102)])
930
1111
'NO_PROXY': self.no_proxy_host})
932
1113
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)
1114
if self._testing_pycurl():
1115
# pycurl *ignores* invalid proxy env variables. If that ever change
1116
# in the future, this test will fail indicating that pycurl do not
1117
# ignore anymore such variables.
1118
self.not_proxied_in_env({'http_proxy': self.proxy_address})
1120
self.assertRaises(errors.InvalidURL,
1121
self.proxied_in_env,
1122
{'http_proxy': self.proxy_address})
1125
class TestRanges(http_utils.TestCaseWithWebserver):
1126
"""Test the Range header in GET methods."""
1129
http_utils.TestCaseWithWebserver.setUp(self)
988
1130
self.build_tree_contents([('a', '0123456789')],)
989
1131
server = self.get_readonly_server()
990
1132
self.transport = self._transport(server.get_url())
1134
def create_transport_readonly_server(self):
1135
return http_server.HttpServer(protocol_version=self._protocol_version)
992
1137
def _file_contents(self, relpath, ranges):
993
1138
offsets = [ (start, end - start + 1) for start, end in ranges]
994
1139
coalesce = self.transport._coalesce_offsets
1010
1155
map(self.assertEqual,['0', '234'],
1011
1156
list(self._file_contents('a', [(0,0), (2,4)])),)
1158
def test_range_header_tail(self):
1013
1159
self.assertEqual('789', self._file_tail('a', 3))
1014
# Syntactically invalid range
1161
def test_syntactically_invalid_range_header(self):
1015
1162
self.assertListRaises(errors.InvalidHttpRange,
1016
1163
self._file_contents, 'a', [(4, 3)])
1017
# Semantically invalid range
1165
def test_semantically_invalid_range_header(self):
1018
1166
self.assertListRaises(errors.InvalidHttpRange,
1019
1167
self._file_contents, 'a', [(42, 128)])
1022
class TestRanges_urllib(TestRanges, TestCaseWithWebserver):
1023
"""Test the Range header in GET methods for urllib implementation"""
1025
_transport = HttpTransport_urllib
1028
class TestRanges_pycurl(TestWithTransport_pycurl,
1030
TestCaseWithWebserver):
1031
"""Test the Range header in GET methods for pycurl implementation"""
1034
class TestHTTPRedirections(object):
1035
"""Test redirection between http servers.
1037
This MUST be used by daughter classes that also inherit from
1038
TestCaseWithRedirectedWebserver.
1040
We can't inherit directly from TestCaseWithTwoWebservers or the
1041
test framework will try to create an instance which cannot
1042
run, its implementation being incomplete.
1170
class TestHTTPRedirections(http_utils.TestCaseWithRedirectedWebserver):
1171
"""Test redirection between http servers."""
1045
1173
def create_transport_secondary_server(self):
1046
1174
"""Create the secondary server redirecting to the primary server"""
1047
1175
new = self.get_readonly_server()
1049
redirecting = HTTPServerRedirecting()
1177
redirecting = http_utils.HTTPServerRedirecting(
1178
protocol_version=self._protocol_version)
1050
1179
redirecting.redirect_to(new.host, new.port)
1051
1180
return redirecting
1072
1201
self.assertEqual([], bundle.revisions)
1075
class TestHTTPRedirections_urllib(TestHTTPRedirections,
1076
TestCaseWithRedirectedWebserver):
1077
"""Tests redirections for urllib implementation"""
1079
_transport = HttpTransport_urllib
1083
class TestHTTPRedirections_pycurl(TestWithTransport_pycurl,
1084
TestHTTPRedirections,
1085
TestCaseWithRedirectedWebserver):
1086
"""Tests redirections for pycurl implementation"""
1089
class RedirectedRequest(Request):
1090
"""Request following redirections"""
1092
init_orig = Request.__init__
1204
class RedirectedRequest(_urllib2_wrappers.Request):
1205
"""Request following redirections. """
1207
init_orig = _urllib2_wrappers.Request.__init__
1094
1209
def __init__(self, method, url, *args, **kwargs):
1213
# Since the tests using this class will replace
1214
# _urllib2_wrappers.Request, we can't just call the base class __init__
1095
1216
RedirectedRequest.init_orig(self, method, url, args, kwargs)
1096
1217
self.follow_redirections = True
1099
class TestHTTPSilentRedirections_urllib(TestCaseWithRedirectedWebserver):
1100
"""Test redirections provided by urllib.
1220
class TestHTTPSilentRedirections(http_utils.TestCaseWithRedirectedWebserver):
1221
"""Test redirections.
1102
1223
http implementations do not redirect silently anymore (they
1103
1224
do not redirect at all in fact). The mechanism is still in
1162
1285
self.old_server.port)
1163
1286
new_prefix = 'http://%s:%s' % (self.new_server.host,
1164
1287
self.new_server.port)
1165
self.old_server.redirections = \
1166
[('/1(.*)', r'%s/2\1' % (old_prefix), 302),
1167
('/2(.*)', r'%s/3\1' % (old_prefix), 303),
1168
('/3(.*)', r'%s/4\1' % (old_prefix), 307),
1169
('/4(.*)', r'%s/5\1' % (new_prefix), 301),
1170
('(/[^/]+)', r'%s/1\1' % (old_prefix), 301),
1288
self.old_server.redirections = [
1289
('/1(.*)', r'%s/2\1' % (old_prefix), 302),
1290
('/2(.*)', r'%s/3\1' % (old_prefix), 303),
1291
('/3(.*)', r'%s/4\1' % (old_prefix), 307),
1292
('/4(.*)', r'%s/5\1' % (new_prefix), 301),
1293
('(/[^/]+)', r'%s/1\1' % (old_prefix), 301),
1172
1295
self.assertEquals('redirected 5 times',t._perform(req).read())
1175
class TestDoCatchRedirections(TestCaseWithRedirectedWebserver):
1176
"""Test transport.do_catching_redirections.
1178
We arbitrarily choose to use urllib transports
1181
_transport = HttpTransport_urllib
1298
class TestDoCatchRedirections(http_utils.TestCaseWithRedirectedWebserver):
1299
"""Test transport.do_catching_redirections."""
1183
1301
def setUp(self):
1184
1302
super(TestDoCatchRedirections, self).setUp()
1220
1337
return self.old_transport.clone(exception.target)
1222
self.assertRaises(errors.TooManyRedirections, do_catching_redirections,
1339
self.assertRaises(errors.TooManyRedirections,
1340
transport.do_catching_redirections,
1223
1341
self.get_a, self.old_transport, redirected)
1226
class TestAuth(object):
1227
"""Test some authentication scheme specified by daughter class.
1229
This MUST be used by daughter classes that also inherit from
1230
either TestCaseWithWebserver or TestCaseWithTwoWebservers.
1344
class TestAuth(http_utils.TestCaseWithWebserver):
1345
"""Test authentication scheme"""
1347
_auth_header = 'Authorization'
1233
1348
_password_prompt_prefix = ''
1235
1350
def setUp(self):
1236
"""Set up the test environment
1238
Daughter classes should set up their own environment
1239
(including self.server) and explicitely call this
1240
method. This is needed because we want to reuse the same
1241
tests for proxy and no-proxy accesses which have
1242
different ways of setting self.server.
1351
super(TestAuth, self).setUp()
1352
self.server = self.get_readonly_server()
1244
1353
self.build_tree_contents([('a', 'contents of a\n'),
1245
1354
('b', 'contents of b\n'),])
1356
def create_transport_readonly_server(self):
1357
if self._auth_scheme == 'basic':
1358
server = http_utils.HTTPBasicAuthServer(
1359
protocol_version=self._protocol_version)
1361
if self._auth_scheme != 'digest':
1362
raise AssertionError('Unknown auth scheme: %r'
1363
% self._auth_scheme)
1364
server = http_utils.HTTPDigestAuthServer(
1365
protocol_version=self._protocol_version)
1368
def _testing_pycurl(self):
1369
return pycurl_present and self._transport == PyCurlTransport
1247
1371
def get_user_url(self, user=None, password=None):
1248
1372
"""Build an url embedding user and password"""
1249
1373
url = '%s://' % self.server._url_protocol
1341
1478
# Only one 'Authentication Required' error should occur
1342
1479
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))
1481
def test_changing_nonce(self):
1482
if self._auth_scheme != 'digest':
1483
raise tests.TestNotApplicable('HTTP auth digest only test')
1484
if self._testing_pycurl():
1485
raise tests.KnownFailure(
1486
'pycurl does not handle a nonce change')
1487
self.server.add_user('joe', 'foo')
1488
t = self.get_user_transport('joe', 'foo')
1489
self.assertEqual('contents of a\n', t.get('a').read())
1490
self.assertEqual('contents of b\n', t.get('b').read())
1491
# Only one 'Authentication Required' error should have
1493
self.assertEqual(1, self.server.auth_required_errors)
1494
# The server invalidates the current nonce
1495
self.server.auth_nonce = self.server.auth_nonce + '. No, now!'
1496
self.assertEqual('contents of a\n', t.get('a').read())
1497
# Two 'Authentication Required' errors should occur (the
1498
# initial 'who are you' and a second 'who are you' with the new nonce)
1499
self.assertEqual(2, self.server.auth_required_errors)
1363
1503
class TestProxyAuth(TestAuth):
1364
"""Test proxy authentication schemes.
1504
"""Test proxy authentication schemes."""
1366
Daughter classes MUST also inherit from TestCaseWithWebserver.
1368
1506
_auth_header = 'Proxy-authorization'
1369
_password_prompt_prefix = 'Proxy '
1507
_password_prompt_prefix='Proxy '
1372
1509
def setUp(self):
1373
TestCaseWithWebserver.setUp(self)
1374
self.server = self.get_readonly_server()
1510
super(TestProxyAuth, self).setUp()
1375
1511
self._old_env = {}
1376
1512
self.addCleanup(self._restore_env)
1377
TestAuth.setUp(self)
1378
1513
# Override the contents to avoid false positives
1379
1514
self.build_tree_contents([('a', 'not proxied contents of a\n'),
1380
1515
('b', 'not proxied contents of b\n'),
1394
1541
for name, value in self._old_env.iteritems():
1395
1542
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')
1544
def test_empty_pass(self):
1545
if self._testing_pycurl():
1547
if pycurl.version_info()[1] < '7.16.0':
1548
raise tests.KnownFailure(
1549
'pycurl < 7.16.0 does not handle empty proxy passwords')
1550
super(TestProxyAuth, self).test_empty_pass()
1553
class SampleSocket(object):
1554
"""A socket-like object for use in testing the HTTP request handler."""
1556
def __init__(self, socket_read_content):
1557
"""Constructs a sample socket.
1559
:param socket_read_content: a byte sequence
1561
# Use plain python StringIO so we can monkey-patch the close method to
1562
# not discard the contents.
1563
from StringIO import StringIO
1564
self.readfile = StringIO(socket_read_content)
1565
self.writefile = StringIO()
1566
self.writefile.close = lambda: None
1568
def makefile(self, mode='r', bufsize=None):
1570
return self.readfile
1572
return self.writefile
1575
class SmartHTTPTunnellingTest(tests.TestCaseWithTransport):
1578
super(SmartHTTPTunnellingTest, self).setUp()
1579
# We use the VFS layer as part of HTTP tunnelling tests.
1580
self._captureVar('BZR_NO_SMART_VFS', None)
1581
self.transport_readonly_server = http_utils.HTTPServerWithSmarts
1583
def create_transport_readonly_server(self):
1584
return http_utils.HTTPServerWithSmarts(
1585
protocol_version=self._protocol_version)
1587
def test_bulk_data(self):
1588
# We should be able to send and receive bulk data in a single message.
1589
# The 'readv' command in the smart protocol both sends and receives
1590
# bulk data, so we use that.
1591
self.build_tree(['data-file'])
1592
http_server = self.get_readonly_server()
1593
http_transport = self._transport(http_server.get_url())
1594
medium = http_transport.get_smart_medium()
1595
# Since we provide the medium, the url below will be mostly ignored
1596
# during the test, as long as the path is '/'.
1597
remote_transport = remote.RemoteTransport('bzr://fake_host/',
1600
[(0, "c")], list(remote_transport.readv("data-file", [(0,1)])))
1602
def test_http_send_smart_request(self):
1604
post_body = 'hello\n'
1605
expected_reply_body = 'ok\x012\n'
1607
http_server = self.get_readonly_server()
1608
http_transport = self._transport(http_server.get_url())
1609
medium = http_transport.get_smart_medium()
1610
response = medium.send_http_smart_request(post_body)
1611
reply_body = response.read()
1612
self.assertEqual(expected_reply_body, reply_body)
1614
def test_smart_http_server_post_request_handler(self):
1615
httpd = self.get_readonly_server()._get_httpd()
1617
socket = SampleSocket(
1618
'POST /.bzr/smart %s \r\n' % self._protocol_version
1619
# HTTP/1.1 posts must have a Content-Length (but it doesn't hurt
1621
+ 'Content-Length: 6\r\n'
1624
# Beware: the ('localhost', 80) below is the
1625
# client_address parameter, but we don't have one because
1626
# we have defined a socket which is not bound to an
1627
# address. The test framework never uses this client
1628
# address, so far...
1629
request_handler = http_utils.SmartRequestHandler(socket,
1632
response = socket.writefile.getvalue()
1633
self.assertStartsWith(response, '%s 200 ' % self._protocol_version)
1634
# This includes the end of the HTTP headers, and all the body.
1635
expected_end_of_response = '\r\n\r\nok\x012\n'
1636
self.assertEndsWith(response, expected_end_of_response)