/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/transport/remote.py

  • Committer: Martin Pool
  • Date: 2007-09-14 06:31:28 UTC
  • mfrom: (2822 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2823.
  • Revision ID: mbp@sourcefrog.net-20070914063128-0p7mh6zfb4pzdg9p
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
import urlparse
28
28
 
29
29
from bzrlib import (
 
30
    debug,
30
31
    errors,
 
32
    trace,
31
33
    transport,
32
34
    urlutils,
33
35
    )
51
53
        self.st_mode = mode
52
54
 
53
55
 
54
 
class RemoteTransport(transport.Transport):
 
56
class RemoteTransport(transport.ConnectedTransport):
55
57
    """Connection to a smart server.
56
58
 
57
59
    The connection holds references to the medium that can be used to send
74
76
    # RemoteTransport is an adapter from the Transport object model to the 
75
77
    # SmartClient model, not an encoder.
76
78
 
77
 
    def __init__(self, url, clone_from=None, medium=None, _client=None):
 
79
    # FIXME: the medium parameter should be private, only the tests requires
 
80
    # it. It may be even clearer to define a TestRemoteTransport that handles
 
81
    # the specific cases of providing a _client and/or a _medium, and leave
 
82
    # RemoteTransport as an abstract class.
 
83
    def __init__(self, url, _from_transport=None, medium=None, _client=None):
78
84
        """Constructor.
79
85
 
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.
 
86
        :param _from_transport: Another RemoteTransport instance that this
 
87
            one is being cloned from.  Attributes such as the medium will
 
88
            be reused.
 
89
 
83
90
        :param medium: The medium to use for this RemoteTransport. This must be
84
 
            supplied if clone_from is None.
 
91
            supplied if _from_transport is None.
 
92
 
85
93
        :param _client: Override the _SmartClient used by this transport.  This
86
94
            should only be used for testing purposes; normally this is
87
95
            determined from the medium.
88
96
        """
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. 
92
 
        if not url.endswith('/'):
93
 
            url += '/'
94
 
        super(RemoteTransport, self).__init__(url)
95
 
        self._scheme, self._username, self._password, self._host, self._port, self._path = \
96
 
                transport.split_url(url)
97
 
        if clone_from is None:
98
 
            self._medium = medium
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
104
 
            self._medium = clone_from._medium
105
 
        assert self._medium is not None
 
97
        super(RemoteTransport, self).__init__(url,
 
98
                                              _from_transport=_from_transport)
 
99
 
 
100
        # The medium is the connection, except when we need to share it with
 
101
        # other objects (RemoteBzrDir, RemoteRepository etc). In these cases
 
102
        # what we want to share is really the shared connection.
 
103
 
 
104
        if _from_transport is None:
 
105
            # If no _from_transport is specified, we need to intialize the
 
106
            # shared medium.
 
107
            credentials = None
 
108
            if medium is None:
 
109
                medium, credentials = self._build_medium()
 
110
                if 'hpss' in debug.debug_flags:
 
111
                    trace.mutter('hpss: Built a new medium: %s',
 
112
                                 medium.__class__.__name__)
 
113
            self._shared_connection = transport._SharedConnection(medium,
 
114
                                                                  credentials)
 
115
 
106
116
        if _client is None:
107
 
            self._client = client._SmartClient(self._medium)
 
117
            self._client = client._SmartClient(self.get_shared_medium())
108
118
        else:
109
119
            self._client = _client
110
120
 
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
 
    
119
 
    def clone(self, relative_url):
120
 
        """Make a new RemoteTransport related to me, sharing the same connection.
 
121
    def _build_medium(self):
 
122
        """Create the medium if _from_transport does not provide one.
121
123
 
122
 
        This essentially opens a handle on a different remote directory.
 
124
        The medium is analogous to the connection for ConnectedTransport: it
 
125
        allows connection sharing.
123
126
        """
124
 
        if relative_url is None:
125
 
            return RemoteTransport(self.base, self)
126
 
        else:
127
 
            return RemoteTransport(self.abspath(relative_url), self)
 
127
        # No credentials
 
128
        return None, None
128
129
 
129
130
    def is_readonly(self):
130
131
        """Smart server transport can do read/write file operations."""
146
147
        raise errors.UnexpectedSmartServerResponse(resp)
147
148
 
148
149
    def get_smart_client(self):
149
 
        return self._medium
 
150
        return self._get_connection()
150
151
 
151
152
    def get_smart_medium(self):
152
 
        return self._medium
153
 
                                                   
154
 
    def _unparse_url(self, path):
155
 
        """Return URL for a path.
 
153
        return self._get_connection()
156
154
 
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, '', '', ''))
 
155
    def get_shared_medium(self):
 
156
        return self._get_shared_connection()
170
157
 
171
158
    def _remote_path(self, relpath):
172
159
        """Returns the Unicode version of the absolute path for relpath."""
206
193
 
207
194
    def get_bytes(self, relpath):
208
195
        remote = self._remote_path(relpath)
209
 
        request = self._medium.get_request()
 
196
        request = self.get_smart_medium().get_request()
210
197
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
211
198
        smart_protocol.call('get', remote)
212
199
        resp = smart_protocol.read_response_tuple(True)
226
213
            self._serialise_optional_mode(mode))
227
214
        self._translate_error(resp)
228
215
 
 
216
    def open_write_stream(self, relpath, mode=None):
 
217
        """See Transport.open_write_stream."""
 
218
        self.put_bytes(relpath, "", mode)
 
219
        result = transport.AppendBasedFileStream(self, relpath)
 
220
        transport._file_streams[self.abspath(relpath)] = result
 
221
        return result
 
222
 
229
223
    def put_bytes(self, relpath, upload_contents, mode=None):
230
224
        # FIXME: upload_file is probably not safe for non-ascii characters -
231
225
        # should probably just pass all parameters as length-delimited
291
285
        resp = self._call2('delete', self._remote_path(relpath))
292
286
        self._translate_error(resp)
293
287
 
 
288
    def external_url(self):
 
289
        """See bzrlib.transport.Transport.external_url."""
 
290
        # the external path for RemoteTransports is the base
 
291
        return self.base
 
292
 
294
293
    def readv(self, relpath, offsets):
295
294
        if not offsets:
296
295
            return
305
304
                               limit=self._max_readv_combine,
306
305
                               fudge_factor=self._bytes_to_read_before_seek))
307
306
 
308
 
        request = self._medium.get_request()
 
307
        request = self.get_smart_medium().get_request()
309
308
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
310
309
        smart_protocol.call_with_body_readv_array(
311
310
            ('readv', self._remote_path(relpath)),
399
398
            raise errors.SmartProtocolError('unexpected smart server error: %r' % (resp,))
400
399
 
401
400
    def disconnect(self):
402
 
        self._medium.disconnect()
 
401
        self.get_smart_medium().disconnect()
403
402
 
404
403
    def delete_tree(self, relpath):
405
404
        raise errors.TransportNotPossible('readonly transport')
449
448
        SmartTCPClientMedium).
450
449
    """
451
450
 
452
 
    def __init__(self, url):
453
 
        _scheme, _username, _password, _host, _port, _path = \
454
 
            transport.split_url(url)
455
 
        if _port is None:
456
 
            _port = BZR_DEFAULT_PORT
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)
463
 
        client_medium = medium.SmartTCPClientMedium(_host, _port)
464
 
        super(RemoteTCPTransport, self).__init__(url, medium=client_medium)
 
451
    def _build_medium(self):
 
452
        assert self.base.startswith('bzr://')
 
453
        if self._port is None:
 
454
            self._port = BZR_DEFAULT_PORT
 
455
        return medium.SmartTCPClientMedium(self._host, self._port), None
465
456
 
466
457
 
467
458
class RemoteSSHTransport(RemoteTransport):
471
462
        SmartSSHClientMedium).
472
463
    """
473
464
 
474
 
    def __init__(self, url):
475
 
        _scheme, _username, _password, _host, _port, _path = \
476
 
            transport.split_url(url)
477
 
        try:
478
 
            if _port is not None:
479
 
                _port = int(_port)
480
 
        except (ValueError, TypeError), e:
481
 
            raise errors.InvalidURL(path=url, extra="invalid port %s" % 
482
 
                _port)
483
 
        client_medium = medium.SmartSSHClientMedium(_host, _port,
484
 
                                                    _username, _password)
485
 
        super(RemoteSSHTransport, self).__init__(url, medium=client_medium)
 
465
    def _build_medium(self):
 
466
        assert self.base.startswith('bzr+ssh://')
 
467
        # ssh will prompt the user for a password if needed and if none is
 
468
        # provided but it will not give it back, so no credentials can be
 
469
        # stored.
 
470
        return medium.SmartSSHClientMedium(self._host, self._port,
 
471
                                           self._user, self._password), None
486
472
 
487
473
 
488
474
class RemoteHTTPTransport(RemoteTransport):
496
482
    HTTP path into a local path.
497
483
    """
498
484
 
499
 
    def __init__(self, url, http_transport=None):
500
 
        assert url.startswith('bzr+http://')
 
485
    def __init__(self, base, _from_transport=None, http_transport=None):
 
486
        assert base.startswith('bzr+http://')
501
487
 
502
488
        if http_transport is None:
503
 
            http_url = url[len('bzr+'):]
 
489
            # FIXME: the password may be lost here because it appears in the
 
490
            # url only for an intial construction (when the url came from the
 
491
            # command-line).
 
492
            http_url = base[len('bzr+'):]
504
493
            self._http_transport = transport.get_transport(http_url)
505
494
        else:
506
495
            self._http_transport = http_transport
507
 
        http_medium = self._http_transport.get_smart_medium()
508
 
        super(RemoteHTTPTransport, self).__init__(url, medium=http_medium)
 
496
        super(RemoteHTTPTransport, self).__init__(
 
497
            base, _from_transport=_from_transport)
 
498
 
 
499
    def _build_medium(self):
 
500
        # We let http_transport take care of the credentials
 
501
        return self._http_transport.get_smart_medium(), None
509
502
 
510
503
    def _remote_path(self, relpath):
511
 
        """After connecting HTTP Transport only deals in relative URLs."""
 
504
        """After connecting, HTTP Transport only deals in relative URLs."""
512
505
        # Adjust the relpath based on which URL this smart transport is
513
506
        # connected to.
514
 
        base = urlutils.normalize_url(self._http_transport.base)
 
507
        http_base = urlutils.normalize_url(self._http_transport.base)
515
508
        url = urlutils.join(self.base[len('bzr+'):], relpath)
516
509
        url = urlutils.normalize_url(url)
517
 
        return urlutils.relative_url(base, url)
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))
 
510
        return urlutils.relative_url(http_base, url)
526
511
 
527
512
    def clone(self, relative_url):
528
513
        """Make a new RemoteHTTPTransport related to me.
553
538
            http_transport = self._http_transport.clone(normalized_rel_url)
554
539
        else:
555
540
            http_transport = self._http_transport
556
 
        return RemoteHTTPTransport(abs_url, http_transport=http_transport)
 
541
        return RemoteHTTPTransport(abs_url,
 
542
                                   _from_transport=self,
 
543
                                   http_transport=http_transport)
557
544
 
558
545
 
559
546
def get_test_permutations():