~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/remote.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-06-28 07:08:27 UTC
  • mfrom: (2553.1.3 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20070628070827-h5s313dg5tnag9vj
(robertc) Show the names of commit hooks during commit.

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.ConnectedTransport):
 
54
class RemoteTransport(transport.Transport):
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
 
    # 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):
 
77
    def __init__(self, url, clone_from=None, medium=None, _client=None):
82
78
        """Constructor.
83
79
 
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
 
 
 
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.
88
83
        :param medium: The medium to use for this RemoteTransport. This must be
89
 
            supplied if _from_transport is None.
90
 
 
 
84
            supplied if clone_from is None.
91
85
        :param _client: Override the _SmartClient used by this transport.  This
92
86
            should only be used for testing purposes; normally this is
93
87
            determined from the medium.
94
88
        """
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
 
 
 
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
111
106
        if _client is None:
112
 
            self._client = client._SmartClient(self.get_shared_medium())
 
107
            self._client = client._SmartClient(self._medium)
113
108
        else:
114
109
            self._client = _client
115
110
 
116
 
    def _build_medium(self):
117
 
        """Create the medium if _from_transport does not provide one.
 
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.
118
121
 
119
 
        The medium is analogous to the connection for ConnectedTransport: it
120
 
        allows connection sharing.
 
122
        This essentially opens a handle on a different remote directory.
121
123
        """
122
 
        # No credentials
123
 
        return None, None
 
124
        if relative_url is None:
 
125
            return RemoteTransport(self.base, self)
 
126
        else:
 
127
            return RemoteTransport(self.abspath(relative_url), self)
124
128
 
125
129
    def is_readonly(self):
126
130
        """Smart server transport can do read/write file operations."""
142
146
        raise errors.UnexpectedSmartServerResponse(resp)
143
147
 
144
148
    def get_smart_client(self):
145
 
        return self._get_connection()
 
149
        return self._medium
146
150
 
147
151
    def get_smart_medium(self):
148
 
        return self._get_connection()
 
152
        return self._medium
 
153
                                                   
 
154
    def _unparse_url(self, path):
 
155
        """Return URL for a path.
149
156
 
150
 
    def get_shared_medium(self):
151
 
        return self._get_shared_connection()
 
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, '', '', ''))
152
170
 
153
171
    def _remote_path(self, relpath):
154
172
        """Returns the Unicode version of the absolute path for relpath."""
188
206
 
189
207
    def get_bytes(self, relpath):
190
208
        remote = self._remote_path(relpath)
191
 
        request = self.get_smart_medium().get_request()
 
209
        request = self._medium.get_request()
192
210
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
193
211
        smart_protocol.call('get', remote)
194
212
        resp = smart_protocol.read_response_tuple(True)
273
291
        resp = self._call2('delete', self._remote_path(relpath))
274
292
        self._translate_error(resp)
275
293
 
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
 
 
281
294
    def readv(self, relpath, offsets):
282
295
        if not offsets:
283
296
            return
292
305
                               limit=self._max_readv_combine,
293
306
                               fudge_factor=self._bytes_to_read_before_seek))
294
307
 
295
 
        request = self.get_smart_medium().get_request()
 
308
        request = self._medium.get_request()
296
309
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
297
310
        smart_protocol.call_with_body_readv_array(
298
311
            ('readv', self._remote_path(relpath)),
376
389
                raise UnicodeEncodeError(encoding, val, start, end, reason)
377
390
        elif what == "ReadOnlyError":
378
391
            raise errors.TransportNotPossible('readonly transport')
379
 
        elif what == "ReadError":
380
 
            if orig_path is not None:
381
 
                error_path = orig_path
382
 
            else:
383
 
                error_path = resp[1]
384
 
            raise errors.ReadError(error_path)
385
392
        else:
386
393
            raise errors.SmartProtocolError('unexpected smart server error: %r' % (resp,))
387
394
 
388
395
    def disconnect(self):
389
 
        self.get_smart_medium().disconnect()
 
396
        self._medium.disconnect()
390
397
 
391
398
    def delete_tree(self, relpath):
392
399
        raise errors.TransportNotPossible('readonly transport')
436
443
        SmartTCPClientMedium).
437
444
    """
438
445
 
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
 
446
    def __init__(self, url):
 
447
        _scheme, _username, _password, _host, _port, _path = \
 
448
            transport.split_url(url)
 
449
        if _port is None:
 
450
            _port = BZR_DEFAULT_PORT
 
451
        else:
 
452
            try:
 
453
                _port = int(_port)
 
454
            except (ValueError, TypeError), e:
 
455
                raise errors.InvalidURL(
 
456
                    path=url, extra="invalid port %s" % _port)
 
457
        client_medium = medium.SmartTCPClientMedium(_host, _port)
 
458
        super(RemoteTCPTransport, self).__init__(url, medium=client_medium)
444
459
 
445
460
 
446
461
class RemoteSSHTransport(RemoteTransport):
450
465
        SmartSSHClientMedium).
451
466
    """
452
467
 
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
 
468
    def __init__(self, url):
 
469
        _scheme, _username, _password, _host, _port, _path = \
 
470
            transport.split_url(url)
 
471
        try:
 
472
            if _port is not None:
 
473
                _port = int(_port)
 
474
        except (ValueError, TypeError), e:
 
475
            raise errors.InvalidURL(path=url, extra="invalid port %s" % 
 
476
                _port)
 
477
        client_medium = medium.SmartSSHClientMedium(_host, _port,
 
478
                                                    _username, _password)
 
479
        super(RemoteSSHTransport, self).__init__(url, medium=client_medium)
460
480
 
461
481
 
462
482
class RemoteHTTPTransport(RemoteTransport):
470
490
    HTTP path into a local path.
471
491
    """
472
492
 
473
 
    def __init__(self, base, _from_transport=None, http_transport=None):
474
 
        assert base.startswith('bzr+http://')
 
493
    def __init__(self, url, http_transport=None):
 
494
        assert url.startswith('bzr+http://')
475
495
 
476
496
        if http_transport is None:
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+'):]
 
497
            http_url = url[len('bzr+'):]
481
498
            self._http_transport = transport.get_transport(http_url)
482
499
        else:
483
500
            self._http_transport = http_transport
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
 
501
        http_medium = self._http_transport.get_smart_medium()
 
502
        super(RemoteHTTPTransport, self).__init__(url, medium=http_medium)
490
503
 
491
504
    def _remote_path(self, relpath):
492
 
        """After connecting, HTTP Transport only deals in relative URLs."""
 
505
        """After connecting HTTP Transport only deals in relative URLs."""
493
506
        # Adjust the relpath based on which URL this smart transport is
494
507
        # connected to.
495
 
        http_base = urlutils.normalize_url(self._http_transport.base)
 
508
        base = urlutils.normalize_url(self._http_transport.base)
496
509
        url = urlutils.join(self.base[len('bzr+'):], relpath)
497
510
        url = urlutils.normalize_url(url)
498
 
        return urlutils.relative_url(http_base, url)
 
511
        return urlutils.relative_url(base, url)
 
512
 
 
513
    def abspath(self, relpath):
 
514
        """Return the full url to the given relative path.
 
515
        
 
516
        :param relpath: the relative path or path components
 
517
        :type relpath: str or list
 
518
        """
 
519
        return self._unparse_url(self._combine_paths(self._path, relpath))
499
520
 
500
521
    def clone(self, relative_url):
501
522
        """Make a new RemoteHTTPTransport related to me.
526
547
            http_transport = self._http_transport.clone(normalized_rel_url)
527
548
        else:
528
549
            http_transport = self._http_transport
529
 
        return RemoteHTTPTransport(abs_url,
530
 
                                   _from_transport=self,
531
 
                                   http_transport=http_transport)
 
550
        return RemoteHTTPTransport(abs_url, http_transport=http_transport)
532
551
 
533
552
 
534
553
def get_test_permutations():