~bzr-pqm/bzr/bzr.dev

1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1
# Copyright (C) 2006 Canonical Ltd
2
#
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.
7
#
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.
12
#
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
17
"""RemoteTransport client for the smart-server.
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
18
19
This module shouldn't be accessed directly.  The classes defined here should be
2018.5.21 by Andrew Bennetts
Move bzrlib.transport.smart to bzrlib.smart
20
imported from bzrlib.smart.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
21
"""
22
2018.5.147 by Andrew Bennetts
Merge rename-SmartTransport branch.
23
__all__ = ['RemoteTransport', 'RemoteTCPTransport', 'RemoteSSHTransport']
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
24
25
from cStringIO import StringIO
26
import urllib
27
import urlparse
28
1910.19.13 by Andrew Bennetts
Address various review comments.
29
from bzrlib import (
30
    errors,
31
    transport,
32
    urlutils,
33
    )
2018.5.137 by Andrew Bennetts
Remove more duplicate code from bzrlib/transport/remote.py that has been moved elsewhere.
34
from bzrlib.smart import client, medium, protocol
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
35
36
# must do this otherwise urllib can't parse the urls properly :(
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
37
for scheme in ['ssh', 'bzr', 'bzr+loopback', 'bzr+ssh', 'bzr+http']:
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
38
    transport.register_urlparse_netloc_protocol(scheme)
39
del scheme
40
41
2298.4.3 by Andrew Bennetts
Use a BZR_DEFAULT_PORT constant instead of bare 4155.
42
# Port 4155 is the default port for bzr://, registered with IANA.
2443.1.2 by Robert Collins
``bzr serve`` now listens on interface 0.0.0.0 by default, making it serve
43
BZR_DEFAULT_INTERFACE = '0.0.0.0'
2298.4.3 by Andrew Bennetts
Use a BZR_DEFAULT_PORT constant instead of bare 4155.
44
BZR_DEFAULT_PORT = 4155
45
46
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
47
class _SmartStat(object):
1752.2.48 by Andrew Bennetts
Support stat
48
49
    def __init__(self, size, mode):
50
        self.st_size = size
51
        self.st_mode = mode
52
53
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
54
class RemoteTransport(transport.Transport):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
55
    """Connection to a smart server.
56
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
57
    The connection holds references to the medium that can be used to send
58
    requests to the server.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
59
60
    The connection has a notion of the current directory to which it's
61
    connected; this is incorporated in filenames passed to the server.
62
    
63
    This supports some higher-level RPC operations and can also be treated 
64
    like a Transport to do file-like operations.
65
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
66
    The connection can be made over a tcp socket, an ssh pipe or a series of
67
    http requests.  There are concrete subclasses for each type:
2018.5.147 by Andrew Bennetts
Merge rename-SmartTransport branch.
68
    RemoteTCPTransport, etc.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
69
    """
70
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
71
    # IMPORTANT FOR IMPLEMENTORS: RemoteTransport MUST NOT be given encoding
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
72
    # responsibilities: Put those on SmartClient or similar. This is vital for
73
    # the ability to support multiple versions of the smart protocol over time:
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
74
    # RemoteTransport is an adapter from the Transport object model to the 
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
75
    # SmartClient model, not an encoder.
76
2466.2.2 by Andrew Bennetts
Add tests for RemoteTransport.is_readonly in the style of the other remote object tests.
77
    def __init__(self, url, clone_from=None, medium=None, _client=None):
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
78
        """Constructor.
79
2466.2.4 by Andrew Bennetts
Tweaks asked for by Robert.
80
        :param clone_from: Another RemoteTransport instance that this one is
81
            being cloned from.  Attributes such as credentials and the medium
82
            will be reused.
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
83
        :param medium: The medium to use for this RemoteTransport. This must be
84
            supplied if clone_from is None.
2466.2.4 by Andrew Bennetts
Tweaks asked for by Robert.
85
        :param _client: Override the _SmartClient used by this transport.  This
86
            should only be used for testing purposes; normally this is
87
            determined from the medium.
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
88
        """
1910.19.6 by Andrew Bennetts
Merge increased transport root tests and fix the smart transport accordingly.
89
        ### Technically super() here is faulty because Transport's __init__
90
        ### fails to take 2 parameters, and if super were to choose a silly
91
        ### initialisation order things would blow up. 
1910.19.14 by Robert Collins
Fix up all tests to pass, remove a couple more deprecated function calls, and break the dependency on sftp for the smart transport.
92
        if not url.endswith('/'):
93
            url += '/'
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
94
        super(RemoteTransport, self).__init__(url)
1910.19.14 by Robert Collins
Fix up all tests to pass, remove a couple more deprecated function calls, and break the dependency on sftp for the smart transport.
95
        self._scheme, self._username, self._password, self._host, self._port, self._path = \
96
                transport.split_url(url)
1910.19.6 by Andrew Bennetts
Merge increased transport root tests and fix the smart transport accordingly.
97
        if clone_from is None:
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
98
            self._medium = medium
1910.19.6 by Andrew Bennetts
Merge increased transport root tests and fix the smart transport accordingly.
99
        else:
100
            # credentials may be stripped from the base in some circumstances
101
            # as yet to be clearly defined or documented, so copy them.
102
            self._username = clone_from._username
103
            # reuse same connection
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
104
            self._medium = clone_from._medium
105
        assert self._medium is not None
2466.2.2 by Andrew Bennetts
Add tests for RemoteTransport.is_readonly in the style of the other remote object tests.
106
        if _client is None:
107
            self._client = client._SmartClient(self._medium)
108
        else:
109
            self._client = _client
1752.2.73 by Andrew Bennetts
Define (and register as bzr+ssh://) SmartSSHTransport, factor out an SSHSubprocess from SFTPSubprocess, and make SmartTransport connect lazily rather than in the constructor.
110
1910.19.14 by Robert Collins
Fix up all tests to pass, remove a couple more deprecated function calls, and break the dependency on sftp for the smart transport.
111
    def abspath(self, relpath):
112
        """Return the full url to the given relative path.
113
        
114
        @param relpath: the relative path or path components
115
        @type relpath: str or list
116
        """
117
        return self._unparse_url(self._remote_path(relpath))
118
    
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
119
    def clone(self, relative_url):
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
120
        """Make a new RemoteTransport related to me, sharing the same connection.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
121
122
        This essentially opens a handle on a different remote directory.
123
        """
1910.19.6 by Andrew Bennetts
Merge increased transport root tests and fix the smart transport accordingly.
124
        if relative_url is None:
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
125
            return RemoteTransport(self.base, self)
1910.19.6 by Andrew Bennetts
Merge increased transport root tests and fix the smart transport accordingly.
126
        else:
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
127
            return RemoteTransport(self.abspath(relative_url), self)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
128
129
    def is_readonly(self):
130
        """Smart server transport can do read/write file operations."""
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
131
        resp = self._call2('Transport.is_readonly')
132
        if resp == ('yes', ):
133
            return True
134
        elif resp == ('no', ):
135
            return False
2471.2.1 by Andrew Bennetts
Fix trivial incompatibility with bzr 0.11 servers, which give a slightly different error to bzr 0.15 servers.
136
        elif (resp == ('error', "Generic bzr smart protocol error: "
137
                                "bad request 'Transport.is_readonly'") or
138
              resp == ('error', "Generic bzr smart protocol error: "
139
                                "bad request u'Transport.is_readonly'")):
2466.2.4 by Andrew Bennetts
Tweaks asked for by Robert.
140
            # XXX: nasty hack: servers before 0.16 don't have a
141
            # 'Transport.is_readonly' verb, so we do what clients before 0.16
142
            # did: assume False.
2466.2.1 by Andrew Bennetts
Fix incompatibility with < 0.16 smart servers.
143
            return False
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
144
        else:
145
            self._translate_error(resp)
2555.1.1 by Martin Pool
Remove use of 'assert False' to raise an exception unconditionally
146
        raise errors.UnexpectedSmartServerResponse(resp)
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
147
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
148
    def get_smart_client(self):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
149
        return self._medium
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
150
151
    def get_smart_medium(self):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
152
        return self._medium
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
153
                                                   
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
154
    def _unparse_url(self, path):
155
        """Return URL for a path.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
156
157
        :see: SFTPUrlHandling._unparse_url
158
        """
159
        # TODO: Eventually it should be possible to unify this with
160
        # SFTPUrlHandling._unparse_url?
161
        if path == '':
162
            path = '/'
163
        path = urllib.quote(path)
164
        netloc = urllib.quote(self._host)
165
        if self._username is not None:
166
            netloc = '%s@%s' % (urllib.quote(self._username), netloc)
167
        if self._port is not None:
168
            netloc = '%s:%d' % (netloc, self._port)
169
        return urlparse.urlunparse((self._scheme, netloc, path, '', '', ''))
170
171
    def _remote_path(self, relpath):
1910.19.14 by Robert Collins
Fix up all tests to pass, remove a couple more deprecated function calls, and break the dependency on sftp for the smart transport.
172
        """Returns the Unicode version of the absolute path for relpath."""
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
173
        return self._combine_paths(self._path, relpath)
174
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
175
    def _call(self, method, *args):
176
        resp = self._call2(method, *args)
177
        self._translate_error(resp)
178
179
    def _call2(self, method, *args):
180
        """Call a method on the remote server."""
2466.2.2 by Andrew Bennetts
Add tests for RemoteTransport.is_readonly in the style of the other remote object tests.
181
        return self._client.call(method, *args)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
182
183
    def _call_with_body_bytes(self, method, args, body):
184
        """Call a method on the remote server with body bytes."""
2466.2.2 by Andrew Bennetts
Add tests for RemoteTransport.is_readonly in the style of the other remote object tests.
185
        return self._client.call_with_body_bytes(method, args, body)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
186
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
187
    def has(self, relpath):
188
        """Indicate whether a remote file of the given name exists or not.
189
190
        :see: Transport.has()
191
        """
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
192
        resp = self._call2('has', self._remote_path(relpath))
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
193
        if resp == ('yes', ):
194
            return True
195
        elif resp == ('no', ):
196
            return False
197
        else:
198
            self._translate_error(resp)
199
1752.2.2 by Martin Pool
SSH get method; more development of framework.
200
    def get(self, relpath):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
201
        """Return file-like object reading the contents of a remote file.
202
        
1910.19.13 by Andrew Bennetts
Address various review comments.
203
        :see: Transport.get_bytes()/get_file()
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
204
        """
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
205
        return StringIO(self.get_bytes(relpath))
206
207
    def get_bytes(self, relpath):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
208
        remote = self._remote_path(relpath)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
209
        request = self._medium.get_request()
210
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
211
        smart_protocol.call('get', remote)
212
        resp = smart_protocol.read_response_tuple(True)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
213
        if resp != ('ok', ):
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
214
            smart_protocol.cancel_read_body()
1752.2.81 by Andrew Bennetts
Merge cleaned up underlying dependencies for remote bzrdir.
215
            self._translate_error(resp, relpath)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
216
        return smart_protocol.read_body_bytes()
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
217
1910.19.13 by Andrew Bennetts
Address various review comments.
218
    def _serialise_optional_mode(self, mode):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
219
        if mode is None:
220
            return ''
221
        else:
222
            return '%d' % mode
223
224
    def mkdir(self, relpath, mode=None):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
225
        resp = self._call2('mkdir', self._remote_path(relpath),
226
            self._serialise_optional_mode(mode))
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
227
        self._translate_error(resp)
228
1910.19.13 by Andrew Bennetts
Address various review comments.
229
    def put_bytes(self, relpath, upload_contents, mode=None):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
230
        # FIXME: upload_file is probably not safe for non-ascii characters -
231
        # should probably just pass all parameters as length-delimited
232
        # strings?
2018.5.131 by Andrew Bennetts
Be strict about unicode passed to transport.put_{bytes,file} and SmartClient.call_with_body_bytes, fixing part of TestLockableFiles_RemoteLockDir.test_read_write.
233
        if type(upload_contents) is unicode:
234
            # Although not strictly correct, we raise UnicodeEncodeError to be
235
            # compatible with other transports.
236
            raise UnicodeEncodeError(
237
                'undefined', upload_contents, 0, 1,
238
                'put_bytes must be given bytes, not unicode.')
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
239
        resp = self._call_with_body_bytes('put',
1910.19.13 by Andrew Bennetts
Address various review comments.
240
            (self._remote_path(relpath), self._serialise_optional_mode(mode)),
241
            upload_contents)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
242
        self._translate_error(resp)
243
2017.2.1 by John Arbash Meinel
Implement put_bytes_non_atomic for the smart transport
244
    def put_bytes_non_atomic(self, relpath, bytes, mode=None,
245
                             create_parent_dir=False,
246
                             dir_mode=None):
1910.19.24 by Robert Collins
Touchup method order.
247
        """See Transport.put_bytes_non_atomic."""
248
        # FIXME: no encoding in the transport!
2017.2.1 by John Arbash Meinel
Implement put_bytes_non_atomic for the smart transport
249
        create_parent_str = 'F'
250
        if create_parent_dir:
251
            create_parent_str = 'T'
252
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
253
        resp = self._call_with_body_bytes(
2017.2.1 by John Arbash Meinel
Implement put_bytes_non_atomic for the smart transport
254
            'put_non_atomic',
255
            (self._remote_path(relpath), self._serialise_optional_mode(mode),
256
             create_parent_str, self._serialise_optional_mode(dir_mode)),
257
            bytes)
258
        self._translate_error(resp)
259
1910.19.24 by Robert Collins
Touchup method order.
260
    def put_file(self, relpath, upload_file, mode=None):
261
        # its not ideal to seek back, but currently put_non_atomic_file depends
262
        # on transports not reading before failing - which is a faulty
263
        # assumption I think - RBC 20060915
264
        pos = upload_file.tell()
265
        try:
266
            return self.put_bytes(relpath, upload_file.read(), mode)
267
        except:
268
            upload_file.seek(pos)
269
            raise
270
271
    def put_file_non_atomic(self, relpath, f, mode=None,
272
                            create_parent_dir=False,
273
                            dir_mode=None):
274
        return self.put_bytes_non_atomic(relpath, f.read(), mode=mode,
275
                                         create_parent_dir=create_parent_dir,
276
                                         dir_mode=dir_mode)
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
277
1910.19.13 by Andrew Bennetts
Address various review comments.
278
    def append_file(self, relpath, from_file, mode=None):
1910.19.14 by Robert Collins
Fix up all tests to pass, remove a couple more deprecated function calls, and break the dependency on sftp for the smart transport.
279
        return self.append_bytes(relpath, from_file.read(), mode)
1910.19.13 by Andrew Bennetts
Address various review comments.
280
        
281
    def append_bytes(self, relpath, bytes, mode=None):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
282
        resp = self._call_with_body_bytes(
1910.19.13 by Andrew Bennetts
Address various review comments.
283
            'append',
284
            (self._remote_path(relpath), self._serialise_optional_mode(mode)),
285
            bytes)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
286
        if resp[0] == 'appended':
287
            return int(resp[1])
288
        self._translate_error(resp)
289
290
    def delete(self, relpath):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
291
        resp = self._call2('delete', self._remote_path(relpath))
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
292
        self._translate_error(resp)
293
1910.19.17 by John Arbash Meinel
Implement readv() for the SmartServer
294
    def readv(self, relpath, offsets):
295
        if not offsets:
296
            return
297
298
        offsets = list(offsets)
1910.19.20 by John Arbash Meinel
Update the client-side requests, so that we collapse nearby ranges, rather than requesting lots of small chunks
299
300
        sorted_offsets = sorted(offsets)
301
        # turn the list of offsets into a stack
302
        offset_stack = iter(offsets)
303
        cur_offset_and_size = offset_stack.next()
304
        coalesced = list(self._coalesce_offsets(sorted_offsets,
305
                               limit=self._max_readv_combine,
306
                               fudge_factor=self._bytes_to_read_before_seek))
307
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
308
        request = self._medium.get_request()
309
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
310
        smart_protocol.call_with_body_readv_array(
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
311
            ('readv', self._remote_path(relpath)),
312
            [(c.start, c.length) for c in coalesced])
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
313
        resp = smart_protocol.read_response_tuple(True)
1910.19.17 by John Arbash Meinel
Implement readv() for the SmartServer
314
315
        if resp[0] != 'readv':
1910.19.20 by John Arbash Meinel
Update the client-side requests, so that we collapse nearby ranges, rather than requesting lots of small chunks
316
            # This should raise an exception
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
317
            smart_protocol.cancel_read_body()
1910.19.17 by John Arbash Meinel
Implement readv() for the SmartServer
318
            self._translate_error(resp)
1910.19.20 by John Arbash Meinel
Update the client-side requests, so that we collapse nearby ranges, rather than requesting lots of small chunks
319
            return
320
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
321
        # FIXME: this should know how many bytes are needed, for clarity.
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
322
        data = smart_protocol.read_body_bytes()
1910.19.20 by John Arbash Meinel
Update the client-side requests, so that we collapse nearby ranges, rather than requesting lots of small chunks
323
        # Cache the results, but only until they have been fulfilled
324
        data_map = {}
325
        for c_offset in coalesced:
326
            if len(data) < c_offset.length:
327
                raise errors.ShortReadvError(relpath, c_offset.start,
328
                            c_offset.length, actual=len(data))
329
            for suboffset, subsize in c_offset.ranges:
330
                key = (c_offset.start+suboffset, subsize)
331
                data_map[key] = data[suboffset:suboffset+subsize]
332
            data = data[c_offset.length:]
333
334
            # Now that we've read some data, see if we can yield anything back
335
            while cur_offset_and_size in data_map:
336
                this_data = data_map.pop(cur_offset_and_size)
337
                yield cur_offset_and_size[0], this_data
338
                cur_offset_and_size = offset_stack.next()
1910.19.17 by John Arbash Meinel
Implement readv() for the SmartServer
339
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
340
    def rename(self, rel_from, rel_to):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
341
        self._call('rename',
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
342
                   self._remote_path(rel_from),
343
                   self._remote_path(rel_to))
344
345
    def move(self, rel_from, rel_to):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
346
        self._call('move',
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
347
                   self._remote_path(rel_from),
348
                   self._remote_path(rel_to))
349
350
    def rmdir(self, relpath):
351
        resp = self._call('rmdir', self._remote_path(relpath))
352
1752.2.81 by Andrew Bennetts
Merge cleaned up underlying dependencies for remote bzrdir.
353
    def _translate_error(self, resp, orig_path=None):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
354
        """Raise an exception from a response"""
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
355
        if resp is None:
356
            what = None
357
        else:
358
            what = resp[0]
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
359
        if what == 'ok':
360
            return
361
        elif what == 'NoSuchFile':
1752.2.81 by Andrew Bennetts
Merge cleaned up underlying dependencies for remote bzrdir.
362
            if orig_path is not None:
363
                error_path = orig_path
364
            else:
365
                error_path = resp[1]
366
            raise errors.NoSuchFile(error_path)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
367
        elif what == 'error':
1910.19.13 by Andrew Bennetts
Address various review comments.
368
            raise errors.SmartProtocolError(unicode(resp[1]))
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
369
        elif what == 'FileExists':
370
            raise errors.FileExists(resp[1])
371
        elif what == 'DirectoryNotEmpty':
372
            raise errors.DirectoryNotEmpty(resp[1])
1910.19.18 by John Arbash Meinel
[merge] short-read code, implement for Smart readv
373
        elif what == 'ShortReadvError':
374
            raise errors.ShortReadvError(resp[1], int(resp[2]),
375
                                         int(resp[3]), int(resp[4]))
1910.19.16 by John Arbash Meinel
Translate UnicodeErrors across the smart server
376
        elif what in ('UnicodeEncodeError', 'UnicodeDecodeError'):
377
            encoding = str(resp[1]) # encoding must always be a string
378
            val = resp[2]
379
            start = int(resp[3])
380
            end = int(resp[4])
381
            reason = str(resp[5]) # reason must always be a string
382
            if val.startswith('u:'):
2018.2.36 by Andrew Bennetts
Don't UTF-8 decode paths in requests. They should be url-quoted (and thus
383
                val = val[2:].decode('utf-8')
1910.19.16 by John Arbash Meinel
Translate UnicodeErrors across the smart server
384
            elif val.startswith('s:'):
385
                val = val[2:].decode('base64')
386
            if what == 'UnicodeDecodeError':
387
                raise UnicodeDecodeError(encoding, val, start, end, reason)
388
            elif what == 'UnicodeEncodeError':
389
                raise UnicodeEncodeError(encoding, val, start, end, reason)
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
390
        elif what == "ReadOnlyError":
391
            raise errors.TransportNotPossible('readonly transport')
2052.6.1 by Robert Collins
``Transport.get`` has had its interface made more clear for ease of use.
392
        elif what == "ReadError":
393
            if orig_path is not None:
394
                error_path = orig_path
395
            else:
396
                error_path = resp[1]
397
            raise errors.ReadError(error_path)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
398
        else:
1910.19.13 by Andrew Bennetts
Address various review comments.
399
            raise errors.SmartProtocolError('unexpected smart server error: %r' % (resp,))
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
400
401
    def disconnect(self):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
402
        self._medium.disconnect()
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
403
404
    def delete_tree(self, relpath):
405
        raise errors.TransportNotPossible('readonly transport')
406
407
    def stat(self, relpath):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
408
        resp = self._call2('stat', self._remote_path(relpath))
1752.2.48 by Andrew Bennetts
Support stat
409
        if resp[0] == 'stat':
2018.5.19 by Andrew Bennetts
Add docstrings to all the new modules, and a few other places.
410
            return _SmartStat(int(resp[1]), int(resp[2], 8))
1752.2.48 by Andrew Bennetts
Support stat
411
        else:
412
            self._translate_error(resp)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
413
414
    ## def lock_read(self, relpath):
415
    ##     """Lock the given file for shared (read) access.
416
    ##     :return: A lock object, which should be passed to Transport.unlock()
417
    ##     """
418
    ##     # The old RemoteBranch ignore lock for reading, so we will
419
    ##     # continue that tradition and return a bogus lock object.
420
    ##     class BogusLock(object):
421
    ##         def __init__(self, path):
422
    ##             self.path = path
423
    ##         def unlock(self):
424
    ##             pass
425
    ##     return BogusLock(relpath)
426
1752.2.42 by Andrew Bennetts
Add list_dir and iter_files_recursive smart transport
427
    def listable(self):
428
        return True
429
430
    def list_dir(self, relpath):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
431
        resp = self._call2('list_dir', self._remote_path(relpath))
1752.2.42 by Andrew Bennetts
Add list_dir and iter_files_recursive smart transport
432
        if resp[0] == 'names':
1752.2.47 by Andrew Bennetts
Fix SmartTransport.list_dir to return ascii.
433
            return [name.encode('ascii') for name in resp[1:]]
1752.2.42 by Andrew Bennetts
Add list_dir and iter_files_recursive smart transport
434
        else:
435
            self._translate_error(resp)
436
437
    def iter_files_recursive(self):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
438
        resp = self._call2('iter_files_recursive', self._remote_path(''))
1752.2.42 by Andrew Bennetts
Add list_dir and iter_files_recursive smart transport
439
        if resp[0] == 'names':
440
            return resp[1:]
441
        else:
442
            self._translate_error(resp)
443
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
444
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
445
class RemoteTCPTransport(RemoteTransport):
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
446
    """Connection to smart server over plain tcp.
447
    
448
    This is essentially just a factory to get 'RemoteTransport(url,
449
        SmartTCPClientMedium).
450
    """
451
452
    def __init__(self, url):
453
        _scheme, _username, _password, _host, _port, _path = \
454
            transport.split_url(url)
2298.4.1 by Andrew Bennetts
Give bzr:// a default port of 4155.
455
        if _port is None:
2298.4.3 by Andrew Bennetts
Use a BZR_DEFAULT_PORT constant instead of bare 4155.
456
            _port = BZR_DEFAULT_PORT
2298.4.1 by Andrew Bennetts
Give bzr:// a default port of 4155.
457
        else:
458
            try:
459
                _port = int(_port)
460
            except (ValueError, TypeError), e:
461
                raise errors.InvalidURL(
462
                    path=url, extra="invalid port %s" % _port)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
463
        client_medium = medium.SmartTCPClientMedium(_host, _port)
2018.5.147 by Andrew Bennetts
Merge rename-SmartTransport branch.
464
        super(RemoteTCPTransport, self).__init__(url, medium=client_medium)
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
465
466
467
class RemoteSSHTransport(RemoteTransport):
2018.2.27 by Andrew Bennetts
Merge from bzr.dev
468
    """Connection to smart server over SSH.
469
470
    This is essentially just a factory to get 'RemoteTransport(url,
471
        SmartSSHClientMedium).
472
    """
473
474
    def __init__(self, url):
475
        _scheme, _username, _password, _host, _port, _path = \
476
            transport.split_url(url)
2039.2.2 by Martin Pool
Define SmartSSHTransport class even without paramiko
477
        try:
2018.2.27 by Andrew Bennetts
Merge from bzr.dev
478
            if _port is not None:
479
                _port = int(_port)
2039.2.2 by Martin Pool
Define SmartSSHTransport class even without paramiko
480
        except (ValueError, TypeError), e:
2018.2.27 by Andrew Bennetts
Merge from bzr.dev
481
            raise errors.InvalidURL(path=url, extra="invalid port %s" % 
482
                _port)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
483
        client_medium = medium.SmartSSHClientMedium(_host, _port,
484
                                                    _username, _password)
2018.5.147 by Andrew Bennetts
Merge rename-SmartTransport branch.
485
        super(RemoteSSHTransport, self).__init__(url, medium=client_medium)
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
486
487
488
class RemoteHTTPTransport(RemoteTransport):
2190.1.3 by John Arbash Meinel
Implement bzr+http://
489
    """Just a way to connect between a bzr+http:// url and http://.
490
    
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
491
    This connection operates slightly differently than the RemoteSSHTransport.
2190.1.3 by John Arbash Meinel
Implement bzr+http://
492
    It uses a plain http:// transport underneath, which defines what remote
493
    .bzr/smart URL we are connected to. From there, all paths that are sent are
494
    sent as relative paths, this way, the remote side can properly
2190.1.5 by John Arbash Meinel
Typo fix from Martin
495
    de-reference them, since it is likely doing rewrite rules to translate an
2190.1.3 by John Arbash Meinel
Implement bzr+http://
496
    HTTP path into a local path.
497
    """
498
499
    def __init__(self, url, http_transport=None):
500
        assert url.startswith('bzr+http://')
501
502
        if http_transport is None:
503
            http_url = url[len('bzr+'):]
504
            self._http_transport = transport.get_transport(http_url)
505
        else:
506
            self._http_transport = http_transport
507
        http_medium = self._http_transport.get_smart_medium()
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
508
        super(RemoteHTTPTransport, self).__init__(url, medium=http_medium)
2190.1.3 by John Arbash Meinel
Implement bzr+http://
509
510
    def _remote_path(self, relpath):
511
        """After connecting HTTP Transport only deals in relative URLs."""
2208.4.2 by Andrew Bennetts
Always POST to the same .bzr/smart URL for a given branch, even when accessing files in subdirectories.
512
        # Adjust the relpath based on which URL this smart transport is
513
        # connected to.
2466.3.1 by Andrew Bennetts
Normalise URLs in RemoteHTTPTransport before doing URL calculations to fix bad results.
514
        base = urlutils.normalize_url(self._http_transport.base)
2208.4.2 by Andrew Bennetts
Always POST to the same .bzr/smart URL for a given branch, even when accessing files in subdirectories.
515
        url = urlutils.join(self.base[len('bzr+'):], relpath)
516
        url = urlutils.normalize_url(url)
517
        return urlutils.relative_url(base, url)
2190.1.3 by John Arbash Meinel
Implement bzr+http://
518
519
    def abspath(self, relpath):
520
        """Return the full url to the given relative path.
521
        
522
        :param relpath: the relative path or path components
523
        :type relpath: str or list
524
        """
525
        return self._unparse_url(self._combine_paths(self._path, relpath))
526
527
    def clone(self, relative_url):
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
528
        """Make a new RemoteHTTPTransport related to me.
2190.1.3 by John Arbash Meinel
Implement bzr+http://
529
530
        This is re-implemented rather than using the default
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
531
        RemoteTransport.clone() because we must be careful about the underlying
2190.1.3 by John Arbash Meinel
Implement bzr+http://
532
        http transport.
2208.4.2 by Andrew Bennetts
Always POST to the same .bzr/smart URL for a given branch, even when accessing files in subdirectories.
533
534
        Also, the cloned smart transport will POST to the same .bzr/smart
535
        location as this transport (although obviously the relative paths in the
536
        smart requests may be different).  This is so that the server doesn't
2208.4.3 by Andrew Bennetts
Let SmartHTTPTransport.clone('..') continue to POST to the cloned URL (unlike clone('child')).
537
        have to handle .bzr/smart requests at arbitrary places inside .bzr
538
        directories, just at the initial URL the user uses.
539
540
        The exception is parent paths (i.e. relative_url of "..").
2190.1.3 by John Arbash Meinel
Implement bzr+http://
541
        """
542
        if relative_url:
543
            abs_url = self.abspath(relative_url)
544
        else:
545
            abs_url = self.base
2208.4.3 by Andrew Bennetts
Let SmartHTTPTransport.clone('..') continue to POST to the cloned URL (unlike clone('child')).
546
        # We either use the exact same http_transport (for child locations), or
547
        # a clone of the underlying http_transport (for parent locations).  This
548
        # means we share the connection.
2466.3.1 by Andrew Bennetts
Normalise URLs in RemoteHTTPTransport before doing URL calculations to fix bad results.
549
        norm_base = urlutils.normalize_url(self.base)
550
        norm_abs_url = urlutils.normalize_url(abs_url)
551
        normalized_rel_url = urlutils.relative_url(norm_base, norm_abs_url)
2208.4.3 by Andrew Bennetts
Let SmartHTTPTransport.clone('..') continue to POST to the cloned URL (unlike clone('child')).
552
        if normalized_rel_url == ".." or normalized_rel_url.startswith("../"):
553
            http_transport = self._http_transport.clone(normalized_rel_url)
554
        else:
555
            http_transport = self._http_transport
2208.4.4 by Andrew Bennetts
Merge bzr.dev.
556
        return RemoteHTTPTransport(abs_url, http_transport=http_transport)
2190.1.3 by John Arbash Meinel
Implement bzr+http://
557
558
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
559
def get_test_permutations():
560
    """Return (transport, server) permutations for testing."""
561
    ### We may need a little more test framework support to construct an
562
    ### appropriate RemoteTransport in the future.
2018.5.21 by Andrew Bennetts
Move bzrlib.transport.smart to bzrlib.smart
563
    from bzrlib.smart import server
2413.2.1 by Andrew Bennetts
Rename Smart.*Transport classes to RemoteTransport, RemoteTCPTransport, etc.
564
    return [(RemoteTCPTransport, server.SmartTCPServer_for_testing)]