/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/stub_sftp.py

  • Committer: Marius Kruger
  • Date: 2010-07-10 21:28:56 UTC
  • mto: (5384.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 5385.
  • Revision ID: marius.kruger@enerweb.co.za-20100710212856-uq4ji3go0u5se7hx
* Update documentation
* add NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2008-2011 Robey Pointer <robey@lag.net>, Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2008, 2009, 2010 Robey Pointer <robey@lag.net>, Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
21
21
 
22
22
import os
23
23
import paramiko
 
24
import select
24
25
import socket
25
 
try:
26
 
    import socketserver
27
 
except ImportError:
28
 
    import SocketServer as socketserver
29
26
import sys
 
27
import threading
30
28
import time
31
29
 
32
 
from .. import (
 
30
from bzrlib import (
33
31
    osutils,
34
32
    trace,
35
33
    urlutils,
36
34
    )
37
 
from ..transport import (
 
35
from bzrlib.transport import (
38
36
    ssh,
39
37
    )
40
 
from . import test_server
41
 
 
42
 
 
43
 
class StubServer(paramiko.ServerInterface):
44
 
 
45
 
    def __init__(self, test_case_server):
 
38
from bzrlib.tests import test_server
 
39
 
 
40
 
 
41
class StubServer (paramiko.ServerInterface):
 
42
 
 
43
    def __init__(self, test_case):
46
44
        paramiko.ServerInterface.__init__(self)
47
 
        self.log = test_case_server.log
 
45
        self._test_case = test_case
48
46
 
49
47
    def check_auth_password(self, username, password):
50
48
        # all are allowed
51
 
        self.log('sftpserver - authorizing: %s' % (username,))
 
49
        self._test_case.log('sftpserver - authorizing: %s' % (username,))
52
50
        return paramiko.AUTH_SUCCESSFUL
53
51
 
54
52
    def check_channel_request(self, kind, chanid):
55
 
        self.log('sftpserver - channel request: %s, %s' % (kind, chanid))
 
53
        self._test_case.log(
 
54
            'sftpserver - channel request: %s, %s' % (kind, chanid))
56
55
        return paramiko.OPEN_SUCCEEDED
57
56
 
58
57
 
59
 
class StubSFTPHandle(paramiko.SFTPHandle):
60
 
 
 
58
class StubSFTPHandle (paramiko.SFTPHandle):
61
59
    def stat(self):
62
60
        try:
63
61
            return paramiko.SFTPAttributes.from_stat(
64
62
                os.fstat(self.readfile.fileno()))
65
 
        except OSError as e:
 
63
        except OSError, e:
66
64
            return paramiko.SFTPServer.convert_errno(e.errno)
67
65
 
68
66
    def chattr(self, attr):
71
69
        trace.mutter('Changing permissions on %s to %s', self.filename, attr)
72
70
        try:
73
71
            paramiko.SFTPServer.set_file_attr(self.filename, attr)
74
 
        except OSError as e:
 
72
        except OSError, e:
75
73
            return paramiko.SFTPServer.convert_errno(e.errno)
76
74
 
77
75
 
78
 
class StubSFTPServer(paramiko.SFTPServerInterface):
 
76
class StubSFTPServer (paramiko.SFTPServerInterface):
79
77
 
80
78
    def __init__(self, server, root, home=None):
81
79
        paramiko.SFTPServerInterface.__init__(self, server)
92
90
            self.home = home[len(self.root):]
93
91
        if self.home.startswith('/'):
94
92
            self.home = self.home[1:]
95
 
        server.log('sftpserver - new connection')
 
93
        server._test_case.log('sftpserver - new connection')
96
94
 
97
95
    def _realpath(self, path):
98
96
        # paths returned from self.canonicalize() always start with
121
119
    else:
122
120
        def canonicalize(self, path):
123
121
            if os.path.isabs(path):
124
 
                return osutils.normpath(path)
 
122
                return os.path.normpath(path)
125
123
            else:
126
 
                return osutils.normpath('/' + os.path.join(self.home, path))
 
124
                return os.path.normpath('/' + os.path.join(self.home, path))
127
125
 
128
126
    def chattr(self, path, attr):
129
127
        try:
130
128
            paramiko.SFTPServer.set_file_attr(path, attr)
131
 
        except OSError as e:
 
129
        except OSError, e:
132
130
            return paramiko.SFTPServer.convert_errno(e.errno)
133
131
        return paramiko.SFTP_OK
134
132
 
135
133
    def list_folder(self, path):
136
134
        path = self._realpath(path)
137
135
        try:
138
 
            out = []
 
136
            out = [ ]
139
137
            # TODO: win32 incorrectly lists paths with non-ascii if path is not
140
138
            # unicode. However on unix the server should only deal with
141
139
            # bytestreams and posix.listdir does the right thing
149
147
                attr.filename = fname
150
148
                out.append(attr)
151
149
            return out
152
 
        except OSError as e:
 
150
        except OSError, e:
153
151
            return paramiko.SFTPServer.convert_errno(e.errno)
154
152
 
155
153
    def stat(self, path):
156
154
        path = self._realpath(path)
157
155
        try:
158
156
            return paramiko.SFTPAttributes.from_stat(os.stat(path))
159
 
        except OSError as e:
 
157
        except OSError, e:
160
158
            return paramiko.SFTPServer.convert_errno(e.errno)
161
159
 
162
160
    def lstat(self, path):
163
161
        path = self._realpath(path)
164
162
        try:
165
163
            return paramiko.SFTPAttributes.from_stat(os.lstat(path))
166
 
        except OSError as e:
 
164
        except OSError, e:
167
165
            return paramiko.SFTPServer.convert_errno(e.errno)
168
166
 
169
167
    def open(self, path, flags, attr):
175
173
            else:
176
174
                # os.open() defaults to 0777 which is
177
175
                # an odd default mode for files
178
 
                fd = os.open(path, flags, 0o666)
179
 
        except OSError as e:
 
176
                fd = os.open(path, flags, 0666)
 
177
        except OSError, e:
180
178
            return paramiko.SFTPServer.convert_errno(e.errno)
181
179
 
182
180
        if (flags & os.O_CREAT) and (attr is not None):
191
189
            fstr = 'rb'
192
190
        try:
193
191
            f = os.fdopen(fd, fstr)
194
 
        except (IOError, OSError) as e:
 
192
        except (IOError, OSError), e:
195
193
            return paramiko.SFTPServer.convert_errno(e.errno)
196
194
        fobj = StubSFTPHandle()
197
195
        fobj.filename = path
203
201
        path = self._realpath(path)
204
202
        try:
205
203
            os.remove(path)
206
 
        except OSError as e:
 
204
        except OSError, e:
207
205
            return paramiko.SFTPServer.convert_errno(e.errno)
208
206
        return paramiko.SFTP_OK
209
207
 
212
210
        newpath = self._realpath(newpath)
213
211
        try:
214
212
            os.rename(oldpath, newpath)
215
 
        except OSError as e:
216
 
            return paramiko.SFTPServer.convert_errno(e.errno)
217
 
        return paramiko.SFTP_OK
218
 
 
219
 
    def symlink(self, target_path, path):
220
 
        path = self._realpath(path)
221
 
        try:
222
 
            os.symlink(target_path, path)
223
 
        except OSError as e:
224
 
            return paramiko.SFTPServer.convert_errno(e.errno)
225
 
        return paramiko.SFTP_OK
226
 
 
227
 
    def readlink(self, path):
228
 
        path = self._realpath(path)
229
 
        try:
230
 
            target_path = os.readlink(path)
231
 
        except OSError as e:
232
 
            return paramiko.SFTPServer.convert_errno(e.errno)
233
 
        return target_path
 
213
        except OSError, e:
 
214
            return paramiko.SFTPServer.convert_errno(e.errno)
 
215
        return paramiko.SFTP_OK
234
216
 
235
217
    def mkdir(self, path, attr):
236
218
        path = self._realpath(path)
244
226
            if attr is not None:
245
227
                attr._flags &= ~attr.FLAG_PERMISSIONS
246
228
                paramiko.SFTPServer.set_file_attr(path, attr)
247
 
        except OSError as e:
 
229
        except OSError, e:
248
230
            return paramiko.SFTPServer.convert_errno(e.errno)
249
231
        return paramiko.SFTP_OK
250
232
 
252
234
        path = self._realpath(path)
253
235
        try:
254
236
            os.rmdir(path)
255
 
        except OSError as e:
 
237
        except OSError, e:
256
238
            return paramiko.SFTPServer.convert_errno(e.errno)
257
239
        return paramiko.SFTP_OK
258
240
 
259
 
    # removed: chattr
 
241
    # removed: chattr, symlink, readlink
260
242
    # (nothing in bzr's sftp transport uses those)
261
243
 
262
 
 
263
244
# ------------- server test implementation --------------
264
245
 
265
 
STUB_SERVER_KEY = """\
 
246
STUB_SERVER_KEY = """
266
247
-----BEGIN RSA PRIVATE KEY-----
267
248
MIICWgIBAAKBgQDTj1bqB4WmayWNPB+8jVSYpZYk80Ujvj680pOTh2bORBjbIAyz
268
249
oWGW+GUjzKxTiiPvVmxFgx5wdsFvF03v34lEVVhMpouqPAYQ15N37K/ir5XY+9m/
281
262
"""
282
263
 
283
264
 
 
265
class SocketListener(threading.Thread):
 
266
 
 
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()
 
276
 
 
277
    def stop(self):
 
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.
 
282
        self.join(5.0)
 
283
        self._socket.close()
 
284
 
 
285
    def run(self):
 
286
        trace.mutter('SocketListener %r has started', self)
 
287
        while True:
 
288
            readable, writable_unused, exception_unused = \
 
289
                select.select([self._socket], [], [], 0.1)
 
290
            if self._stop_event.isSet():
 
291
                trace.mutter('SocketListener %r has stopped', self)
 
292
                return
 
293
            if len(readable) == 0:
 
294
                continue
 
295
            try:
 
296
                s, addr_unused = self._socket.accept()
 
297
                trace.mutter('SocketListener %r has accepted connection %r',
 
298
                    self, s)
 
299
                # because the loopback socket is inline, and transports are
 
300
                # never explicitly closed, best to launch a new thread.
 
301
                threading.Thread(target=self._callback, args=(s,)).start()
 
302
            except socket.error, x:
 
303
                sys.excepthook(*sys.exc_info())
 
304
                trace.warning('Socket error during accept() '
 
305
                              'within unit test server thread: %r' % x)
 
306
            except Exception, x:
 
307
                # probably a failed test; unit test thread will log the
 
308
                # failure/error
 
309
                sys.excepthook(*sys.exc_info())
 
310
                trace.warning(
 
311
                    'Exception from within unit test server thread: %r' % x)
 
312
 
 
313
 
284
314
class SocketDelay(object):
285
315
    """A socket decorator to make TCP appear slower.
286
316
 
356
386
        return bytes_sent
357
387
 
358
388
 
359
 
class TestingSFTPConnectionHandler(socketserver.BaseRequestHandler):
360
 
 
361
 
    def setup(self):
362
 
        self.wrap_for_latency()
363
 
        tcs = self.server.test_case_server
364
 
        ptrans = paramiko.Transport(self.request)
365
 
        self.paramiko_transport = ptrans
366
 
        # Set it to a channel under 'bzr' so that we get debug info
367
 
        ptrans.set_log_channel('brz.paramiko.transport')
368
 
        ptrans.add_server_key(tcs.get_host_key())
369
 
        ptrans.set_subsystem_handler('sftp', paramiko.SFTPServer,
370
 
                                     StubSFTPServer, root=tcs._root,
371
 
                                     home=tcs._server_homedir)
372
 
        server = tcs._server_interface(tcs)
373
 
        # This blocks until the key exchange has been done
374
 
        ptrans.start_server(None, server)
375
 
 
376
 
    def finish(self):
377
 
        # Wait for the conversation to finish, when the paramiko.Transport
378
 
        # thread finishes
379
 
        # TODO: Consider timing out after XX seconds rather than hanging.
380
 
        #       Also we could check paramiko_transport.active and possibly
381
 
        #       paramiko_transport.getException().
382
 
        self.paramiko_transport.join()
383
 
 
384
 
    def wrap_for_latency(self):
385
 
        tcs = self.server.test_case_server
386
 
        if tcs.add_latency:
387
 
            # Give the socket (which the request really is) a latency adding
388
 
            # decorator.
389
 
            self.request = SocketDelay(self.request, tcs.add_latency)
390
 
 
391
 
 
392
 
class TestingSFTPWithoutSSHConnectionHandler(TestingSFTPConnectionHandler):
393
 
 
394
 
    def setup(self):
395
 
        self.wrap_for_latency()
396
 
        # Re-import these as locals, so that they're still accessible during
397
 
        # interpreter shutdown (when all module globals get set to None, leading
398
 
        # to confusing errors like "'NoneType' object has no attribute 'error'".
399
 
 
400
 
        class FakeChannel(object):
401
 
            def get_transport(self):
402
 
                return self
403
 
 
404
 
            def get_log_channel(self):
405
 
                return 'brz.paramiko'
406
 
 
407
 
            def get_name(self):
408
 
                return '1'
409
 
 
410
 
            def get_hexdump(self):
411
 
                return False
412
 
 
413
 
            def close(self):
414
 
                pass
415
 
 
416
 
        tcs = self.server.test_case_server
417
 
        sftp_server = paramiko.SFTPServer(
418
 
            FakeChannel(), 'sftp', StubServer(tcs), StubSFTPServer,
419
 
            root=tcs._root, home=tcs._server_homedir)
420
 
        self.sftp_server = sftp_server
421
 
        sys_stderr = sys.stderr  # Used in error reporting during shutdown
422
 
        try:
423
 
            sftp_server.start_subsystem(
424
 
                'sftp', None, ssh.SocketAsChannelAdapter(self.request))
425
 
        except socket.error as e:
426
 
            if (len(e.args) > 0) and (e.args[0] == errno.EPIPE):
427
 
                # it's okay for the client to disconnect abruptly
428
 
                # (bug in paramiko 1.6: it should absorb this exception)
429
 
                pass
430
 
            else:
431
 
                raise
432
 
        except Exception as e:
433
 
            # This typically seems to happen during interpreter shutdown, so
434
 
            # most of the useful ways to report this error won't work.
435
 
            # Writing the exception type, and then the text of the exception,
436
 
            # seems to be the best we can do.
437
 
            # FIXME: All interpreter shutdown errors should have been related
438
 
            # to daemon threads, cleanup needed -- vila 20100623
439
 
            sys_stderr.write('\nEXCEPTION %r: ' % (e.__class__,))
440
 
            sys_stderr.write('%s\n\n' % (e,))
441
 
 
442
 
    def finish(self):
443
 
        self.sftp_server.finish_subsystem()
444
 
 
445
 
 
446
 
class TestingSFTPServer(test_server.TestingThreadingTCPServer):
447
 
 
448
 
    def __init__(self, server_address, request_handler_class, test_case_server):
449
 
        test_server.TestingThreadingTCPServer.__init__(
450
 
            self, server_address, request_handler_class)
451
 
        self.test_case_server = test_case_server
452
 
 
453
 
 
454
 
class SFTPServer(test_server.TestingTCPServerInAThread):
 
389
class SFTPServer(test_server.TestServer):
455
390
    """Common code for SFTP server facilities."""
456
391
 
457
392
    def __init__(self, server_interface=StubServer):
458
 
        self.host = '127.0.0.1'
459
 
        self.port = 0
460
 
        super(SFTPServer, self).__init__((self.host, self.port),
461
 
                                         TestingSFTPServer,
462
 
                                         TestingSFTPConnectionHandler)
463
393
        self._original_vendor = None
 
394
        self._homedir = None
 
395
        self._server_homedir = None
 
396
        self._listener = None
 
397
        self._root = None
464
398
        self._vendor = ssh.ParamikoVendor()
465
399
        self._server_interface = server_interface
466
 
        self._host_key = None
 
400
        # sftp server logs
467
401
        self.logs = []
468
402
        self.add_latency = 0
469
 
        self._homedir = None
470
 
        self._server_homedir = None
471
 
        self._root = None
472
403
 
473
404
    def _get_sftp_url(self, path):
474
405
        """Calculate an sftp url to this server for path."""
475
 
        return "sftp://foo:bar@%s:%s/%s" % (self.host, self.port, path)
 
406
        return 'sftp://foo:bar@%s:%d/%s' % (self._listener.host,
 
407
                                            self._listener.port, path)
476
408
 
477
409
    def log(self, message):
478
410
        """StubServer uses this to log when a new server is created."""
479
411
        self.logs.append(message)
480
412
 
481
 
    def create_server(self):
482
 
        server = self.server_class((self.host, self.port),
483
 
                                   self.request_handler_class,
484
 
                                   self)
485
 
        return server
486
 
 
487
 
    def get_host_key(self):
488
 
        if self._host_key is None:
489
 
            key_file = osutils.pathjoin(self._homedir, 'test_rsa.key')
490
 
            f = open(key_file, 'w')
491
 
            try:
492
 
                f.write(STUB_SERVER_KEY)
493
 
            finally:
494
 
                f.close()
495
 
            self._host_key = paramiko.RSAKey.from_private_key_file(key_file)
496
 
        return self._host_key
 
413
    def _run_server_entry(self, sock):
 
414
        """Entry point for all implementations of _run_server.
 
415
 
 
416
        If self.add_latency is > 0.000001 then sock is given a latency adding
 
417
        decorator.
 
418
        """
 
419
        if self.add_latency > 0.000001:
 
420
            sock = SocketDelay(sock, self.add_latency)
 
421
        return self._run_server(sock)
 
422
 
 
423
    def _run_server(self, s):
 
424
        ssh_server = paramiko.Transport(s)
 
425
        key_file = osutils.pathjoin(self._homedir, 'test_rsa.key')
 
426
        f = open(key_file, 'w')
 
427
        f.write(STUB_SERVER_KEY)
 
428
        f.close()
 
429
        host_key = paramiko.RSAKey.from_private_key_file(key_file)
 
430
        ssh_server.add_server_key(host_key)
 
431
        server = self._server_interface(self)
 
432
        ssh_server.set_subsystem_handler('sftp', paramiko.SFTPServer,
 
433
                                         StubSFTPServer, root=self._root,
 
434
                                         home=self._server_homedir)
 
435
        event = threading.Event()
 
436
        ssh_server.start_server(event, server)
 
437
        event.wait(5.0)
497
438
 
498
439
    def start_server(self, backing_server=None):
499
440
        # XXX: TODO: make sftpserver back onto backing_server rather than local
500
441
        # disk.
501
 
        if not (backing_server is None
502
 
                or isinstance(backing_server, test_server.LocalURLServer)):
 
442
        if not (backing_server is None or
 
443
                isinstance(backing_server, test_server.LocalURLServer)):
503
444
            raise AssertionError(
504
445
                'backing_server should not be %r, because this can only serve '
505
446
                'the local current working directory.' % (backing_server,))
506
447
        self._original_vendor = ssh._ssh_vendor_manager._cached_ssh_vendor
507
448
        ssh._ssh_vendor_manager._cached_ssh_vendor = self._vendor
508
 
        self._homedir = osutils.getcwd()
509
449
        if sys.platform == 'win32':
 
450
            # Win32 needs to use the UNICODE api
 
451
            self._homedir = os.getcwdu()
510
452
            # Normalize the path or it will be wrongly escaped
511
453
            self._homedir = osutils.normpath(self._homedir)
512
454
        else:
513
 
            self._homedir = self._homedir
 
455
            # But unix SFTP servers should just deal in bytestreams
 
456
            self._homedir = os.getcwd()
514
457
        if self._server_homedir is None:
515
458
            self._server_homedir = self._homedir
516
459
        self._root = '/'
517
460
        if sys.platform == 'win32':
518
461
            self._root = ''
519
 
        super(SFTPServer, self).start_server()
 
462
        self._listener = SocketListener(self._run_server_entry)
 
463
        self._listener.setDaemon(True)
 
464
        self._listener.start()
520
465
 
521
466
    def stop_server(self):
522
 
        try:
523
 
            super(SFTPServer, self).stop_server()
524
 
        finally:
525
 
            ssh._ssh_vendor_manager._cached_ssh_vendor = self._original_vendor
 
467
        self._listener.stop()
 
468
        ssh._ssh_vendor_manager._cached_ssh_vendor = self._original_vendor
526
469
 
527
470
    def get_bogus_url(self):
528
 
        """See breezy.transport.Server.get_bogus_url."""
529
 
        # this is chosen to try to prevent trouble with proxies, weird dns, etc
 
471
        """See bzrlib.transport.Server.get_bogus_url."""
 
472
        # this is chosen to try to prevent trouble with proxies, wierd dns, etc
530
473
        # we bind a random socket, so that we get a guaranteed unused port
531
474
        # we just never listen on that port
532
475
        s = socket.socket()
538
481
    """A test server for sftp transports, using absolute urls and ssh."""
539
482
 
540
483
    def get_url(self):
541
 
        """See breezy.transport.Server.get_url."""
 
484
        """See bzrlib.transport.Server.get_url."""
542
485
        homedir = self._homedir
543
486
        if sys.platform != 'win32':
544
487
            # Remove the initial '/' on all platforms but win32
552
495
    def __init__(self):
553
496
        super(SFTPServerWithoutSSH, self).__init__()
554
497
        self._vendor = ssh.LoopbackVendor()
555
 
        self.request_handler_class = TestingSFTPWithoutSSHConnectionHandler
556
 
 
557
 
    def get_host_key():
558
 
        return None
 
498
 
 
499
    def _run_server(self, sock):
 
500
        # Re-import these as locals, so that they're still accessible during
 
501
        # interpreter shutdown (when all module globals get set to None, leading
 
502
        # to confusing errors like "'NoneType' object has no attribute 'error'".
 
503
        class FakeChannel(object):
 
504
            def get_transport(self):
 
505
                return self
 
506
            def get_log_channel(self):
 
507
                return 'paramiko'
 
508
            def get_name(self):
 
509
                return '1'
 
510
            def get_hexdump(self):
 
511
                return False
 
512
            def close(self):
 
513
                pass
 
514
 
 
515
        server = paramiko.SFTPServer(
 
516
            FakeChannel(), 'sftp', StubServer(self), StubSFTPServer,
 
517
            root=self._root, home=self._server_homedir)
 
518
        try:
 
519
            server.start_subsystem(
 
520
                'sftp', None, ssh.SocketAsChannelAdapter(sock))
 
521
        except socket.error, e:
 
522
            if (len(e.args) > 0) and (e.args[0] == errno.EPIPE):
 
523
                # it's okay for the client to disconnect abruptly
 
524
                # (bug in paramiko 1.6: it should absorb this exception)
 
525
                pass
 
526
            else:
 
527
                raise
 
528
        except Exception, e:
 
529
            # This typically seems to happen during interpreter shutdown, so
 
530
            # most of the useful ways to report this error are won't work.
 
531
            # Writing the exception type, and then the text of the exception,
 
532
            # seems to be the best we can do.
 
533
            import sys
 
534
            sys.stderr.write('\nEXCEPTION %r: ' % (e.__class__,))
 
535
            sys.stderr.write('%s\n\n' % (e,))
 
536
        server.finish_subsystem()
559
537
 
560
538
 
561
539
class SFTPAbsoluteServer(SFTPServerWithoutSSH):
562
540
    """A test server for sftp transports, using absolute urls."""
563
541
 
564
542
    def get_url(self):
565
 
        """See breezy.transport.Server.get_url."""
 
543
        """See bzrlib.transport.Server.get_url."""
566
544
        homedir = self._homedir
567
545
        if sys.platform != 'win32':
568
546
            # Remove the initial '/' on all platforms but win32
574
552
    """A test server for sftp transports, using homedir relative urls."""
575
553
 
576
554
    def get_url(self):
577
 
        """See breezy.transport.Server.get_url."""
578
 
        return self._get_sftp_url("%7E/")
 
555
        """See bzrlib.transport.Server.get_url."""
 
556
        return self._get_sftp_url("~/")
579
557
 
580
558
 
581
559
class SFTPSiblingAbsoluteServer(SFTPAbsoluteServer):
584
562
    It does this by serving from a deeply-nested directory that doesn't exist.
585
563
    """
586
564
 
587
 
    def create_server(self):
588
 
        # FIXME: Can't we do that in a cleaner way ? -- vila 20100623
589
 
        server = super(SFTPSiblingAbsoluteServer, self).create_server()
590
 
        server._server_homedir = '/dev/noone/runs/tests/here'
591
 
        return server
 
565
    def start_server(self, backing_server=None):
 
566
        self._server_homedir = '/dev/noone/runs/tests/here'
 
567
        super(SFTPSiblingAbsoluteServer, self).start_server(backing_server)
 
568