39
45
from bzrlib.tests import (
45
from bzrlib.tests.http_server import (
50
from bzrlib.tests.http_utils import (
51
BadProtocolRequestHandler,
52
BadStatusRequestHandler,
53
ForbiddenRequestHandler,
56
HTTPServerRedirecting,
57
InvalidStatusRequestHandler,
58
LimitedRangeHTTPServer,
59
NoRangeRequestHandler,
61
ProxyDigestAuthServer,
63
SingleRangeRequestHandler,
64
SingleOnlyRangeRequestHandler,
65
TestCaseWithRedirectedWebserver,
66
TestCaseWithTwoWebservers,
67
TestCaseWithWebserver,
70
49
from bzrlib.transport import (
72
do_catching_redirections,
76
53
from bzrlib.transport.http import (
81
from bzrlib.transport.http._urllib import HttpTransport_urllib
82
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)
88
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)
154
304
class TestWithTransport_pycurl(object):
155
305
"""Test case to inherit from if pycurl is present"""
257
387
except ImportError:
258
388
raise tests.TestSkipped('pycurl not present')
259
# Now that we have pycurl imported, we can fake its version_info
260
# This was taken from a windows pycurl without SSL
262
pycurl.version_info = lambda : (2,
270
('ftp', 'gopher', 'telnet',
271
'dict', 'ldap', 'http', 'file'),
275
self.assertRaises(errors.DependencyNotPresent, self._transport,
276
'https://launchpad.net')
278
class TestHttpConnections(object):
279
"""Test the http connections.
281
This MUST be used by daughter classes that also inherit from
282
TestCaseWithWebserver.
284
We can't inherit directly from TestCaseWithWebserver or the
285
test framework will try to create an instance which cannot
286
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."""
290
TestCaseWithWebserver.setUp(self)
291
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',
292
421
transport=self.get_transport())
294
423
def test_http_has(self):
340
469
socket.setdefaulttimeout(default_timeout)
343
class TestHttpConnections_urllib(TestHttpConnections, TestCaseWithWebserver):
344
"""Test http connections with urllib"""
346
_transport = HttpTransport_urllib
350
class TestHttpConnections_pycurl(TestWithTransport_pycurl,
352
TestCaseWithWebserver):
353
"""Test http connections with pycurl"""
356
472
class TestHttpTransportRegistration(tests.TestCase):
357
473
"""Test registrations of various http implementations"""
359
475
def test_http_registered(self):
360
# urlllib should always be present
361
t = get_transport('http+urllib://bzr.google.com/')
362
self.assertIsInstance(t, Transport)
363
self.assertIsInstance(t, HttpTransport_urllib)
366
class TestPost(object):
368
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):
369
484
server = RecordingServer(expect_body_tail='end-of-body')
371
486
self.addCleanup(server.tearDown)
487
scheme = self._qualified_prefix
372
488
url = '%s://%s:%s/' % (scheme, server.host, server.port)
374
http_transport = get_transport(url)
375
except errors.UnsupportedProtocol:
376
raise tests.TestSkipped('%s not available' % scheme)
489
http_transport = self._transport(url)
377
490
code, response = http_transport._post('abc def end-of-body')
379
492
server.received_bytes.startswith('POST /.bzr/smart HTTP/1.'))
501
618
Both implementations raises the same error as for a bad status.
504
def create_transport_readonly_server(self):
505
return HttpServer(InvalidStatusRequestHandler)
508
class TestInvalidStatusServer_urllib(TestInvalidStatusServer,
509
TestCaseWithWebserver):
510
"""Tests invalid status server for urllib implementation"""
512
_transport = HttpTransport_urllib
515
class TestInvalidStatusServer_pycurl(TestWithTransport_pycurl,
516
TestInvalidStatusServer,
517
TestCaseWithWebserver):
518
"""Tests invalid status server for pycurl implementation"""
521
class TestBadProtocolServer(object):
621
_req_handler_class = InvalidStatusRequestHandler
623
def test_http_has(self):
624
if self._testing_pycurl() and self._protocol_version == 'HTTP/1.1':
625
raise tests.KnownFailure(
626
'pycurl hangs if the server send back garbage')
627
super(TestInvalidStatusServer, self).test_http_has()
629
def test_http_get(self):
630
if self._testing_pycurl() and self._protocol_version == 'HTTP/1.1':
631
raise tests.KnownFailure(
632
'pycurl hangs if the server send back garbage')
633
super(TestInvalidStatusServer, self).test_http_get()
636
class BadProtocolRequestHandler(http_server.TestingHTTPRequestHandler):
637
"""Whatever request comes in, returns a bad protocol version"""
639
def parse_request(self):
640
"""Fakes handling a single HTTP request, returns a bad status"""
641
ignored = http_server.TestingHTTPRequestHandler.parse_request(self)
642
# Returns an invalid protocol version, but curl just
643
# ignores it and those cannot be tested.
644
self.wfile.write("%s %d %s\r\n" % ('HTTP/0.0',
646
'Look at my protocol version'))
650
class TestBadProtocolServer(TestSpecificRequestHandler):
522
651
"""Tests bad protocol from server."""
524
def create_transport_readonly_server(self):
525
return HttpServer(BadProtocolRequestHandler)
653
_req_handler_class = BadProtocolRequestHandler
656
if pycurl_present and self._transport == PyCurlTransport:
657
raise tests.TestNotApplicable(
658
"pycurl doesn't check the protocol version")
659
super(TestBadProtocolServer, self).setUp()
527
661
def test_http_has(self):
528
662
server = self.get_readonly_server()
684
797
# The server should have issued 3 requests
685
798
self.assertEqual(3, server.GET_request_nb)
800
def test_complete_readv_leave_pipe_clean(self):
801
server = self.get_readonly_server()
802
t = self._transport(server.get_url())
803
# force transport to issue multiple requests
805
l = list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
806
# The server should have issued 3 requests
807
self.assertEqual(3, server.GET_request_nb)
808
self.assertEqual('0123456789', t.get_bytes('a'))
809
self.assertEqual(4, server.GET_request_nb)
811
def test_incomplete_readv_leave_pipe_clean(self):
812
server = self.get_readonly_server()
813
t = self._transport(server.get_url())
814
# force transport to issue multiple requests
816
# Don't collapse readv results into a list so that we leave unread
817
# bytes on the socket
818
ireadv = iter(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
819
self.assertEqual((0, '0'), ireadv.next())
820
# The server should have issued one request so far
821
self.assertEqual(1, server.GET_request_nb)
822
self.assertEqual('0123456789', t.get_bytes('a'))
823
# get_bytes issued an additional request, the readv pending ones are
825
self.assertEqual(2, server.GET_request_nb)
828
class SingleRangeRequestHandler(http_server.TestingHTTPRequestHandler):
829
"""Always reply to range request as if they were single.
831
Don't be explicit about it, just to annoy the clients.
834
def get_multiple_ranges(self, file, file_size, ranges):
835
"""Answer as if it was a single range request and ignores the rest"""
836
(start, end) = ranges[0]
837
return self.get_single_range(file, file_size, start, end)
688
840
class TestSingleRangeRequestServer(TestRangeRequestServer):
689
841
"""Test readv against a server which accept only single range requests"""
691
def create_transport_readonly_server(self):
692
return HttpServer(SingleRangeRequestHandler)
695
class TestSingleRangeRequestServer_urllib(TestSingleRangeRequestServer,
696
TestCaseWithWebserver):
697
"""Tests single range requests accepting server for urllib implementation"""
699
_transport = HttpTransport_urllib
702
class TestSingleRangeRequestServer_pycurl(TestWithTransport_pycurl,
703
TestSingleRangeRequestServer,
704
TestCaseWithWebserver):
705
"""Tests single range requests accepting server for pycurl implementation"""
843
_req_handler_class = SingleRangeRequestHandler
846
class SingleOnlyRangeRequestHandler(http_server.TestingHTTPRequestHandler):
847
"""Only reply to simple range requests, errors out on multiple"""
849
def get_multiple_ranges(self, file, file_size, ranges):
850
"""Refuses the multiple ranges request"""
853
self.send_error(416, "Requested range not satisfiable")
855
(start, end) = ranges[0]
856
return self.get_single_range(file, file_size, start, end)
708
859
class TestSingleOnlyRangeRequestServer(TestRangeRequestServer):
709
860
"""Test readv against a server which only accept single range requests"""
711
def create_transport_readonly_server(self):
712
return HttpServer(SingleOnlyRangeRequestHandler)
715
class TestSingleOnlyRangeRequestServer_urllib(TestSingleOnlyRangeRequestServer,
716
TestCaseWithWebserver):
717
"""Tests single range requests accepting server for urllib implementation"""
719
_transport = HttpTransport_urllib
722
class TestSingleOnlyRangeRequestServer_pycurl(TestWithTransport_pycurl,
723
TestSingleOnlyRangeRequestServer,
724
TestCaseWithWebserver):
725
"""Tests single range requests accepting server for pycurl implementation"""
862
_req_handler_class = SingleOnlyRangeRequestHandler
865
class NoRangeRequestHandler(http_server.TestingHTTPRequestHandler):
866
"""Ignore range requests without notice"""
869
# Update the statistics
870
self.server.test_case_server.GET_request_nb += 1
871
# Just bypass the range handling done by TestingHTTPRequestHandler
872
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
728
875
class TestNoRangeRequestServer(TestRangeRequestServer):
729
876
"""Test readv against a server which do not accept range requests"""
731
def create_transport_readonly_server(self):
732
return HttpServer(NoRangeRequestHandler)
735
class TestNoRangeRequestServer_urllib(TestNoRangeRequestServer,
736
TestCaseWithWebserver):
737
"""Tests range requests refusing server for urllib implementation"""
739
_transport = HttpTransport_urllib
742
class TestNoRangeRequestServer_pycurl(TestWithTransport_pycurl,
743
TestNoRangeRequestServer,
744
TestCaseWithWebserver):
745
"""Tests range requests refusing server for pycurl implementation"""
748
class TestLimitedRangeRequestServer(object):
749
"""Tests readv requests against server that errors out on too much ranges.
751
This MUST be used by daughter classes that also inherit from
752
TestCaseWithWebserver.
754
We can't inherit directly from TestCaseWithWebserver or the
755
test framework will try to create an instance which cannot
756
run, its implementation being incomplete.
878
_req_handler_class = NoRangeRequestHandler
881
class MultipleRangeWithoutContentLengthRequestHandler(
882
http_server.TestingHTTPRequestHandler):
883
"""Reply to multiple range requests without content length header."""
885
def get_multiple_ranges(self, file, file_size, ranges):
886
self.send_response(206)
887
self.send_header('Accept-Ranges', 'bytes')
888
boundary = "%d" % random.randint(0,0x7FFFFFFF)
889
self.send_header("Content-Type",
890
"multipart/byteranges; boundary=%s" % boundary)
892
for (start, end) in ranges:
893
self.wfile.write("--%s\r\n" % boundary)
894
self.send_header("Content-type", 'application/octet-stream')
895
self.send_header("Content-Range", "bytes %d-%d/%d" % (start,
899
self.send_range_content(file, start, end - start + 1)
901
self.wfile.write("--%s\r\n" % boundary)
904
class TestMultipleRangeWithoutContentLengthServer(TestRangeRequestServer):
906
_req_handler_class = MultipleRangeWithoutContentLengthRequestHandler
908
class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
909
"""Errors out when range specifiers exceed the limit"""
911
def get_multiple_ranges(self, file, file_size, ranges):
912
"""Refuses the multiple ranges request"""
913
tcs = self.server.test_case_server
914
if tcs.range_limit is not None and len(ranges) > tcs.range_limit:
916
# Emulate apache behavior
917
self.send_error(400, "Bad Request")
919
return http_server.TestingHTTPRequestHandler.get_multiple_ranges(
920
self, file, file_size, ranges)
923
class LimitedRangeHTTPServer(http_server.HttpServer):
924
"""An HttpServer erroring out on requests with too much range specifiers"""
926
def __init__(self, request_handler=LimitedRangeRequestHandler,
927
protocol_version=None,
929
http_server.HttpServer.__init__(self, request_handler,
930
protocol_version=protocol_version)
931
self.range_limit = range_limit
934
class TestLimitedRangeRequestServer(http_utils.TestCaseWithWebserver):
935
"""Tests readv requests against a server erroring out on too much ranges."""
937
# Requests with more range specifiers will error out
761
940
def create_transport_readonly_server(self):
762
# Requests with more range specifiers will error out
763
return LimitedRangeHTTPServer(range_limit=self.range_limit)
941
return LimitedRangeHTTPServer(range_limit=self.range_limit,
942
protocol_version=self._protocol_version)
765
944
def get_transport(self):
766
945
return self._transport(self.get_readonly_server().get_url())
769
TestCaseWithWebserver.setUp(self)
948
http_utils.TestCaseWithWebserver.setUp(self)
770
949
# We need to manipulate ranges that correspond to real chunks in the
771
950
# response, so we build a content appropriately.
772
951
filler = ''.join(['abcdefghij' for x in range(102)])
937
1112
'NO_PROXY': self.no_proxy_host})
939
1114
def test_http_proxy_without_scheme(self):
940
self.assertRaises(errors.InvalidURL,
942
{'http_proxy': self.proxy_address})
945
class TestProxyHttpServer_urllib(TestProxyHttpServer,
946
TestCaseWithTwoWebservers):
947
"""Tests proxy server for urllib implementation"""
949
_transport = HttpTransport_urllib
952
class TestProxyHttpServer_pycurl(TestWithTransport_pycurl,
954
TestCaseWithTwoWebservers):
955
"""Tests proxy server for pycurl implementation"""
958
TestProxyHttpServer.setUp(self)
959
# Oh my ! pycurl does not check for the port as part of
960
# no_proxy :-( So we just test the host part
961
self.no_proxy_host = 'localhost'
963
def test_HTTP_PROXY(self):
964
# pycurl does not check HTTP_PROXY for security reasons
965
# (for use in a CGI context that we do not care
966
# about. Should we ?)
967
raise tests.TestNotApplicable(
968
'pycurl does not check HTTP_PROXY for security reasons')
970
def test_HTTP_PROXY_with_NO_PROXY(self):
971
raise tests.TestNotApplicable(
972
'pycurl does not check HTTP_PROXY for security reasons')
974
def test_http_proxy_without_scheme(self):
975
# pycurl *ignores* invalid proxy env variables. If that
976
# ever change in the future, this test will fail
977
# indicating that pycurl do not ignore anymore such
979
self.not_proxied_in_env({'http_proxy': self.proxy_address})
982
class TestRanges(object):
983
"""Test the Range header in GET methods..
985
This MUST be used by daughter classes that also inherit from
986
TestCaseWithWebserver.
988
We can't inherit directly from TestCaseWithWebserver or the
989
test framework will try to create an instance which cannot
990
run, its implementation being incomplete.
994
TestCaseWithWebserver.setUp(self)
1115
if self._testing_pycurl():
1116
# pycurl *ignores* invalid proxy env variables. If that ever change
1117
# in the future, this test will fail indicating that pycurl do not
1118
# ignore anymore such variables.
1119
self.not_proxied_in_env({'http_proxy': self.proxy_address})
1121
self.assertRaises(errors.InvalidURL,
1122
self.proxied_in_env,
1123
{'http_proxy': self.proxy_address})
1126
class TestRanges(http_utils.TestCaseWithWebserver):
1127
"""Test the Range header in GET methods."""
1130
http_utils.TestCaseWithWebserver.setUp(self)
995
1131
self.build_tree_contents([('a', '0123456789')],)
996
1132
server = self.get_readonly_server()
997
1133
self.transport = self._transport(server.get_url())
1135
def create_transport_readonly_server(self):
1136
return http_server.HttpServer(protocol_version=self._protocol_version)
999
1138
def _file_contents(self, relpath, ranges):
1000
1139
offsets = [ (start, end - start + 1) for start, end in ranges]
1001
1140
coalesce = self.transport._coalesce_offsets
1017
1156
map(self.assertEqual,['0', '234'],
1018
1157
list(self._file_contents('a', [(0,0), (2,4)])),)
1159
def test_range_header_tail(self):
1020
1160
self.assertEqual('789', self._file_tail('a', 3))
1021
# Syntactically invalid range
1162
def test_syntactically_invalid_range_header(self):
1022
1163
self.assertListRaises(errors.InvalidHttpRange,
1023
1164
self._file_contents, 'a', [(4, 3)])
1024
# Semantically invalid range
1166
def test_semantically_invalid_range_header(self):
1025
1167
self.assertListRaises(errors.InvalidHttpRange,
1026
1168
self._file_contents, 'a', [(42, 128)])
1029
class TestRanges_urllib(TestRanges, TestCaseWithWebserver):
1030
"""Test the Range header in GET methods for urllib implementation"""
1032
_transport = HttpTransport_urllib
1035
class TestRanges_pycurl(TestWithTransport_pycurl,
1037
TestCaseWithWebserver):
1038
"""Test the Range header in GET methods for pycurl implementation"""
1041
class TestHTTPRedirections(object):
1042
"""Test redirection between http servers.
1044
This MUST be used by daughter classes that also inherit from
1045
TestCaseWithRedirectedWebserver.
1047
We can't inherit directly from TestCaseWithTwoWebservers or the
1048
test framework will try to create an instance which cannot
1049
run, its implementation being incomplete.
1171
class TestHTTPRedirections(http_utils.TestCaseWithRedirectedWebserver):
1172
"""Test redirection between http servers."""
1052
1174
def create_transport_secondary_server(self):
1053
1175
"""Create the secondary server redirecting to the primary server"""
1054
1176
new = self.get_readonly_server()
1056
redirecting = HTTPServerRedirecting()
1178
redirecting = http_utils.HTTPServerRedirecting(
1179
protocol_version=self._protocol_version)
1057
1180
redirecting.redirect_to(new.host, new.port)
1058
1181
return redirecting
1079
1202
self.assertEqual([], bundle.revisions)
1082
class TestHTTPRedirections_urllib(TestHTTPRedirections,
1083
TestCaseWithRedirectedWebserver):
1084
"""Tests redirections for urllib implementation"""
1086
_transport = HttpTransport_urllib
1090
class TestHTTPRedirections_pycurl(TestWithTransport_pycurl,
1091
TestHTTPRedirections,
1092
TestCaseWithRedirectedWebserver):
1093
"""Tests redirections for pycurl implementation"""
1096
class RedirectedRequest(Request):
1097
"""Request following redirections"""
1099
init_orig = Request.__init__
1205
class RedirectedRequest(_urllib2_wrappers.Request):
1206
"""Request following redirections. """
1208
init_orig = _urllib2_wrappers.Request.__init__
1101
1210
def __init__(self, method, url, *args, **kwargs):
1214
# Since the tests using this class will replace
1215
# _urllib2_wrappers.Request, we can't just call the base class __init__
1102
1217
RedirectedRequest.init_orig(self, method, url, args, kwargs)
1103
1218
self.follow_redirections = True
1106
class TestHTTPSilentRedirections_urllib(TestCaseWithRedirectedWebserver):
1107
"""Test redirections provided by urllib.
1221
class TestHTTPSilentRedirections(http_utils.TestCaseWithRedirectedWebserver):
1222
"""Test redirections.
1109
1224
http implementations do not redirect silently anymore (they
1110
1225
do not redirect at all in fact). The mechanism is still in
1169
1286
self.old_server.port)
1170
1287
new_prefix = 'http://%s:%s' % (self.new_server.host,
1171
1288
self.new_server.port)
1172
self.old_server.redirections = \
1173
[('/1(.*)', r'%s/2\1' % (old_prefix), 302),
1174
('/2(.*)', r'%s/3\1' % (old_prefix), 303),
1175
('/3(.*)', r'%s/4\1' % (old_prefix), 307),
1176
('/4(.*)', r'%s/5\1' % (new_prefix), 301),
1177
('(/[^/]+)', r'%s/1\1' % (old_prefix), 301),
1289
self.old_server.redirections = [
1290
('/1(.*)', r'%s/2\1' % (old_prefix), 302),
1291
('/2(.*)', r'%s/3\1' % (old_prefix), 303),
1292
('/3(.*)', r'%s/4\1' % (old_prefix), 307),
1293
('/4(.*)', r'%s/5\1' % (new_prefix), 301),
1294
('(/[^/]+)', r'%s/1\1' % (old_prefix), 301),
1179
1296
self.assertEquals('redirected 5 times',t._perform(req).read())
1182
class TestDoCatchRedirections(TestCaseWithRedirectedWebserver):
1183
"""Test transport.do_catching_redirections.
1185
We arbitrarily choose to use urllib transports
1188
_transport = HttpTransport_urllib
1299
class TestDoCatchRedirections(http_utils.TestCaseWithRedirectedWebserver):
1300
"""Test transport.do_catching_redirections."""
1190
1302
def setUp(self):
1191
1303
super(TestDoCatchRedirections, self).setUp()
1227
1338
return self.old_transport.clone(exception.target)
1229
self.assertRaises(errors.TooManyRedirections, do_catching_redirections,
1340
self.assertRaises(errors.TooManyRedirections,
1341
transport.do_catching_redirections,
1230
1342
self.get_a, self.old_transport, redirected)
1233
class TestAuth(object):
1234
"""Test some authentication scheme specified by daughter class.
1236
This MUST be used by daughter classes that also inherit from
1237
either TestCaseWithWebserver or TestCaseWithTwoWebservers.
1345
class TestAuth(http_utils.TestCaseWithWebserver):
1346
"""Test authentication scheme"""
1348
_auth_header = 'Authorization'
1240
1349
_password_prompt_prefix = ''
1242
1351
def setUp(self):
1243
"""Set up the test environment
1245
Daughter classes should set up their own environment
1246
(including self.server) and explicitely call this
1247
method. This is needed because we want to reuse the same
1248
tests for proxy and no-proxy accesses which have
1249
different ways of setting self.server.
1352
super(TestAuth, self).setUp()
1353
self.server = self.get_readonly_server()
1251
1354
self.build_tree_contents([('a', 'contents of a\n'),
1252
1355
('b', 'contents of b\n'),])
1357
def create_transport_readonly_server(self):
1358
if self._auth_scheme == 'basic':
1359
server = http_utils.HTTPBasicAuthServer(
1360
protocol_version=self._protocol_version)
1362
if self._auth_scheme != 'digest':
1363
raise AssertionError('Unknown auth scheme: %r'
1364
% self._auth_scheme)
1365
server = http_utils.HTTPDigestAuthServer(
1366
protocol_version=self._protocol_version)
1369
def _testing_pycurl(self):
1370
return pycurl_present and self._transport == PyCurlTransport
1254
1372
def get_user_url(self, user=None, password=None):
1255
1373
"""Build an url embedding user and password"""
1256
1374
url = '%s://' % self.server._url_protocol
1348
1479
# Only one 'Authentication Required' error should occur
1349
1480
self.assertEqual(1, self.server.auth_required_errors)
1353
class TestHTTPAuth(TestAuth):
1354
"""Test HTTP authentication schemes.
1356
Daughter classes MUST inherit from TestCaseWithWebserver too.
1359
_auth_header = 'Authorization'
1362
TestCaseWithWebserver.setUp(self)
1363
self.server = self.get_readonly_server()
1364
TestAuth.setUp(self)
1366
def get_user_transport(self, user=None, password=None):
1367
return self._transport(self.get_user_url(user, password))
1482
def test_changing_nonce(self):
1483
if self._auth_scheme != 'digest':
1484
raise tests.TestNotApplicable('HTTP auth digest only test')
1485
if self._testing_pycurl():
1486
raise tests.KnownFailure(
1487
'pycurl does not handle a nonce change')
1488
self.server.add_user('joe', 'foo')
1489
t = self.get_user_transport('joe', 'foo')
1490
self.assertEqual('contents of a\n', t.get('a').read())
1491
self.assertEqual('contents of b\n', t.get('b').read())
1492
# Only one 'Authentication Required' error should have
1494
self.assertEqual(1, self.server.auth_required_errors)
1495
# The server invalidates the current nonce
1496
self.server.auth_nonce = self.server.auth_nonce + '. No, now!'
1497
self.assertEqual('contents of a\n', t.get('a').read())
1498
# Two 'Authentication Required' errors should occur (the
1499
# initial 'who are you' and a second 'who are you' with the new nonce)
1500
self.assertEqual(2, self.server.auth_required_errors)
1370
1504
class TestProxyAuth(TestAuth):
1371
"""Test proxy authentication schemes.
1505
"""Test proxy authentication schemes."""
1373
Daughter classes MUST also inherit from TestCaseWithWebserver.
1375
1507
_auth_header = 'Proxy-authorization'
1376
_password_prompt_prefix = 'Proxy '
1508
_password_prompt_prefix='Proxy '
1379
1510
def setUp(self):
1380
TestCaseWithWebserver.setUp(self)
1381
self.server = self.get_readonly_server()
1511
super(TestProxyAuth, self).setUp()
1382
1512
self._old_env = {}
1383
1513
self.addCleanup(self._restore_env)
1384
TestAuth.setUp(self)
1385
1514
# Override the contents to avoid false positives
1386
1515
self.build_tree_contents([('a', 'not proxied contents of a\n'),
1387
1516
('b', 'not proxied contents of b\n'),
1401
1542
for name, value in self._old_env.iteritems():
1402
1543
osutils.set_or_unset_env(name, value)
1405
class TestHTTPBasicAuth(TestHTTPAuth, TestCaseWithWebserver):
1406
"""Test http basic authentication scheme"""
1408
_transport = HttpTransport_urllib
1410
def create_transport_readonly_server(self):
1411
return HTTPBasicAuthServer()
1414
class TestHTTPProxyBasicAuth(TestProxyAuth, TestCaseWithWebserver):
1415
"""Test proxy basic authentication scheme"""
1417
_transport = HttpTransport_urllib
1419
def create_transport_readonly_server(self):
1420
return ProxyBasicAuthServer()
1423
class TestDigestAuth(object):
1424
"""Digest Authentication specific tests"""
1426
def test_changing_nonce(self):
1427
self.server.add_user('joe', 'foo')
1428
t = self.get_user_transport('joe', 'foo')
1429
self.assertEqual('contents of a\n', t.get('a').read())
1430
self.assertEqual('contents of b\n', t.get('b').read())
1431
# Only one 'Authentication Required' error should have
1433
self.assertEqual(1, self.server.auth_required_errors)
1434
# The server invalidates the current nonce
1435
self.server.auth_nonce = self.server.auth_nonce + '. No, now!'
1436
self.assertEqual('contents of a\n', t.get('a').read())
1437
# Two 'Authentication Required' errors should occur (the
1438
# initial 'who are you' and a second 'who are you' with the new nonce)
1439
self.assertEqual(2, self.server.auth_required_errors)
1442
class TestHTTPDigestAuth(TestHTTPAuth, TestDigestAuth, TestCaseWithWebserver):
1443
"""Test http digest authentication scheme"""
1445
_transport = HttpTransport_urllib
1447
def create_transport_readonly_server(self):
1448
return HTTPDigestAuthServer()
1451
class TestHTTPProxyDigestAuth(TestProxyAuth, TestDigestAuth,
1452
TestCaseWithWebserver):
1453
"""Test proxy digest authentication scheme"""
1455
_transport = HttpTransport_urllib
1457
def create_transport_readonly_server(self):
1458
return ProxyDigestAuthServer()
1545
def test_empty_pass(self):
1546
if self._testing_pycurl():
1548
if pycurl.version_info()[1] < '7.16.0':
1549
raise tests.KnownFailure(
1550
'pycurl < 7.16.0 does not handle empty proxy passwords')
1551
super(TestProxyAuth, self).test_empty_pass()
1554
class SampleSocket(object):
1555
"""A socket-like object for use in testing the HTTP request handler."""
1557
def __init__(self, socket_read_content):
1558
"""Constructs a sample socket.
1560
:param socket_read_content: a byte sequence
1562
# Use plain python StringIO so we can monkey-patch the close method to
1563
# not discard the contents.
1564
from StringIO import StringIO
1565
self.readfile = StringIO(socket_read_content)
1566
self.writefile = StringIO()
1567
self.writefile.close = lambda: None
1569
def makefile(self, mode='r', bufsize=None):
1571
return self.readfile
1573
return self.writefile
1576
class SmartHTTPTunnellingTest(tests.TestCaseWithTransport):
1579
super(SmartHTTPTunnellingTest, self).setUp()
1580
# We use the VFS layer as part of HTTP tunnelling tests.
1581
self._captureVar('BZR_NO_SMART_VFS', None)
1582
self.transport_readonly_server = http_utils.HTTPServerWithSmarts
1584
def create_transport_readonly_server(self):
1585
return http_utils.HTTPServerWithSmarts(
1586
protocol_version=self._protocol_version)
1588
def test_bulk_data(self):
1589
# We should be able to send and receive bulk data in a single message.
1590
# The 'readv' command in the smart protocol both sends and receives
1591
# bulk data, so we use that.
1592
self.build_tree(['data-file'])
1593
http_server = self.get_readonly_server()
1594
http_transport = self._transport(http_server.get_url())
1595
medium = http_transport.get_smart_medium()
1596
# Since we provide the medium, the url below will be mostly ignored
1597
# during the test, as long as the path is '/'.
1598
remote_transport = remote.RemoteTransport('bzr://fake_host/',
1601
[(0, "c")], list(remote_transport.readv("data-file", [(0,1)])))
1603
def test_http_send_smart_request(self):
1605
post_body = 'hello\n'
1606
expected_reply_body = 'ok\x012\n'
1608
http_server = self.get_readonly_server()
1609
http_transport = self._transport(http_server.get_url())
1610
medium = http_transport.get_smart_medium()
1611
response = medium.send_http_smart_request(post_body)
1612
reply_body = response.read()
1613
self.assertEqual(expected_reply_body, reply_body)
1615
def test_smart_http_server_post_request_handler(self):
1616
httpd = self.get_readonly_server()._get_httpd()
1618
socket = SampleSocket(
1619
'POST /.bzr/smart %s \r\n' % self._protocol_version
1620
# HTTP/1.1 posts must have a Content-Length (but it doesn't hurt
1622
+ 'Content-Length: 6\r\n'
1625
# Beware: the ('localhost', 80) below is the
1626
# client_address parameter, but we don't have one because
1627
# we have defined a socket which is not bound to an
1628
# address. The test framework never uses this client
1629
# address, so far...
1630
request_handler = http_utils.SmartRequestHandler(socket,
1633
response = socket.writefile.getvalue()
1634
self.assertStartsWith(response, '%s 200 ' % self._protocol_version)
1635
# This includes the end of the HTTP headers, and all the body.
1636
expected_end_of_response = '\r\n\r\nok\x012\n'
1637
self.assertEndsWith(response, expected_end_of_response)