~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/request.py

  • Committer: Martin Pool
  • Date: 2009-03-24 05:21:02 UTC
  • mfrom: (4192 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4202.
  • Revision ID: mbp@sourcefrog.net-20090324052102-8kk087b32tep3d9h
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
33
33
    errors,
34
34
    registry,
35
35
    revision,
 
36
    trace,
36
37
    urlutils,
37
38
    )
38
 
from bzrlib.bundle.serializer import write_bundle
 
39
from bzrlib.lazy_import import lazy_import
 
40
lazy_import(globals(), """
 
41
from bzrlib.bundle import serializer
 
42
""")
39
43
 
40
44
 
41
45
class SmartServerRequest(object):
42
46
    """Base class for request handlers.
43
 
    
 
47
 
44
48
    To define a new request, subclass this class and override the `do` method
45
49
    (and if appropriate, `do_body` as well).  Request implementors should take
46
50
    care to call `translate_client_path` and `transport_from_client_path` as
67
71
            if not root_client_path.endswith('/'):
68
72
                root_client_path += '/'
69
73
        self._root_client_path = root_client_path
 
74
        self._body_chunks = []
70
75
 
71
76
    def _check_enabled(self):
72
77
        """Raises DisabledMethod if this method is disabled."""
74
79
 
75
80
    def do(self, *args):
76
81
        """Mandatory extension point for SmartServerRequest subclasses.
77
 
        
 
82
 
78
83
        Subclasses must implement this.
79
 
        
 
84
 
80
85
        This should return a SmartServerResponse if this command expects to
81
86
        receive no body.
82
87
        """
97
102
        """Called if the client sends a body with the request.
98
103
 
99
104
        The do() method is still called, and must have returned None.
100
 
        
 
105
 
101
106
        Must return a SmartServerResponse.
102
107
        """
103
 
        raise NotImplementedError(self.do_body)
 
108
        if body_bytes != '':
 
109
            raise errors.SmartProtocolError('Request does not expect a body')
104
110
 
105
111
    def do_chunk(self, chunk_bytes):
106
112
        """Called with each body chunk if the request has a streamed body.
107
113
 
108
114
        The do() method is still called, and must have returned None.
109
115
        """
110
 
        raise NotImplementedError(self.do_chunk)
 
116
        self._body_chunks.append(chunk_bytes)
111
117
 
112
118
    def do_end(self):
113
119
        """Called when the end of the request has been received."""
114
 
        pass
115
 
    
 
120
        body_bytes = ''.join(self._body_chunks)
 
121
        self._body_chunks = None
 
122
        return self.do_body(body_bytes)
 
123
 
116
124
    def translate_client_path(self, client_path):
117
125
        """Translate a path received from a network client into a local
118
126
        relpath.
151
159
 
152
160
class SmartServerResponse(object):
153
161
    """A response to a client request.
154
 
    
 
162
 
155
163
    This base class should not be used. Instead use
156
164
    SuccessfulSmartServerResponse and FailedSmartServerResponse as appropriate.
157
165
    """
179
187
                other.body_stream is self.body_stream)
180
188
 
181
189
    def __repr__(self):
182
 
        status = {True: 'OK', False: 'ERR'}[self.is_successful()]
183
 
        return "<SmartServerResponse status=%s args=%r body=%r>" % (status,
 
190
        return "<%s args=%r body=%r>" % (self.__class__.__name__,
184
191
            self.args, self.body)
185
192
 
186
193
 
202
209
 
203
210
class SmartServerRequestHandler(object):
204
211
    """Protocol logic for smart server.
205
 
    
 
212
 
206
213
    This doesn't handle serialization at all, it just processes requests and
207
214
    creates responses.
208
215
    """
227
234
        self._backing_transport = backing_transport
228
235
        self._root_client_path = root_client_path
229
236
        self._commands = commands
230
 
        self._body_bytes = ''
231
237
        self.response = None
232
238
        self.finished_reading = False
233
239
        self._command = None
234
240
 
235
241
    def accept_body(self, bytes):
236
242
        """Accept body data."""
237
 
 
238
 
        # TODO: This should be overriden for each command that desired body data
239
 
        # to handle the right format of that data, i.e. plain bytes, a bundle,
240
 
        # etc.  The deserialisation into that format should be done in the
241
 
        # Protocol object.
242
 
 
243
 
        # default fallback is to accumulate bytes.
244
 
        self._body_bytes += bytes
245
 
        
 
243
        self._run_handler_code(self._command.do_chunk, (bytes,), {})
 
244
 
246
245
    def end_of_body(self):
247
246
        """No more body data will be received."""
248
 
        self._run_handler_code(self._command.do_body, (self._body_bytes,), {})
 
247
        self._run_handler_code(self._command.do_end, (), {})
249
248
        # cannot read after this.
250
249
        self.finished_reading = True
251
250
 
279
278
        # be in SmartServerVFSRequestHandler somewhere.
280
279
        try:
281
280
            return callable(*args, **kwargs)
282
 
        except errors.NoSuchFile, e:
283
 
            return FailedSmartServerResponse(('NoSuchFile', e.path))
284
 
        except errors.FileExists, e:
285
 
            return FailedSmartServerResponse(('FileExists', e.path))
286
 
        except errors.DirectoryNotEmpty, e:
287
 
            return FailedSmartServerResponse(('DirectoryNotEmpty', e.path))
288
 
        except errors.ShortReadvError, e:
289
 
            return FailedSmartServerResponse(('ShortReadvError',
290
 
                e.path, str(e.offset), str(e.length), str(e.actual)))
291
 
        except UnicodeError, e:
292
 
            # If it is a DecodeError, than most likely we are starting
293
 
            # with a plain string
294
 
            str_or_unicode = e.object
295
 
            if isinstance(str_or_unicode, unicode):
296
 
                # XXX: UTF-8 might have \x01 (our seperator byte) in it.  We
297
 
                # should escape it somehow.
298
 
                val = 'u:' + str_or_unicode.encode('utf-8')
299
 
            else:
300
 
                val = 's:' + str_or_unicode.encode('base64')
301
 
            # This handles UnicodeEncodeError or UnicodeDecodeError
302
 
            return FailedSmartServerResponse((e.__class__.__name__,
303
 
                    e.encoding, val, str(e.start), str(e.end), e.reason))
304
 
        except errors.TransportNotPossible, e:
305
 
            if e.msg == "readonly transport":
306
 
                return FailedSmartServerResponse(('ReadOnlyError', ))
307
 
            else:
308
 
                raise
 
281
        except (KeyboardInterrupt, SystemExit):
 
282
            raise
 
283
        except Exception, err:
 
284
            err_struct = _translate_error(err)
 
285
            return FailedSmartServerResponse(err_struct)
309
286
 
310
287
    def headers_received(self, headers):
311
288
        # Just a no-op at the moment.
321
298
        self._command = command(self._backing_transport)
322
299
        self._run_handler_code(self._command.execute, args, {})
323
300
 
324
 
    def prefixed_body_received(self, body_bytes):
325
 
        """No more body data will be received."""
326
 
        self._run_handler_code(self._command.do_body, (body_bytes,), {})
327
 
        # cannot read after this.
328
 
        self.finished_reading = True
329
 
 
330
 
    def body_chunk_received(self, chunk_bytes):
331
 
        self._run_handler_code(self._command.do_chunk, (chunk_bytes,), {})
332
 
 
333
301
    def end_received(self):
334
302
        self._run_handler_code(self._command.do_end, (), {})
335
303
 
 
304
    def post_body_error_received(self, error_args):
 
305
        # Just a no-op at the moment.
 
306
        pass
 
307
 
 
308
 
 
309
def _translate_error(err):
 
310
    if isinstance(err, errors.NoSuchFile):
 
311
        return ('NoSuchFile', err.path)
 
312
    elif isinstance(err, errors.FileExists):
 
313
        return ('FileExists', err.path)
 
314
    elif isinstance(err, errors.DirectoryNotEmpty):
 
315
        return ('DirectoryNotEmpty', err.path)
 
316
    elif isinstance(err, errors.ShortReadvError):
 
317
        return ('ShortReadvError', err.path, str(err.offset), str(err.length),
 
318
                str(err.actual))
 
319
    elif isinstance(err, errors.UnstackableRepositoryFormat):
 
320
        return (('UnstackableRepositoryFormat', str(err.format), err.url))
 
321
    elif isinstance(err, errors.UnstackableBranchFormat):
 
322
        return ('UnstackableBranchFormat', str(err.format), err.url)
 
323
    elif isinstance(err, errors.NotStacked):
 
324
        return ('NotStacked',)
 
325
    elif isinstance(err, UnicodeError):
 
326
        # If it is a DecodeError, than most likely we are starting
 
327
        # with a plain string
 
328
        str_or_unicode = err.object
 
329
        if isinstance(str_or_unicode, unicode):
 
330
            # XXX: UTF-8 might have \x01 (our protocol v1 and v2 seperator
 
331
            # byte) in it, so this encoding could cause broken responses.
 
332
            # Newer clients use protocol v3, so will be fine.
 
333
            val = 'u:' + str_or_unicode.encode('utf-8')
 
334
        else:
 
335
            val = 's:' + str_or_unicode.encode('base64')
 
336
        # This handles UnicodeEncodeError or UnicodeDecodeError
 
337
        return (err.__class__.__name__, err.encoding, val, str(err.start),
 
338
                str(err.end), err.reason)
 
339
    elif isinstance(err, errors.TransportNotPossible):
 
340
        if err.msg == "readonly transport":
 
341
            return ('ReadOnlyError', )
 
342
    elif isinstance(err, errors.ReadError):
 
343
        # cannot read the file
 
344
        return ('ReadError', err.path)
 
345
    elif isinstance(err, errors.PermissionDenied):
 
346
        return ('PermissionDenied', err.path, err.extra)
 
347
    elif isinstance(err, errors.TokenMismatch):
 
348
        return ('TokenMismatch', err.given_token, err.lock_token)
 
349
    elif isinstance(err, errors.LockContention):
 
350
        return ('LockContention', err.lock, err.msg)
 
351
    # Unserialisable error.  Log it, and return a generic error
 
352
    trace.log_exception_quietly()
 
353
    return ('error', str(err))
 
354
 
336
355
 
337
356
class HelloRequest(SmartServerRequest):
338
357
    """Answer a version request with the highest protocol version this server
353
372
        repo = control.open_repository()
354
373
        tmpf = tempfile.TemporaryFile()
355
374
        base_revision = revision.NULL_REVISION
356
 
        write_bundle(repo, revision_id, base_revision, tmpf)
 
375
        serializer.write_bundle(repo, revision_id, base_revision, tmpf)
357
376
        tmpf.seek(0)
358
377
        return SuccessfulSmartServerResponse((), tmpf.read())
359
378
 
373
392
request_handlers.register_lazy(
374
393
    'append', 'bzrlib.smart.vfs', 'AppendRequest')
375
394
request_handlers.register_lazy(
376
 
    'Branch.get_config_file', 'bzrlib.smart.branch', 'SmartServerBranchGetConfigFile')
 
395
    'Branch.get_config_file', 'bzrlib.smart.branch',
 
396
    'SmartServerBranchGetConfigFile')
 
397
request_handlers.register_lazy(
 
398
    'Branch.get_parent', 'bzrlib.smart.branch', 'SmartServerBranchGetParent')
 
399
request_handlers.register_lazy(
 
400
    'Branch.get_tags_bytes', 'bzrlib.smart.branch',
 
401
    'SmartServerBranchGetTagsBytes')
 
402
request_handlers.register_lazy(
 
403
    'Branch.get_stacked_on_url', 'bzrlib.smart.branch', 'SmartServerBranchRequestGetStackedOnURL')
377
404
request_handlers.register_lazy(
378
405
    'Branch.last_revision_info', 'bzrlib.smart.branch', 'SmartServerBranchRequestLastRevisionInfo')
379
406
request_handlers.register_lazy(
386
413
    'Branch.set_last_revision_info', 'bzrlib.smart.branch',
387
414
    'SmartServerBranchRequestSetLastRevisionInfo')
388
415
request_handlers.register_lazy(
 
416
    'Branch.set_last_revision_ex', 'bzrlib.smart.branch',
 
417
    'SmartServerBranchRequestSetLastRevisionEx')
 
418
request_handlers.register_lazy(
389
419
    'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
390
420
request_handlers.register_lazy(
391
 
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepositoryV1')
392
 
request_handlers.register_lazy(
393
 
    'BzrDir.find_repositoryV2', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepositoryV2')
394
 
request_handlers.register_lazy(
395
 
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir', 'SmartServerRequestInitializeBzrDir')
396
 
request_handlers.register_lazy(
397
 
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBranch')
 
421
    'BzrDir.cloning_metadir', 'bzrlib.smart.bzrdir',
 
422
    'SmartServerBzrDirRequestCloningMetaDir')
 
423
request_handlers.register_lazy(
 
424
    'BzrDir.create_branch', 'bzrlib.smart.bzrdir',
 
425
    'SmartServerRequestCreateBranch')
 
426
request_handlers.register_lazy(
 
427
    'BzrDir.create_repository', 'bzrlib.smart.bzrdir',
 
428
    'SmartServerRequestCreateRepository')
 
429
request_handlers.register_lazy(
 
430
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir',
 
431
    'SmartServerRequestFindRepositoryV1')
 
432
request_handlers.register_lazy(
 
433
    'BzrDir.find_repositoryV2', 'bzrlib.smart.bzrdir',
 
434
    'SmartServerRequestFindRepositoryV2')
 
435
request_handlers.register_lazy(
 
436
    'BzrDir.find_repositoryV3', 'bzrlib.smart.bzrdir',
 
437
    'SmartServerRequestFindRepositoryV3')
 
438
request_handlers.register_lazy(
 
439
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir',
 
440
    'SmartServerRequestInitializeBzrDir')
 
441
request_handlers.register_lazy(
 
442
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir',
 
443
    'SmartServerRequestOpenBranch')
 
444
request_handlers.register_lazy(
 
445
    'BzrDir.open_branchV2', 'bzrlib.smart.bzrdir',
 
446
    'SmartServerRequestOpenBranchV2')
398
447
request_handlers.register_lazy(
399
448
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
400
449
request_handlers.register_lazy(
421
470
    'readv', 'bzrlib.smart.vfs', 'ReadvRequest')
422
471
request_handlers.register_lazy(
423
472
    'rename', 'bzrlib.smart.vfs', 'RenameRequest')
 
473
request_handlers.register_lazy(
 
474
    'PackRepository.autopack', 'bzrlib.smart.packrepository',
 
475
    'SmartServerPackRepositoryAutopack')
424
476
request_handlers.register_lazy('Repository.gather_stats',
425
477
                               'bzrlib.smart.repository',
426
478
                               'SmartServerRepositoryGatherStats')
428
480
                               'bzrlib.smart.repository',
429
481
                               'SmartServerRepositoryGetParentMap')
430
482
request_handlers.register_lazy(
431
 
    'Repository.stream_knit_data_for_revisions',
432
 
    'bzrlib.smart.repository',
433
 
    'SmartServerRepositoryStreamKnitDataForRevisions')
434
 
request_handlers.register_lazy(
435
 
    'Repository.stream_revisions_chunked',
436
 
    'bzrlib.smart.repository',
437
 
    'SmartServerRepositoryStreamRevisionsChunked')
438
 
request_handlers.register_lazy(
439
483
    'Repository.get_revision_graph', 'bzrlib.smart.repository', 'SmartServerRepositoryGetRevisionGraph')
440
484
request_handlers.register_lazy(
441
485
    'Repository.has_revision', 'bzrlib.smart.repository', 'SmartServerRequestHasRevision')
442
486
request_handlers.register_lazy(
 
487
    'Repository.insert_stream', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStream')
 
488
request_handlers.register_lazy(
 
489
    'Repository.insert_stream_locked', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStreamLocked')
 
490
request_handlers.register_lazy(
443
491
    'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
444
492
request_handlers.register_lazy(
445
493
    'Repository.lock_write', 'bzrlib.smart.repository', 'SmartServerRepositoryLockWrite')
446
494
request_handlers.register_lazy(
 
495
    'Repository.set_make_working_trees', 'bzrlib.smart.repository',
 
496
    'SmartServerRepositorySetMakeWorkingTrees')
 
497
request_handlers.register_lazy(
447
498
    'Repository.unlock', 'bzrlib.smart.repository', 'SmartServerRepositoryUnlock')
448
499
request_handlers.register_lazy(
 
500
    'Repository.get_stream', 'bzrlib.smart.repository',
 
501
    'SmartServerRepositoryGetStream')
 
502
request_handlers.register_lazy(
449
503
    'Repository.tarball', 'bzrlib.smart.repository',
450
504
    'SmartServerRepositoryTarball')
451
505
request_handlers.register_lazy(