~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/request.py

  • Committer: Andrew Bennetts
  • Date: 2008-04-07 08:20:13 UTC
  • mfrom: (3340 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3344.
  • Revision ID: andrew.bennetts@canonical.com-20080407082013-ca1n1tqqon7ugxiy
Merge from bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
    errors,
25
25
    registry,
26
26
    revision,
 
27
    urlutils,
27
28
    )
28
29
from bzrlib.bundle.serializer import write_bundle
 
30
from bzrlib.transport import get_transport
 
31
from bzrlib.transport.chroot import ChrootServer
29
32
 
30
33
 
31
34
class SmartServerRequest(object):
32
 
    """Base class for request handlers."""
 
35
    """Base class for request handlers.
 
36
    
 
37
    To define a new request, subclass this class and override the `do` method
 
38
    (and if appropriate, `do_body` as well).  Request implementors should take
 
39
    care to call `translate_client_path` and `transport_from_client_path` as
 
40
    appropriate when dealing with paths received from the client.
 
41
    """
33
42
 
34
 
    def __init__(self, backing_transport):
 
43
    def __init__(self, backing_transport, root_client_path='/'):
35
44
        """Constructor.
36
45
 
37
46
        :param backing_transport: the base transport to be used when performing
38
47
            this request.
 
48
        :param root_client_path: the client path that maps to the root of
 
49
            backing_transport.  This is used to interpret relpaths received
 
50
            from the client.  Clients will not be able to refer to paths above
 
51
            this root.  If root_client_path is None, then no translation will
 
52
            be performed on client paths.  Default is '/'.
39
53
        """
40
54
        self._backing_transport = backing_transport
 
55
        if root_client_path is not None:
 
56
            if not root_client_path.startswith('/'):
 
57
                root_client_path = '/' + root_client_path
 
58
            if not root_client_path.endswith('/'):
 
59
                root_client_path += '/'
 
60
        self._root_client_path = root_client_path
41
61
 
42
62
    def _check_enabled(self):
43
63
        """Raises DisabledMethod if this method is disabled."""
76
96
        # this NotImplementedError and translate it into a 'bad request' error
77
97
        # to send to the client.
78
98
        raise NotImplementedError(self.do_body)
 
99
    
 
100
    def translate_client_path(self, client_path):
 
101
        """Translate a path received from a network client into a local
 
102
        relpath.
 
103
 
 
104
        All paths received from the client *must* be translated.
 
105
 
 
106
        :param client_path: the path from the client.
 
107
        :returns: a relpath that may be used with self._backing_transport
 
108
            (unlike the untranslated client_path, which must not be used with
 
109
            the backing transport).
 
110
        """
 
111
        if self._root_client_path is None:
 
112
            # no translation necessary!
 
113
            return client_path
 
114
        if not client_path.startswith('/'):
 
115
            client_path = '/' + client_path
 
116
        if client_path.startswith(self._root_client_path):
 
117
            path = client_path[len(self._root_client_path):]
 
118
            relpath = urlutils.joinpath('/', path)
 
119
            assert relpath.startswith('/')
 
120
            return '.' + relpath
 
121
        else:
 
122
            raise errors.PathNotChild(client_path, self._root_client_path)
 
123
 
 
124
    def transport_from_client_path(self, client_path):
 
125
        """Get a backing transport corresponding to the location referred to by
 
126
        a network client.
 
127
 
 
128
        :seealso: translate_client_path
 
129
        :returns: a transport cloned from self._backing_transport
 
130
        """
 
131
        relpath = self.translate_client_path(client_path)
 
132
        return self._backing_transport.clone(relpath)
79
133
 
80
134
 
81
135
class SmartServerResponse(object):
108
162
                other.body_stream is self.body_stream)
109
163
 
110
164
    def __repr__(self):
111
 
        return ("<SmartServerResponse successful=%s args=%r body=%r>"
112
 
                % (self.is_successful(), self.args, self.body))
 
165
        status = {True: 'OK', False: 'ERR'}[self.is_successful()]
 
166
        return "<SmartServerResponse status=%s args=%r body=%r>" % (status,
 
167
            self.args, self.body)
113
168
 
114
169
 
115
170
class FailedSmartServerResponse(SmartServerResponse):
145
200
    # TODO: Better way of representing the body for commands that take it,
146
201
    # and allow it to be streamed into the server.
147
202
 
148
 
    def __init__(self, backing_transport, commands):
 
203
    def __init__(self, backing_transport, commands, root_client_path):
149
204
        """Constructor.
150
205
 
151
206
        :param backing_transport: a Transport to handle requests for.
153
208
            subclasses. e.g. bzrlib.transport.smart.vfs.vfs_commands.
154
209
        """
155
210
        self._backing_transport = backing_transport
 
211
        self._root_client_path = root_client_path
156
212
        self._commands = commands
157
213
        self._body_bytes = ''
158
214
        self.response = None
182
238
            command = self._commands.get(cmd)
183
239
        except LookupError:
184
240
            raise errors.SmartProtocolError("bad request %r" % (cmd,))
185
 
        self._command = command(self._backing_transport)
 
241
        self._command = command(self._backing_transport, self._root_client_path)
186
242
        self._run_handler_code(self._command.execute, args, {})
187
243
 
188
244
    def _run_handler_code(self, callable, args, kwargs):
249
305
 
250
306
    def do(self, path, revision_id):
251
307
        # open transport relative to our base
252
 
        t = self._backing_transport.clone(path)
 
308
        t = self.transport_from_client_path(path)
253
309
        control, extra_path = bzrdir.BzrDir.open_containing_from_transport(t)
254
310
        repo = control.open_repository()
255
311
        tmpf = tempfile.TemporaryFile()