91
92
self.credentials.append([realm, host, username, password])
94
class RecordingServer(object):
95
"""A fake HTTP server.
97
It records the bytes sent to it, and replies with a 200.
100
def __init__(self, expect_body_tail=None):
103
:type expect_body_tail: str
104
:param expect_body_tail: a reply won't be sent until this string is
107
self._expect_body_tail = expect_body_tail
110
self.received_bytes = ''
113
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
114
self._sock.bind(('127.0.0.1', 0))
115
self.host, self.port = self._sock.getsockname()
116
self._ready = threading.Event()
117
self._thread = threading.Thread(target=self._accept_read_and_reply)
118
self._thread.setDaemon(True)
122
def _accept_read_and_reply(self):
125
self._sock.settimeout(5)
127
conn, address = self._sock.accept()
128
# On win32, the accepted connection will be non-blocking to start
129
# with because we're using settimeout.
130
conn.setblocking(True)
131
while not self.received_bytes.endswith(self._expect_body_tail):
132
self.received_bytes += conn.recv(4096)
133
conn.sendall('HTTP/1.1 200 OK\r\n')
134
except socket.timeout:
135
# Make sure the client isn't stuck waiting for us to e.g. accept.
138
# The client may have already closed the socket.
145
# We might have already closed it. We don't care.
151
95
class TestHTTPServer(tests.TestCase):
152
96
"""Test the HTTP servers implementations."""
265
209
self.assertRaises(errors.DependencyNotPresent, self._transport,
266
210
'https://launchpad.net')
268
class TestHttpConnections(object):
269
"""Test the http connections.
271
This MUST be used by daughter classes that also inherit from
272
TestCaseWithWebserver.
274
We can't inherit directly from TestCaseWithWebserver or the
275
test framework will try to create an instance which cannot
276
run, its implementation being incomplete.
280
TestCaseWithWebserver.setUp(self)
281
self.build_tree(['xxx', 'foo/', 'foo/bar'], line_endings='binary',
282
transport=self.get_transport())
284
def test_http_has(self):
285
server = self.get_readonly_server()
286
t = self._transport(server.get_url())
287
self.assertEqual(t.has('foo/bar'), True)
288
self.assertEqual(len(server.logs), 1)
289
self.assertContainsRe(server.logs[0],
290
r'"HEAD /foo/bar HTTP/1.." (200|302) - "-" "bzr/')
292
def test_http_has_not_found(self):
293
server = self.get_readonly_server()
294
t = self._transport(server.get_url())
295
self.assertEqual(t.has('not-found'), False)
296
self.assertContainsRe(server.logs[1],
297
r'"HEAD /not-found HTTP/1.." 404 - "-" "bzr/')
299
def test_http_get(self):
300
server = self.get_readonly_server()
301
t = self._transport(server.get_url())
302
fp = t.get('foo/bar')
303
self.assertEqualDiff(
305
'contents of foo/bar\n')
306
self.assertEqual(len(server.logs), 1)
307
self.assertTrue(server.logs[0].find(
308
'"GET /foo/bar HTTP/1.1" 200 - "-" "bzr/%s'
309
% bzrlib.__version__) > -1)
311
def test_get_smart_medium(self):
312
# For HTTP, get_smart_medium should return the transport object.
313
server = self.get_readonly_server()
314
http_transport = self._transport(server.get_url())
315
medium = http_transport.get_smart_medium()
316
self.assertIs(medium, http_transport)
318
def test_has_on_bogus_host(self):
319
# Get a free address and don't 'accept' on it, so that we
320
# can be sure there is no http handler there, but set a
321
# reasonable timeout to not slow down tests too much.
322
default_timeout = socket.getdefaulttimeout()
324
socket.setdefaulttimeout(2)
326
s.bind(('localhost', 0))
327
t = self._transport('http://%s:%s/' % s.getsockname())
328
self.assertRaises(errors.ConnectionError, t.has, 'foo/bar')
330
socket.setdefaulttimeout(default_timeout)
333
class TestHttpConnections_urllib(TestHttpConnections, TestCaseWithWebserver):
334
"""Test http connections with urllib"""
336
_transport = HttpTransport_urllib
340
class TestHttpConnections_pycurl(TestWithTransport_pycurl,
342
TestCaseWithWebserver):
343
"""Test http connections with pycurl"""
346
212
class TestHttpTransportRegistration(tests.TestCase):
347
213
"""Test registrations of various http implementations"""
353
219
self.assertIsInstance(t, HttpTransport_urllib)
356
class TestPost(object):
358
def _test_post_body_is_received(self, scheme):
359
server = RecordingServer(expect_body_tail='end-of-body')
361
self.addCleanup(server.tearDown)
362
url = '%s://%s:%s/' % (scheme, server.host, server.port)
364
http_transport = get_transport(url)
365
except errors.UnsupportedProtocol:
366
raise tests.TestSkipped('%s not available' % scheme)
367
code, response = http_transport._post('abc def end-of-body')
369
server.received_bytes.startswith('POST /.bzr/smart HTTP/1.'))
370
self.assertTrue('content-length: 19\r' in server.received_bytes.lower())
371
# The transport should not be assuming that the server can accept
372
# chunked encoding the first time it connects, because HTTP/1.1, so we
373
# check for the literal string.
375
server.received_bytes.endswith('\r\n\r\nabc def end-of-body'))
378
class TestPost_urllib(tests.TestCase, TestPost):
379
"""TestPost for urllib implementation"""
381
_transport = HttpTransport_urllib
383
def test_post_body_is_received_urllib(self):
384
self._test_post_body_is_received('http+urllib')
387
class TestPost_pycurl(TestWithTransport_pycurl, tests.TestCase, TestPost):
388
"""TestPost for pycurl implementation"""
390
def test_post_body_is_received_pycurl(self):
391
self._test_post_body_is_received('http+pycurl')
394
222
class TestRangeHeader(tests.TestCase):
395
223
"""Test range_header method"""
569
397
class TestRecordingServer(tests.TestCase):
571
399
def test_create(self):
572
server = RecordingServer(expect_body_tail=None)
400
server = http_utils.RecordingServer(expect_body_tail=None)
573
401
self.assertEqual('', server.received_bytes)
574
402
self.assertEqual(None, server.host)
575
403
self.assertEqual(None, server.port)
577
405
def test_setUp_and_tearDown(self):
578
server = RecordingServer(expect_body_tail=None)
406
server = http_utils.RecordingServer(expect_body_tail=None)
581
409
self.assertNotEqual(None, server.host)