~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/request.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-04-07 07:52:50 UTC
  • mfrom: (3340.1.1 208418-1.4)
  • Revision ID: pqm@pqm.ubuntu.com-20080407075250-phs53xnslo8boaeo
Return the correct knit serialisation method in _StreamAccess.
        (Andrew Bennetts, Martin Pool, Robert Collins)

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."""
66
86
 
67
87
    def do_body(self, body_bytes):
68
88
        """Called if the client sends a body with the request.
 
89
 
 
90
        The do() method is still called, and must have returned None.
69
91
        
70
92
        Must return a SmartServerResponse.
71
93
        """
74
96
        # this NotImplementedError and translate it into a 'bad request' error
75
97
        # to send to the client.
76
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)
77
133
 
78
134
 
79
135
class SmartServerResponse(object):
83
139
    SuccessfulSmartServerResponse and FailedSmartServerResponse as appropriate.
84
140
    """
85
141
 
86
 
    def __init__(self, args, body=None):
 
142
    def __init__(self, args, body=None, body_stream=None):
 
143
        """Constructor.
 
144
 
 
145
        :param args: tuple of response arguments.
 
146
        :param body: string of a response body.
 
147
        :param body_stream: iterable of bytestrings to be streamed to the
 
148
            client.
 
149
        """
87
150
        self.args = args
 
151
        if body is not None and body_stream is not None:
 
152
            raise errors.BzrError(
 
153
                "'body' and 'body_stream' are mutually exclusive.")
88
154
        self.body = body
 
155
        self.body_stream = body_stream
89
156
 
90
157
    def __eq__(self, other):
91
158
        if other is None:
92
159
            return False
93
 
        return other.args == self.args and other.body == self.body
 
160
        return (other.args == self.args and
 
161
                other.body == self.body and
 
162
                other.body_stream is self.body_stream)
94
163
 
95
164
    def __repr__(self):
96
 
        return "<SmartServerResponse args=%r body=%r>" % (self.is_successful(), 
 
165
        status = {True: 'OK', False: 'ERR'}[self.is_successful()]
 
166
        return "<SmartServerResponse status=%s args=%r body=%r>" % (status,
97
167
            self.args, self.body)
98
168
 
99
169
 
130
200
    # TODO: Better way of representing the body for commands that take it,
131
201
    # and allow it to be streamed into the server.
132
202
 
133
 
    def __init__(self, backing_transport, commands):
 
203
    def __init__(self, backing_transport, commands, root_client_path):
134
204
        """Constructor.
135
205
 
136
206
        :param backing_transport: a Transport to handle requests for.
138
208
            subclasses. e.g. bzrlib.transport.smart.vfs.vfs_commands.
139
209
        """
140
210
        self._backing_transport = backing_transport
 
211
        self._root_client_path = root_client_path
141
212
        self._commands = commands
142
213
        self._body_bytes = ''
143
214
        self.response = None
167
238
            command = self._commands.get(cmd)
168
239
        except LookupError:
169
240
            raise errors.SmartProtocolError("bad request %r" % (cmd,))
170
 
        self._command = command(self._backing_transport)
 
241
        self._command = command(self._backing_transport, self._root_client_path)
171
242
        self._run_handler_code(self._command.execute, args, {})
172
243
 
173
244
    def _run_handler_code(self, callable, args, kwargs):
234
305
 
235
306
    def do(self, path, revision_id):
236
307
        # open transport relative to our base
237
 
        t = self._backing_transport.clone(path)
 
308
        t = self.transport_from_client_path(path)
238
309
        control, extra_path = bzrdir.BzrDir.open_containing_from_transport(t)
239
310
        repo = control.open_repository()
240
311
        tmpf = tempfile.TemporaryFile()
271
342
request_handlers.register_lazy(
272
343
    'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
273
344
request_handlers.register_lazy(
274
 
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepository')
 
345
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepositoryV1')
 
346
request_handlers.register_lazy(
 
347
    'BzrDir.find_repositoryV2', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepositoryV2')
275
348
request_handlers.register_lazy(
276
349
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir', 'SmartServerRequestInitializeBzrDir')
277
350
request_handlers.register_lazy(
305
378
request_handlers.register_lazy('Repository.gather_stats',
306
379
                               'bzrlib.smart.repository',
307
380
                               'SmartServerRepositoryGatherStats')
 
381
request_handlers.register_lazy('Repository.get_parent_map',
 
382
                               'bzrlib.smart.repository',
 
383
                               'SmartServerRepositoryGetParentMap')
 
384
request_handlers.register_lazy(
 
385
    'Repository.stream_knit_data_for_revisions',
 
386
    'bzrlib.smart.repository',
 
387
    'SmartServerRepositoryStreamKnitDataForRevisions')
 
388
request_handlers.register_lazy(
 
389
    'Repository.stream_revisions_chunked',
 
390
    'bzrlib.smart.repository',
 
391
    'SmartServerRepositoryStreamRevisionsChunked')
308
392
request_handlers.register_lazy(
309
393
    'Repository.get_revision_graph', 'bzrlib.smart.repository', 'SmartServerRepositoryGetRevisionGraph')
310
394
request_handlers.register_lazy(