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.assertEquals(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):
387
class TestHttpUrls(tests.TestCase):
168
389
# TODO: This should be moved to authorization tests once they
171
392
def test_url_parsing(self):
172
393
f = FakeManager()
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.
394
url = http.extract_auth('http://example.com', f)
395
self.assertEqual('http://example.com', url)
396
self.assertEqual(0, len(f.credentials))
397
url = http.extract_auth(
398
'http://user:pass@example.com/bzr/bzr.dev', f)
399
self.assertEqual('http://example.com/bzr/bzr.dev', url)
400
self.assertEqual(1, len(f.credentials))
401
self.assertEqual([None, 'example.com', 'user', 'pass'],
405
class TestHttpTransportUrls(tests.TestCase):
406
"""Test the http urls."""
408
scenarios = vary_by_http_client_implementation()
194
410
def test_abs_url(self):
195
411
"""Construction of absolute http URLs"""
196
t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
412
t = self._transport('http://example.com/bzr/bzr.dev/')
197
413
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')
414
eq(t.abspath('.'), 'http://example.com/bzr/bzr.dev')
415
eq(t.abspath('foo/bar'), 'http://example.com/bzr/bzr.dev/foo/bar')
416
eq(t.abspath('.bzr'), 'http://example.com/bzr/bzr.dev/.bzr')
201
417
eq(t.abspath('.bzr/1//2/./3'),
202
'http://bazaar-vcs.org/bzr/bzr.dev/.bzr/1/2/3')
418
'http://example.com/bzr/bzr.dev/.bzr/1/2/3')
204
420
def test_invalid_http_urls(self):
205
421
"""Trap invalid construction of urls"""
206
t = self._transport('http://bazaar-vcs.org/bzr/bzr.dev/')
207
self.assertRaises(ValueError, t.abspath, '.bzr/')
208
t = self._transport('http://http://bazaar-vcs.org/bzr/bzr.dev/')
209
self.assertRaises((errors.InvalidURL, errors.ConnectionError),
422
self._transport('http://example.com/bzr/bzr.dev/')
423
self.assertRaises(errors.InvalidURL,
425
'http://http://example.com/bzr/bzr.dev/')
212
427
def test_http_root_urls(self):
213
428
"""Construction of URLs from server root"""
214
t = self._transport('http://bzr.ozlabs.org/')
429
t = self._transport('http://example.com/')
215
430
eq = self.assertEqualDiff
216
431
eq(t.abspath('.bzr/tree-version'),
217
'http://bzr.ozlabs.org/.bzr/tree-version')
432
'http://example.com/.bzr/tree-version')
219
434
def test_http_impl_urls(self):
220
435
"""There are servers which ask for particular clients to connect"""
221
436
server = self._server()
437
server.start_server()
224
439
url = server.get_url()
225
self.assertTrue(url.startswith('%s://' % self._qualified_prefix))
440
self.assertTrue(url.startswith('%s://' % self._url_protocol))
230
class TestHttpUrls_urllib(TestHttpTransportUrls, TestCase):
231
"""Test http urls with urllib"""
233
_transport = HttpTransport_urllib
234
_server = HttpServer_urllib
235
_qualified_prefix = 'http+urllib'
238
class TestHttpUrls_pycurl(TestWithTransport_pycurl, TestHttpTransportUrls,
240
"""Test http urls with pycurl"""
242
_server = HttpServer_PyCurl
243
_qualified_prefix = 'http+pycurl'
445
class TestHttps_pycurl(TestWithTransport_pycurl, tests.TestCase):
245
447
# TODO: This should really be moved into another pycurl
246
448
# specific test. When https tests will be implemented, take
654
833
self.assertListRaises((errors.InvalidRange, errors.ShortReadvError,),
655
834
t.readv, 'a', [(12,2)])
836
def test_readv_multiple_get_requests(self):
837
server = self.get_readonly_server()
838
t = self.get_readonly_transport()
839
# force transport to issue multiple requests
840
t._max_readv_combine = 1
841
t._max_get_ranges = 1
842
l = list(t.readv('a', ((0, 1), (1, 1), (3, 2), (9, 1))))
843
self.assertEqual(l[0], (0, '0'))
844
self.assertEqual(l[1], (1, '1'))
845
self.assertEqual(l[2], (3, '34'))
846
self.assertEqual(l[3], (9, '9'))
847
# The server should have issued 4 requests
848
self.assertEqual(4, server.GET_request_nb)
850
def test_readv_get_max_size(self):
851
server = self.get_readonly_server()
852
t = self.get_readonly_transport()
853
# force transport to issue multiple requests by limiting the number of
854
# bytes by request. Note that this apply to coalesced offsets only, a
855
# single range will keep its size even if bigger than the limit.
857
l = list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
858
self.assertEqual(l[0], (0, '0'))
859
self.assertEqual(l[1], (1, '1'))
860
self.assertEqual(l[2], (2, '2345'))
861
self.assertEqual(l[3], (6, '6789'))
862
# The server should have issued 3 requests
863
self.assertEqual(3, server.GET_request_nb)
865
def test_complete_readv_leave_pipe_clean(self):
866
server = self.get_readonly_server()
867
t = self.get_readonly_transport()
868
# force transport to issue multiple requests
870
list(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
871
# The server should have issued 3 requests
872
self.assertEqual(3, server.GET_request_nb)
873
self.assertEqual('0123456789', t.get_bytes('a'))
874
self.assertEqual(4, server.GET_request_nb)
876
def test_incomplete_readv_leave_pipe_clean(self):
877
server = self.get_readonly_server()
878
t = self.get_readonly_transport()
879
# force transport to issue multiple requests
881
# Don't collapse readv results into a list so that we leave unread
882
# bytes on the socket
883
ireadv = iter(t.readv('a', ((0, 1), (1, 1), (2, 4), (6, 4))))
884
self.assertEqual((0, '0'), ireadv.next())
885
# The server should have issued one request so far
886
self.assertEqual(1, server.GET_request_nb)
887
self.assertEqual('0123456789', t.get_bytes('a'))
888
# get_bytes issued an additional request, the readv pending ones are
890
self.assertEqual(2, server.GET_request_nb)
893
class SingleRangeRequestHandler(http_server.TestingHTTPRequestHandler):
894
"""Always reply to range request as if they were single.
896
Don't be explicit about it, just to annoy the clients.
899
def get_multiple_ranges(self, file, file_size, ranges):
900
"""Answer as if it was a single range request and ignores the rest"""
901
(start, end) = ranges[0]
902
return self.get_single_range(file, file_size, start, end)
658
905
class TestSingleRangeRequestServer(TestRangeRequestServer):
659
906
"""Test readv against a server which accept only single range requests"""
661
def create_transport_readonly_server(self):
662
return HttpServer(SingleRangeRequestHandler)
665
class TestSingleRangeRequestServer_urllib(TestSingleRangeRequestServer,
666
TestCaseWithWebserver):
667
"""Tests single range requests accepting server for urllib implementation"""
669
_transport = HttpTransport_urllib
672
class TestSingleRangeRequestServer_pycurl(TestWithTransport_pycurl,
673
TestSingleRangeRequestServer,
674
TestCaseWithWebserver):
675
"""Tests single range requests accepting server for pycurl implementation"""
908
_req_handler_class = SingleRangeRequestHandler
911
class SingleOnlyRangeRequestHandler(http_server.TestingHTTPRequestHandler):
912
"""Only reply to simple range requests, errors out on multiple"""
914
def get_multiple_ranges(self, file, file_size, ranges):
915
"""Refuses the multiple ranges request"""
918
self.send_error(416, "Requested range not satisfiable")
920
(start, end) = ranges[0]
921
return self.get_single_range(file, file_size, start, end)
678
924
class TestSingleOnlyRangeRequestServer(TestRangeRequestServer):
679
925
"""Test readv against a server which only accept single range requests"""
681
def create_transport_readonly_server(self):
682
return HttpServer(SingleOnlyRangeRequestHandler)
685
class TestSingleOnlyRangeRequestServer_urllib(TestSingleOnlyRangeRequestServer,
686
TestCaseWithWebserver):
687
"""Tests single range requests accepting server for urllib implementation"""
689
_transport = HttpTransport_urllib
692
class TestSingleOnlyRangeRequestServer_pycurl(TestWithTransport_pycurl,
693
TestSingleOnlyRangeRequestServer,
694
TestCaseWithWebserver):
695
"""Tests single range requests accepting server for pycurl implementation"""
927
_req_handler_class = SingleOnlyRangeRequestHandler
930
class NoRangeRequestHandler(http_server.TestingHTTPRequestHandler):
931
"""Ignore range requests without notice"""
934
# Update the statistics
935
self.server.test_case_server.GET_request_nb += 1
936
# Just bypass the range handling done by TestingHTTPRequestHandler
937
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
698
940
class TestNoRangeRequestServer(TestRangeRequestServer):
699
941
"""Test readv against a server which do not accept range requests"""
701
def create_transport_readonly_server(self):
702
return HttpServer(NoRangeRequestHandler)
705
class TestNoRangeRequestServer_urllib(TestNoRangeRequestServer,
706
TestCaseWithWebserver):
707
"""Tests range requests refusing server for urllib implementation"""
709
_transport = HttpTransport_urllib
712
class TestNoRangeRequestServer_pycurl(TestWithTransport_pycurl,
713
TestNoRangeRequestServer,
714
TestCaseWithWebserver):
715
"""Tests range requests refusing server for pycurl implementation"""
718
class TestLimitedRangeRequestServer(object):
719
"""Tests readv requests against server that errors out on too much ranges.
721
This MUST be used by daughter classes that also inherit from
722
TestCaseWithWebserver.
724
We can't inherit directly from TestCaseWithWebserver or the
725
test framework will try to create an instance which cannot
726
run, its implementation being incomplete.
943
_req_handler_class = NoRangeRequestHandler
946
class MultipleRangeWithoutContentLengthRequestHandler(
947
http_server.TestingHTTPRequestHandler):
948
"""Reply to multiple range requests without content length header."""
950
def get_multiple_ranges(self, file, file_size, ranges):
951
self.send_response(206)
952
self.send_header('Accept-Ranges', 'bytes')
953
# XXX: this is strange; the 'random' name below seems undefined and
954
# yet the tests pass -- mbp 2010-10-11 bug 658773
955
boundary = "%d" % random.randint(0,0x7FFFFFFF)
956
self.send_header("Content-Type",
957
"multipart/byteranges; boundary=%s" % boundary)
959
for (start, end) in ranges:
960
self.wfile.write("--%s\r\n" % boundary)
961
self.send_header("Content-type", 'application/octet-stream')
962
self.send_header("Content-Range", "bytes %d-%d/%d" % (start,
966
self.send_range_content(file, start, end - start + 1)
968
self.wfile.write("--%s\r\n" % boundary)
971
class TestMultipleRangeWithoutContentLengthServer(TestRangeRequestServer):
973
_req_handler_class = MultipleRangeWithoutContentLengthRequestHandler
976
class TruncatedMultipleRangeRequestHandler(
977
http_server.TestingHTTPRequestHandler):
978
"""Reply to multiple range requests truncating the last ones.
980
This server generates responses whose Content-Length describes all the
981
ranges, but fail to include the last ones leading to client short reads.
982
This has been observed randomly with lighttpd (bug #179368).
985
_truncated_ranges = 2
987
def get_multiple_ranges(self, file, file_size, ranges):
988
self.send_response(206)
989
self.send_header('Accept-Ranges', 'bytes')
991
self.send_header('Content-Type',
992
'multipart/byteranges; boundary=%s' % boundary)
993
boundary_line = '--%s\r\n' % boundary
994
# Calculate the Content-Length
996
for (start, end) in ranges:
997
content_length += len(boundary_line)
998
content_length += self._header_line_length(
999
'Content-type', 'application/octet-stream')
1000
content_length += self._header_line_length(
1001
'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
1002
content_length += len('\r\n') # end headers
1003
content_length += end - start # + 1
1004
content_length += len(boundary_line)
1005
self.send_header('Content-length', content_length)
1008
# Send the multipart body
1010
for (start, end) in ranges:
1011
self.wfile.write(boundary_line)
1012
self.send_header('Content-type', 'application/octet-stream')
1013
self.send_header('Content-Range', 'bytes %d-%d/%d'
1014
% (start, end, file_size))
1016
if cur + self._truncated_ranges >= len(ranges):
1017
# Abruptly ends the response and close the connection
1018
self.close_connection = 1
1020
self.send_range_content(file, start, end - start + 1)
1023
self.wfile.write(boundary_line)
1026
class TestTruncatedMultipleRangeServer(TestSpecificRequestHandler):
1028
_req_handler_class = TruncatedMultipleRangeRequestHandler
1031
super(TestTruncatedMultipleRangeServer, self).setUp()
1032
self.build_tree_contents([('a', '0123456789')],)
1034
def test_readv_with_short_reads(self):
1035
server = self.get_readonly_server()
1036
t = self.get_readonly_transport()
1037
# Force separate ranges for each offset
1038
t._bytes_to_read_before_seek = 0
1039
ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
1040
self.assertEqual((0, '0'), ireadv.next())
1041
self.assertEqual((2, '2'), ireadv.next())
1042
if not self._testing_pycurl():
1043
# Only one request have been issued so far (except for pycurl that
1044
# try to read the whole response at once)
1045
self.assertEqual(1, server.GET_request_nb)
1046
self.assertEqual((4, '45'), ireadv.next())
1047
self.assertEqual((9, '9'), ireadv.next())
1048
# Both implementations issue 3 requests but:
1049
# - urllib does two multiple (4 ranges, then 2 ranges) then a single
1051
# - pycurl does two multiple (4 ranges, 4 ranges) then a single range
1052
self.assertEqual(3, server.GET_request_nb)
1053
# Finally the client have tried a single range request and stays in
1055
self.assertEqual('single', t._range_hint)
1058
class TruncatedBeforeBoundaryRequestHandler(
1059
http_server.TestingHTTPRequestHandler):
1060
"""Truncation before a boundary, like in bug 198646"""
1062
_truncated_ranges = 1
1064
def get_multiple_ranges(self, file, file_size, ranges):
1065
self.send_response(206)
1066
self.send_header('Accept-Ranges', 'bytes')
1068
self.send_header('Content-Type',
1069
'multipart/byteranges; boundary=%s' % boundary)
1070
boundary_line = '--%s\r\n' % boundary
1071
# Calculate the Content-Length
1073
for (start, end) in ranges:
1074
content_length += len(boundary_line)
1075
content_length += self._header_line_length(
1076
'Content-type', 'application/octet-stream')
1077
content_length += self._header_line_length(
1078
'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
1079
content_length += len('\r\n') # end headers
1080
content_length += end - start # + 1
1081
content_length += len(boundary_line)
1082
self.send_header('Content-length', content_length)
1085
# Send the multipart body
1087
for (start, end) in ranges:
1088
if cur + self._truncated_ranges >= len(ranges):
1089
# Abruptly ends the response and close the connection
1090
self.close_connection = 1
1092
self.wfile.write(boundary_line)
1093
self.send_header('Content-type', 'application/octet-stream')
1094
self.send_header('Content-Range', 'bytes %d-%d/%d'
1095
% (start, end, file_size))
1097
self.send_range_content(file, start, end - start + 1)
1100
self.wfile.write(boundary_line)
1103
class TestTruncatedBeforeBoundary(TestSpecificRequestHandler):
1104
"""Tests the case of bug 198646, disconnecting before a boundary."""
1106
_req_handler_class = TruncatedBeforeBoundaryRequestHandler
1109
super(TestTruncatedBeforeBoundary, self).setUp()
1110
self.build_tree_contents([('a', '0123456789')],)
1112
def test_readv_with_short_reads(self):
1113
server = self.get_readonly_server()
1114
t = self.get_readonly_transport()
1115
# Force separate ranges for each offset
1116
t._bytes_to_read_before_seek = 0
1117
ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
1118
self.assertEqual((0, '0'), ireadv.next())
1119
self.assertEqual((2, '2'), ireadv.next())
1120
self.assertEqual((4, '45'), ireadv.next())
1121
self.assertEqual((9, '9'), ireadv.next())
1124
class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
1125
"""Errors out when range specifiers exceed the limit"""
1127
def get_multiple_ranges(self, file, file_size, ranges):
1128
"""Refuses the multiple ranges request"""
1129
tcs = self.server.test_case_server
1130
if tcs.range_limit is not None and len(ranges) > tcs.range_limit:
1132
# Emulate apache behavior
1133
self.send_error(400, "Bad Request")
1135
return http_server.TestingHTTPRequestHandler.get_multiple_ranges(
1136
self, file, file_size, ranges)
1139
class LimitedRangeHTTPServer(http_server.HttpServer):
1140
"""An HttpServer erroring out on requests with too much range specifiers"""
1142
def __init__(self, request_handler=LimitedRangeRequestHandler,
1143
protocol_version=None,
1145
http_server.HttpServer.__init__(self, request_handler,
1146
protocol_version=protocol_version)
1147
self.range_limit = range_limit
1150
class TestLimitedRangeRequestServer(http_utils.TestCaseWithWebserver):
1151
"""Tests readv requests against a server erroring out on too much ranges."""
1153
scenarios = multiply_scenarios(
1154
vary_by_http_client_implementation(),
1155
vary_by_http_protocol_version(),
1158
# Requests with more range specifiers will error out
731
1161
def create_transport_readonly_server(self):
732
# Requests with more range specifiers will error out
733
return LimitedRangeHTTPServer(range_limit=self.range_limit)
735
def get_transport(self):
736
return self._transport(self.get_readonly_server().get_url())
1162
return LimitedRangeHTTPServer(range_limit=self.range_limit,
1163
protocol_version=self._protocol_version)
738
1165
def setUp(self):
739
TestCaseWithWebserver.setUp(self)
1166
http_utils.TestCaseWithWebserver.setUp(self)
740
1167
# We need to manipulate ranges that correspond to real chunks in the
741
1168
# response, so we build a content appropriately.
742
filler = ''.join(['abcdefghij' for _ in range(102)])
1169
filler = ''.join(['abcdefghij' for x in range(102)])
743
1170
content = ''.join(['%04d' % v + filler for v in range(16)])
744
1171
self.build_tree_contents([('a', content)],)
746
1173
def test_few_ranges(self):
747
t = self.get_transport()
1174
t = self.get_readonly_transport()
748
1175
l = list(t.readv('a', ((0, 4), (1024, 4), )))
749
1176
self.assertEqual(l[0], (0, '0000'))
750
1177
self.assertEqual(l[1], (1024, '0001'))
751
1178
self.assertEqual(1, self.get_readonly_server().GET_request_nb)
753
def test_a_lot_of_ranges(self):
754
t = self.get_transport()
1180
def test_more_ranges(self):
1181
t = self.get_readonly_transport()
755
1182
l = list(t.readv('a', ((0, 4), (1024, 4), (4096, 4), (8192, 4))))
756
1183
self.assertEqual(l[0], (0, '0000'))
757
1184
self.assertEqual(l[1], (1024, '0001'))
758
1185
self.assertEqual(l[2], (4096, '0004'))
759
1186
self.assertEqual(l[3], (8192, '0008'))
760
1187
# The server will refuse to serve the first request (too much ranges),
761
# a second request will succeeds.
1188
# a second request will succeed.
762
1189
self.assertEqual(2, self.get_readonly_server().GET_request_nb)
765
class TestLimitedRangeRequestServer_urllib(TestLimitedRangeRequestServer,
766
TestCaseWithWebserver):
767
"""Tests limited range requests server for urllib implementation"""
769
_transport = HttpTransport_urllib
772
class TestLimitedRangeRequestServer_pycurl(TestWithTransport_pycurl,
773
TestLimitedRangeRequestServer,
774
TestCaseWithWebserver):
775
"""Tests limited range requests server for pycurl implementation"""
779
class TestHttpProxyWhiteBox(TestCase):
1192
class TestHttpProxyWhiteBox(tests.TestCase):
780
1193
"""Whitebox test proxy http authorization.
782
1195
Only the urllib implementation is tested here.
792
def _install_env(self, env):
793
for name, value in env.iteritems():
794
self._old_env[name] = osutils.set_or_unset_env(name, value)
796
def _restore_env(self):
797
for name, value in self._old_env.iteritems():
798
osutils.set_or_unset_env(name, value)
800
1198
def _proxied_request(self):
801
handler = ProxyHandler(PasswordManager())
802
request = Request('GET','http://baz/buzzle')
1199
handler = _urllib2_wrappers.ProxyHandler()
1200
request = _urllib2_wrappers.Request('GET', 'http://baz/buzzle')
803
1201
handler.set_proxy(request, 'http')
1204
def assertEvaluateProxyBypass(self, expected, host, no_proxy):
1205
handler = _urllib2_wrappers.ProxyHandler()
1206
self.assertEquals(expected,
1207
handler.evaluate_proxy_bypass(host, no_proxy))
806
1209
def test_empty_user(self):
807
self._install_env({'http_proxy': 'http://bar.com'})
1210
self.overrideEnv('http_proxy', 'http://bar.com')
1211
request = self._proxied_request()
1212
self.assertFalse(request.headers.has_key('Proxy-authorization'))
1214
def test_user_with_at(self):
1215
self.overrideEnv('http_proxy',
1216
'http://username@domain:password@proxy_host:1234')
808
1217
request = self._proxied_request()
809
1218
self.assertFalse(request.headers.has_key('Proxy-authorization'))
811
1220
def test_invalid_proxy(self):
812
1221
"""A proxy env variable without scheme"""
813
self._install_env({'http_proxy': 'host:1234'})
1222
self.overrideEnv('http_proxy', 'host:1234')
814
1223
self.assertRaises(errors.InvalidURL, self._proxied_request)
817
class TestProxyHttpServer(object):
1225
def test_evaluate_proxy_bypass_true(self):
1226
"""The host is not proxied"""
1227
self.assertEvaluateProxyBypass(True, 'example.com', 'example.com')
1228
self.assertEvaluateProxyBypass(True, 'bzr.example.com', '*example.com')
1230
def test_evaluate_proxy_bypass_false(self):
1231
"""The host is proxied"""
1232
self.assertEvaluateProxyBypass(False, 'bzr.example.com', None)
1234
def test_evaluate_proxy_bypass_unknown(self):
1235
"""The host is not explicitly proxied"""
1236
self.assertEvaluateProxyBypass(None, 'example.com', 'not.example.com')
1237
self.assertEvaluateProxyBypass(None, 'bzr.example.com', 'example.com')
1239
def test_evaluate_proxy_bypass_empty_entries(self):
1240
"""Ignore empty entries"""
1241
self.assertEvaluateProxyBypass(None, 'example.com', '')
1242
self.assertEvaluateProxyBypass(None, 'example.com', ',')
1243
self.assertEvaluateProxyBypass(None, 'example.com', 'foo,,bar')
1246
class TestProxyHttpServer(http_utils.TestCaseWithTwoWebservers):
818
1247
"""Tests proxy server.
820
This MUST be used by daughter classes that also inherit from
821
TestCaseWithTwoWebservers.
823
We can't inherit directly from TestCaseWithTwoWebservers or
824
the test framework will try to create an instance which
825
cannot run, its implementation being incomplete.
827
1249
Be aware that we do not setup a real proxy here. Instead, we
828
1250
check that the *connection* goes through the proxy by serving
829
1251
different content (the faked proxy server append '-proxied'
830
1252
to the file names).
1255
scenarios = multiply_scenarios(
1256
vary_by_http_client_implementation(),
1257
vary_by_http_protocol_version(),
833
1260
# FIXME: We don't have an https server available, so we don't
834
# test https connections.
836
# FIXME: Once the test suite is better fitted to test
837
# authorization schemes, test proxy authorizations too (see
1261
# test https connections. --vila toolongago
840
1263
def setUp(self):
841
TestCaseWithTwoWebservers.setUp(self)
1264
super(TestProxyHttpServer, self).setUp()
1265
self.transport_secondary_server = http_utils.ProxyServer
842
1266
self.build_tree_contents([('foo', 'contents of foo\n'),
843
1267
('foo-proxied', 'proxied contents of foo\n')])
844
1268
# Let's setup some attributes for tests
845
self.server = self.get_readonly_server()
846
self.proxy_address = '%s:%d' % (self.server.host, self.server.port)
847
self.no_proxy_host = self.proxy_address
1269
server = self.get_readonly_server()
1270
self.server_host_port = '%s:%d' % (server.host, server.port)
1271
if self._testing_pycurl():
1272
# Oh my ! pycurl does not check for the port as part of
1273
# no_proxy :-( So we just test the host part
1274
self.no_proxy_host = server.host
1276
self.no_proxy_host = self.server_host_port
848
1277
# The secondary server is the proxy
849
self.proxy = self.get_secondary_server()
850
self.proxy_url = self.proxy.get_url()
853
def create_transport_secondary_server(self):
854
"""Creates an http server that will serve files with
855
'-proxied' appended to their names.
859
def _install_env(self, env):
860
for name, value in env.iteritems():
861
self._old_env[name] = osutils.set_or_unset_env(name, value)
863
def _restore_env(self):
864
for name, value in self._old_env.iteritems():
865
osutils.set_or_unset_env(name, value)
867
def proxied_in_env(self, env):
868
self._install_env(env)
869
url = self.server.get_url()
870
t = self._transport(url)
872
self.assertEqual(t.get('foo').read(), 'proxied contents of foo\n')
876
def not_proxied_in_env(self, env):
877
self._install_env(env)
878
url = self.server.get_url()
879
t = self._transport(url)
881
self.assertEqual(t.get('foo').read(), 'contents of foo\n')
1278
self.proxy_url = self.get_secondary_url()
1280
def _testing_pycurl(self):
1281
# TODO: This is duplicated for lots of the classes in this file
1282
return (features.pycurl.available()
1283
and self._transport == PyCurlTransport)
1285
def assertProxied(self):
1286
t = self.get_readonly_transport()
1287
self.assertEqual('proxied contents of foo\n', t.get('foo').read())
1289
def assertNotProxied(self):
1290
t = self.get_readonly_transport()
1291
self.assertEqual('contents of foo\n', t.get('foo').read())
885
1293
def test_http_proxy(self):
886
self.proxied_in_env({'http_proxy': self.proxy_url})
1294
self.overrideEnv('http_proxy', self.proxy_url)
1295
self.assertProxied()
888
1297
def test_HTTP_PROXY(self):
889
self.proxied_in_env({'HTTP_PROXY': self.proxy_url})
1298
if self._testing_pycurl():
1299
# pycurl does not check HTTP_PROXY for security reasons
1300
# (for use in a CGI context that we do not care
1301
# about. Should we ?)
1302
raise tests.TestNotApplicable(
1303
'pycurl does not check HTTP_PROXY for security reasons')
1304
self.overrideEnv('HTTP_PROXY', self.proxy_url)
1305
self.assertProxied()
891
1307
def test_all_proxy(self):
892
self.proxied_in_env({'all_proxy': self.proxy_url})
1308
self.overrideEnv('all_proxy', self.proxy_url)
1309
self.assertProxied()
894
1311
def test_ALL_PROXY(self):
895
self.proxied_in_env({'ALL_PROXY': self.proxy_url})
1312
self.overrideEnv('ALL_PROXY', self.proxy_url)
1313
self.assertProxied()
897
1315
def test_http_proxy_with_no_proxy(self):
898
self.not_proxied_in_env({'http_proxy': self.proxy_url,
899
'no_proxy': self.no_proxy_host})
1316
self.overrideEnv('no_proxy', self.no_proxy_host)
1317
self.overrideEnv('http_proxy', self.proxy_url)
1318
self.assertNotProxied()
901
1320
def test_HTTP_PROXY_with_NO_PROXY(self):
902
self.not_proxied_in_env({'HTTP_PROXY': self.proxy_url,
903
'NO_PROXY': self.no_proxy_host})
1321
if self._testing_pycurl():
1322
raise tests.TestNotApplicable(
1323
'pycurl does not check HTTP_PROXY for security reasons')
1324
self.overrideEnv('NO_PROXY', self.no_proxy_host)
1325
self.overrideEnv('HTTP_PROXY', self.proxy_url)
1326
self.assertNotProxied()
905
1328
def test_all_proxy_with_no_proxy(self):
906
self.not_proxied_in_env({'all_proxy': self.proxy_url,
907
'no_proxy': self.no_proxy_host})
1329
self.overrideEnv('no_proxy', self.no_proxy_host)
1330
self.overrideEnv('all_proxy', self.proxy_url)
1331
self.assertNotProxied()
909
1333
def test_ALL_PROXY_with_NO_PROXY(self):
910
self.not_proxied_in_env({'ALL_PROXY': self.proxy_url,
911
'NO_PROXY': self.no_proxy_host})
913
def test_http_proxy_without_scheme(self):
914
self.assertRaises(errors.InvalidURL,
916
{'http_proxy': self.proxy_address})
919
class TestProxyHttpServer_urllib(TestProxyHttpServer,
920
TestCaseWithTwoWebservers):
921
"""Tests proxy server for urllib implementation"""
923
_transport = HttpTransport_urllib
926
class TestProxyHttpServer_pycurl(TestWithTransport_pycurl,
928
TestCaseWithTwoWebservers):
929
"""Tests proxy server for pycurl implementation"""
932
TestProxyHttpServer.setUp(self)
933
# Oh my ! pycurl does not check for the port as part of
934
# no_proxy :-( So we just test the host part
935
self.no_proxy_host = 'localhost'
937
def test_HTTP_PROXY(self):
938
# pycurl do not check HTTP_PROXY for security reasons
939
# (for use in a CGI context that we do not care
940
# about. Should we ?)
943
def test_HTTP_PROXY_with_NO_PROXY(self):
946
def test_http_proxy_without_scheme(self):
947
# pycurl *ignores* invalid proxy env variables. If that
948
# ever change in the future, this test will fail
949
# indicating that pycurl do not ignore anymore such
951
self.not_proxied_in_env({'http_proxy': self.proxy_address})
954
class TestRanges(object):
955
"""Test the Range header in GET methods..
957
This MUST be used by daughter classes that also inherit from
958
TestCaseWithWebserver.
960
We can't inherit directly from TestCaseWithWebserver or the
961
test framework will try to create an instance which cannot
962
run, its implementation being incomplete.
966
TestCaseWithWebserver.setUp(self)
1334
self.overrideEnv('NO_PROXY', self.no_proxy_host)
1335
self.overrideEnv('ALL_PROXY', self.proxy_url)
1336
self.assertNotProxied()
1338
def test_http_proxy_without_scheme(self):
1339
self.overrideEnv('http_proxy', self.server_host_port)
1340
if self._testing_pycurl():
1341
# pycurl *ignores* invalid proxy env variables. If that ever change
1342
# in the future, this test will fail indicating that pycurl do not
1343
# ignore anymore such variables.
1344
self.assertNotProxied()
1346
self.assertRaises(errors.InvalidURL, self.assertProxied)
1349
class TestRanges(http_utils.TestCaseWithWebserver):
1350
"""Test the Range header in GET methods."""
1352
scenarios = multiply_scenarios(
1353
vary_by_http_client_implementation(),
1354
vary_by_http_protocol_version(),
1358
http_utils.TestCaseWithWebserver.setUp(self)
967
1359
self.build_tree_contents([('a', '0123456789')],)
968
server = self.get_readonly_server()
969
self.transport = self._transport(server.get_url())
1361
def create_transport_readonly_server(self):
1362
return http_server.HttpServer(protocol_version=self._protocol_version)
971
1364
def _file_contents(self, relpath, ranges):
1365
t = self.get_readonly_transport()
972
1366
offsets = [ (start, end - start + 1) for start, end in ranges]
973
coalesce = self.transport._coalesce_offsets
1367
coalesce = t._coalesce_offsets
974
1368
coalesced = list(coalesce(offsets, limit=0, fudge_factor=0))
975
code, data = self.transport._get(relpath, coalesced)
1369
code, data = t._get(relpath, coalesced)
976
1370
self.assertTrue(code in (200, 206),'_get returns: %d' % code)
977
1371
for start, end in ranges:
978
1372
data.seek(start)
979
1373
yield data.read(end - start + 1)
981
1375
def _file_tail(self, relpath, tail_amount):
982
code, data = self.transport._get(relpath, [], tail_amount)
1376
t = self.get_readonly_transport()
1377
code, data = t._get(relpath, [], tail_amount)
983
1378
self.assertTrue(code in (200, 206),'_get returns: %d' % code)
984
data.seek(-tail_amount + 1, 2)
1379
data.seek(-tail_amount, 2)
985
1380
return data.read(tail_amount)
987
1382
def test_range_header(self):
989
1384
map(self.assertEqual,['0', '234'],
990
1385
list(self._file_contents('a', [(0,0), (2,4)])),)
1387
def test_range_header_tail(self):
992
1388
self.assertEqual('789', self._file_tail('a', 3))
993
# Syntactically invalid range
994
self.assertListRaises(errors.InvalidRange,
1390
def test_syntactically_invalid_range_header(self):
1391
self.assertListRaises(errors.InvalidHttpRange,
995
1392
self._file_contents, 'a', [(4, 3)])
996
# Semantically invalid range
997
self.assertListRaises(errors.InvalidRange,
1394
def test_semantically_invalid_range_header(self):
1395
self.assertListRaises(errors.InvalidHttpRange,
998
1396
self._file_contents, 'a', [(42, 128)])
1001
class TestRanges_urllib(TestRanges, TestCaseWithWebserver):
1002
"""Test the Range header in GET methods for urllib implementation"""
1004
_transport = HttpTransport_urllib
1007
class TestRanges_pycurl(TestWithTransport_pycurl,
1009
TestCaseWithWebserver):
1010
"""Test the Range header in GET methods for pycurl implementation"""
1013
class TestHTTPRedirections(object):
1014
"""Test redirection between http servers.
1016
This MUST be used by daughter classes that also inherit from
1017
TestCaseWithRedirectedWebserver.
1019
We can't inherit directly from TestCaseWithTwoWebservers or the
1020
test framework will try to create an instance which cannot
1021
run, its implementation being incomplete.
1024
def create_transport_secondary_server(self):
1025
"""Create the secondary server redirecting to the primary server"""
1026
new = self.get_readonly_server()
1028
redirecting = HTTPServerRedirecting()
1029
redirecting.redirect_to(new.host, new.port)
1399
class TestHTTPRedirections(http_utils.TestCaseWithRedirectedWebserver):
1400
"""Test redirection between http servers."""
1402
scenarios = multiply_scenarios(
1403
vary_by_http_client_implementation(),
1404
vary_by_http_protocol_version(),
1032
1407
def setUp(self):
1033
1408
super(TestHTTPRedirections, self).setUp()
1336
1855
('b-proxied', 'contents of b\n'),
1339
def get_user_transport(self, user=None, password=None):
1340
self._install_env({'all_proxy': self.get_user_url(user, password)})
1341
return self._transport(self.server.get_url())
1343
def _install_env(self, env):
1344
for name, value in env.iteritems():
1345
self._old_env[name] = osutils.set_or_unset_env(name, value)
1347
def _restore_env(self):
1348
for name, value in self._old_env.iteritems():
1349
osutils.set_or_unset_env(name, value)
1352
class TestHTTPBasicAuth(TestHTTPAuth, TestCaseWithWebserver):
1353
"""Test http basic authentication scheme"""
1355
_transport = HttpTransport_urllib
1357
def create_transport_readonly_server(self):
1358
return HTTPBasicAuthServer()
1361
class TestHTTPProxyBasicAuth(TestProxyAuth, TestCaseWithWebserver):
1362
"""Test proxy basic authentication scheme"""
1364
_transport = HttpTransport_urllib
1366
def create_transport_readonly_server(self):
1367
return ProxyBasicAuthServer()
1370
class TestDigestAuth(object):
1371
"""Digest Authentication specific tests"""
1373
def test_changing_nonce(self):
1374
self.server.add_user('joe', 'foo')
1375
t = self.get_user_transport('joe', 'foo')
1376
self.assertEqual('contents of a\n', t.get('a').read())
1377
self.assertEqual('contents of b\n', t.get('b').read())
1378
# Only one 'Authentication Required' error should have
1380
self.assertEqual(1, self.server.auth_required_errors)
1381
# The server invalidates the current nonce
1382
self.server.auth_nonce = self.server.auth_nonce + '. No, now!'
1383
self.assertEqual('contents of a\n', t.get('a').read())
1384
# Two 'Authentication Required' errors should occur (the
1385
# initial 'who are you' and a second 'who are you' with the new nonce)
1386
self.assertEqual(2, self.server.auth_required_errors)
1389
class TestHTTPDigestAuth(TestHTTPAuth, TestDigestAuth, TestCaseWithWebserver):
1390
"""Test http digest authentication scheme"""
1392
_transport = HttpTransport_urllib
1394
def create_transport_readonly_server(self):
1395
return HTTPDigestAuthServer()
1398
class TestHTTPProxyDigestAuth(TestProxyAuth, TestDigestAuth,
1399
TestCaseWithWebserver):
1400
"""Test proxy digest authentication scheme"""
1402
_transport = HttpTransport_urllib
1404
def create_transport_readonly_server(self):
1405
return ProxyDigestAuthServer()
1858
def get_user_transport(self, user, password):
1859
self.overrideEnv('all_proxy', self.get_user_url(user, password))
1860
return TestAuth.get_user_transport(self, user, password)
1862
def test_empty_pass(self):
1863
if self._testing_pycurl():
1865
if pycurl.version_info()[1] < '7.16.0':
1867
'pycurl < 7.16.0 does not handle empty proxy passwords')
1868
super(TestProxyAuth, self).test_empty_pass()
1871
class SampleSocket(object):
1872
"""A socket-like object for use in testing the HTTP request handler."""
1874
def __init__(self, socket_read_content):
1875
"""Constructs a sample socket.
1877
:param socket_read_content: a byte sequence
1879
# Use plain python StringIO so we can monkey-patch the close method to
1880
# not discard the contents.
1881
from StringIO import StringIO
1882
self.readfile = StringIO(socket_read_content)
1883
self.writefile = StringIO()
1884
self.writefile.close = lambda: None
1885
self.close = lambda: None
1887
def makefile(self, mode='r', bufsize=None):
1889
return self.readfile
1891
return self.writefile
1894
class SmartHTTPTunnellingTest(tests.TestCaseWithTransport):
1896
scenarios = multiply_scenarios(
1897
vary_by_http_client_implementation(),
1898
vary_by_http_protocol_version(),
1902
super(SmartHTTPTunnellingTest, self).setUp()
1903
# We use the VFS layer as part of HTTP tunnelling tests.
1904
self.overrideEnv('BZR_NO_SMART_VFS', None)
1905
self.transport_readonly_server = http_utils.HTTPServerWithSmarts
1906
self.http_server = self.get_readonly_server()
1908
def create_transport_readonly_server(self):
1909
server = http_utils.HTTPServerWithSmarts(
1910
protocol_version=self._protocol_version)
1911
server._url_protocol = self._url_protocol
1914
def test_open_controldir(self):
1915
branch = self.make_branch('relpath')
1916
url = self.http_server.get_url() + 'relpath'
1917
bd = controldir.ControlDir.open(url)
1918
self.addCleanup(bd.transport.disconnect)
1919
self.assertIsInstance(bd, _mod_remote.RemoteBzrDir)
1921
def test_bulk_data(self):
1922
# We should be able to send and receive bulk data in a single message.
1923
# The 'readv' command in the smart protocol both sends and receives
1924
# bulk data, so we use that.
1925
self.build_tree(['data-file'])
1926
http_transport = transport.get_transport_from_url(
1927
self.http_server.get_url())
1928
medium = http_transport.get_smart_medium()
1929
# Since we provide the medium, the url below will be mostly ignored
1930
# during the test, as long as the path is '/'.
1931
remote_transport = remote.RemoteTransport('bzr://fake_host/',
1934
[(0, "c")], list(remote_transport.readv("data-file", [(0,1)])))
1936
def test_http_send_smart_request(self):
1938
post_body = 'hello\n'
1939
expected_reply_body = 'ok\x012\n'
1941
http_transport = transport.get_transport_from_url(
1942
self.http_server.get_url())
1943
medium = http_transport.get_smart_medium()
1944
response = medium.send_http_smart_request(post_body)
1945
reply_body = response.read()
1946
self.assertEqual(expected_reply_body, reply_body)
1948
def test_smart_http_server_post_request_handler(self):
1949
httpd = self.http_server.server
1951
socket = SampleSocket(
1952
'POST /.bzr/smart %s \r\n' % self._protocol_version
1953
# HTTP/1.1 posts must have a Content-Length (but it doesn't hurt
1955
+ 'Content-Length: 6\r\n'
1958
# Beware: the ('localhost', 80) below is the
1959
# client_address parameter, but we don't have one because
1960
# we have defined a socket which is not bound to an
1961
# address. The test framework never uses this client
1962
# address, so far...
1963
request_handler = http_utils.SmartRequestHandler(socket,
1966
response = socket.writefile.getvalue()
1967
self.assertStartsWith(response, '%s 200 ' % self._protocol_version)
1968
# This includes the end of the HTTP headers, and all the body.
1969
expected_end_of_response = '\r\n\r\nok\x012\n'
1970
self.assertEndsWith(response, expected_end_of_response)
1973
class ForbiddenRequestHandler(http_server.TestingHTTPRequestHandler):
1974
"""No smart server here request handler."""
1977
self.send_error(403, "Forbidden")
1980
class SmartClientAgainstNotSmartServer(TestSpecificRequestHandler):
1981
"""Test smart client behaviour against an http server without smarts."""
1983
_req_handler_class = ForbiddenRequestHandler
1985
def test_probe_smart_server(self):
1986
"""Test error handling against server refusing smart requests."""
1987
t = self.get_readonly_transport()
1988
# No need to build a valid smart request here, the server will not even
1989
# try to interpret it.
1990
self.assertRaises(errors.SmartProtocolError,
1991
t.get_smart_medium().send_http_smart_request,
1995
class Test_redirected_to(tests.TestCase):
1997
scenarios = vary_by_http_client_implementation()
1999
def test_redirected_to_subdir(self):
2000
t = self._transport('http://www.example.com/foo')
2001
r = t._redirected_to('http://www.example.com/foo',
2002
'http://www.example.com/foo/subdir')
2003
self.assertIsInstance(r, type(t))
2004
# Both transports share the some connection
2005
self.assertEqual(t._get_connection(), r._get_connection())
2006
self.assertEquals('http://www.example.com/foo/subdir/', r.base)
2008
def test_redirected_to_self_with_slash(self):
2009
t = self._transport('http://www.example.com/foo')
2010
r = t._redirected_to('http://www.example.com/foo',
2011
'http://www.example.com/foo/')
2012
self.assertIsInstance(r, type(t))
2013
# Both transports share the some connection (one can argue that we
2014
# should return the exact same transport here, but that seems
2016
self.assertEqual(t._get_connection(), r._get_connection())
2018
def test_redirected_to_host(self):
2019
t = self._transport('http://www.example.com/foo')
2020
r = t._redirected_to('http://www.example.com/foo',
2021
'http://foo.example.com/foo/subdir')
2022
self.assertIsInstance(r, type(t))
2023
self.assertEquals('http://foo.example.com/foo/subdir/',
2026
def test_redirected_to_same_host_sibling_protocol(self):
2027
t = self._transport('http://www.example.com/foo')
2028
r = t._redirected_to('http://www.example.com/foo',
2029
'https://www.example.com/foo')
2030
self.assertIsInstance(r, type(t))
2031
self.assertEquals('https://www.example.com/foo/',
2034
def test_redirected_to_same_host_different_protocol(self):
2035
t = self._transport('http://www.example.com/foo')
2036
r = t._redirected_to('http://www.example.com/foo',
2037
'ftp://www.example.com/foo')
2038
self.assertNotEquals(type(r), type(t))
2039
self.assertEquals('ftp://www.example.com/foo/', r.external_url())
2041
def test_redirected_to_same_host_specific_implementation(self):
2042
t = self._transport('http://www.example.com/foo')
2043
r = t._redirected_to('http://www.example.com/foo',
2044
'https+urllib://www.example.com/foo')
2045
self.assertEquals('https://www.example.com/foo/', r.external_url())
2047
def test_redirected_to_different_host_same_user(self):
2048
t = self._transport('http://joe@www.example.com/foo')
2049
r = t._redirected_to('http://www.example.com/foo',
2050
'https://foo.example.com/foo')
2051
self.assertIsInstance(r, type(t))
2052
self.assertEqual(t._parsed_url.user, r._parsed_url.user)
2053
self.assertEquals('https://joe@foo.example.com/foo/', r.external_url())
2056
class PredefinedRequestHandler(http_server.TestingHTTPRequestHandler):
2057
"""Request handler for a unique and pre-defined request.
2059
The only thing we care about here is how many bytes travel on the wire. But
2060
since we want to measure it for a real http client, we have to send it
2063
We expect to receive a *single* request nothing more (and we won't even
2064
check what request it is, we just measure the bytes read until an empty
2068
def _handle_one_request(self):
2069
tcs = self.server.test_case_server
2070
requestline = self.rfile.readline()
2071
headers = self.MessageClass(self.rfile, 0)
2072
# We just read: the request, the headers, an empty line indicating the
2073
# end of the headers.
2074
bytes_read = len(requestline)
2075
for line in headers.headers:
2076
bytes_read += len(line)
2077
bytes_read += len('\r\n')
2078
if requestline.startswith('POST'):
2079
# The body should be a single line (or we don't know where it ends
2080
# and we don't want to issue a blocking read)
2081
body = self.rfile.readline()
2082
bytes_read += len(body)
2083
tcs.bytes_read = bytes_read
2085
# We set the bytes written *before* issuing the write, the client is
2086
# supposed to consume every produced byte *before* checking that value.
2088
# Doing the oppposite may lead to test failure: we may be interrupted
2089
# after the write but before updating the value. The client can then
2090
# continue and read the value *before* we can update it. And yes,
2091
# this has been observed -- vila 20090129
2092
tcs.bytes_written = len(tcs.canned_response)
2093
self.wfile.write(tcs.canned_response)
2096
class ActivityServerMixin(object):
2098
def __init__(self, protocol_version):
2099
super(ActivityServerMixin, self).__init__(
2100
request_handler=PredefinedRequestHandler,
2101
protocol_version=protocol_version)
2102
# Bytes read and written by the server
2104
self.bytes_written = 0
2105
self.canned_response = None
2108
class ActivityHTTPServer(ActivityServerMixin, http_server.HttpServer):
2112
if features.HTTPSServerFeature.available():
2113
from bzrlib.tests import https_server
2114
class ActivityHTTPSServer(ActivityServerMixin, https_server.HTTPSServer):
2118
class TestActivityMixin(object):
2119
"""Test socket activity reporting.
2121
We use a special purpose server to control the bytes sent and received and
2122
be able to predict the activity on the client socket.
2126
tests.TestCase.setUp(self)
2127
self.server = self._activity_server(self._protocol_version)
2128
self.server.start_server()
2129
self.addCleanup(self.server.stop_server)
2130
_activities = {} # Don't close over self and create a cycle
2131
def report_activity(t, bytes, direction):
2132
count = _activities.get(direction, 0)
2134
_activities[direction] = count
2135
self.activities = _activities
2136
# We override at class level because constructors may propagate the
2137
# bound method and render instance overriding ineffective (an
2138
# alternative would be to define a specific ui factory instead...)
2139
self.overrideAttr(self._transport, '_report_activity', report_activity)
2141
def get_transport(self):
2142
t = self._transport(self.server.get_url())
2143
# FIXME: Needs cleanup -- vila 20100611
2146
def assertActivitiesMatch(self):
2147
self.assertEqual(self.server.bytes_read,
2148
self.activities.get('write', 0), 'written bytes')
2149
self.assertEqual(self.server.bytes_written,
2150
self.activities.get('read', 0), 'read bytes')
2153
self.server.canned_response = '''HTTP/1.1 200 OK\r
2154
Date: Tue, 11 Jul 2006 04:32:56 GMT\r
2155
Server: Apache/2.0.54 (Fedora)\r
2156
Last-Modified: Sun, 23 Apr 2006 19:35:20 GMT\r
2157
ETag: "56691-23-38e9ae00"\r
2158
Accept-Ranges: bytes\r
2159
Content-Length: 35\r
2161
Content-Type: text/plain; charset=UTF-8\r
2163
Bazaar-NG meta directory, format 1
2165
t = self.get_transport()
2166
self.assertEqual('Bazaar-NG meta directory, format 1\n',
2167
t.get('foo/bar').read())
2168
self.assertActivitiesMatch()
2171
self.server.canned_response = '''HTTP/1.1 200 OK\r
2172
Server: SimpleHTTP/0.6 Python/2.5.2\r
2173
Date: Thu, 29 Jan 2009 20:21:47 GMT\r
2174
Content-type: application/octet-stream\r
2175
Content-Length: 20\r
2176
Last-Modified: Thu, 29 Jan 2009 20:21:47 GMT\r
2179
t = self.get_transport()
2180
self.assertTrue(t.has('foo/bar'))
2181
self.assertActivitiesMatch()
2183
def test_readv(self):
2184
self.server.canned_response = '''HTTP/1.1 206 Partial Content\r
2185
Date: Tue, 11 Jul 2006 04:49:48 GMT\r
2186
Server: Apache/2.0.54 (Fedora)\r
2187
Last-Modified: Thu, 06 Jul 2006 20:22:05 GMT\r
2188
ETag: "238a3c-16ec2-805c5540"\r
2189
Accept-Ranges: bytes\r
2190
Content-Length: 1534\r
2192
Content-Type: multipart/byteranges; boundary=418470f848b63279b\r
2195
--418470f848b63279b\r
2196
Content-type: text/plain; charset=UTF-8\r
2197
Content-range: bytes 0-254/93890\r
2199
mbp@sourcefrog.net-20050309040815-13242001617e4a06
2200
mbp@sourcefrog.net-20050309040929-eee0eb3e6d1e7627
2201
mbp@sourcefrog.net-20050309040957-6cad07f466bb0bb8
2202
mbp@sourcefrog.net-20050309041501-c840e09071de3b67
2203
mbp@sourcefrog.net-20050309044615-c24a3250be83220a
2205
--418470f848b63279b\r
2206
Content-type: text/plain; charset=UTF-8\r
2207
Content-range: bytes 1000-2049/93890\r
2210
mbp@sourcefrog.net-20050311063625-07858525021f270b
2211
mbp@sourcefrog.net-20050311231934-aa3776aff5200bb9
2212
mbp@sourcefrog.net-20050311231953-73aeb3a131c3699a
2213
mbp@sourcefrog.net-20050311232353-f5e33da490872c6a
2214
mbp@sourcefrog.net-20050312071639-0a8f59a34a024ff0
2215
mbp@sourcefrog.net-20050312073432-b2c16a55e0d6e9fb
2216
mbp@sourcefrog.net-20050312073831-a47c3335ece1920f
2217
mbp@sourcefrog.net-20050312085412-13373aa129ccbad3
2218
mbp@sourcefrog.net-20050313052251-2bf004cb96b39933
2219
mbp@sourcefrog.net-20050313052856-3edd84094687cb11
2220
mbp@sourcefrog.net-20050313053233-e30a4f28aef48f9d
2221
mbp@sourcefrog.net-20050313053853-7c64085594ff3072
2222
mbp@sourcefrog.net-20050313054757-a86c3f5871069e22
2223
mbp@sourcefrog.net-20050313061422-418f1f73b94879b9
2224
mbp@sourcefrog.net-20050313120651-497bd231b19df600
2225
mbp@sourcefrog.net-20050314024931-eae0170ef25a5d1a
2226
mbp@sourcefrog.net-20050314025438-d52099f915fe65fc
2227
mbp@sourcefrog.net-20050314025539-637a636692c055cf
2228
mbp@sourcefrog.net-20050314025737-55eb441f430ab4ba
2229
mbp@sourcefrog.net-20050314025901-d74aa93bb7ee8f62
2231
--418470f848b63279b--\r
2233
t = self.get_transport()
2234
# Remember that the request is ignored and that the ranges below
2235
# doesn't have to match the canned response.
2236
l = list(t.readv('/foo/bar', ((0, 255), (1000, 1050))))
2237
self.assertEqual(2, len(l))
2238
self.assertActivitiesMatch()
2240
def test_post(self):
2241
self.server.canned_response = '''HTTP/1.1 200 OK\r
2242
Date: Tue, 11 Jul 2006 04:32:56 GMT\r
2243
Server: Apache/2.0.54 (Fedora)\r
2244
Last-Modified: Sun, 23 Apr 2006 19:35:20 GMT\r
2245
ETag: "56691-23-38e9ae00"\r
2246
Accept-Ranges: bytes\r
2247
Content-Length: 35\r
2249
Content-Type: text/plain; charset=UTF-8\r
2251
lalala whatever as long as itsssss
2253
t = self.get_transport()
2254
# We must send a single line of body bytes, see
2255
# PredefinedRequestHandler._handle_one_request
2256
code, f = t._post('abc def end-of-body\n')
2257
self.assertEqual('lalala whatever as long as itsssss\n', f.read())
2258
self.assertActivitiesMatch()
2261
class TestActivity(tests.TestCase, TestActivityMixin):
2263
scenarios = multiply_scenarios(
2264
vary_by_http_activity(),
2265
vary_by_http_protocol_version(),
2269
TestActivityMixin.setUp(self)
2272
class TestNoReportActivity(tests.TestCase, TestActivityMixin):
2274
# Unlike TestActivity, we are really testing ReportingFileSocket and
2275
# ReportingSocket, so we don't need all the parametrization. Since
2276
# ReportingFileSocket and ReportingSocket are wrappers, it's easier to
2277
# test them through their use by the transport than directly (that's a
2278
# bit less clean but far more simpler and effective).
2279
_activity_server = ActivityHTTPServer
2280
_protocol_version = 'HTTP/1.1'
2283
self._transport =_urllib.HttpTransport_urllib
2284
TestActivityMixin.setUp(self)
2286
def assertActivitiesMatch(self):
2287
# Nothing to check here
2291
class TestAuthOnRedirected(http_utils.TestCaseWithRedirectedWebserver):
2292
"""Test authentication on the redirected http server."""
2294
scenarios = vary_by_http_protocol_version()
2296
_auth_header = 'Authorization'
2297
_password_prompt_prefix = ''
2298
_username_prompt_prefix = ''
2299
_auth_server = http_utils.HTTPBasicAuthServer
2300
_transport = _urllib.HttpTransport_urllib
2303
super(TestAuthOnRedirected, self).setUp()
2304
self.build_tree_contents([('a','a'),
2306
('1/a', 'redirected once'),
2308
new_prefix = 'http://%s:%s' % (self.new_server.host,
2309
self.new_server.port)
2310
self.old_server.redirections = [
2311
('(.*)', r'%s/1\1' % (new_prefix), 301),]
2312
self.old_transport = self.get_old_transport()
2313
self.new_server.add_user('joe', 'foo')
2314
cleanup_http_redirection_connections(self)
2316
def create_transport_readonly_server(self):
2317
server = self._auth_server(protocol_version=self._protocol_version)
2318
server._url_protocol = self._url_protocol
2324
def test_auth_on_redirected_via_do_catching_redirections(self):
2325
self.redirections = 0
2327
def redirected(t, exception, redirection_notice):
2328
self.redirections += 1
2329
redirected_t = t._redirected_to(exception.source, exception.target)
2330
self.addCleanup(redirected_t.disconnect)
2333
stdout = tests.StringIOWrapper()
2334
stderr = tests.StringIOWrapper()
2335
ui.ui_factory = tests.TestUIFactory(stdin='joe\nfoo\n',
2336
stdout=stdout, stderr=stderr)
2337
self.assertEqual('redirected once',
2338
transport.do_catching_redirections(
2339
self.get_a, self.old_transport, redirected).read())
2340
self.assertEqual(1, self.redirections)
2341
# stdin should be empty
2342
self.assertEqual('', ui.ui_factory.stdin.readline())
2343
# stdout should be empty, stderr will contains the prompts
2344
self.assertEqual('', stdout.getvalue())
2346
def test_auth_on_redirected_via_following_redirections(self):
2347
self.new_server.add_user('joe', 'foo')
2348
stdout = tests.StringIOWrapper()
2349
stderr = tests.StringIOWrapper()
2350
ui.ui_factory = tests.TestUIFactory(stdin='joe\nfoo\n',
2351
stdout=stdout, stderr=stderr)
2352
t = self.old_transport
2353
req = RedirectedRequest('GET', t.abspath('a'))
2354
new_prefix = 'http://%s:%s' % (self.new_server.host,
2355
self.new_server.port)
2356
self.old_server.redirections = [
2357
('(.*)', r'%s/1\1' % (new_prefix), 301),]
2358
self.assertEqual('redirected once', t._perform(req).read())
2359
# stdin should be empty
2360
self.assertEqual('', ui.ui_factory.stdin.readline())
2361
# stdout should be empty, stderr will contains the prompts
2362
self.assertEqual('', stdout.getvalue())