112
191
self.received_bytes = ''
195
return '%s://%s:%s/' % (self.scheme, self.host, self.port)
197
def start_server(self):
115
198
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
116
199
self._sock.bind(('127.0.0.1', 0))
117
200
self.host, self.port = self._sock.getsockname()
118
201
self._ready = threading.Event()
119
self._thread = threading.Thread(target=self._accept_read_and_reply)
120
self._thread.setDaemon(True)
202
self._thread = test_server.TestThread(
203
sync_event=self._ready, target=self._accept_read_and_reply)
121
204
self._thread.start()
205
if 'threads' in tests.selftest_debug_flags:
206
sys.stderr.write('Thread started: %s\n' % (self._thread.ident,))
124
209
def _accept_read_and_reply(self):
125
210
self._sock.listen(1)
126
211
self._ready.set()
127
self._sock.settimeout(5)
129
conn, address = self._sock.accept()
130
# On win32, the accepted connection will be non-blocking to start
131
# with because we're using settimeout.
132
conn.setblocking(True)
212
conn, address = self._sock.accept()
213
if self._expect_body_tail is not None:
133
214
while not self.received_bytes.endswith(self._expect_body_tail):
134
215
self.received_bytes += conn.recv(4096)
135
216
conn.sendall('HTTP/1.1 200 OK\r\n')
136
except socket.timeout:
137
# Make sure the client isn't stuck waiting for us to e.g. accept.
138
218
self._sock.close()
139
219
except socket.error:
140
220
# The client may have already closed the socket.
223
def stop_server(self):
225
# Issue a fake connection to wake up the server and allow it to
227
fake_conn = osutils.connect_socket((self.host, self.port))
146
229
except socket.error:
147
230
# We might have already closed it. We don't care.
235
if 'threads' in tests.selftest_debug_flags:
236
sys.stderr.write('Thread joined: %s\n' % (self._thread.ident,))
239
class TestAuthHeader(tests.TestCase):
241
def parse_header(self, header, auth_handler_class=None):
242
if auth_handler_class is None:
243
auth_handler_class = _urllib2_wrappers.AbstractAuthHandler
244
self.auth_handler = auth_handler_class()
245
return self.auth_handler._parse_auth_header(header)
247
def test_empty_header(self):
248
scheme, remainder = self.parse_header('')
249
self.assertEqual('', scheme)
250
self.assertIs(None, remainder)
252
def test_negotiate_header(self):
253
scheme, remainder = self.parse_header('Negotiate')
254
self.assertEqual('negotiate', scheme)
255
self.assertIs(None, remainder)
257
def test_basic_header(self):
258
scheme, remainder = self.parse_header(
259
'Basic realm="Thou should not pass"')
260
self.assertEqual('basic', scheme)
261
self.assertEqual('realm="Thou should not pass"', remainder)
263
def test_basic_extract_realm(self):
264
scheme, remainder = self.parse_header(
265
'Basic realm="Thou should not pass"',
266
_urllib2_wrappers.BasicAuthHandler)
267
match, realm = self.auth_handler.extract_realm(remainder)
268
self.assertTrue(match is not None)
269
self.assertEqual('Thou should not pass', realm)
271
def test_digest_header(self):
272
scheme, remainder = self.parse_header(
273
'Digest realm="Thou should not pass"')
274
self.assertEqual('digest', scheme)
275
self.assertEqual('realm="Thou should not pass"', remainder)
278
class TestHTTPRangeParsing(tests.TestCase):
281
super(TestHTTPRangeParsing, self).setUp()
282
# We focus on range parsing here and ignore everything else
283
class RequestHandler(http_server.TestingHTTPRequestHandler):
284
def setup(self): pass
285
def handle(self): pass
286
def finish(self): pass
288
self.req_handler = RequestHandler(None, None, None)
290
def assertRanges(self, ranges, header, file_size):
291
self.assertEqual(ranges,
292
self.req_handler._parse_ranges(header, file_size))
294
def test_simple_range(self):
295
self.assertRanges([(0,2)], 'bytes=0-2', 12)
298
self.assertRanges([(8, 11)], 'bytes=-4', 12)
300
def test_tail_bigger_than_file(self):
301
self.assertRanges([(0, 11)], 'bytes=-99', 12)
303
def test_range_without_end(self):
304
self.assertRanges([(4, 11)], 'bytes=4-', 12)
306
def test_invalid_ranges(self):
307
self.assertRanges(None, 'bytes=12-22', 12)
308
self.assertRanges(None, 'bytes=1-3,12-22', 12)
309
self.assertRanges(None, 'bytes=-', 12)
312
class TestHTTPServer(tests.TestCase):
313
"""Test the HTTP servers implementations."""
315
def test_invalid_protocol(self):
316
class BogusRequestHandler(http_server.TestingHTTPRequestHandler):
318
protocol_version = 'HTTP/0.1'
320
self.assertRaises(httplib.UnknownProtocol,
321
http_server.HttpServer, BogusRequestHandler)
323
def test_force_invalid_protocol(self):
324
self.assertRaises(httplib.UnknownProtocol,
325
http_server.HttpServer, protocol_version='HTTP/0.1')
327
def test_server_start_and_stop(self):
328
server = http_server.HttpServer()
329
self.addCleanup(server.stop_server)
330
server.start_server()
331
self.assertTrue(server.server is not None)
332
self.assertTrue(server.server.serving is not None)
333
self.assertTrue(server.server.serving)
335
def test_create_http_server_one_zero(self):
336
class RequestHandlerOneZero(http_server.TestingHTTPRequestHandler):
338
protocol_version = 'HTTP/1.0'
340
server = http_server.HttpServer(RequestHandlerOneZero)
341
self.start_server(server)
342
self.assertIsInstance(server.server, http_server.TestingHTTPServer)
344
def test_create_http_server_one_one(self):
345
class RequestHandlerOneOne(http_server.TestingHTTPRequestHandler):
347
protocol_version = 'HTTP/1.1'
349
server = http_server.HttpServer(RequestHandlerOneOne)
350
self.start_server(server)
351
self.assertIsInstance(server.server,
352
http_server.TestingThreadingHTTPServer)
354
def test_create_http_server_force_one_one(self):
355
class RequestHandlerOneZero(http_server.TestingHTTPRequestHandler):
357
protocol_version = 'HTTP/1.0'
359
server = http_server.HttpServer(RequestHandlerOneZero,
360
protocol_version='HTTP/1.1')
361
self.start_server(server)
362
self.assertIsInstance(server.server,
363
http_server.TestingThreadingHTTPServer)
365
def test_create_http_server_force_one_zero(self):
366
class RequestHandlerOneOne(http_server.TestingHTTPRequestHandler):
368
protocol_version = 'HTTP/1.1'
370
server = http_server.HttpServer(RequestHandlerOneOne,
371
protocol_version='HTTP/1.0')
372
self.start_server(server)
373
self.assertIsInstance(server.server,
374
http_server.TestingHTTPServer)
153
377
class TestWithTransport_pycurl(object):
154
378
"""Test case to inherit from if pycurl is present"""
156
380
def _get_pycurl_maybe(self):
158
from bzrlib.transport.http._pycurl import PyCurlTransport
159
return PyCurlTransport
160
except errors.DependencyNotPresent:
161
raise TestSkipped('pycurl not present')
381
self.requireFeature(features.pycurl)
382
return PyCurlTransport
163
384
_transport = property(_get_pycurl_maybe)
166
class TestHttpUrls(TestCase):
168
# TODO: This should be moved to authorization tests once they
171
def test_url_parsing(self):
173
url = extract_auth('http://example.com', f)
174
self.assertEquals('http://example.com', url)
175
self.assertEquals(0, len(f.credentials))
176
url = extract_auth('http://user:pass@www.bazaar-vcs.org/bzr/bzr.dev', f)
177
self.assertEquals('http://www.bazaar-vcs.org/bzr/bzr.dev', url)
178
self.assertEquals(1, len(f.credentials))
179
self.assertEquals([None, 'www.bazaar-vcs.org', 'user', 'pass'],
183
class TestHttpTransportUrls(object):
184
"""Test the http urls.
186
This MUST be used by daughter classes that also inherit from
189
We can't inherit directly from TestCase or the
190
test framework will try to create an instance which cannot
191
run, its implementation being incomplete.
387
class TestHttpTransportUrls(tests.TestCase):
388
"""Test the http urls."""
390
scenarios = vary_by_http_client_implementation()
194
392
def test_abs_url(self):
195
393
"""Construction of absolute http URLs"""
196
t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
394
t = self._transport('http://example.com/bzr/bzr.dev/')
197
395
eq = self.assertEqualDiff
198
eq(t.abspath('.'), 'http://bazaar-vcs.org/bzr/bzr.dev')
199
eq(t.abspath('foo/bar'), 'http://bazaar-vcs.org/bzr/bzr.dev/foo/bar')
200
eq(t.abspath('.bzr'), 'http://bazaar-vcs.org/bzr/bzr.dev/.bzr')
396
eq(t.abspath('.'), 'http://example.com/bzr/bzr.dev')
397
eq(t.abspath('foo/bar'), 'http://example.com/bzr/bzr.dev/foo/bar')
398
eq(t.abspath('.bzr'), 'http://example.com/bzr/bzr.dev/.bzr')
201
399
eq(t.abspath('.bzr/1//2/./3'),
202
'http://bazaar-vcs.org/bzr/bzr.dev/.bzr/1/2/3')
400
'http://example.com/bzr/bzr.dev/.bzr/1/2/3')
204
402
def test_invalid_http_urls(self):
205
403
"""Trap invalid construction of urls"""
206
t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
404
self._transport('http://example.com/bzr/bzr.dev/')
207
405
self.assertRaises(errors.InvalidURL,
209
'http://http://bazaar-vcs.org/bzr/bzr.dev/')
407
'http://http://example.com/bzr/bzr.dev/')
211
409
def test_http_root_urls(self):
212
410
"""Construction of URLs from server root"""
213
t = self._transport('http://bzr.ozlabs.org/')
411
t = self._transport('http://example.com/')
214
412
eq = self.assertEqualDiff
215
413
eq(t.abspath('.bzr/tree-version'),
216
'http://bzr.ozlabs.org/.bzr/tree-version')
414
'http://example.com/.bzr/tree-version')
218
416
def test_http_impl_urls(self):
219
417
"""There are servers which ask for particular clients to connect"""
220
418
server = self._server()
419
server.start_server()
223
421
url = server.get_url()
224
self.assertTrue(url.startswith('%s://' % self._qualified_prefix))
422
self.assertTrue(url.startswith('%s://' % self._url_protocol))
229
class TestHttpUrls_urllib(TestHttpTransportUrls, TestCase):
230
"""Test http urls with urllib"""
232
_transport = HttpTransport_urllib
233
_server = HttpServer_urllib
234
_qualified_prefix = 'http+urllib'
237
class TestHttpUrls_pycurl(TestWithTransport_pycurl, TestHttpTransportUrls,
239
"""Test http urls with pycurl"""
241
_server = HttpServer_PyCurl
242
_qualified_prefix = 'http+pycurl'
427
class TestHttps_pycurl(TestWithTransport_pycurl, tests.TestCase):
244
429
# TODO: This should really be moved into another pycurl
245
430
# specific test. When https tests will be implemented, take
653
830
self.assertListRaises((errors.InvalidRange, errors.ShortReadvError,),
654
831
t.readv, 'a', [(12,2)])
833
def test_readv_multiple_get_requests(self):
834
server = self.get_readonly_server()
835
t = self.get_readonly_transport()
836
# force transport to issue multiple requests
837
t._max_readv_combine = 1
838
t._max_get_ranges = 1
839
l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
840
self.assertEqual(l[0], (0, '0'))
841
self.assertEqual(l[1], (1, '1'))
842
self.assertEqual(l[2], (3, '34'))
843
self.assertEqual(l[3], (9, '9'))
844
# The server should have issued 4 requests
845
self.assertEqual(4, server.GET_request_nb)
847
def test_readv_get_max_size(self):
848
server = self.get_readonly_server()
849
t = self.get_readonly_transport()
850
# force transport to issue multiple requests by limiting the number of
851
# bytes by request. Note that this apply to coalesced offsets only, a
852
# single range will keep its size even if bigger than the limit.
854
l = list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
855
self.assertEqual(l[0], (0, '0'))
856
self.assertEqual(l[1], (1, '1'))
857
self.assertEqual(l[2], (2, '2345'))
858
self.assertEqual(l[3], (6, '6789'))
859
# The server should have issued 3 requests
860
self.assertEqual(3, server.GET_request_nb)
862
def test_complete_readv_leave_pipe_clean(self):
863
server = self.get_readonly_server()
864
t = self.get_readonly_transport()
865
# force transport to issue multiple requests
867
list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
868
# The server should have issued 3 requests
869
self.assertEqual(3, server.GET_request_nb)
870
self.assertEqual('0123456789', t.get_bytes('a'))
871
self.assertEqual(4, server.GET_request_nb)
873
def test_incomplete_readv_leave_pipe_clean(self):
874
server = self.get_readonly_server()
875
t = self.get_readonly_transport()
876
# force transport to issue multiple requests
878
# Don't collapse readv results into a list so that we leave unread
879
# bytes on the socket
880
ireadv = iter(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
881
self.assertEqual((0, '0'), ireadv.next())
882
# The server should have issued one request so far
883
self.assertEqual(1, server.GET_request_nb)
884
self.assertEqual('0123456789', t.get_bytes('a'))
885
# get_bytes issued an additional request, the readv pending ones are
887
self.assertEqual(2, server.GET_request_nb)
890
class SingleRangeRequestHandler(http_server.TestingHTTPRequestHandler):
891
"""Always reply to range request as if they were single.
893
Don't be explicit about it, just to annoy the clients.
896
def get_multiple_ranges(self, file, file_size, ranges):
897
"""Answer as if it was a single range request and ignores the rest"""
898
(start, end) = ranges[0]
899
return self.get_single_range(file, file_size, start, end)
657
902
class TestSingleRangeRequestServer(TestRangeRequestServer):
658
903
"""Test readv against a server which accept only single range requests"""
660
def create_transport_readonly_server(self):
661
return HttpServer(SingleRangeRequestHandler)
664
class TestSingleRangeRequestServer_urllib(TestSingleRangeRequestServer,
665
TestCaseWithWebserver):
666
"""Tests single range requests accepting server for urllib implementation"""
668
_transport = HttpTransport_urllib
671
class TestSingleRangeRequestServer_pycurl(TestWithTransport_pycurl,
672
TestSingleRangeRequestServer,
673
TestCaseWithWebserver):
674
"""Tests single range requests accepting server for pycurl implementation"""
905
_req_handler_class = SingleRangeRequestHandler
908
class SingleOnlyRangeRequestHandler(http_server.TestingHTTPRequestHandler):
909
"""Only reply to simple range requests, errors out on multiple"""
911
def get_multiple_ranges(self, file, file_size, ranges):
912
"""Refuses the multiple ranges request"""
915
self.send_error(416, "Requested range not satisfiable")
917
(start, end) = ranges[0]
918
return self.get_single_range(file, file_size, start, end)
677
921
class TestSingleOnlyRangeRequestServer(TestRangeRequestServer):
678
922
"""Test readv against a server which only accept single range requests"""
680
def create_transport_readonly_server(self):
681
return HttpServer(SingleOnlyRangeRequestHandler)
684
class TestSingleOnlyRangeRequestServer_urllib(TestSingleOnlyRangeRequestServer,
685
TestCaseWithWebserver):
686
"""Tests single range requests accepting server for urllib implementation"""
688
_transport = HttpTransport_urllib
691
class TestSingleOnlyRangeRequestServer_pycurl(TestWithTransport_pycurl,
692
TestSingleOnlyRangeRequestServer,
693
TestCaseWithWebserver):
694
"""Tests single range requests accepting server for pycurl implementation"""
924
_req_handler_class = SingleOnlyRangeRequestHandler
927
class NoRangeRequestHandler(http_server.TestingHTTPRequestHandler):
928
"""Ignore range requests without notice"""
931
# Update the statistics
932
self.server.test_case_server.GET_request_nb += 1
933
# Just bypass the range handling done by TestingHTTPRequestHandler
934
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
697
937
class TestNoRangeRequestServer(TestRangeRequestServer):
698
938
"""Test readv against a server which do not accept range requests"""
700
def create_transport_readonly_server(self):
701
return HttpServer(NoRangeRequestHandler)
704
class TestNoRangeRequestServer_urllib(TestNoRangeRequestServer,
705
TestCaseWithWebserver):
706
"""Tests range requests refusing server for urllib implementation"""
708
_transport = HttpTransport_urllib
711
class TestNoRangeRequestServer_pycurl(TestWithTransport_pycurl,
712
TestNoRangeRequestServer,
713
TestCaseWithWebserver):
714
"""Tests range requests refusing server for pycurl implementation"""
717
class TestLimitedRangeRequestServer(object):
718
"""Tests readv requests against server that errors out on too much ranges.
720
This MUST be used by daughter classes that also inherit from
721
TestCaseWithWebserver.
723
We can't inherit directly from TestCaseWithWebserver or the
724
test framework will try to create an instance which cannot
725
run, its implementation being incomplete.
940
_req_handler_class = NoRangeRequestHandler
943
class MultipleRangeWithoutContentLengthRequestHandler(
944
http_server.TestingHTTPRequestHandler):
945
"""Reply to multiple range requests without content length header."""
947
def get_multiple_ranges(self, file, file_size, ranges):
948
self.send_response(206)
949
self.send_header('Accept-Ranges', 'bytes')
950
# XXX: this is strange; the 'random' name below seems undefined and
951
# yet the tests pass -- mbp 2010-10-11 bug 658773
952
boundary = "%d" % random.randint(0,0x7FFFFFFF)
953
self.send_header("Content-Type",
954
"multipart/byteranges; boundary=%s" % boundary)
956
for (start, end) in ranges:
957
self.wfile.write("--%s\r\n" % boundary)
958
self.send_header("Content-type", 'application/octet-stream')
959
self.send_header("Content-Range", "bytes %d-%d/%d" % (start,
963
self.send_range_content(file, start, end - start + 1)
965
self.wfile.write("--%s\r\n" % boundary)
968
class TestMultipleRangeWithoutContentLengthServer(TestRangeRequestServer):
970
_req_handler_class = MultipleRangeWithoutContentLengthRequestHandler
973
class TruncatedMultipleRangeRequestHandler(
974
http_server.TestingHTTPRequestHandler):
975
"""Reply to multiple range requests truncating the last ones.
977
This server generates responses whose Content-Length describes all the
978
ranges, but fail to include the last ones leading to client short reads.
979
This has been observed randomly with lighttpd (bug #179368).
982
_truncated_ranges = 2
984
def get_multiple_ranges(self, file, file_size, ranges):
985
self.send_response(206)
986
self.send_header('Accept-Ranges', 'bytes')
988
self.send_header('Content-Type',
989
'multipart/byteranges; boundary=%s' % boundary)
990
boundary_line = '--%s\r\n' % boundary
991
# Calculate the Content-Length
993
for (start, end) in ranges:
994
content_length += len(boundary_line)
995
content_length += self._header_line_length(
996
'Content-type', 'application/octet-stream')
997
content_length += self._header_line_length(
998
'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
999
content_length += len('\r\n') # end headers
1000
content_length += end - start # + 1
1001
content_length += len(boundary_line)
1002
self.send_header('Content-length', content_length)
1005
# Send the multipart body
1007
for (start, end) in ranges:
1008
self.wfile.write(boundary_line)
1009
self.send_header('Content-type', 'application/octet-stream')
1010
self.send_header('Content-Range', 'bytes %d-%d/%d'
1011
% (start, end, file_size))
1013
if cur + self._truncated_ranges >= len(ranges):
1014
# Abruptly ends the response and close the connection
1015
self.close_connection = 1
1017
self.send_range_content(file, start, end - start + 1)
1020
self.wfile.write(boundary_line)
1023
class TestTruncatedMultipleRangeServer(TestSpecificRequestHandler):
1025
_req_handler_class = TruncatedMultipleRangeRequestHandler
1028
super(TestTruncatedMultipleRangeServer, self).setUp()
1029
self.build_tree_contents([('a', '0123456789')],)
1031
def test_readv_with_short_reads(self):
1032
server = self.get_readonly_server()
1033
t = self.get_readonly_transport()
1034
# Force separate ranges for each offset
1035
t._bytes_to_read_before_seek = 0
1036
ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
1037
self.assertEqual((0, '0'), ireadv.next())
1038
self.assertEqual((2, '2'), ireadv.next())
1039
if not self._testing_pycurl():
1040
# Only one request have been issued so far (except for pycurl that
1041
# try to read the whole response at once)
1042
self.assertEqual(1, server.GET_request_nb)
1043
self.assertEqual((4, '45'), ireadv.next())
1044
self.assertEqual((9, '9'), ireadv.next())
1045
# Both implementations issue 3 requests but:
1046
# - urllib does two multiple (4 ranges, then 2 ranges) then a single
1048
# - pycurl does two multiple (4 ranges, 4 ranges) then a single range
1049
self.assertEqual(3, server.GET_request_nb)
1050
# Finally the client have tried a single range request and stays in
1052
self.assertEqual('single', t._range_hint)
1055
class TruncatedBeforeBoundaryRequestHandler(
1056
http_server.TestingHTTPRequestHandler):
1057
"""Truncation before a boundary, like in bug 198646"""
1059
_truncated_ranges = 1
1061
def get_multiple_ranges(self, file, file_size, ranges):
1062
self.send_response(206)
1063
self.send_header('Accept-Ranges', 'bytes')
1065
self.send_header('Content-Type',
1066
'multipart/byteranges; boundary=%s' % boundary)
1067
boundary_line = '--%s\r\n' % boundary
1068
# Calculate the Content-Length
1070
for (start, end) in ranges:
1071
content_length += len(boundary_line)
1072
content_length += self._header_line_length(
1073
'Content-type', 'application/octet-stream')
1074
content_length += self._header_line_length(
1075
'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
1076
content_length += len('\r\n') # end headers
1077
content_length += end - start # + 1
1078
content_length += len(boundary_line)
1079
self.send_header('Content-length', content_length)
1082
# Send the multipart body
1084
for (start, end) in ranges:
1085
if cur + self._truncated_ranges >= len(ranges):
1086
# Abruptly ends the response and close the connection
1087
self.close_connection = 1
1089
self.wfile.write(boundary_line)
1090
self.send_header('Content-type', 'application/octet-stream')
1091
self.send_header('Content-Range', 'bytes %d-%d/%d'
1092
% (start, end, file_size))
1094
self.send_range_content(file, start, end - start + 1)
1097
self.wfile.write(boundary_line)
1100
class TestTruncatedBeforeBoundary(TestSpecificRequestHandler):
1101
"""Tests the case of bug 198646, disconnecting before a boundary."""
1103
_req_handler_class = TruncatedBeforeBoundaryRequestHandler
1106
super(TestTruncatedBeforeBoundary, self).setUp()
1107
self.build_tree_contents([('a', '0123456789')],)
1109
def test_readv_with_short_reads(self):
1110
server = self.get_readonly_server()
1111
t = self.get_readonly_transport()
1112
# Force separate ranges for each offset
1113
t._bytes_to_read_before_seek = 0
1114
ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
1115
self.assertEqual((0, '0'), ireadv.next())
1116
self.assertEqual((2, '2'), ireadv.next())
1117
self.assertEqual((4, '45'), ireadv.next())
1118
self.assertEqual((9, '9'), ireadv.next())
1121
class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
1122
"""Errors out when range specifiers exceed the limit"""
1124
def get_multiple_ranges(self, file, file_size, ranges):
1125
"""Refuses the multiple ranges request"""
1126
tcs = self.server.test_case_server
1127
if tcs.range_limit is not None and len(ranges) > tcs.range_limit:
1129
# Emulate apache behavior
1130
self.send_error(400, "Bad Request")
1132
return http_server.TestingHTTPRequestHandler.get_multiple_ranges(
1133
self, file, file_size, ranges)
1136
class LimitedRangeHTTPServer(http_server.HttpServer):
1137
"""An HttpServer erroring out on requests with too much range specifiers"""
1139
def __init__(self, request_handler=LimitedRangeRequestHandler,
1140
protocol_version=None,
1142
http_server.HttpServer.__init__(self, request_handler,
1143
protocol_version=protocol_version)
1144
self.range_limit = range_limit
1147
class TestLimitedRangeRequestServer(http_utils.TestCaseWithWebserver):
1148
"""Tests readv requests against a server erroring out on too much ranges."""
1150
scenarios = multiply_scenarios(
1151
vary_by_http_client_implementation(),
1152
vary_by_http_protocol_version(),
1155
# Requests with more range specifiers will error out
730
1158
def create_transport_readonly_server(self):
731
# Requests with more range specifiers will error out
732
return LimitedRangeHTTPServer(range_limit=self.range_limit)
734
def get_transport(self):
735
return self._transport(self.get_readonly_server().get_url())
1159
return LimitedRangeHTTPServer(range_limit=self.range_limit,
1160
protocol_version=self._protocol_version)
737
1162
def setUp(self):
738
TestCaseWithWebserver.setUp(self)
1163
super(TestLimitedRangeRequestServer, self).setUp()
739
1164
# We need to manipulate ranges that correspond to real chunks in the
740
1165
# response, so we build a content appropriately.
741
filler = ''.join(['abcdefghij' for _ in range(102)])
1166
filler = ''.join(['abcdefghij' for x in range(102)])
742
1167
content = ''.join(['%04d' % v + filler for v in range(16)])
743
1168
self.build_tree_contents([('a', content)],)
745
1170
def test_few_ranges(self):
746
t = self.get_transport()
1171
t = self.get_readonly_transport()
747
1172
l = list(t.readv('a', ((0, 4), (1024, 4), )))
748
1173
self.assertEqual(l[0], (0, '0000'))
749
1174
self.assertEqual(l[1], (1024, '0001'))
750
1175
self.assertEqual(1, self.get_readonly_server().GET_request_nb)
752
def test_a_lot_of_ranges(self):
753
t = self.get_transport()
1177
def test_more_ranges(self):
1178
t = self.get_readonly_transport()
754
1179
l = list(t.readv('a', ((0, 4), (1024, 4), (4096, 4), (8192, 4))))
755
1180
self.assertEqual(l[0], (0, '0000'))
756
1181
self.assertEqual(l[1], (1024, '0001'))
757
1182
self.assertEqual(l[2], (4096, '0004'))
758
1183
self.assertEqual(l[3], (8192, '0008'))
759
1184
# The server will refuse to serve the first request (too much ranges),
760
# a second request will succeeds.
1185
# a second request will succeed.
761
1186
self.assertEqual(2, self.get_readonly_server().GET_request_nb)
764
class TestLimitedRangeRequestServer_urllib(TestLimitedRangeRequestServer,
765
TestCaseWithWebserver):
766
"""Tests limited range requests server for urllib implementation"""
768
_transport = HttpTransport_urllib
771
class TestLimitedRangeRequestServer_pycurl(TestWithTransport_pycurl,
772
TestLimitedRangeRequestServer,
773
TestCaseWithWebserver):
774
"""Tests limited range requests server for pycurl implementation"""
778
class TestHttpProxyWhiteBox(TestCase):
1189
class TestHttpProxyWhiteBox(tests.TestCase):
779
1190
"""Whitebox test proxy http authorization.
781
1192
Only the urllib implementation is tested here.
791
def _install_env(self, env):
792
for name, value in env.iteritems():
793
self._old_env[name] = osutils.set_or_unset_env(name, value)
795
def _restore_env(self):
796
for name, value in self._old_env.iteritems():
797
osutils.set_or_unset_env(name, value)
799
1195
def _proxied_request(self):
800
handler = ProxyHandler()
801
request = Request('GET','http://baz/buzzle')
1196
handler = _urllib2_wrappers.ProxyHandler()
1197
request = _urllib2_wrappers.Request('GET', 'http://baz/buzzle')
802
1198
handler.set_proxy(request, 'http')
1201
def assertEvaluateProxyBypass(self, expected, host, no_proxy):
1202
handler = _urllib2_wrappers.ProxyHandler()
1203
self.assertEqual(expected,
1204
handler.evaluate_proxy_bypass(host, no_proxy))
805
1206
def test_empty_user(self):
806
self._install_env({'http_proxy': 'http://bar.com'})
1207
self.overrideEnv('http_proxy', 'http://bar.com')
1208
request = self._proxied_request()
1209
self.assertFalse(request.headers.has_key('Proxy-authorization'))
1211
def test_user_with_at(self):
1212
self.overrideEnv('http_proxy',
1213
'http://username@domain:password@proxy_host:1234')
807
1214
request = self._proxied_request()
808
1215
self.assertFalse(request.headers.has_key('Proxy-authorization'))
810
1217
def test_invalid_proxy(self):
811
1218
"""A proxy env variable without scheme"""
812
self._install_env({'http_proxy': 'host:1234'})
1219
self.overrideEnv('http_proxy', 'host:1234')
813
1220
self.assertRaises(errors.InvalidURL, self._proxied_request)
816
class TestProxyHttpServer(object):
1222
def test_evaluate_proxy_bypass_true(self):
1223
"""The host is not proxied"""
1224
self.assertEvaluateProxyBypass(True, 'example.com', 'example.com')
1225
self.assertEvaluateProxyBypass(True, 'bzr.example.com', '*example.com')
1227
def test_evaluate_proxy_bypass_false(self):
1228
"""The host is proxied"""
1229
self.assertEvaluateProxyBypass(False, 'bzr.example.com', None)
1231
def test_evaluate_proxy_bypass_unknown(self):
1232
"""The host is not explicitly proxied"""
1233
self.assertEvaluateProxyBypass(None, 'example.com', 'not.example.com')
1234
self.assertEvaluateProxyBypass(None, 'bzr.example.com', 'example.com')
1236
def test_evaluate_proxy_bypass_empty_entries(self):
1237
"""Ignore empty entries"""
1238
self.assertEvaluateProxyBypass(None, 'example.com', '')
1239
self.assertEvaluateProxyBypass(None, 'example.com', ',')
1240
self.assertEvaluateProxyBypass(None, 'example.com', 'foo,,bar')
1243
class TestProxyHttpServer(http_utils.TestCaseWithTwoWebservers):
817
1244
"""Tests proxy server.
819
This MUST be used by daughter classes that also inherit from
820
TestCaseWithTwoWebservers.
822
We can't inherit directly from TestCaseWithTwoWebservers or
823
the test framework will try to create an instance which
824
cannot run, its implementation being incomplete.
826
1246
Be aware that we do not setup a real proxy here. Instead, we
827
1247
check that the *connection* goes through the proxy by serving
828
1248
different content (the faked proxy server append '-proxied'
829
1249
to the file names).
1252
scenarios = multiply_scenarios(
1253
vary_by_http_client_implementation(),
1254
vary_by_http_protocol_version(),
832
1257
# FIXME: We don't have an https server available, so we don't
833
# test https connections.
1258
# test https connections. --vila toolongago
835
1260
def setUp(self):
836
TestCaseWithTwoWebservers.setUp(self)
1261
super(TestProxyHttpServer, self).setUp()
1262
self.transport_secondary_server = http_utils.ProxyServer
837
1263
self.build_tree_contents([('foo', 'contents of foo\n'),
838
1264
('foo-proxied', 'proxied contents of foo\n')])
839
1265
# Let's setup some attributes for tests
840
self.server = self.get_readonly_server()
841
self.proxy_address = '%s:%d' % (self.server.host, self.server.port)
842
self.no_proxy_host = self.proxy_address
1266
server = self.get_readonly_server()
1267
self.server_host_port = '%s:%d' % (server.host, server.port)
1268
if self._testing_pycurl():
1269
# Oh my ! pycurl does not check for the port as part of
1270
# no_proxy :-( So we just test the host part
1271
self.no_proxy_host = server.host
1273
self.no_proxy_host = self.server_host_port
843
1274
# The secondary server is the proxy
844
self.proxy = self.get_secondary_server()
845
self.proxy_url = self.proxy.get_url()
848
def create_transport_secondary_server(self):
849
"""Creates an http server that will serve files with
850
'-proxied' appended to their names.
854
def _install_env(self, env):
855
for name, value in env.iteritems():
856
self._old_env[name] = osutils.set_or_unset_env(name, value)
858
def _restore_env(self):
859
for name, value in self._old_env.iteritems():
860
osutils.set_or_unset_env(name, value)
862
def proxied_in_env(self, env):
863
self._install_env(env)
864
url = self.server.get_url()
865
t = self._transport(url)
867
self.assertEqual(t.get('foo').read(), 'proxied contents of foo\n')
871
def not_proxied_in_env(self, env):
872
self._install_env(env)
873
url = self.server.get_url()
874
t = self._transport(url)
876
self.assertEqual(t.get('foo').read(), 'contents of foo\n')
1275
self.proxy_url = self.get_secondary_url()
1277
def _testing_pycurl(self):
1278
# TODO: This is duplicated for lots of the classes in this file
1279
return (features.pycurl.available()
1280
and self._transport == PyCurlTransport)
1282
def assertProxied(self):
1283
t = self.get_readonly_transport()
1284
self.assertEqual('proxied contents of foo\n', t.get('foo').read())
1286
def assertNotProxied(self):
1287
t = self.get_readonly_transport()
1288
self.assertEqual('contents of foo\n', t.get('foo').read())
880
1290
def test_http_proxy(self):
881
self.proxied_in_env({'http_proxy': self.proxy_url})
1291
self.overrideEnv('http_proxy', self.proxy_url)
1292
self.assertProxied()
883
1294
def test_HTTP_PROXY(self):
884
self.proxied_in_env({'HTTP_PROXY': self.proxy_url})
1295
if self._testing_pycurl():
1296
# pycurl does not check HTTP_PROXY for security reasons
1297
# (for use in a CGI context that we do not care
1298
# about. Should we ?)
1299
raise tests.TestNotApplicable(
1300
'pycurl does not check HTTP_PROXY for security reasons')
1301
self.overrideEnv('HTTP_PROXY', self.proxy_url)
1302
self.assertProxied()
886
1304
def test_all_proxy(self):
887
self.proxied_in_env({'all_proxy': self.proxy_url})
1305
self.overrideEnv('all_proxy', self.proxy_url)
1306
self.assertProxied()
889
1308
def test_ALL_PROXY(self):
890
self.proxied_in_env({'ALL_PROXY': self.proxy_url})
1309
self.overrideEnv('ALL_PROXY', self.proxy_url)
1310
self.assertProxied()
892
1312
def test_http_proxy_with_no_proxy(self):
893
self.not_proxied_in_env({'http_proxy': self.proxy_url,
894
'no_proxy': self.no_proxy_host})
1313
self.overrideEnv('no_proxy', self.no_proxy_host)
1314
self.overrideEnv('http_proxy', self.proxy_url)
1315
self.assertNotProxied()
896
1317
def test_HTTP_PROXY_with_NO_PROXY(self):
897
self.not_proxied_in_env({'HTTP_PROXY': self.proxy_url,
898
'NO_PROXY': self.no_proxy_host})
1318
if self._testing_pycurl():
1319
raise tests.TestNotApplicable(
1320
'pycurl does not check HTTP_PROXY for security reasons')
1321
self.overrideEnv('NO_PROXY', self.no_proxy_host)
1322
self.overrideEnv('HTTP_PROXY', self.proxy_url)
1323
self.assertNotProxied()
900
1325
def test_all_proxy_with_no_proxy(self):
901
self.not_proxied_in_env({'all_proxy': self.proxy_url,
902
'no_proxy': self.no_proxy_host})
1326
self.overrideEnv('no_proxy', self.no_proxy_host)
1327
self.overrideEnv('all_proxy', self.proxy_url)
1328
self.assertNotProxied()
904
1330
def test_ALL_PROXY_with_NO_PROXY(self):
905
self.not_proxied_in_env({'ALL_PROXY': self.proxy_url,
906
'NO_PROXY': self.no_proxy_host})
908
def test_http_proxy_without_scheme(self):
909
self.assertRaises(errors.InvalidURL,
911
{'http_proxy': self.proxy_address})
914
class TestProxyHttpServer_urllib(TestProxyHttpServer,
915
TestCaseWithTwoWebservers):
916
"""Tests proxy server for urllib implementation"""
918
_transport = HttpTransport_urllib
921
class TestProxyHttpServer_pycurl(TestWithTransport_pycurl,
923
TestCaseWithTwoWebservers):
924
"""Tests proxy server for pycurl implementation"""
927
TestProxyHttpServer.setUp(self)
928
# Oh my ! pycurl does not check for the port as part of
929
# no_proxy :-( So we just test the host part
930
self.no_proxy_host = 'localhost'
932
def test_HTTP_PROXY(self):
933
# pycurl does not check HTTP_PROXY for security reasons
934
# (for use in a CGI context that we do not care
935
# about. Should we ?)
936
raise TestSkipped('pycurl does not check HTTP_PROXY '
937
'for security reasons')
939
def test_HTTP_PROXY_with_NO_PROXY(self):
940
raise TestSkipped('pycurl does not check HTTP_PROXY '
941
'for security reasons')
943
def test_http_proxy_without_scheme(self):
944
# pycurl *ignores* invalid proxy env variables. If that
945
# ever change in the future, this test will fail
946
# indicating that pycurl do not ignore anymore such
948
self.not_proxied_in_env({'http_proxy': self.proxy_address})
951
class TestRanges(object):
952
"""Test the Range header in GET methods..
954
This MUST be used by daughter classes that also inherit from
955
TestCaseWithWebserver.
957
We can't inherit directly from TestCaseWithWebserver or the
958
test framework will try to create an instance which cannot
959
run, its implementation being incomplete.
963
TestCaseWithWebserver.setUp(self)
1331
self.overrideEnv('NO_PROXY', self.no_proxy_host)
1332
self.overrideEnv('ALL_PROXY', self.proxy_url)
1333
self.assertNotProxied()
1335
def test_http_proxy_without_scheme(self):
1336
self.overrideEnv('http_proxy', self.server_host_port)
1337
if self._testing_pycurl():
1338
# pycurl *ignores* invalid proxy env variables. If that ever change
1339
# in the future, this test will fail indicating that pycurl do not
1340
# ignore anymore such variables.
1341
self.assertNotProxied()
1343
self.assertRaises(errors.InvalidURL, self.assertProxied)
1346
class TestRanges(http_utils.TestCaseWithWebserver):
1347
"""Test the Range header in GET methods."""
1349
scenarios = multiply_scenarios(
1350
vary_by_http_client_implementation(),
1351
vary_by_http_protocol_version(),
1355
super(TestRanges, self).setUp()
964
1356
self.build_tree_contents([('a', '0123456789')],)
965
server = self.get_readonly_server()
966
self.transport = self._transport(server.get_url())
1358
def create_transport_readonly_server(self):
1359
return http_server.HttpServer(protocol_version=self._protocol_version)
968
1361
def _file_contents(self, relpath, ranges):
1362
t = self.get_readonly_transport()
969
1363
offsets = [ (start, end - start + 1) for start, end in ranges]
970
coalesce = self.transport._coalesce_offsets
1364
coalesce = t._coalesce_offsets
971
1365
coalesced = list(coalesce(offsets, limit=0, fudge_factor=0))
972
code, data = self.transport._get(relpath, coalesced)
1366
code, data = t._get(relpath, coalesced)
973
1367
self.assertTrue(code in (200, 206),'_get returns: %d' % code)
974
1368
for start, end in ranges:
975
1369
data.seek(start)
976
1370
yield data.read(end - start + 1)
978
1372
def _file_tail(self, relpath, tail_amount):
979
code, data = self.transport._get(relpath, [], tail_amount)
1373
t = self.get_readonly_transport()
1374
code, data = t._get(relpath, [], tail_amount)
980
1375
self.assertTrue(code in (200, 206),'_get returns: %d' % code)
981
data.seek(-tail_amount + 1, 2)
1376
data.seek(-tail_amount, 2)
982
1377
return data.read(tail_amount)
984
1379
def test_range_header(self):
986
1381
map(self.assertEqual,['0', '234'],
987
1382
list(self._file_contents('a', [(0,0), (2,4)])),)
1384
def test_range_header_tail(self):
989
1385
self.assertEqual('789', self._file_tail('a', 3))
990
# Syntactically invalid range
991
self.assertListRaises(errors.InvalidRange,
1387
def test_syntactically_invalid_range_header(self):
1388
self.assertListRaises(errors.InvalidHttpRange,
992
1389
self._file_contents, 'a', [(4, 3)])
993
# Semantically invalid range
994
self.assertListRaises(errors.InvalidRange,
1391
def test_semantically_invalid_range_header(self):
1392
self.assertListRaises(errors.InvalidHttpRange,
995
1393
self._file_contents, 'a', [(42, 128)])
998
class TestRanges_urllib(TestRanges, TestCaseWithWebserver):
999
"""Test the Range header in GET methods for urllib implementation"""
1001
_transport = HttpTransport_urllib
1004
class TestRanges_pycurl(TestWithTransport_pycurl,
1006
TestCaseWithWebserver):
1007
"""Test the Range header in GET methods for pycurl implementation"""
1010
class TestHTTPRedirections(object):
1011
"""Test redirection between http servers.
1013
This MUST be used by daughter classes that also inherit from
1014
TestCaseWithRedirectedWebserver.
1016
We can't inherit directly from TestCaseWithTwoWebservers or the
1017
test framework will try to create an instance which cannot
1018
run, its implementation being incomplete.
1021
def create_transport_secondary_server(self):
1022
"""Create the secondary server redirecting to the primary server"""
1023
new = self.get_readonly_server()
1025
redirecting = HTTPServerRedirecting()
1026
redirecting.redirect_to(new.host, new.port)
1396
class TestHTTPRedirections(http_utils.TestCaseWithRedirectedWebserver):
1397
"""Test redirection between http servers."""
1399
scenarios = multiply_scenarios(
1400
vary_by_http_client_implementation(),
1401
vary_by_http_protocol_version(),
1029
1404
def setUp(self):
1030
1405
super(TestHTTPRedirections, self).setUp()
1358
1852
('b-proxied', 'contents of b\n'),
1361
def get_user_transport(self, user=None, password=None):
1362
self._install_env({'all_proxy': self.get_user_url(user, password)})
1363
return self._transport(self.server.get_url())
1365
def _install_env(self, env):
1366
for name, value in env.iteritems():
1367
self._old_env[name] = osutils.set_or_unset_env(name, value)
1369
def _restore_env(self):
1370
for name, value in self._old_env.iteritems():
1371
osutils.set_or_unset_env(name, value)
1374
class TestHTTPBasicAuth(TestHTTPAuth, TestCaseWithWebserver):
1375
"""Test http basic authentication scheme"""
1377
_transport = HttpTransport_urllib
1379
def create_transport_readonly_server(self):
1380
return HTTPBasicAuthServer()
1383
class TestHTTPProxyBasicAuth(TestProxyAuth, TestCaseWithWebserver):
1384
"""Test proxy basic authentication scheme"""
1386
_transport = HttpTransport_urllib
1388
def create_transport_readonly_server(self):
1389
return ProxyBasicAuthServer()
1392
class TestDigestAuth(object):
1393
"""Digest Authentication specific tests"""
1395
def test_changing_nonce(self):
1396
self.server.add_user('joe', 'foo')
1397
t = self.get_user_transport('joe', 'foo')
1398
self.assertEqual('contents of a\n', t.get('a').read())
1399
self.assertEqual('contents of b\n', t.get('b').read())
1400
# Only one 'Authentication Required' error should have
1402
self.assertEqual(1, self.server.auth_required_errors)
1403
# The server invalidates the current nonce
1404
self.server.auth_nonce = self.server.auth_nonce + '. No, now!'
1405
self.assertEqual('contents of a\n', t.get('a').read())
1406
# Two 'Authentication Required' errors should occur (the
1407
# initial 'who are you' and a second 'who are you' with the new nonce)
1408
self.assertEqual(2, self.server.auth_required_errors)
1411
class TestHTTPDigestAuth(TestHTTPAuth, TestDigestAuth, TestCaseWithWebserver):
1412
"""Test http digest authentication scheme"""
1414
_transport = HttpTransport_urllib
1416
def create_transport_readonly_server(self):
1417
return HTTPDigestAuthServer()
1420
class TestHTTPProxyDigestAuth(TestProxyAuth, TestDigestAuth,
1421
TestCaseWithWebserver):
1422
"""Test proxy digest authentication scheme"""
1424
_transport = HttpTransport_urllib
1426
def create_transport_readonly_server(self):
1427
return ProxyDigestAuthServer()
1855
def get_user_transport(self, user, password):
1856
self.overrideEnv('all_proxy', self.get_user_url(user, password))
1857
return TestAuth.get_user_transport(self, user, password)
1859
def test_empty_pass(self):
1860
if self._testing_pycurl():
1862
if pycurl.version_info()[1] < '7.16.0':
1864
'pycurl < 7.16.0 does not handle empty proxy passwords')
1865
super(TestProxyAuth, self).test_empty_pass()
1868
class SampleSocket(object):
1869
"""A socket-like object for use in testing the HTTP request handler."""
1871
def __init__(self, socket_read_content):
1872
"""Constructs a sample socket.
1874
:param socket_read_content: a byte sequence
1876
# Use plain python StringIO so we can monkey-patch the close method to
1877
# not discard the contents.
1878
from StringIO import StringIO
1879
self.readfile = StringIO(socket_read_content)
1880
self.writefile = StringIO()
1881
self.writefile.close = lambda: None
1882
self.close = lambda: None
1884
def makefile(self, mode='r', bufsize=None):
1886
return self.readfile
1888
return self.writefile
1891
class SmartHTTPTunnellingTest(tests.TestCaseWithTransport):
1893
scenarios = multiply_scenarios(
1894
vary_by_http_client_implementation(),
1895
vary_by_http_protocol_version(),
1899
super(SmartHTTPTunnellingTest, self).setUp()
1900
# We use the VFS layer as part of HTTP tunnelling tests.
1901
self.overrideEnv('BZR_NO_SMART_VFS', None)
1902
self.transport_readonly_server = http_utils.HTTPServerWithSmarts
1903
self.http_server = self.get_readonly_server()
1905
def create_transport_readonly_server(self):
1906
server = http_utils.HTTPServerWithSmarts(
1907
protocol_version=self._protocol_version)
1908
server._url_protocol = self._url_protocol
1911
def test_open_controldir(self):
1912
branch = self.make_branch('relpath')
1913
url = self.http_server.get_url() + 'relpath'
1914
bd = controldir.ControlDir.open(url)
1915
self.addCleanup(bd.transport.disconnect)
1916
self.assertIsInstance(bd, _mod_remote.RemoteBzrDir)
1918
def test_bulk_data(self):
1919
# We should be able to send and receive bulk data in a single message.
1920
# The 'readv' command in the smart protocol both sends and receives
1921
# bulk data, so we use that.
1922
self.build_tree(['data-file'])
1923
http_transport = transport.get_transport_from_url(
1924
self.http_server.get_url())
1925
medium = http_transport.get_smart_medium()
1926
# Since we provide the medium, the url below will be mostly ignored
1927
# during the test, as long as the path is '/'.
1928
remote_transport = remote.RemoteTransport('bzr://fake_host/',
1931
[(0, "c")], list(remote_transport.readv("data-file", [(0,1)])))
1933
def test_http_send_smart_request(self):
1935
post_body = 'hello\n'
1936
expected_reply_body = 'ok\x012\n'
1938
http_transport = transport.get_transport_from_url(
1939
self.http_server.get_url())
1940
medium = http_transport.get_smart_medium()
1941
response = medium.send_http_smart_request(post_body)
1942
reply_body = response.read()
1943
self.assertEqual(expected_reply_body, reply_body)
1945
def test_smart_http_server_post_request_handler(self):
1946
httpd = self.http_server.server
1948
socket = SampleSocket(
1949
'POST /.bzr/smart %s \r\n' % self._protocol_version
1950
# HTTP/1.1 posts must have a Content-Length (but it doesn't hurt
1952
+ 'Content-Length: 6\r\n'
1955
# Beware: the ('localhost', 80) below is the
1956
# client_address parameter, but we don't have one because
1957
# we have defined a socket which is not bound to an
1958
# address. The test framework never uses this client
1959
# address, so far...
1960
request_handler = http_utils.SmartRequestHandler(socket,
1963
response = socket.writefile.getvalue()
1964
self.assertStartsWith(response, '%s 200 ' % self._protocol_version)
1965
# This includes the end of the HTTP headers, and all the body.
1966
expected_end_of_response = '\r\n\r\nok\x012\n'
1967
self.assertEndsWith(response, expected_end_of_response)
1970
class ForbiddenRequestHandler(http_server.TestingHTTPRequestHandler):
1971
"""No smart server here request handler."""
1974
self.send_error(403, "Forbidden")
1977
class SmartClientAgainstNotSmartServer(TestSpecificRequestHandler):
1978
"""Test smart client behaviour against an http server without smarts."""
1980
_req_handler_class = ForbiddenRequestHandler
1982
def test_probe_smart_server(self):
1983
"""Test error handling against server refusing smart requests."""
1984
t = self.get_readonly_transport()
1985
# No need to build a valid smart request here, the server will not even
1986
# try to interpret it.
1987
self.assertRaises(errors.SmartProtocolError,
1988
t.get_smart_medium().send_http_smart_request,
1992
class Test_redirected_to(tests.TestCase):
1994
scenarios = vary_by_http_client_implementation()
1996
def test_redirected_to_subdir(self):
1997
t = self._transport('http://www.example.com/foo')
1998
r = t._redirected_to('http://www.example.com/foo',
1999
'http://www.example.com/foo/subdir')
2000
self.assertIsInstance(r, type(t))
2001
# Both transports share the some connection
2002
self.assertEqual(t._get_connection(), r._get_connection())
2003
self.assertEqual('http://www.example.com/foo/subdir/', r.base)
2005
def test_redirected_to_self_with_slash(self):
2006
t = self._transport('http://www.example.com/foo')
2007
r = t._redirected_to('http://www.example.com/foo',
2008
'http://www.example.com/foo/')
2009
self.assertIsInstance(r, type(t))
2010
# Both transports share the some connection (one can argue that we
2011
# should return the exact same transport here, but that seems
2013
self.assertEqual(t._get_connection(), r._get_connection())
2015
def test_redirected_to_host(self):
2016
t = self._transport('http://www.example.com/foo')
2017
r = t._redirected_to('http://www.example.com/foo',
2018
'http://foo.example.com/foo/subdir')
2019
self.assertIsInstance(r, type(t))
2020
self.assertEqual('http://foo.example.com/foo/subdir/',
2023
def test_redirected_to_same_host_sibling_protocol(self):
2024
t = self._transport('http://www.example.com/foo')
2025
r = t._redirected_to('http://www.example.com/foo',
2026
'https://www.example.com/foo')
2027
self.assertIsInstance(r, type(t))
2028
self.assertEqual('https://www.example.com/foo/',
2031
def test_redirected_to_same_host_different_protocol(self):
2032
t = self._transport('http://www.example.com/foo')
2033
r = t._redirected_to('http://www.example.com/foo',
2034
'ftp://www.example.com/foo')
2035
self.assertNotEqual(type(r), type(t))
2036
self.assertEqual('ftp://www.example.com/foo/', r.external_url())
2038
def test_redirected_to_same_host_specific_implementation(self):
2039
t = self._transport('http://www.example.com/foo')
2040
r = t._redirected_to('http://www.example.com/foo',
2041
'https+urllib://www.example.com/foo')
2042
self.assertEqual('https://www.example.com/foo/', r.external_url())
2044
def test_redirected_to_different_host_same_user(self):
2045
t = self._transport('http://joe@www.example.com/foo')
2046
r = t._redirected_to('http://www.example.com/foo',
2047
'https://foo.example.com/foo')
2048
self.assertIsInstance(r, type(t))
2049
self.assertEqual(t._parsed_url.user, r._parsed_url.user)
2050
self.assertEqual('https://joe@foo.example.com/foo/', r.external_url())
2053
class PredefinedRequestHandler(http_server.TestingHTTPRequestHandler):
2054
"""Request handler for a unique and pre-defined request.
2056
The only thing we care about here is how many bytes travel on the wire. But
2057
since we want to measure it for a real http client, we have to send it
2060
We expect to receive a *single* request nothing more (and we won't even
2061
check what request it is, we just measure the bytes read until an empty
2065
def _handle_one_request(self):
2066
tcs = self.server.test_case_server
2067
requestline = self.rfile.readline()
2068
headers = self.MessageClass(self.rfile, 0)
2069
# We just read: the request, the headers, an empty line indicating the
2070
# end of the headers.
2071
bytes_read = len(requestline)
2072
for line in headers.headers:
2073
bytes_read += len(line)
2074
bytes_read += len('\r\n')
2075
if requestline.startswith('POST'):
2076
# The body should be a single line (or we don't know where it ends
2077
# and we don't want to issue a blocking read)
2078
body = self.rfile.readline()
2079
bytes_read += len(body)
2080
tcs.bytes_read = bytes_read
2082
# We set the bytes written *before* issuing the write, the client is
2083
# supposed to consume every produced byte *before* checking that value.
2085
# Doing the oppposite may lead to test failure: we may be interrupted
2086
# after the write but before updating the value. The client can then
2087
# continue and read the value *before* we can update it. And yes,
2088
# this has been observed -- vila 20090129
2089
tcs.bytes_written = len(tcs.canned_response)
2090
self.wfile.write(tcs.canned_response)
2093
class ActivityServerMixin(object):
2095
def __init__(self, protocol_version):
2096
super(ActivityServerMixin, self).__init__(
2097
request_handler=PredefinedRequestHandler,
2098
protocol_version=protocol_version)
2099
# Bytes read and written by the server
2101
self.bytes_written = 0
2102
self.canned_response = None
2105
class ActivityHTTPServer(ActivityServerMixin, http_server.HttpServer):
2109
if features.HTTPSServerFeature.available():
2110
from bzrlib.tests import https_server
2111
class ActivityHTTPSServer(ActivityServerMixin, https_server.HTTPSServer):
2115
class TestActivityMixin(object):
2116
"""Test socket activity reporting.
2118
We use a special purpose server to control the bytes sent and received and
2119
be able to predict the activity on the client socket.
2123
self.server = self._activity_server(self._protocol_version)
2124
self.server.start_server()
2125
self.addCleanup(self.server.stop_server)
2126
_activities = {} # Don't close over self and create a cycle
2127
def report_activity(t, bytes, direction):
2128
count = _activities.get(direction, 0)
2130
_activities[direction] = count
2131
self.activities = _activities
2132
# We override at class level because constructors may propagate the
2133
# bound method and render instance overriding ineffective (an
2134
# alternative would be to define a specific ui factory instead...)
2135
self.overrideAttr(self._transport, '_report_activity', report_activity)
2137
def get_transport(self):
2138
t = self._transport(self.server.get_url())
2139
# FIXME: Needs cleanup -- vila 20100611
2142
def assertActivitiesMatch(self):
2143
self.assertEqual(self.server.bytes_read,
2144
self.activities.get('write', 0), 'written bytes')
2145
self.assertEqual(self.server.bytes_written,
2146
self.activities.get('read', 0), 'read bytes')
2149
self.server.canned_response = '''HTTP/1.1 200 OK\r
2150
Date: Tue, 11 Jul 2006 04:32:56 GMT\r
2151
Server: Apache/2.0.54 (Fedora)\r
2152
Last-Modified: Sun, 23 Apr 2006 19:35:20 GMT\r
2153
ETag: "56691-23-38e9ae00"\r
2154
Accept-Ranges: bytes\r
2155
Content-Length: 35\r
2157
Content-Type: text/plain; charset=UTF-8\r
2159
Bazaar-NG meta directory, format 1
2161
t = self.get_transport()
2162
self.assertEqual('Bazaar-NG meta directory, format 1\n',
2163
t.get('foo/bar').read())
2164
self.assertActivitiesMatch()
2167
self.server.canned_response = '''HTTP/1.1 200 OK\r
2168
Server: SimpleHTTP/0.6 Python/2.5.2\r
2169
Date: Thu, 29 Jan 2009 20:21:47 GMT\r
2170
Content-type: application/octet-stream\r
2171
Content-Length: 20\r
2172
Last-Modified: Thu, 29 Jan 2009 20:21:47 GMT\r
2175
t = self.get_transport()
2176
self.assertTrue(t.has('foo/bar'))
2177
self.assertActivitiesMatch()
2179
def test_readv(self):
2180
self.server.canned_response = '''HTTP/1.1 206 Partial Content\r
2181
Date: Tue, 11 Jul 2006 04:49:48 GMT\r
2182
Server: Apache/2.0.54 (Fedora)\r
2183
Last-Modified: Thu, 06 Jul 2006 20:22:05 GMT\r
2184
ETag: "238a3c-16ec2-805c5540"\r
2185
Accept-Ranges: bytes\r
2186
Content-Length: 1534\r
2188
Content-Type: multipart/byteranges; boundary=418470f848b63279b\r
2191
--418470f848b63279b\r
2192
Content-type: text/plain; charset=UTF-8\r
2193
Content-range: bytes 0-254/93890\r
2195
mbp@sourcefrog.net-20050309040815-13242001617e4a06
2196
mbp@sourcefrog.net-20050309040929-eee0eb3e6d1e7627
2197
mbp@sourcefrog.net-20050309040957-6cad07f466bb0bb8
2198
mbp@sourcefrog.net-20050309041501-c840e09071de3b67
2199
mbp@sourcefrog.net-20050309044615-c24a3250be83220a
2201
--418470f848b63279b\r
2202
Content-type: text/plain; charset=UTF-8\r
2203
Content-range: bytes 1000-2049/93890\r
2206
mbp@sourcefrog.net-20050311063625-07858525021f270b
2207
mbp@sourcefrog.net-20050311231934-aa3776aff5200bb9
2208
mbp@sourcefrog.net-20050311231953-73aeb3a131c3699a
2209
mbp@sourcefrog.net-20050311232353-f5e33da490872c6a
2210
mbp@sourcefrog.net-20050312071639-0a8f59a34a024ff0
2211
mbp@sourcefrog.net-20050312073432-b2c16a55e0d6e9fb
2212
mbp@sourcefrog.net-20050312073831-a47c3335ece1920f
2213
mbp@sourcefrog.net-20050312085412-13373aa129ccbad3
2214
mbp@sourcefrog.net-20050313052251-2bf004cb96b39933
2215
mbp@sourcefrog.net-20050313052856-3edd84094687cb11
2216
mbp@sourcefrog.net-20050313053233-e30a4f28aef48f9d
2217
mbp@sourcefrog.net-20050313053853-7c64085594ff3072
2218
mbp@sourcefrog.net-20050313054757-a86c3f5871069e22
2219
mbp@sourcefrog.net-20050313061422-418f1f73b94879b9
2220
mbp@sourcefrog.net-20050313120651-497bd231b19df600
2221
mbp@sourcefrog.net-20050314024931-eae0170ef25a5d1a
2222
mbp@sourcefrog.net-20050314025438-d52099f915fe65fc
2223
mbp@sourcefrog.net-20050314025539-637a636692c055cf
2224
mbp@sourcefrog.net-20050314025737-55eb441f430ab4ba
2225
mbp@sourcefrog.net-20050314025901-d74aa93bb7ee8f62
2227
--418470f848b63279b--\r
2229
t = self.get_transport()
2230
# Remember that the request is ignored and that the ranges below
2231
# doesn't have to match the canned response.
2232
l = list(t.readv('/foo/bar', ((0, 255), (1000, 1050))))
2233
self.assertEqual(2, len(l))
2234
self.assertActivitiesMatch()
2236
def test_post(self):
2237
self.server.canned_response = '''HTTP/1.1 200 OK\r
2238
Date: Tue, 11 Jul 2006 04:32:56 GMT\r
2239
Server: Apache/2.0.54 (Fedora)\r
2240
Last-Modified: Sun, 23 Apr 2006 19:35:20 GMT\r
2241
ETag: "56691-23-38e9ae00"\r
2242
Accept-Ranges: bytes\r
2243
Content-Length: 35\r
2245
Content-Type: text/plain; charset=UTF-8\r
2247
lalala whatever as long as itsssss
2249
t = self.get_transport()
2250
# We must send a single line of body bytes, see
2251
# PredefinedRequestHandler._handle_one_request
2252
code, f = t._post('abc def end-of-body\n')
2253
self.assertEqual('lalala whatever as long as itsssss\n', f.read())
2254
self.assertActivitiesMatch()
2257
class TestActivity(tests.TestCase, TestActivityMixin):
2259
scenarios = multiply_scenarios(
2260
vary_by_http_activity(),
2261
vary_by_http_protocol_version(),
2265
super(TestActivity, self).setUp()
2266
TestActivityMixin.setUp(self)
2269
class TestNoReportActivity(tests.TestCase, TestActivityMixin):
2271
# Unlike TestActivity, we are really testing ReportingFileSocket and
2272
# ReportingSocket, so we don't need all the parametrization. Since
2273
# ReportingFileSocket and ReportingSocket are wrappers, it's easier to
2274
# test them through their use by the transport than directly (that's a
2275
# bit less clean but far more simpler and effective).
2276
_activity_server = ActivityHTTPServer
2277
_protocol_version = 'HTTP/1.1'
2280
super(TestNoReportActivity, self).setUp()
2281
self._transport =_urllib.HttpTransport_urllib
2282
TestActivityMixin.setUp(self)
2284
def assertActivitiesMatch(self):
2285
# Nothing to check here
2289
class TestAuthOnRedirected(http_utils.TestCaseWithRedirectedWebserver):
2290
"""Test authentication on the redirected http server."""
2292
scenarios = vary_by_http_protocol_version()
2294
_auth_header = 'Authorization'
2295
_password_prompt_prefix = ''
2296
_username_prompt_prefix = ''
2297
_auth_server = http_utils.HTTPBasicAuthServer
2298
_transport = _urllib.HttpTransport_urllib
2301
super(TestAuthOnRedirected, self).setUp()
2302
self.build_tree_contents([('a','a'),
2304
('1/a', 'redirected once'),
2306
new_prefix = 'http://%s:%s' % (self.new_server.host,
2307
self.new_server.port)
2308
self.old_server.redirections = [
2309
('(.*)', r'%s/1\1' % (new_prefix), 301),]
2310
self.old_transport = self.get_old_transport()
2311
self.new_server.add_user('joe', 'foo')
2312
cleanup_http_redirection_connections(self)
2314
def create_transport_readonly_server(self):
2315
server = self._auth_server(protocol_version=self._protocol_version)
2316
server._url_protocol = self._url_protocol
2322
def test_auth_on_redirected_via_do_catching_redirections(self):
2323
self.redirections = 0
2325
def redirected(t, exception, redirection_notice):
2326
self.redirections += 1
2327
redirected_t = t._redirected_to(exception.source, exception.target)
2328
self.addCleanup(redirected_t.disconnect)
2331
stdout = tests.StringIOWrapper()
2332
stderr = tests.StringIOWrapper()
2333
ui.ui_factory = tests.TestUIFactory(stdin='joe\nfoo\n',
2334
stdout=stdout, stderr=stderr)
2335
self.assertEqual('redirected once',
2336
transport.do_catching_redirections(
2337
self.get_a, self.old_transport, redirected).read())
2338
self.assertEqual(1, self.redirections)
2339
# stdin should be empty
2340
self.assertEqual('', ui.ui_factory.stdin.readline())
2341
# stdout should be empty, stderr will contains the prompts
2342
self.assertEqual('', stdout.getvalue())
2344
def test_auth_on_redirected_via_following_redirections(self):
2345
self.new_server.add_user('joe', 'foo')
2346
stdout = tests.StringIOWrapper()
2347
stderr = tests.StringIOWrapper()
2348
ui.ui_factory = tests.TestUIFactory(stdin='joe\nfoo\n',
2349
stdout=stdout, stderr=stderr)
2350
t = self.old_transport
2351
req = RedirectedRequest('GET', t.abspath('a'))
2352
new_prefix = 'http://%s:%s' % (self.new_server.host,
2353
self.new_server.port)
2354
self.old_server.redirections = [
2355
('(.*)', r'%s/1\1' % (new_prefix), 301),]
2356
self.assertEqual('redirected once', t._perform(req).read())
2357
# stdin should be empty
2358
self.assertEqual('', ui.ui_factory.stdin.readline())
2359
# stdout should be empty, stderr will contains the prompts
2360
self.assertEqual('', stdout.getvalue())