~bzr-pqm/bzr/bzr.dev

5557.1.15 by John Arbash Meinel
Merge bzr.dev 5597 to resolve NEWS, aka bzr-2.3.txt
1
# Copyright (C) 2005, 2006, 2008-2011 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 socket
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
25
import SocketServer
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 time
1666.1.6 by Robert Collins
Make knit the default format.
28
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
29
from bzrlib import (
30
    osutils,
31
    trace,
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
32
    urlutils,
33
    )
34
from bzrlib.transport import (
35
    ssh,
36
    )
5017.3.34 by Vincent Ladeuil
-s bt.per_branch passing
37
from bzrlib.tests import test_server
38
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
39
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
40
class StubServer(paramiko.ServerInterface):
1666.1.6 by Robert Collins
Make knit the default format.
41
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
42
    def __init__(self, test_case_server):
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
43
        paramiko.ServerInterface.__init__(self)
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
44
        self.log = test_case_server.log
1185.49.10 by John Arbash Meinel
Use a weakref dictionary to enable re-use of a connection (for sftp).
45
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
46
    def check_auth_password(self, username, password):
47
        # all are allowed
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
48
        self.log('sftpserver - authorizing: %s' % (username,))
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
49
        return paramiko.AUTH_SUCCESSFUL
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
50
51
    def check_channel_request(self, kind, chanid):
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
52
        self.log('sftpserver - channel request: %s, %s' % (kind, chanid))
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
53
        return paramiko.OPEN_SUCCEEDED
54
55
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
56
class StubSFTPHandle(paramiko.SFTPHandle):
57
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
58
    def stat(self):
59
        try:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
60
            return paramiko.SFTPAttributes.from_stat(
61
                os.fstat(self.readfile.fileno()))
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
62
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
63
            return paramiko.SFTPServer.convert_errno(e.errno)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
64
65
    def chattr(self, attr):
66
        # python doesn't have equivalents to fchown or fchmod, so we have to
67
        # use the stored filename
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
68
        trace.mutter('Changing permissions on %s to %s', self.filename, attr)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
69
        try:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
70
            paramiko.SFTPServer.set_file_attr(self.filename, attr)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
71
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
72
            return paramiko.SFTPServer.convert_errno(e.errno)
73
74
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
75
class StubSFTPServer(paramiko.SFTPServerInterface):
1666.1.6 by Robert Collins
Make knit the default format.
76
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
77
    def __init__(self, server, root, home=None):
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
78
        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
79
        # All paths are actually relative to 'root'.
80
        # this is like implementing chroot().
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
81
        self.root = root
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
82
        if home is None:
1711.5.4 by John Arbash Meinel
Update stub_sftp based on Robey's comments.
83
            self.home = ''
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
84
        else:
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
85
            if not home.startswith(self.root):
86
                raise AssertionError(
87
                    "home must be a subdirectory of root (%s vs %s)"
88
                    % (home, root))
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
89
            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
90
        if self.home.startswith('/'):
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
91
            self.home = self.home[1:]
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
92
        server.log('sftpserver - new connection')
1524.1.1 by Robert Collins
Test sftp with relative, absolute-in-homedir and absolute-not-in-homedir
93
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
94
    def _realpath(self, path):
1711.5.4 by John Arbash Meinel
Update stub_sftp based on Robey's comments.
95
        # paths returned from self.canonicalize() always start with
96
        # a path separator. So if 'root' is just '/', this would cause
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
97
        # a double slash at the beginning '//home/dir'.
1711.5.4 by John Arbash Meinel
Update stub_sftp based on Robey's comments.
98
        if self.root == '/':
99
            return self.canonicalize(path)
100
        return self.root + self.canonicalize(path)
101
102
    if sys.platform == 'win32':
103
        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
104
            # Win32 sftp paths end up looking like
1711.5.4 by John Arbash Meinel
Update stub_sftp based on Robey's comments.
105
            #     sftp://host@foo/h:/foo/bar
106
            # which means absolute paths look like:
107
            #     /h:/foo/bar
108
            # and relative paths stay the same:
109
            #     foo/bar
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
110
            # 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.
111
            # 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
112
            thispath = path.decode('utf8')
113
            if path.startswith('/'):
1711.5.4 by John Arbash Meinel
Update stub_sftp based on Robey's comments.
114
                # Abspath H:/foo/bar
115
                return os.path.normpath(thispath[1:])
116
            else:
117
                return os.path.normpath(os.path.join(self.home, thispath))
118
    else:
119
        def canonicalize(self, path):
120
            if os.path.isabs(path):
6015.39.2 by Florian Vichot
Fixed an infinite loop when creating a repo at the root of the filesystem,
121
                return osutils.normpath(path)
1711.5.4 by John Arbash Meinel
Update stub_sftp based on Robey's comments.
122
            else:
6015.39.2 by Florian Vichot
Fixed an infinite loop when creating a repo at the root of the filesystem,
123
                return osutils.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
124
1185.58.2 by John Arbash Meinel
Added mode to the appropriate transport functions, and tests to make sure they work.
125
    def chattr(self, path, attr):
126
        try:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
127
            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.
128
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
129
            return paramiko.SFTPServer.convert_errno(e.errno)
130
        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.
131
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
132
    def list_folder(self, path):
133
        path = self._realpath(path)
134
        try:
135
            out = [ ]
1685.1.72 by Wouter van Heyst
StubSFTPServer should use bytestreams rather than unicode
136
            # TODO: win32 incorrectly lists paths with non-ascii if path is not
5278.1.2 by Martin Pool
Don't say 'Linux' except when specifically talking about the kernel
137
            # unicode. However on unix the server should only deal with
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
138
            # 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
139
            if sys.platform == 'win32':
140
                flist = [f.encode('utf8') for f in os.listdir(path)]
141
            else:
142
                flist = os.listdir(path)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
143
            for fname in flist:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
144
                attr = paramiko.SFTPAttributes.from_stat(
145
                    os.stat(osutils.pathjoin(path, fname)))
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
146
                attr.filename = fname
147
                out.append(attr)
148
            return out
149
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
150
            return paramiko.SFTPServer.convert_errno(e.errno)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
151
152
    def stat(self, path):
153
        path = self._realpath(path)
154
        try:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
155
            return paramiko.SFTPAttributes.from_stat(os.stat(path))
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
156
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
157
            return paramiko.SFTPServer.convert_errno(e.errno)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
158
159
    def lstat(self, path):
160
        path = self._realpath(path)
161
        try:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
162
            return paramiko.SFTPAttributes.from_stat(os.lstat(path))
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
163
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
164
            return paramiko.SFTPServer.convert_errno(e.errno)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
165
166
    def open(self, path, flags, attr):
167
        path = self._realpath(path)
168
        try:
1963.2.4 by Robey Pointer
remove usage of hasattr
169
            flags |= getattr(os, 'O_BINARY', 0)
1540.1.9 by John Arbash Meinel
Cleanup getattr() code, since getattr(None, '', None) still works
170
            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)
171
                fd = os.open(path, flags, attr.st_mode)
172
            else:
1988.1.1 by John Arbash Meinel
Restore mode bit tests for sftp, and track down bugs
173
                # os.open() defaults to 0777 which is
174
                # an odd default mode for files
175
                fd = os.open(path, flags, 0666)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
176
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
177
            return paramiko.SFTPServer.convert_errno(e.errno)
1685.1.72 by Wouter van Heyst
StubSFTPServer should use bytestreams rather than unicode
178
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
179
        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)
180
            attr._flags &= ~attr.FLAG_PERMISSIONS
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
181
            paramiko.SFTPServer.set_file_attr(path, attr)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
182
        if flags & os.O_WRONLY:
1185.31.51 by John Arbash Meinel
Setting binary flags for sftp.
183
            fstr = 'wb'
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
184
        elif flags & os.O_RDWR:
1185.31.51 by John Arbash Meinel
Setting binary flags for sftp.
185
            fstr = 'rb+'
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
186
        else:
187
            # O_RDONLY (== 0)
1185.31.51 by John Arbash Meinel
Setting binary flags for sftp.
188
            fstr = 'rb'
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
189
        try:
190
            f = os.fdopen(fd, fstr)
1711.3.1 by John Arbash Meinel
opening a dir instead of a file raises an IOError, not OSError
191
        except (IOError, OSError), e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
192
            return paramiko.SFTPServer.convert_errno(e.errno)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
193
        fobj = StubSFTPHandle()
194
        fobj.filename = path
195
        fobj.readfile = f
196
        fobj.writefile = f
197
        return fobj
198
199
    def remove(self, path):
200
        path = self._realpath(path)
201
        try:
202
            os.remove(path)
203
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
204
            return paramiko.SFTPServer.convert_errno(e.errno)
205
        return paramiko.SFTP_OK
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
206
207
    def rename(self, oldpath, newpath):
208
        oldpath = self._realpath(oldpath)
209
        newpath = self._realpath(newpath)
210
        try:
211
            os.rename(oldpath, newpath)
212
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
213
            return paramiko.SFTPServer.convert_errno(e.errno)
214
        return paramiko.SFTP_OK
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
215
216
    def mkdir(self, path, attr):
217
        path = self._realpath(path)
218
        try:
1540.1.5 by John Arbash Meinel
Bugfix to allow using paramiko > 1.5.2
219
            # Using getattr() in case st_mode is None or 0
220
            # both evaluate to False
1540.1.9 by John Arbash Meinel
Cleanup getattr() code, since getattr(None, '', None) still works
221
            if getattr(attr, 'st_mode', None):
1185.58.11 by John Arbash Meinel
Made the StubSFTPServer use umask even for mkdir()
222
                os.mkdir(path, attr.st_mode)
223
            else:
224
                os.mkdir(path)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
225
            if attr is not None:
1185.58.11 by John Arbash Meinel
Made the StubSFTPServer use umask even for mkdir()
226
                attr._flags &= ~attr.FLAG_PERMISSIONS
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
227
                paramiko.SFTPServer.set_file_attr(path, attr)
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
228
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
229
            return paramiko.SFTPServer.convert_errno(e.errno)
230
        return paramiko.SFTP_OK
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
231
232
    def rmdir(self, path):
233
        path = self._realpath(path)
234
        try:
235
            os.rmdir(path)
236
        except OSError, e:
4797.11.1 by Vincent Ladeuil
Fix test.stub_sftp imports.
237
            return paramiko.SFTPServer.convert_errno(e.errno)
238
        return paramiko.SFTP_OK
1185.16.127 by Martin Pool
[patch] paramiko sftp tests (robey)
239
240
    # removed: chattr, symlink, readlink
241
    # (nothing in bzr's sftp transport uses those)
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
242
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
243
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
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 SocketDelay(object):
266
    """A socket decorator to make TCP appear slower.
267
268
    This changes recv, send, and sendall to add a fixed latency to each python
269
    call if a new roundtrip is detected. That is, when a recv is called and the
270
    flag new_roundtrip is set, latency is charged. Every send and send_all
271
    sets this flag.
272
273
    In addition every send, sendall and recv sleeps a bit per character send to
274
    simulate bandwidth.
275
276
    Not all methods are implemented, this is deliberate as this class is not a
277
    replacement for the builtin sockets layer. fileno is not implemented to
278
    prevent the proxy being bypassed.
279
    """
280
281
    simulated_time = 0
282
    _proxied_arguments = dict.fromkeys([
283
        "close", "getpeername", "getsockname", "getsockopt", "gettimeout",
284
        "setblocking", "setsockopt", "settimeout", "shutdown"])
285
286
    def __init__(self, sock, latency, bandwidth=1.0,
287
                 really_sleep=True):
288
        """
289
        :param bandwith: simulated bandwith (MegaBit)
290
        :param really_sleep: If set to false, the SocketDelay will just
291
        increase a counter, instead of calling time.sleep. This is useful for
292
        unittesting the SocketDelay.
293
        """
294
        self.sock = sock
295
        self.latency = latency
296
        self.really_sleep = really_sleep
297
        self.time_per_byte = 1 / (bandwidth / 8.0 * 1024 * 1024)
298
        self.new_roundtrip = False
299
300
    def sleep(self, s):
301
        if self.really_sleep:
302
            time.sleep(s)
303
        else:
304
            SocketDelay.simulated_time += s
305
306
    def __getattr__(self, attr):
307
        if attr in SocketDelay._proxied_arguments:
308
            return getattr(self.sock, attr)
309
        raise AttributeError("'SocketDelay' object has no attribute %r" %
310
                             attr)
311
312
    def dup(self):
313
        return SocketDelay(self.sock.dup(), self.latency, self.time_per_byte,
314
                           self._sleep)
315
316
    def recv(self, *args):
317
        data = self.sock.recv(*args)
318
        if data and self.new_roundtrip:
319
            self.new_roundtrip = False
320
            self.sleep(self.latency)
321
        self.sleep(len(data) * self.time_per_byte)
322
        return data
323
324
    def sendall(self, data, flags=0):
325
        if not self.new_roundtrip:
326
            self.new_roundtrip = True
327
            self.sleep(self.latency)
328
        self.sleep(len(data) * self.time_per_byte)
329
        return self.sock.sendall(data, flags)
330
331
    def send(self, data, flags=0):
332
        if not self.new_roundtrip:
333
            self.new_roundtrip = True
334
            self.sleep(self.latency)
335
        bytes_sent = self.sock.send(data, flags)
336
        self.sleep(bytes_sent * self.time_per_byte)
337
        return bytes_sent
338
339
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
340
class TestingSFTPConnectionHandler(SocketServer.BaseRequestHandler):
341
342
    def setup(self):
343
        self.wrap_for_latency()
344
        tcs = self.server.test_case_server
5397.2.10 by John Arbash Meinel
refactor a little bit, so it is clearer what is going on.
345
        ptrans = paramiko.Transport(self.request)
346
        self.paramiko_transport = ptrans
5397.2.3 by John Arbash Meinel
Do it a different way by overriding the logging chosen
347
        # Set it to a channel under 'bzr' so that we get debug info
5397.2.10 by John Arbash Meinel
refactor a little bit, so it is clearer what is going on.
348
        ptrans.set_log_channel('bzr.paramiko.transport')
349
        ptrans.add_server_key(tcs.get_host_key())
350
        ptrans.set_subsystem_handler('sftp', paramiko.SFTPServer,
351
                                     StubSFTPServer, root=tcs._root,
352
                                     home=tcs._server_homedir)
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
353
        server = tcs._server_interface(tcs)
5397.2.1 by John Arbash Meinel
Instead of time.sleep() wait for paramiko to actually finish.
354
        # This blocks until the key exchange has been done
5397.2.10 by John Arbash Meinel
refactor a little bit, so it is clearer what is going on.
355
        ptrans.start_server(None, server)
356
357
    def finish(self):
358
        # Wait for the conversation to finish, when the paramiko.Transport
359
        # thread finishes
360
        # TODO: Consider timing out after XX seconds rather than hanging.
361
        #       Also we could check paramiko_transport.active and possibly
362
        #       paramiko_transport.getException().
363
        self.paramiko_transport.join()
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
364
365
    def wrap_for_latency(self):
366
        tcs = self.server.test_case_server
367
        if tcs.add_latency:
368
            # Give the socket (which the request really is) a latency adding
369
            # decorator.
370
            self.request = SocketDelay(self.request, tcs.add_latency)
371
372
373
class TestingSFTPWithoutSSHConnectionHandler(TestingSFTPConnectionHandler):
374
375
    def setup(self):
376
        self.wrap_for_latency()
377
        # Re-import these as locals, so that they're still accessible during
378
        # interpreter shutdown (when all module globals get set to None, leading
379
        # to confusing errors like "'NoneType' object has no attribute 'error'".
380
        class FakeChannel(object):
381
            def get_transport(self):
382
                return self
383
            def get_log_channel(self):
5397.2.11 by John Arbash Meinel
Add a .finish method to the sftp version as well.
384
                return 'bzr.paramiko'
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
385
            def get_name(self):
386
                return '1'
387
            def get_hexdump(self):
388
                return False
389
            def close(self):
390
                pass
391
392
        tcs = self.server.test_case_server
5397.2.11 by John Arbash Meinel
Add a .finish method to the sftp version as well.
393
        sftp_server = paramiko.SFTPServer(
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
394
            FakeChannel(), 'sftp', StubServer(tcs), StubSFTPServer,
395
            root=tcs._root, home=tcs._server_homedir)
5397.2.11 by John Arbash Meinel
Add a .finish method to the sftp version as well.
396
        self.sftp_server = sftp_server
397
        sys_stderr = sys.stderr # Used in error reporting during shutdown
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
398
        try:
5397.2.11 by John Arbash Meinel
Add a .finish method to the sftp version as well.
399
            sftp_server.start_subsystem(
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
400
                'sftp', None, ssh.SocketAsChannelAdapter(self.request))
401
        except socket.error, e:
402
            if (len(e.args) > 0) and (e.args[0] == errno.EPIPE):
403
                # it's okay for the client to disconnect abruptly
404
                # (bug in paramiko 1.6: it should absorb this exception)
405
                pass
406
            else:
407
                raise
408
        except Exception, e:
409
            # This typically seems to happen during interpreter shutdown, so
410
            # most of the useful ways to report this error won't work.
411
            # Writing the exception type, and then the text of the exception,
412
            # seems to be the best we can do.
413
            # FIXME: All interpreter shutdown errors should have been related
414
            # to daemon threads, cleanup needed -- vila 20100623
5397.2.11 by John Arbash Meinel
Add a .finish method to the sftp version as well.
415
            sys_stderr.write('\nEXCEPTION %r: ' % (e.__class__,))
416
            sys_stderr.write('%s\n\n' % (e,))
417
418
    def finish(self):
419
        self.sftp_server.finish_subsystem()
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
420
421
422
class TestingSFTPServer(test_server.TestingThreadingTCPServer):
423
424
    def __init__(self, server_address, request_handler_class, test_case_server):
425
        test_server.TestingThreadingTCPServer.__init__(
426
            self, server_address, request_handler_class)
427
        self.test_case_server = test_case_server
428
429
430
class SFTPServer(test_server.TestingTCPServerInAThread):
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
431
    """Common code for SFTP server facilities."""
432
433
    def __init__(self, server_interface=StubServer):
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
434
        self.host = '127.0.0.1'
435
        self.port = 0
436
        super(SFTPServer, self).__init__((self.host, self.port),
437
                                         TestingSFTPServer,
438
                                         TestingSFTPConnectionHandler)
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
439
        self._original_vendor = None
440
        self._vendor = ssh.ParamikoVendor()
441
        self._server_interface = server_interface
5247.4.24 by Vincent Ladeuil
Create the ssh host key only once for a given sftp test server.
442
        self._host_key = None
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
443
        self.logs = []
444
        self.add_latency = 0
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
445
        self._homedir = None
446
        self._server_homedir = None
447
        self._root = None
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
448
449
    def _get_sftp_url(self, path):
450
        """Calculate an sftp url to this server for path."""
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
451
        return "sftp://foo:bar@%s:%s/%s" % (self.host, self.port, path)
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
452
453
    def log(self, message):
454
        """StubServer uses this to log when a new server is created."""
455
        self.logs.append(message)
456
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
457
    def create_server(self):
458
        server = self.server_class((self.host, self.port),
459
                                   self.request_handler_class,
460
                                   self)
461
        return server
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
462
5247.4.24 by Vincent Ladeuil
Create the ssh host key only once for a given sftp test server.
463
    def get_host_key(self):
464
        if self._host_key is None:
465
            key_file = osutils.pathjoin(self._homedir, 'test_rsa.key')
466
            f = open(key_file, 'w')
467
            try:
468
                f.write(STUB_SERVER_KEY)
469
            finally:
470
                f.close()
471
            self._host_key = paramiko.RSAKey.from_private_key_file(key_file)
472
        return self._host_key
473
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
474
    def start_server(self, backing_server=None):
475
        # XXX: TODO: make sftpserver back onto backing_server rather than local
476
        # disk.
477
        if not (backing_server is None or
5017.3.34 by Vincent Ladeuil
-s bt.per_branch passing
478
                isinstance(backing_server, test_server.LocalURLServer)):
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
479
            raise AssertionError(
5055.1.2 by Vincent Ladeuil
Add a FIXME about using osutils.getcwd()
480
                'backing_server should not be %r, because this can only serve '
481
                'the local current working directory.' % (backing_server,))
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
482
        self._original_vendor = ssh._ssh_vendor_manager._cached_ssh_vendor
483
        ssh._ssh_vendor_manager._cached_ssh_vendor = self._vendor
484
        if sys.platform == 'win32':
485
            # Win32 needs to use the UNICODE api
5055.1.1 by Vincent Ladeuil
Fix bug #526221 and #526353.
486
            self._homedir = os.getcwdu()
5229.1.9 by Vincent Ladeuil
Fix sftp homedir path handling on windows.
487
            # Normalize the path or it will be wrongly escaped
5229.1.7 by Vincent Ladeuil
Fix sftp paths for windows.
488
            self._homedir = osutils.normpath(self._homedir)
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
489
        else:
5278.1.2 by Martin Pool
Don't say 'Linux' except when specifically talking about the kernel
490
            # But unix SFTP servers should just deal in bytestreams
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
491
            self._homedir = os.getcwd()
492
        if self._server_homedir is None:
493
            self._server_homedir = self._homedir
494
        self._root = '/'
495
        if sys.platform == 'win32':
496
            self._root = ''
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
497
        super(SFTPServer, self).start_server()
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
498
499
    def stop_server(self):
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
500
        try:
501
            super(SFTPServer, self).stop_server()
502
        finally:
503
            ssh._ssh_vendor_manager._cached_ssh_vendor = self._original_vendor
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
504
505
    def get_bogus_url(self):
506
        """See bzrlib.transport.Server.get_bogus_url."""
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
507
        # this is chosen to try to prevent trouble with proxies, weird dns, etc
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
508
        # we bind a random socket, so that we get a guaranteed unused port
509
        # we just never listen on that port
510
        s = socket.socket()
511
        s.bind(('localhost', 0))
512
        return 'sftp://%s:%s/' % s.getsockname()
513
514
515
class SFTPFullAbsoluteServer(SFTPServer):
516
    """A test server for sftp transports, using absolute urls and ssh."""
517
518
    def get_url(self):
519
        """See bzrlib.transport.Server.get_url."""
520
        homedir = self._homedir
521
        if sys.platform != 'win32':
522
            # Remove the initial '/' on all platforms but win32
523
            homedir = homedir[1:]
524
        return self._get_sftp_url(urlutils.escape(homedir))
525
526
527
class SFTPServerWithoutSSH(SFTPServer):
528
    """An SFTP server that uses a simple TCP socket pair rather than SSH."""
529
530
    def __init__(self):
531
        super(SFTPServerWithoutSSH, self).__init__()
532
        self._vendor = ssh.LoopbackVendor()
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
533
        self.request_handler_class = TestingSFTPWithoutSSHConnectionHandler
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
534
5247.4.24 by Vincent Ladeuil
Create the ssh host key only once for a given sftp test server.
535
    def get_host_key():
536
        return None
537
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
538
539
class SFTPAbsoluteServer(SFTPServerWithoutSSH):
540
    """A test server for sftp transports, using absolute urls."""
541
542
    def get_url(self):
543
        """See bzrlib.transport.Server.get_url."""
544
        homedir = self._homedir
545
        if sys.platform != 'win32':
546
            # Remove the initial '/' on all platforms but win32
547
            homedir = homedir[1:]
548
        return self._get_sftp_url(urlutils.escape(homedir))
549
550
551
class SFTPHomeDirServer(SFTPServerWithoutSSH):
552
    """A test server for sftp transports, using homedir relative urls."""
553
554
    def get_url(self):
555
        """See bzrlib.transport.Server.get_url."""
5268.7.20 by Jelmer Vernooij
Fix URL for transport tests.
556
        return self._get_sftp_url("%7E/")
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
557
558
559
class SFTPSiblingAbsoluteServer(SFTPAbsoluteServer):
560
    """A test server for sftp transports where only absolute paths will work.
561
562
    It does this by serving from a deeply-nested directory that doesn't exist.
563
    """
564
5247.4.18 by Vincent Ladeuil
Replace SocketListener by TestingTCPServerInAThread and fallouts,
565
    def create_server(self):
566
        # FIXME: Can't we do that in a cleaner way ? -- vila 20100623
567
        server = super(SFTPSiblingAbsoluteServer, self).create_server()
568
        server._server_homedir = '/dev/noone/runs/tests/here'
569
        return server
4797.11.2 by Vincent Ladeuil
Stop requiring testtools for sftp use.
570