18
18
"""Tests of the bzr serve command."""
28
26
from bzrlib import (
33
31
revision as _mod_revision,
37
36
from bzrlib.branch import Branch
38
from bzrlib.bzrdir import BzrDir
37
from bzrlib.controldir import ControlDir
39
38
from bzrlib.smart import client, medium
40
from bzrlib.smart.server import BzrServerFactory, SmartTCPServer
39
from bzrlib.smart.server import (
41
43
from bzrlib.tests import (
42
44
TestCaseWithMemoryTransport,
43
45
TestCaseWithTransport,
46
from bzrlib.trace import mutter
47
47
from bzrlib.transport import remote
53
53
*func_args, **func_kwargs):
54
54
"""Run 'bzr serve', and run the given func in a thread once the server
57
57
When 'func' terminates, the server will be terminated too.
59
59
Returns stdout and stderr.
62
def on_server_start(backing_urls, tcp_server):
64
target=on_server_start_thread, args=(tcp_server,))
66
61
def on_server_start_thread(tcp_server):
62
"""This runs concurrently with the server thread.
64
The server is interrupted as soon as ``func`` finishes, even if an
65
exception is encountered.
69
69
self.tcp_server = tcp_server
72
72
func(*func_args, **func_kwargs)
73
73
except Exception, e:
74
74
# Log errors to make some test failures a little less
76
mutter('func broke: %r', e)
76
trace.mutter('func broke: %r', e)
78
78
# Then stop the server
79
mutter('interrupting...')
79
trace.mutter('interrupting...')
80
80
thread.interrupt_main()
81
# When the hook is fired, it just starts ``on_server_start_thread`` and
83
def on_server_start(backing_urls, tcp_server):
85
target=on_server_start_thread, args=(tcp_server,))
81
88
SmartTCPServer.hooks.install_named_hook(
82
89
'server_started_ex', on_server_start,
83
90
'run_bzr_serve_then_func hook')
91
# It seesm thread.interrupt_main() will not raise KeyboardInterrupt
92
# until after socket.accept returns. So we set the timeout low to make
94
self.overrideAttr(SmartTCPServer, '_ACCEPT_TIMEOUT', 0.1)
84
95
# start a TCP server
86
out, err = self.run_bzr(['serve'] + list(serve_args))
97
out, err = self.run_bzr(['serve'] + list(serve_args),
87
99
except KeyboardInterrupt, e:
95
107
super(TestBzrServe, self).setUp()
96
108
self.disable_missing_extensions_warning()
110
def test_server_exception_with_hook(self):
111
"""Catch exception from the server in the server_exception hook.
113
We use ``run_bzr_serve_then_func`` without a ``func`` so the server
114
will receive a KeyboardInterrupt exception we want to catch.
117
if exception[0] is KeyboardInterrupt:
118
sys.stderr.write('catching KeyboardInterrupt\n')
122
SmartTCPServer.hooks.install_named_hook(
123
'server_exception', hook,
124
'test_server_except_hook hook')
125
args = ['--listen', 'localhost', '--port', '0', '--quiet']
126
out, err = self.run_bzr_serve_then_func(args, retcode=0)
127
self.assertEqual('catching KeyboardInterrupt\n', err)
129
def test_server_exception_no_hook(self):
130
"""test exception without hook returns error"""
132
out, err = self.run_bzr_serve_then_func(args, retcode=3)
98
134
def assertInetServerShutsdownCleanly(self, process):
99
135
"""Shutdown the server process looking for errors."""
100
136
# Shutdown the server: the server should shut down when it cannot read
154
190
finish_bzr_subprocess, and the base url for the server.
156
192
# Serve from the current directory
157
args = ['serve', '--port', 'localhost:0']
193
args = ['serve', '--listen', 'localhost', '--port', '0']
158
194
args.extend(extra_options)
159
195
process = self.start_bzr_subprocess(args, skip_if_plan_to_signal=True)
160
196
port_line = process.stderr.readline()
164
200
url = 'bzr://localhost:%d/' % port
165
201
self.permit_url(url)
166
202
return process, url
168
204
def test_bzr_serve_quiet(self):
169
205
self.make_branch('.')
170
args = ['--port', 'localhost:0', '--quiet']
206
args = ['--listen', 'localhost', '--port', '0', '--quiet']
171
207
out, err = self.run_bzr_serve_then_func(args, retcode=3)
172
208
self.assertEqual('', out)
173
209
self.assertEqual('', err)
225
261
# -Dhpss, and does drop some hpss logging to the file.
226
262
self.make_branch('.')
227
263
log_fname = os.getcwd() + '/server.log'
228
self._captureVar('BZR_LOG', log_fname)
264
self.overrideEnv('BZR_LOG', log_fname)
229
265
process, transport = self.start_server_inet(['-Dhpss'])
230
branch = BzrDir.open_from_transport(transport).open_branch()
266
branch = ControlDir.open_from_transport(transport).open_branch()
231
267
self.make_read_requests(branch)
232
268
self.assertInetServerShutsdownCleanly(process)
233
269
f = open(log_fname, 'rb')
236
272
self.assertContainsRe(content, r'hpss request: \[[0-9-]+\]')
274
def test_bzr_serve_supports_configurable_timeout(self):
275
gs = config.GlobalStack()
276
gs.set('serve.client_timeout', 0.2)
277
# Save the config as the subprocess will use it
279
process, url = self.start_server_port()
280
self.build_tree_contents([('a_file', 'contents\n')])
281
# We can connect and issue a request
282
t = transport.get_transport_from_url(url)
283
self.assertEqual('contents\n', t.get_bytes('a_file'))
284
# However, if we just wait for more content from the server, it will
285
# eventually disconnect us.
286
m = t.get_smart_medium()
288
# Now, we wait for timeout to trigger
289
err = process.stderr.readline()
291
'Connection Timeout: disconnecting client after 0.2 seconds\n',
293
self.assertServerFinishesCleanly(process)
295
def test_bzr_serve_supports_client_timeout(self):
296
process, url = self.start_server_port(['--client-timeout=0.1'])
297
self.build_tree_contents([('a_file', 'contents\n')])
298
# We can connect and issue a request
299
t = transport.get_transport_from_url(url)
300
self.assertEqual('contents\n', t.get_bytes('a_file'))
301
# However, if we just wait for more content from the server, it will
302
# eventually disconnect us.
303
# TODO: Use something like signal.alarm() so that if the server doesn't
304
# properly handle the timeout, we end up failing the test instead
305
# of hanging forever.
306
m = t.get_smart_medium()
308
# Now, we wait for timeout to trigger
309
err = process.stderr.readline()
311
'Connection Timeout: disconnecting client after 0.1 seconds\n',
313
self.assertServerFinishesCleanly(process)
315
def test_bzr_serve_graceful_shutdown(self):
316
big_contents = 'a'*64*1024
317
self.build_tree_contents([('bigfile', big_contents)])
318
process, url = self.start_server_port(['--client-timeout=1.0'])
319
t = transport.get_transport_from_url(url)
320
m = t.get_smart_medium()
321
c = client._SmartClient(m)
322
# Start, but don't finish a response
323
resp, response_handler = c.call_expecting_body('get', 'bigfile')
324
self.assertEqual(('ok',), resp)
325
# Note: process.send_signal is a Python 2.6ism
326
process.send_signal(signal.SIGHUP)
327
# Wait for the server to notice the signal, and then read the actual
328
# body of the response. That way we know that it is waiting for the
330
self.assertEqual('Requested to stop gracefully\n',
331
process.stderr.readline())
332
self.assertEqual('Waiting for 1 client(s) to finish\n',
333
process.stderr.readline())
334
body = response_handler.read_body_bytes()
335
if body != big_contents:
336
self.fail('Failed to properly read the contents of "bigfile"')
337
# Now that our request is finished, the medium should notice it has
339
self.assertEqual('', m.read_bytes(1))
340
# And the server should be stopping
341
self.assertEqual(0, process.wait())
239
344
class TestCmdServeChrooting(TestBzrServeBase):