265
class SocketListener(threading.Thread):
267
def __init__(self, callback):
268
threading.Thread.__init__(self)
269
self._callback = callback
270
self._socket = socket.socket()
271
self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
272
self._socket.bind(('localhost', 0))
273
self._socket.listen(1)
274
self.host, self.port = self._socket.getsockname()[:2]
275
self._stop_event = threading.Event()
278
# called from outside this thread
279
self._stop_event.set()
280
# use a timeout here, because if the test fails, the server thread may
281
# never notice the stop_event.
287
readable, writable_unused, exception_unused = \
288
select.select([self._socket], [], [], 0.1)
289
if self._stop_event.isSet():
291
if len(readable) == 0:
294
s, addr_unused = self._socket.accept()
295
# because the loopback socket is inline, and transports are
296
# never explicitly closed, best to launch a new thread.
297
threading.Thread(target=self._callback, args=(s,)).start()
298
except socket.error, x:
299
sys.excepthook(*sys.exc_info())
300
warning('Socket error during accept() within unit test server'
303
# probably a failed test; unit test thread will log the
305
sys.excepthook(*sys.exc_info())
306
warning('Exception from within unit test server thread: %r' %
267
310
class SocketDelay(object):
268
311
"""A socket decorator to make TCP appear slower.
339
382
return bytes_sent
342
class TestingSFTPConnectionHandler(SocketServer.BaseRequestHandler):
345
self.wrap_for_latency()
346
tcs = self.server.test_case_server
347
ssh_server = paramiko.Transport(self.request)
348
ssh_server.add_server_key(tcs.get_host_key())
349
ssh_server.set_subsystem_handler('sftp', paramiko.SFTPServer,
350
StubSFTPServer, root=tcs._root,
351
home=tcs._server_homedir)
352
server = tcs._server_interface(tcs)
353
ssh_server.start_server(None, server)
354
# FIXME: Long story short:
355
# bt.test_transport.TestSSHConnections.test_bzr_connect_to_bzr_ssh
356
# fails if we wait less than 0.2 seconds... paramiko uses a lot of
357
# timeouts internally which probably mask a synchronisation
358
# problem. Note that this is the only test that requires this hack and
359
# the test may need to be fixed instead, but it's late and the test is
360
# horrible as mentioned in its comments :) -- vila 20100623
364
def wrap_for_latency(self):
365
tcs = self.server.test_case_server
367
# Give the socket (which the request really is) a latency adding
369
self.request = SocketDelay(self.request, tcs.add_latency)
372
class TestingSFTPWithoutSSHConnectionHandler(TestingSFTPConnectionHandler):
375
self.wrap_for_latency()
376
# Re-import these as locals, so that they're still accessible during
377
# interpreter shutdown (when all module globals get set to None, leading
378
# to confusing errors like "'NoneType' object has no attribute 'error'".
379
class FakeChannel(object):
380
def get_transport(self):
382
def get_log_channel(self):
386
def get_hexdump(self):
391
tcs = self.server.test_case_server
392
server = paramiko.SFTPServer(
393
FakeChannel(), 'sftp', StubServer(tcs), StubSFTPServer,
394
root=tcs._root, home=tcs._server_homedir)
396
server.start_subsystem(
397
'sftp', None, ssh.SocketAsChannelAdapter(self.request))
398
except socket.error, e:
399
if (len(e.args) > 0) and (e.args[0] == errno.EPIPE):
400
# it's okay for the client to disconnect abruptly
401
# (bug in paramiko 1.6: it should absorb this exception)
406
# This typically seems to happen during interpreter shutdown, so
407
# most of the useful ways to report this error won't work.
408
# Writing the exception type, and then the text of the exception,
409
# seems to be the best we can do.
410
# FIXME: All interpreter shutdown errors should have been related
411
# to daemon threads, cleanup needed -- vila 20100623
413
sys.stderr.write('\nEXCEPTION %r: ' % (e.__class__,))
414
sys.stderr.write('%s\n\n' % (e,))
415
server.finish_subsystem()
418
class TestingSFTPServer(test_server.TestingThreadingTCPServer):
420
def __init__(self, server_address, request_handler_class, test_case_server):
421
test_server.TestingThreadingTCPServer.__init__(
422
self, server_address, request_handler_class)
423
self.test_case_server = test_case_server
426
class SFTPServer(test_server.TestingTCPServerInAThread):
385
class SFTPServer(Server):
427
386
"""Common code for SFTP server facilities."""
429
388
def __init__(self, server_interface=StubServer):
430
self.host = '127.0.0.1'
432
super(SFTPServer, self).__init__((self.host, self.port),
434
TestingSFTPConnectionHandler)
435
389
self._original_vendor = None
391
self._server_homedir = None
392
self._listener = None
436
394
self._vendor = ssh.ParamikoVendor()
437
395
self._server_interface = server_interface
438
self._host_key = None
440
398
self.add_latency = 0
442
self._server_homedir = None
445
400
def _get_sftp_url(self, path):
446
401
"""Calculate an sftp url to this server for path."""
447
return "sftp://foo:bar@%s:%s/%s" % (self.host, self.port, path)
402
return 'sftp://foo:bar@%s:%d/%s' % (self._listener.host,
403
self._listener.port, path)
449
405
def log(self, message):
450
406
"""StubServer uses this to log when a new server is created."""
451
407
self.logs.append(message)
453
def create_server(self):
454
server = self.server_class((self.host, self.port),
455
self.request_handler_class,
459
def get_host_key(self):
460
if self._host_key is None:
461
key_file = osutils.pathjoin(self._homedir, 'test_rsa.key')
462
f = open(key_file, 'w')
464
f.write(STUB_SERVER_KEY)
467
self._host_key = paramiko.RSAKey.from_private_key_file(key_file)
468
return self._host_key
409
def _run_server_entry(self, sock):
410
"""Entry point for all implementations of _run_server.
412
If self.add_latency is > 0.000001 then sock is given a latency adding
415
if self.add_latency > 0.000001:
416
sock = SocketDelay(sock, self.add_latency)
417
return self._run_server(sock)
419
def _run_server(self, s):
420
ssh_server = paramiko.Transport(s)
421
key_file = osutils.pathjoin(self._homedir, 'test_rsa.key')
422
f = open(key_file, 'w')
423
f.write(STUB_SERVER_KEY)
425
host_key = paramiko.RSAKey.from_private_key_file(key_file)
426
ssh_server.add_server_key(host_key)
427
server = self._server_interface(self)
428
ssh_server.set_subsystem_handler('sftp', paramiko.SFTPServer,
429
StubSFTPServer, root=self._root,
430
home=self._server_homedir)
431
event = threading.Event()
432
ssh_server.start_server(event, server)
470
435
def start_server(self, backing_server=None):
471
436
# XXX: TODO: make sftpserver back onto backing_server rather than local
473
438
if not (backing_server is None or
474
isinstance(backing_server, test_server.LocalURLServer)):
439
isinstance(backing_server, local.LocalURLServer)):
475
440
raise AssertionError(
476
'backing_server should not be %r, because this can only serve '
477
'the local current working directory.' % (backing_server,))
441
"backing_server should not be %r, because this can only serve the "
442
"local current working directory." % (backing_server,))
478
443
self._original_vendor = ssh._ssh_vendor_manager._cached_ssh_vendor
479
444
ssh._ssh_vendor_manager._cached_ssh_vendor = self._vendor
480
445
if sys.platform == 'win32':
481
446
# Win32 needs to use the UNICODE api
482
self._homedir = os.getcwdu()
483
# Normalize the path or it will be wrongly escaped
484
self._homedir = osutils.normpath(self._homedir)
447
self._homedir = getcwd()
486
# But unix SFTP servers should just deal in bytestreams
449
# But Linux SFTP servers should just deal in bytestreams
487
450
self._homedir = os.getcwd()
488
451
if self._server_homedir is None:
489
452
self._server_homedir = self._homedir
491
454
if sys.platform == 'win32':
493
super(SFTPServer, self).start_server()
456
self._listener = SocketListener(self._run_server_entry)
457
self._listener.setDaemon(True)
458
self._listener.start()
495
460
def stop_server(self):
497
super(SFTPServer, self).stop_server()
499
ssh._ssh_vendor_manager._cached_ssh_vendor = self._original_vendor
461
self._listener.stop()
462
ssh._ssh_vendor_manager._cached_ssh_vendor = self._original_vendor
501
464
def get_bogus_url(self):
502
465
"""See bzrlib.transport.Server.get_bogus_url."""
503
# this is chosen to try to prevent trouble with proxies, weird dns, etc
466
# this is chosen to try to prevent trouble with proxies, wierd dns, etc
504
467
# we bind a random socket, so that we get a guaranteed unused port
505
468
# we just never listen on that port
506
469
s = socket.socket()
526
489
def __init__(self):
527
490
super(SFTPServerWithoutSSH, self).__init__()
528
491
self._vendor = ssh.LoopbackVendor()
529
self.request_handler_class = TestingSFTPWithoutSSHConnectionHandler
493
def _run_server(self, sock):
494
# Re-import these as locals, so that they're still accessible during
495
# interpreter shutdown (when all module globals get set to None, leading
496
# to confusing errors like "'NoneType' object has no attribute 'error'".
497
class FakeChannel(object):
498
def get_transport(self):
500
def get_log_channel(self):
504
def get_hexdump(self):
509
server = paramiko.SFTPServer(
510
FakeChannel(), 'sftp', StubServer(self), StubSFTPServer,
511
root=self._root, home=self._server_homedir)
513
server.start_subsystem(
514
'sftp', None, ssh.SocketAsChannelAdapter(sock))
515
except socket.error, e:
516
if (len(e.args) > 0) and (e.args[0] == errno.EPIPE):
517
# it's okay for the client to disconnect abruptly
518
# (bug in paramiko 1.6: it should absorb this exception)
523
# This typically seems to happen during interpreter shutdown, so
524
# most of the useful ways to report this error are won't work.
525
# Writing the exception type, and then the text of the exception,
526
# seems to be the best we can do.
528
sys.stderr.write('\nEXCEPTION %r: ' % (e.__class__,))
529
sys.stderr.write('%s\n\n' % (e,))
530
server.finish_subsystem()
535
533
class SFTPAbsoluteServer(SFTPServerWithoutSSH):