~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/remote.py

  • Committer: Robert Collins
  • Date: 2007-07-25 00:52:21 UTC
  • mfrom: (2650 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2651.
  • Revision ID: robertc@robertcollins.net-20070725005221-0ysm6il5mqnme3wz
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
51
51
        self.st_mode = mode
52
52
 
53
53
 
54
 
class RemoteTransport(transport.Transport):
 
54
class RemoteTransport(transport.ConnectedTransport):
55
55
    """Connection to a smart server.
56
56
 
57
57
    The connection holds references to the medium that can be used to send
74
74
    # RemoteTransport is an adapter from the Transport object model to the 
75
75
    # SmartClient model, not an encoder.
76
76
 
77
 
    def __init__(self, url, clone_from=None, medium=None, _client=None):
 
77
    # FIXME: the medium parameter should be private, only the tests requires
 
78
    # it. It may be even clearer to define a TestRemoteTransport that handles
 
79
    # the specific cases of providing a _client and/or a _medium, and leave
 
80
    # RemoteTransport as an abstract class.
 
81
    def __init__(self, url, _from_transport=None, medium=None, _client=None):
78
82
        """Constructor.
79
83
 
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.
 
84
        :param _from_transport: Another RemoteTransport instance that this
 
85
            one is being cloned from.  Attributes such as the medium will
 
86
            be reused.
 
87
 
83
88
        :param medium: The medium to use for this RemoteTransport. This must be
84
 
            supplied if clone_from is None.
 
89
            supplied if _from_transport is None.
 
90
 
85
91
        :param _client: Override the _SmartClient used by this transport.  This
86
92
            should only be used for testing purposes; normally this is
87
93
            determined from the medium.
88
94
        """
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
 
95
        super(RemoteTransport, self).__init__(url,
 
96
                                              _from_transport=_from_transport)
 
97
 
 
98
        # The medium is the connection, except when we need to share it with
 
99
        # other objects (RemoteBzrDir, RemoteRepository etc). In these cases
 
100
        # what we want to share is really the shared connection.
 
101
 
 
102
        if _from_transport is None:
 
103
            # If no _from_transport is specified, we need to intialize the
 
104
            # shared medium.
 
105
            credentials = None
 
106
            if medium is None:
 
107
                medium, credentials = self._build_medium()
 
108
            self._shared_connection= transport._SharedConnection(medium,
 
109
                                                                 credentials)
 
110
 
106
111
        if _client is None:
107
 
            self._client = client._SmartClient(self._medium)
 
112
            self._client = client._SmartClient(self.get_shared_medium())
108
113
        else:
109
114
            self._client = _client
110
115
 
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.
 
116
    def _build_medium(self):
 
117
        """Create the medium if _from_transport does not provide one.
121
118
 
122
 
        This essentially opens a handle on a different remote directory.
 
119
        The medium is analogous to the connection for ConnectedTransport: it
 
120
        allows connection sharing.
123
121
        """
124
 
        if relative_url is None:
125
 
            return RemoteTransport(self.base, self)
126
 
        else:
127
 
            return RemoteTransport(self.abspath(relative_url), self)
 
122
        # No credentials
 
123
        return None, None
128
124
 
129
125
    def is_readonly(self):
130
126
        """Smart server transport can do read/write file operations."""
146
142
        raise errors.UnexpectedSmartServerResponse(resp)
147
143
 
148
144
    def get_smart_client(self):
149
 
        return self._medium
 
145
        return self._get_connection()
150
146
 
151
147
    def get_smart_medium(self):
152
 
        return self._medium
153
 
                                                   
154
 
    def _unparse_url(self, path):
155
 
        """Return URL for a path.
 
148
        return self._get_connection()
156
149
 
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, '', '', ''))
 
150
    def get_shared_medium(self):
 
151
        return self._get_shared_connection()
170
152
 
171
153
    def _remote_path(self, relpath):
172
154
        """Returns the Unicode version of the absolute path for relpath."""
206
188
 
207
189
    def get_bytes(self, relpath):
208
190
        remote = self._remote_path(relpath)
209
 
        request = self._medium.get_request()
 
191
        request = self.get_smart_medium().get_request()
210
192
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
211
193
        smart_protocol.call('get', remote)
212
194
        resp = smart_protocol.read_response_tuple(True)
291
273
        resp = self._call2('delete', self._remote_path(relpath))
292
274
        self._translate_error(resp)
293
275
 
 
276
    def external_url(self):
 
277
        """See bzrlib.transport.Transport.external_url."""
 
278
        # the external path for RemoteTransports is the base
 
279
        return self.base
 
280
 
294
281
    def readv(self, relpath, offsets):
295
282
        if not offsets:
296
283
            return
305
292
                               limit=self._max_readv_combine,
306
293
                               fudge_factor=self._bytes_to_read_before_seek))
307
294
 
308
 
        request = self._medium.get_request()
 
295
        request = self.get_smart_medium().get_request()
309
296
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
310
297
        smart_protocol.call_with_body_readv_array(
311
298
            ('readv', self._remote_path(relpath)),
399
386
            raise errors.SmartProtocolError('unexpected smart server error: %r' % (resp,))
400
387
 
401
388
    def disconnect(self):
402
 
        self._medium.disconnect()
 
389
        self.get_smart_medium().disconnect()
403
390
 
404
391
    def delete_tree(self, relpath):
405
392
        raise errors.TransportNotPossible('readonly transport')
449
436
        SmartTCPClientMedium).
450
437
    """
451
438
 
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)
 
439
    def _build_medium(self):
 
440
        assert self.base.startswith('bzr://')
 
441
        if self._port is None:
 
442
            self._port = BZR_DEFAULT_PORT
 
443
        return medium.SmartTCPClientMedium(self._host, self._port), None
465
444
 
466
445
 
467
446
class RemoteSSHTransport(RemoteTransport):
471
450
        SmartSSHClientMedium).
472
451
    """
473
452
 
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)
 
453
    def _build_medium(self):
 
454
        assert self.base.startswith('bzr+ssh://')
 
455
        # ssh will prompt the user for a password if needed and if none is
 
456
        # provided but it will not give it back, so no credentials can be
 
457
        # stored.
 
458
        return medium.SmartSSHClientMedium(self._host, self._port,
 
459
                                           self._user, self._password), None
486
460
 
487
461
 
488
462
class RemoteHTTPTransport(RemoteTransport):
496
470
    HTTP path into a local path.
497
471
    """
498
472
 
499
 
    def __init__(self, url, http_transport=None):
500
 
        assert url.startswith('bzr+http://')
 
473
    def __init__(self, base, _from_transport=None, http_transport=None):
 
474
        assert base.startswith('bzr+http://')
501
475
 
502
476
        if http_transport is None:
503
 
            http_url = url[len('bzr+'):]
 
477
            # FIXME: the password may be lost here because it appears in the
 
478
            # url only for an intial construction (when the url came from the
 
479
            # command-line).
 
480
            http_url = base[len('bzr+'):]
504
481
            self._http_transport = transport.get_transport(http_url)
505
482
        else:
506
483
            self._http_transport = http_transport
507
 
        http_medium = self._http_transport.get_smart_medium()
508
 
        super(RemoteHTTPTransport, self).__init__(url, medium=http_medium)
 
484
        super(RemoteHTTPTransport, self).__init__(
 
485
            base, _from_transport=_from_transport)
 
486
 
 
487
    def _build_medium(self):
 
488
        # We let http_transport take care of the credentials
 
489
        return self._http_transport.get_smart_medium(), None
509
490
 
510
491
    def _remote_path(self, relpath):
511
 
        """After connecting HTTP Transport only deals in relative URLs."""
 
492
        """After connecting, HTTP Transport only deals in relative URLs."""
512
493
        # Adjust the relpath based on which URL this smart transport is
513
494
        # connected to.
514
 
        base = urlutils.normalize_url(self._http_transport.base)
 
495
        http_base = urlutils.normalize_url(self._http_transport.base)
515
496
        url = urlutils.join(self.base[len('bzr+'):], relpath)
516
497
        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))
 
498
        return urlutils.relative_url(http_base, url)
526
499
 
527
500
    def clone(self, relative_url):
528
501
        """Make a new RemoteHTTPTransport related to me.
553
526
            http_transport = self._http_transport.clone(normalized_rel_url)
554
527
        else:
555
528
            http_transport = self._http_transport
556
 
        return RemoteHTTPTransport(abs_url, http_transport=http_transport)
 
529
        return RemoteHTTPTransport(abs_url,
 
530
                                   _from_transport=self,
 
531
                                   http_transport=http_transport)
557
532
 
558
533
 
559
534
def get_test_permutations():