/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: Robert Collins
  • Date: 2007-08-07 22:59:45 UTC
  • mfrom: (2681 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2682.
  • Revision ID: robertc@robertcollins.net-20070807225945-dlxppeb3we4lh897
Merge bzr.dev.

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)
291
278
        resp = self._call2('delete', self._remote_path(relpath))
292
279
        self._translate_error(resp)
293
280
 
 
281
    def external_url(self):
 
282
        """See bzrlib.transport.Transport.external_url."""
 
283
        # the external path for RemoteTransports is the base
 
284
        return self.base
 
285
 
294
286
    def readv(self, relpath, offsets):
295
287
        if not offsets:
296
288
            return
305
297
                               limit=self._max_readv_combine,
306
298
                               fudge_factor=self._bytes_to_read_before_seek))
307
299
 
308
 
        request = self._medium.get_request()
 
300
        request = self.get_smart_medium().get_request()
309
301
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
310
302
        smart_protocol.call_with_body_readv_array(
311
303
            ('readv', self._remote_path(relpath)),
399
391
            raise errors.SmartProtocolError('unexpected smart server error: %r' % (resp,))
400
392
 
401
393
    def disconnect(self):
402
 
        self._medium.disconnect()
 
394
        self.get_smart_medium().disconnect()
403
395
 
404
396
    def delete_tree(self, relpath):
405
397
        raise errors.TransportNotPossible('readonly transport')
449
441
        SmartTCPClientMedium).
450
442
    """
451
443
 
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)
 
444
    def _build_medium(self):
 
445
        assert self.base.startswith('bzr://')
 
446
        if self._port is None:
 
447
            self._port = BZR_DEFAULT_PORT
 
448
        return medium.SmartTCPClientMedium(self._host, self._port), None
465
449
 
466
450
 
467
451
class RemoteSSHTransport(RemoteTransport):
471
455
        SmartSSHClientMedium).
472
456
    """
473
457
 
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)
 
458
    def _build_medium(self):
 
459
        assert self.base.startswith('bzr+ssh://')
 
460
        # ssh will prompt the user for a password if needed and if none is
 
461
        # provided but it will not give it back, so no credentials can be
 
462
        # stored.
 
463
        return medium.SmartSSHClientMedium(self._host, self._port,
 
464
                                           self._user, self._password), None
486
465
 
487
466
 
488
467
class RemoteHTTPTransport(RemoteTransport):
496
475
    HTTP path into a local path.
497
476
    """
498
477
 
499
 
    def __init__(self, url, http_transport=None):
500
 
        assert url.startswith('bzr+http://')
 
478
    def __init__(self, base, _from_transport=None, http_transport=None):
 
479
        assert base.startswith('bzr+http://')
501
480
 
502
481
        if http_transport is None:
503
 
            http_url = url[len('bzr+'):]
 
482
            # FIXME: the password may be lost here because it appears in the
 
483
            # url only for an intial construction (when the url came from the
 
484
            # command-line).
 
485
            http_url = base[len('bzr+'):]
504
486
            self._http_transport = transport.get_transport(http_url)
505
487
        else:
506
488
            self._http_transport = http_transport
507
 
        http_medium = self._http_transport.get_smart_medium()
508
 
        super(RemoteHTTPTransport, self).__init__(url, medium=http_medium)
 
489
        super(RemoteHTTPTransport, self).__init__(
 
490
            base, _from_transport=_from_transport)
 
491
 
 
492
    def _build_medium(self):
 
493
        # We let http_transport take care of the credentials
 
494
        return self._http_transport.get_smart_medium(), None
509
495
 
510
496
    def _remote_path(self, relpath):
511
 
        """After connecting HTTP Transport only deals in relative URLs."""
 
497
        """After connecting, HTTP Transport only deals in relative URLs."""
512
498
        # Adjust the relpath based on which URL this smart transport is
513
499
        # connected to.
514
 
        base = urlutils.normalize_url(self._http_transport.base)
 
500
        http_base = urlutils.normalize_url(self._http_transport.base)
515
501
        url = urlutils.join(self.base[len('bzr+'):], relpath)
516
502
        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))
 
503
        return urlutils.relative_url(http_base, url)
526
504
 
527
505
    def clone(self, relative_url):
528
506
        """Make a new RemoteHTTPTransport related to me.
553
531
            http_transport = self._http_transport.clone(normalized_rel_url)
554
532
        else:
555
533
            http_transport = self._http_transport
556
 
        return RemoteHTTPTransport(abs_url, http_transport=http_transport)
 
534
        return RemoteHTTPTransport(abs_url,
 
535
                                   _from_transport=self,
 
536
                                   http_transport=http_transport)
557
537
 
558
538
 
559
539
def get_test_permutations():