~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/__init__.py

  • Committer: Vincent Ladeuil
  • Date: 2007-05-31 18:01:11 UTC
  • mto: (2485.8.44 bzr.connection.sharing)
  • mto: This revision was merged to the branch mainline in revision 2646.
  • Revision ID: v.ladeuil+lp@free.fr-20070531180111-xef34xmn8l3hjyz0
Add a new ConnectedTransport class refactored from [s]ftp and http.

* bzrlib/transport/__init__.py:
(split_url): Deprecated.
(ConnectedTransport): New class.

* bzrlib/tests/test_transport.py:
(TestConnectedTransport): New class.

Show diffs side-by-side

added added

removed removed

Lines of Context:
57
57
        DEPRECATED_PARAMETER,
58
58
        zero_eight,
59
59
        zero_eleven,
 
60
        zero_seventeen,
60
61
        )
61
62
from bzrlib.trace import (
62
63
    note,
169
170
 
170
171
 
171
172
 
 
173
@deprecated_function(zero_seventeen)
172
174
def split_url(url):
173
175
    # TODO: jam 20060606 urls should only be ascii, or they should raise InvalidURL
174
176
    if isinstance(url, unicode):
377
379
        :param relpath: relative url string for relative part of remote path.
378
380
        :return: urlencoded string for final path.
379
381
        """
380
 
        # FIXME: share the common code across more transports; variants of
381
 
        # this likely occur in http and sftp too.
382
 
        #
383
 
        # TODO: Also need to consider handling of ~, which might vary between
384
 
        # transports?
385
382
        if not isinstance(relpath, str):
386
 
            raise errors.InvalidURL("not a valid url: %r" % relpath)
 
383
            raise errors.InvalidURL(relpath)
387
384
        if relpath.startswith('/'):
388
385
            base_parts = []
389
386
        else:
1032
1029
        return False
1033
1030
 
1034
1031
 
 
1032
class ConnectedTransport(Transport):
 
1033
    """A transport connected to a remote server.
 
1034
 
 
1035
    Base class for transports that connect to a remote server
 
1036
    with optional user and password. Cloning preserves existing
 
1037
    connection and credentials.
 
1038
    """
 
1039
 
 
1040
    def __init__(self, base, from_transport=None):
 
1041
        if base[-1] != '/':
 
1042
            base += '/'
 
1043
        if from_transport is not None:
 
1044
            # Copy the passowrd as it does not appear in base
 
1045
            self._password = from_transport._password
 
1046
        (self._scheme,
 
1047
         self._user, self._password,
 
1048
         self._host, self._port,
 
1049
         self._path) = self._split_url(base)
 
1050
        # Rebuild base taken any special handling into account
 
1051
        base = self._unsplit_url(self._scheme,
 
1052
                                 self._user, self._password,
 
1053
                                 self._host, self._port,
 
1054
                                 self._urlencode_abspath(self._path))
 
1055
        super(ConnectedTransport, self).__init__(base)
 
1056
        if from_transport is not None:
 
1057
            connection = from_transport.get_connection()
 
1058
        else:
 
1059
            connection = None
 
1060
        self.set_connection(connection)
 
1061
 
 
1062
    def _split_url(self, url):
 
1063
        if isinstance(url, unicode):
 
1064
            import pdb; pdb.set_trace()
 
1065
            raise errors.InvalidURL('should be ascii:\n%r' % url)
 
1066
        url = url.encode('utf-8')
 
1067
        (scheme, netloc, path, params,
 
1068
         query, fragment) = urlparse.urlparse(url, allow_fragments=False)
 
1069
        username = password = host = port = None
 
1070
        if '@' in netloc:
 
1071
            username, host = netloc.split('@', 1)
 
1072
            if ':' in username:
 
1073
                username, password = username.split(':', 1)
 
1074
                password = urllib.unquote(password)
 
1075
            username = urllib.unquote(username)
 
1076
        else:
 
1077
            host = netloc
 
1078
 
 
1079
        if ':' in host:
 
1080
            host, port = host.rsplit(':', 1)
 
1081
            try:
 
1082
                port = int(port)
 
1083
            except ValueError:
 
1084
                raise errors.InvalidURL('invalid port number %s in url:\n%s' %
 
1085
                                        (port, url))
 
1086
        host = urllib.unquote(host)
 
1087
        path = self._urldecode_abspath(path)
 
1088
 
 
1089
        return (scheme, username, password, host, port, path)
 
1090
 
 
1091
    def _unsplit_url(self, scheme, user, password, host, port, path):
 
1092
        netloc = urllib.quote(host)
 
1093
        if user is not None:
 
1094
            # Note that we don't put the password back even if we
 
1095
            # have one so that it doesn't get accidentally
 
1096
            # exposed.
 
1097
            netloc = '%s@%s' % (urllib.quote(user), netloc)
 
1098
        if port is not None:
 
1099
            netloc = '%s:%d' % (netloc, port)
 
1100
        return urlparse.urlunparse((scheme, netloc, path, None, None, None))
 
1101
 
 
1102
    def _urlencode_abspath(self, abspath):
 
1103
        return urllib.quote(abspath)
 
1104
 
 
1105
    def _urldecode_abspath(self, abspath):
 
1106
        return urllib.unquote(abspath)
 
1107
 
 
1108
    def relpath(self, abspath):
 
1109
        scheme, user, password, host, port, path = self._split_url(abspath)
 
1110
        error = []
 
1111
        if (scheme != self._scheme):
 
1112
            error.append('scheme mismatch')
 
1113
        if (user != self._user):
 
1114
            error.append('username mismatch')
 
1115
        if (host != self._host):
 
1116
            error.append('host mismatch')
 
1117
        if (port != self._port):
 
1118
            error.append('port mismatch')
 
1119
        if not (path == self._path[:-1] or path.startswith(self._path)):
 
1120
            error.append('path mismatch')
 
1121
        if error:
 
1122
            extra = ', '.join(error)
 
1123
            raise errors.PathNotChild(abspath, self.base, extra=extra)
 
1124
        pl = len(self._path)
 
1125
        return path[pl:].strip('/')
 
1126
 
 
1127
    def abspath(self, relpath):
 
1128
        """Return the full url to the given relative path.
 
1129
        
 
1130
        :param relpath: the relative path urlencoded
 
1131
        """
 
1132
        path = self._remote_path(relpath)
 
1133
        return self._unsplit_url(self._scheme, self._user, self._password,
 
1134
                                 self._host, self._port,
 
1135
                                 self._urlencode_abspath(path))
 
1136
 
 
1137
    def _remote_path(self, relpath):
 
1138
        """Return the absolute path part of the url to the given relative path.
 
1139
        
 
1140
        :param relpath: is a urlencoded string.
 
1141
        """
 
1142
        relative = urlutils.unescape(relpath).encode('utf-8')
 
1143
        remote_path = self._combine_paths(self._path, relative)
 
1144
        return remote_path
 
1145
 
 
1146
    def get_connection(self):
 
1147
        """Returns the transport specific connection object"""
 
1148
        return self._connection
 
1149
 
 
1150
    def set_connection(self, connection):
 
1151
        """Set the transport specific connection object.
 
1152
 
 
1153
        Note: daughter classes should ensure that the connection
 
1154
        is still shared if the connection is reset during the
 
1155
        transport lifetime (using a list containing the single
 
1156
        connection can help avoid aliasing bugs).
 
1157
        """
 
1158
        self._connection = connection
 
1159
 
 
1160
 
1035
1161
# jam 20060426 For compatibility we copy the functions here
1036
1162
# TODO: The should be marked as deprecated
1037
1163
urlescape = urlutils.escape
1147
1273
    return None, last_err
1148
1274
 
1149
1275
 
1150
 
class ConnectedTransport(Transport):
1151
 
    """A transport connected to a remote server"""
1152
 
 
1153
 
 
1154
1276
class Server(object):
1155
1277
    """A Transport Server.
1156
1278