~bzr-pqm/bzr/bzr.dev

4988.10.5 by John Arbash Meinel
Merge bzr.dev 5021 to resolve NEWS
1
# Copyright (C) 2005, 2006, 2008, 2009, 2010 Robey Pointer <robey@lag.net>, Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
16
17
"""
18
A stub SFTP server for loopback SFTP testing.
19
Adapted from the one in paramiko's unit tests.
20
"""
21
22
import os
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
23
import paramiko
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
24
import select
25
import socket
1711.5.1 by John Arbash Meinel
Get most SFTP tests to pass. StubSFTPServer now talks the same path protocol that SFTPTransport talks. on win32
26
import sys
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
27
import threading
28
import time
1666.1.6 by Robert Collins
Make knit the default format.
29
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
30
from bzrlib import (
31
    osutils,
32
    trace,
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
33
    urlutils,
34
    )
35
from bzrlib.transport import (
36
    ssh,
37
    )
5017.3.34 by Vincent Ladeuil
-s bt.per_branch passing
38
from bzrlib.tests import test_server
39
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
40
41
class StubServer (paramiko.ServerInterface):
1666.1.6 by Robert Collins
Make knit the default format.
42
1185.49.10 by John Arbash Meinel
Use a weakref dictionary to enable re-use of a connection (for sftp).
43
    def __init__(self, test_case):
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
44
        paramiko.ServerInterface.__init__(self)
1185.49.10 by John Arbash Meinel
Use a weakref dictionary to enable re-use of a connection (for sftp).
45
        self._test_case = test_case
46
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
47
    def check_auth_password(self, username, password):
48
        # all are allowed
1185.49.10 by John Arbash Meinel
Use a weakref dictionary to enable re-use of a connection (for sftp).
49
        self._test_case.log('sftpserver - authorizing: %s' % (username,))
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
50
        return paramiko.AUTH_SUCCESSFUL
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
51
52
    def check_channel_request(self, kind, chanid):
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
53
        self._test_case.log(
54
            'sftpserver - channel request: %s, %s' % (kind, chanid))
55
        return paramiko.OPEN_SUCCEEDED
56
57
58
class StubSFTPHandle (paramiko.SFTPHandle):
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
59
    def stat(self):
60
        try:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
61
            return paramiko.SFTPAttributes.from_stat(
62
                os.fstat(self.readfile.fileno()))
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
63
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
64
            return paramiko.SFTPServer.convert_errno(e.errno)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
65
66
    def chattr(self, attr):
67
        # python doesn't have equivalents to fchown or fchmod, so we have to
68
        # use the stored filename
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
69
        trace.mutter('Changing permissions on %s to %s', self.filename, attr)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
70
        try:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
71
            paramiko.SFTPServer.set_file_attr(self.filename, attr)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
72
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
73
            return paramiko.SFTPServer.convert_errno(e.errno)
74
75
76
class StubSFTPServer (paramiko.SFTPServerInterface):
1666.1.6 by Robert Collins
Make knit the default format.
77
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
78
    def __init__(self, server, root, home=None):
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
79
        paramiko.SFTPServerInterface.__init__(self, server)
1711.5.1 by John Arbash Meinel
Get most SFTP tests to pass. StubSFTPServer now talks the same path protocol that SFTPTransport talks. on win32
80
        # All paths are actually relative to 'root'.
81
        # this is like implementing chroot().
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
82
        self.root = root
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
83
        if home is None:
1711.5.4 by John Arbash Meinel
Update stub_sftp based on Robey's comments.
84
            self.home = ''
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
85
        else:
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
86
            if not home.startswith(self.root):
87
                raise AssertionError(
88
                    "home must be a subdirectory of root (%s vs %s)"
89
                    % (home, root))
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
90
            self.home = home[len(self.root):]
1711.5.1 by John Arbash Meinel
Get most SFTP tests to pass. StubSFTPServer now talks the same path protocol that SFTPTransport talks. on win32
91
        if self.home.startswith('/'):
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
92
            self.home = self.home[1:]
1547.1.1 by Robey Pointer
modify the sftp unit tests to perform sftp over a direct localhost socket instead of over an actual ssh2 transport
93
        server._test_case.log('sftpserver - new connection')
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
94
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
95
    def _realpath(self, path):
1711.5.4 by John Arbash Meinel
Update stub_sftp based on Robey's comments.
96
        # paths returned from self.canonicalize() always start with
97
        # a path separator. So if 'root' is just '/', this would cause
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
98
        # a double slash at the beginning '//home/dir'.
1711.5.4 by John Arbash Meinel
Update stub_sftp based on Robey's comments.
99
        if self.root == '/':
100
            return self.canonicalize(path)
101
        return self.root + self.canonicalize(path)
102
103
    if sys.platform == 'win32':
104
        def canonicalize(self, path):
1711.5.1 by John Arbash Meinel
Get most SFTP tests to pass. StubSFTPServer now talks the same path protocol that SFTPTransport talks. on win32
105
            # Win32 sftp paths end up looking like
1711.5.4 by John Arbash Meinel
Update stub_sftp based on Robey's comments.
106
            #     sftp://host@foo/h:/foo/bar
107
            # which means absolute paths look like:
108
            #     /h:/foo/bar
109
            # and relative paths stay the same:
110
            #     foo/bar
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
111
            # win32 needs to use the Unicode APIs. so we require the
1711.5.4 by John Arbash Meinel
Update stub_sftp based on Robey's comments.
112
            # paths to be utf8 (Linux just uses bytestreams)
1711.5.1 by John Arbash Meinel
Get most SFTP tests to pass. StubSFTPServer now talks the same path protocol that SFTPTransport talks. on win32
113
            thispath = path.decode('utf8')
114
            if path.startswith('/'):
1711.5.4 by John Arbash Meinel
Update stub_sftp based on Robey's comments.
115
                # Abspath H:/foo/bar
116
                return os.path.normpath(thispath[1:])
117
            else:
118
                return os.path.normpath(os.path.join(self.home, thispath))
119
    else:
120
        def canonicalize(self, path):
121
            if os.path.isabs(path):
122
                return os.path.normpath(path)
123
            else:
124
                return os.path.normpath('/' + os.path.join(self.home, path))
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
125
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
126
    def chattr(self, path, attr):
127
        try:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
128
            paramiko.SFTPServer.set_file_attr(path, attr)
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
129
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
130
            return paramiko.SFTPServer.convert_errno(e.errno)
131
        return paramiko.SFTP_OK
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
132
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
133
    def list_folder(self, path):
134
        path = self._realpath(path)
135
        try:
136
            out = [ ]
1685.1.72 by Wouter van Heyst
StubSFTPServer should use bytestreams rather than unicode
137
            # TODO: win32 incorrectly lists paths with non-ascii if path is not
138
            # unicode. However on Linux the server should only deal with
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
139
            # bytestreams and posix.listdir does the right thing
1711.5.1 by John Arbash Meinel
Get most SFTP tests to pass. StubSFTPServer now talks the same path protocol that SFTPTransport talks. on win32
140
            if sys.platform == 'win32':
141
                flist = [f.encode('utf8') for f in os.listdir(path)]
142
            else:
143
                flist = os.listdir(path)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
144
            for fname in flist:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
145
                attr = paramiko.SFTPAttributes.from_stat(
146
                    os.stat(osutils.pathjoin(path, fname)))
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
147
                attr.filename = fname
148
                out.append(attr)
149
            return out
150
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
151
            return paramiko.SFTPServer.convert_errno(e.errno)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
152
153
    def stat(self, path):
154
        path = self._realpath(path)
155
        try:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
156
            return paramiko.SFTPAttributes.from_stat(os.stat(path))
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
157
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
158
            return paramiko.SFTPServer.convert_errno(e.errno)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
159
160
    def lstat(self, path):
161
        path = self._realpath(path)
162
        try:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
163
            return paramiko.SFTPAttributes.from_stat(os.lstat(path))
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
164
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
165
            return paramiko.SFTPServer.convert_errno(e.errno)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
166
167
    def open(self, path, flags, attr):
168
        path = self._realpath(path)
169
        try:
1963.2.4 by Robey Pointer
remove usage of hasattr
170
            flags |= getattr(os, 'O_BINARY', 0)
1540.1.9 by John Arbash Meinel
Cleanup getattr() code, since getattr(None, '', None) still works
171
            if getattr(attr, 'st_mode', None):
1185.58.10 by John Arbash Meinel
[patch] Robey Pointer to fix sftp server using umask for files (failing tests for directories)
172
                fd = os.open(path, flags, attr.st_mode)
173
            else:
1988.1.1 by John Arbash Meinel
Restore mode bit tests for sftp, and track down bugs
174
                # os.open() defaults to 0777 which is
175
                # an odd default mode for files
176
                fd = os.open(path, flags, 0666)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
177
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
178
            return paramiko.SFTPServer.convert_errno(e.errno)
1685.1.72 by Wouter van Heyst
StubSFTPServer should use bytestreams rather than unicode
179
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
180
        if (flags & os.O_CREAT) and (attr is not None):
1185.58.10 by John Arbash Meinel
[patch] Robey Pointer to fix sftp server using umask for files (failing tests for directories)
181
            attr._flags &= ~attr.FLAG_PERMISSIONS
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
182
            paramiko.SFTPServer.set_file_attr(path, attr)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
183
        if flags & os.O_WRONLY:
1185.31.51 by John Arbash Meinel
Setting binary flags for sftp.
184
            fstr = 'wb'
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
185
        elif flags & os.O_RDWR:
1185.31.51 by John Arbash Meinel
Setting binary flags for sftp.
186
            fstr = 'rb+'
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
187
        else:
188
            # O_RDONLY (== 0)
1185.31.51 by John Arbash Meinel
Setting binary flags for sftp.
189
            fstr = 'rb'
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
190
        try:
191
            f = os.fdopen(fd, fstr)
1711.3.1 by John Arbash Meinel
opening a dir instead of a file raises an IOError, not OSError
192
        except (IOError, OSError), e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
193
            return paramiko.SFTPServer.convert_errno(e.errno)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
194
        fobj = StubSFTPHandle()
195
        fobj.filename = path
196
        fobj.readfile = f
197
        fobj.writefile = f
198
        return fobj
199
200
    def remove(self, path):
201
        path = self._realpath(path)
202
        try:
203
            os.remove(path)
204
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
205
            return paramiko.SFTPServer.convert_errno(e.errno)
206
        return paramiko.SFTP_OK
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
207
208
    def rename(self, oldpath, newpath):
209
        oldpath = self._realpath(oldpath)
210
        newpath = self._realpath(newpath)
211
        try:
212
            os.rename(oldpath, newpath)
213
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
214
            return paramiko.SFTPServer.convert_errno(e.errno)
215
        return paramiko.SFTP_OK
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
216
217
    def mkdir(self, path, attr):
218
        path = self._realpath(path)
219
        try:
1540.1.5 by John Arbash Meinel
Bugfix to allow using paramiko > 1.5.2
220
            # Using getattr() in case st_mode is None or 0
221
            # both evaluate to False
1540.1.9 by John Arbash Meinel
Cleanup getattr() code, since getattr(None, '', None) still works
222
            if getattr(attr, 'st_mode', None):
1185.58.11 by John Arbash Meinel
Made the StubSFTPServer use umask even for mkdir()
223
                os.mkdir(path, attr.st_mode)
224
            else:
225
                os.mkdir(path)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
226
            if attr is not None:
1185.58.11 by John Arbash Meinel
Made the StubSFTPServer use umask even for mkdir()
227
                attr._flags &= ~attr.FLAG_PERMISSIONS
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
228
                paramiko.SFTPServer.set_file_attr(path, attr)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
229
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
230
            return paramiko.SFTPServer.convert_errno(e.errno)
231
        return paramiko.SFTP_OK
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
232
233
    def rmdir(self, path):
234
        path = self._realpath(path)
235
        try:
236
            os.rmdir(path)
237
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
238
            return paramiko.SFTPServer.convert_errno(e.errno)
239
        return paramiko.SFTP_OK
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
240
241
    # removed: chattr, symlink, readlink
242
    # (nothing in bzr's sftp transport uses those)
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
243
244
# ------------- server test implementation --------------
245
246
STUB_SERVER_KEY = """
247
-----BEGIN RSA PRIVATE KEY-----
248
MIICWgIBAAKBgQDTj1bqB4WmayWNPB+8jVSYpZYk80Ujvj680pOTh2bORBjbIAyz
249
oWGW+GUjzKxTiiPvVmxFgx5wdsFvF03v34lEVVhMpouqPAYQ15N37K/ir5XY+9m/
250
d8ufMCkjeXsQkKqFbAlQcnWMCRnOoPHS3I4vi6hmnDDeeYTSRvfLbW0fhwIBIwKB
251
gBIiOqZYaoqbeD9OS9z2K9KR2atlTxGxOJPXiP4ESqP3NVScWNwyZ3NXHpyrJLa0
252
EbVtzsQhLn6rF+TzXnOlcipFvjsem3iYzCpuChfGQ6SovTcOjHV9z+hnpXvQ/fon
253
soVRZY65wKnF7IAoUwTmJS9opqgrN6kRgCd3DASAMd1bAkEA96SBVWFt/fJBNJ9H
254
tYnBKZGw0VeHOYmVYbvMSstssn8un+pQpUm9vlG/bp7Oxd/m+b9KWEh2xPfv6zqU
255
avNwHwJBANqzGZa/EpzF4J8pGti7oIAPUIDGMtfIcmqNXVMckrmzQ2vTfqtkEZsA
256
4rE1IERRyiJQx6EJsz21wJmGV9WJQ5kCQQDwkS0uXqVdFzgHO6S++tjmjYcxwr3g
257
H0CoFYSgbddOT6miqRskOQF3DZVkJT3kyuBgU2zKygz52ukQZMqxCb1fAkASvuTv
258
qfpH87Qq5kQhNKdbbwbmd2NxlNabazPijWuphGTdW0VfJdWfklyS2Kr+iqrs/5wV
259
HhathJt636Eg7oIjAkA8ht3MQ+XSl9yIJIS8gVpbPxSw5OMfw0PjVE7tBdQruiSc
260
nvuQES5C9BMHjF39LZiGH1iLQy7FgdHyoP+eodI7
261
-----END RSA PRIVATE KEY-----
262
"""
263
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
        while True:
287
            readable, writable_unused, exception_unused = \
288
                select.select([self._socket], [], [], 0.1)
289
            if self._stop_event.isSet():
290
                return
291
            if len(readable) == 0:
292
                continue
293
            try:
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())
5055.1.1 by Vincent Ladeuil
Fix bug #526221 and #526353.
300
                trace.warning('Socket error during accept() '
301
                              'within unit test server thread: %r' % x)
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
302
            except Exception, x:
303
                # probably a failed test; unit test thread will log the
304
                # failure/error
305
                sys.excepthook(*sys.exc_info())
5055.1.1 by Vincent Ladeuil
Fix bug #526221 and #526353.
306
                trace.warning(
307
                    'Exception from within unit test server thread: %r' % x)
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
308
309
310
class SocketDelay(object):
311
    """A socket decorator to make TCP appear slower.
312
313
    This changes recv, send, and sendall to add a fixed latency to each python
314
    call if a new roundtrip is detected. That is, when a recv is called and the
315
    flag new_roundtrip is set, latency is charged. Every send and send_all
316
    sets this flag.
317
318
    In addition every send, sendall and recv sleeps a bit per character send to
319
    simulate bandwidth.
320
321
    Not all methods are implemented, this is deliberate as this class is not a
322
    replacement for the builtin sockets layer. fileno is not implemented to
323
    prevent the proxy being bypassed.
324
    """
325
326
    simulated_time = 0
327
    _proxied_arguments = dict.fromkeys([
328
        "close", "getpeername", "getsockname", "getsockopt", "gettimeout",
329
        "setblocking", "setsockopt", "settimeout", "shutdown"])
330
331
    def __init__(self, sock, latency, bandwidth=1.0,
332
                 really_sleep=True):
333
        """
334
        :param bandwith: simulated bandwith (MegaBit)
335
        :param really_sleep: If set to false, the SocketDelay will just
336
        increase a counter, instead of calling time.sleep. This is useful for
337
        unittesting the SocketDelay.
338
        """
339
        self.sock = sock
340
        self.latency = latency
341
        self.really_sleep = really_sleep
342
        self.time_per_byte = 1 / (bandwidth / 8.0 * 1024 * 1024)
343
        self.new_roundtrip = False
344
345
    def sleep(self, s):
346
        if self.really_sleep:
347
            time.sleep(s)
348
        else:
349
            SocketDelay.simulated_time += s
350
351
    def __getattr__(self, attr):
352
        if attr in SocketDelay._proxied_arguments:
353
            return getattr(self.sock, attr)
354
        raise AttributeError("'SocketDelay' object has no attribute %r" %
355
                             attr)
356
357
    def dup(self):
358
        return SocketDelay(self.sock.dup(), self.latency, self.time_per_byte,
359
                           self._sleep)
360
361
    def recv(self, *args):
362
        data = self.sock.recv(*args)
363
        if data and self.new_roundtrip:
364
            self.new_roundtrip = False
365
            self.sleep(self.latency)
366
        self.sleep(len(data) * self.time_per_byte)
367
        return data
368
369
    def sendall(self, data, flags=0):
370
        if not self.new_roundtrip:
371
            self.new_roundtrip = True
372
            self.sleep(self.latency)
373
        self.sleep(len(data) * self.time_per_byte)
374
        return self.sock.sendall(data, flags)
375
376
    def send(self, data, flags=0):
377
        if not self.new_roundtrip:
378
            self.new_roundtrip = True
379
            self.sleep(self.latency)
380
        bytes_sent = self.sock.send(data, flags)
381
        self.sleep(bytes_sent * self.time_per_byte)
382
        return bytes_sent
383
384
5017.3.34 by Vincent Ladeuil
-s bt.per_branch passing
385
class SFTPServer(test_server.TestServer):
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
386
    """Common code for SFTP server facilities."""
387
388
    def __init__(self, server_interface=StubServer):
389
        self._original_vendor = None
390
        self._homedir = None
391
        self._server_homedir = None
392
        self._listener = None
393
        self._root = None
394
        self._vendor = ssh.ParamikoVendor()
395
        self._server_interface = server_interface
396
        # sftp server logs
397
        self.logs = []
398
        self.add_latency = 0
399
400
    def _get_sftp_url(self, path):
401
        """Calculate an sftp url to this server for path."""
402
        return 'sftp://foo:bar@%s:%d/%s' % (self._listener.host,
403
                                            self._listener.port, path)
404
405
    def log(self, message):
406
        """StubServer uses this to log when a new server is created."""
407
        self.logs.append(message)
408
409
    def _run_server_entry(self, sock):
410
        """Entry point for all implementations of _run_server.
411
412
        If self.add_latency is > 0.000001 then sock is given a latency adding
413
        decorator.
414
        """
415
        if self.add_latency > 0.000001:
416
            sock = SocketDelay(sock, self.add_latency)
417
        return self._run_server(sock)
418
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)
424
        f.close()
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)
433
        event.wait(5.0)
434
435
    def start_server(self, backing_server=None):
436
        # XXX: TODO: make sftpserver back onto backing_server rather than local
437
        # disk.
438
        if not (backing_server is None or
5017.3.34 by Vincent Ladeuil
-s bt.per_branch passing
439
                isinstance(backing_server, test_server.LocalURLServer)):
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
440
            raise AssertionError(
5055.1.2 by Vincent Ladeuil
Add a FIXME about using osutils.getcwd()
441
                'backing_server should not be %r, because this can only serve '
442
                'the local current working directory.' % (backing_server,))
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
443
        self._original_vendor = ssh._ssh_vendor_manager._cached_ssh_vendor
444
        ssh._ssh_vendor_manager._cached_ssh_vendor = self._vendor
5055.1.2 by Vincent Ladeuil
Add a FIXME about using osutils.getcwd()
445
        # FIXME: the following block should certainly just be self._homedir =
446
        # osutils.getcwd() but that fails badly on Unix -- vila 20100224
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
447
        if sys.platform == 'win32':
448
            # Win32 needs to use the UNICODE api
5055.1.1 by Vincent Ladeuil
Fix bug #526221 and #526353.
449
            self._homedir = os.getcwdu()
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
450
        else:
451
            # But Linux SFTP servers should just deal in bytestreams
452
            self._homedir = os.getcwd()
453
        if self._server_homedir is None:
454
            self._server_homedir = self._homedir
455
        self._root = '/'
456
        if sys.platform == 'win32':
457
            self._root = ''
458
        self._listener = SocketListener(self._run_server_entry)
459
        self._listener.setDaemon(True)
460
        self._listener.start()
461
462
    def stop_server(self):
463
        self._listener.stop()
464
        ssh._ssh_vendor_manager._cached_ssh_vendor = self._original_vendor
465
466
    def get_bogus_url(self):
467
        """See bzrlib.transport.Server.get_bogus_url."""
468
        # this is chosen to try to prevent trouble with proxies, wierd dns, etc
469
        # we bind a random socket, so that we get a guaranteed unused port
470
        # we just never listen on that port
471
        s = socket.socket()
472
        s.bind(('localhost', 0))
473
        return 'sftp://%s:%s/' % s.getsockname()
474
475
476
class SFTPFullAbsoluteServer(SFTPServer):
477
    """A test server for sftp transports, using absolute urls and ssh."""
478
479
    def get_url(self):
480
        """See bzrlib.transport.Server.get_url."""
481
        homedir = self._homedir
482
        if sys.platform != 'win32':
483
            # Remove the initial '/' on all platforms but win32
484
            homedir = homedir[1:]
485
        return self._get_sftp_url(urlutils.escape(homedir))
486
487
488
class SFTPServerWithoutSSH(SFTPServer):
489
    """An SFTP server that uses a simple TCP socket pair rather than SSH."""
490
491
    def __init__(self):
492
        super(SFTPServerWithoutSSH, self).__init__()
493
        self._vendor = ssh.LoopbackVendor()
494
495
    def _run_server(self, sock):
496
        # Re-import these as locals, so that they're still accessible during
497
        # interpreter shutdown (when all module globals get set to None, leading
498
        # to confusing errors like "'NoneType' object has no attribute 'error'".
499
        class FakeChannel(object):
500
            def get_transport(self):
501
                return self
502
            def get_log_channel(self):
503
                return 'paramiko'
504
            def get_name(self):
505
                return '1'
506
            def get_hexdump(self):
507
                return False
508
            def close(self):
509
                pass
510
511
        server = paramiko.SFTPServer(
512
            FakeChannel(), 'sftp', StubServer(self), StubSFTPServer,
513
            root=self._root, home=self._server_homedir)
514
        try:
515
            server.start_subsystem(
516
                'sftp', None, ssh.SocketAsChannelAdapter(sock))
517
        except socket.error, e:
518
            if (len(e.args) > 0) and (e.args[0] == errno.EPIPE):
519
                # it's okay for the client to disconnect abruptly
520
                # (bug in paramiko 1.6: it should absorb this exception)
521
                pass
522
            else:
523
                raise
524
        except Exception, e:
525
            # This typically seems to happen during interpreter shutdown, so
526
            # most of the useful ways to report this error are won't work.
527
            # Writing the exception type, and then the text of the exception,
528
            # seems to be the best we can do.
529
            import sys
530
            sys.stderr.write('\nEXCEPTION %r: ' % (e.__class__,))
531
            sys.stderr.write('%s\n\n' % (e,))
532
        server.finish_subsystem()
533
534
535
class SFTPAbsoluteServer(SFTPServerWithoutSSH):
536
    """A test server for sftp transports, using absolute urls."""
537
538
    def get_url(self):
539
        """See bzrlib.transport.Server.get_url."""
540
        homedir = self._homedir
541
        if sys.platform != 'win32':
542
            # Remove the initial '/' on all platforms but win32
543
            homedir = homedir[1:]
544
        return self._get_sftp_url(urlutils.escape(homedir))
545
546
547
class SFTPHomeDirServer(SFTPServerWithoutSSH):
548
    """A test server for sftp transports, using homedir relative urls."""
549
550
    def get_url(self):
551
        """See bzrlib.transport.Server.get_url."""
552
        return self._get_sftp_url("~/")
553
554
555
class SFTPSiblingAbsoluteServer(SFTPAbsoluteServer):
556
    """A test server for sftp transports where only absolute paths will work.
557
558
    It does this by serving from a deeply-nested directory that doesn't exist.
559
    """
560
561
    def start_server(self, backing_server=None):
562
        self._server_homedir = '/dev/noone/runs/tests/here'
563
        super(SFTPSiblingAbsoluteServer, self).start_server(backing_server)
564