/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/smart/request.py

  • Committer: Robert Collins
  • Date: 2009-03-13 02:25:46 UTC
  • mfrom: (4133 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4183.
  • Revision ID: robertc@robertcollins.net-20090313022546-e7de5zsdkbay5okf
MergeĀ .dev.

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
39
from bzrlib.lazy_import import lazy_import
43
44
 
44
45
class SmartServerRequest(object):
45
46
    """Base class for request handlers.
46
 
    
 
47
 
47
48
    To define a new request, subclass this class and override the `do` method
48
49
    (and if appropriate, `do_body` as well).  Request implementors should take
49
50
    care to call `translate_client_path` and `transport_from_client_path` as
70
71
            if not root_client_path.endswith('/'):
71
72
                root_client_path += '/'
72
73
        self._root_client_path = root_client_path
 
74
        self._body_chunks = []
73
75
 
74
76
    def _check_enabled(self):
75
77
        """Raises DisabledMethod if this method is disabled."""
77
79
 
78
80
    def do(self, *args):
79
81
        """Mandatory extension point for SmartServerRequest subclasses.
80
 
        
 
82
 
81
83
        Subclasses must implement this.
82
 
        
 
84
 
83
85
        This should return a SmartServerResponse if this command expects to
84
86
        receive no body.
85
87
        """
100
102
        """Called if the client sends a body with the request.
101
103
 
102
104
        The do() method is still called, and must have returned None.
103
 
        
 
105
 
104
106
        Must return a SmartServerResponse.
105
107
        """
106
 
        raise NotImplementedError(self.do_body)
 
108
        if body_bytes != '':
 
109
            raise errors.SmartProtocolError('Request does not expect a body')
107
110
 
108
111
    def do_chunk(self, chunk_bytes):
109
112
        """Called with each body chunk if the request has a streamed body.
110
113
 
111
114
        The do() method is still called, and must have returned None.
112
115
        """
113
 
        raise NotImplementedError(self.do_chunk)
 
116
        self._body_chunks.append(chunk_bytes)
114
117
 
115
118
    def do_end(self):
116
119
        """Called when the end of the request has been received."""
117
 
        pass
118
 
    
 
120
        body_bytes = ''.join(self._body_chunks)
 
121
        self._body_chunks = None
 
122
        return self.do_body(body_bytes)
 
123
 
119
124
    def translate_client_path(self, client_path):
120
125
        """Translate a path received from a network client into a local
121
126
        relpath.
154
159
 
155
160
class SmartServerResponse(object):
156
161
    """A response to a client request.
157
 
    
 
162
 
158
163
    This base class should not be used. Instead use
159
164
    SuccessfulSmartServerResponse and FailedSmartServerResponse as appropriate.
160
165
    """
204
209
 
205
210
class SmartServerRequestHandler(object):
206
211
    """Protocol logic for smart server.
207
 
    
 
212
 
208
213
    This doesn't handle serialization at all, it just processes requests and
209
214
    creates responses.
210
215
    """
229
234
        self._backing_transport = backing_transport
230
235
        self._root_client_path = root_client_path
231
236
        self._commands = commands
232
 
        self._body_bytes = ''
233
237
        self.response = None
234
238
        self.finished_reading = False
235
239
        self._command = None
236
240
 
237
241
    def accept_body(self, bytes):
238
242
        """Accept body data."""
239
 
 
240
 
        # TODO: This should be overriden for each command that desired body data
241
 
        # to handle the right format of that data, i.e. plain bytes, a bundle,
242
 
        # etc.  The deserialisation into that format should be done in the
243
 
        # Protocol object.
244
 
 
245
 
        # default fallback is to accumulate bytes.
246
 
        self._body_bytes += bytes
247
 
        
 
243
        self._run_handler_code(self._command.do_chunk, (bytes,), {})
 
244
 
248
245
    def end_of_body(self):
249
246
        """No more body data will be received."""
250
 
        self._run_handler_code(self._command.do_body, (self._body_bytes,), {})
 
247
        self._run_handler_code(self._command.do_end, (), {})
251
248
        # cannot read after this.
252
249
        self.finished_reading = True
253
250
 
281
278
        # be in SmartServerVFSRequestHandler somewhere.
282
279
        try:
283
280
            return callable(*args, **kwargs)
284
 
        except errors.NoSuchFile, e:
285
 
            return FailedSmartServerResponse(('NoSuchFile', e.path))
286
 
        except errors.FileExists, e:
287
 
            return FailedSmartServerResponse(('FileExists', e.path))
288
 
        except errors.DirectoryNotEmpty, e:
289
 
            return FailedSmartServerResponse(('DirectoryNotEmpty', e.path))
290
 
        except errors.ShortReadvError, e:
291
 
            return FailedSmartServerResponse(('ShortReadvError',
292
 
                e.path, str(e.offset), str(e.length), str(e.actual)))
293
 
        except errors.UnstackableRepositoryFormat, e:
294
 
            return FailedSmartServerResponse(('UnstackableRepositoryFormat',
295
 
                str(e.format), e.url))
296
 
        except errors.UnstackableBranchFormat, e:
297
 
            return FailedSmartServerResponse(('UnstackableBranchFormat',
298
 
                str(e.format), e.url))
299
 
        except errors.NotStacked, e:
300
 
            return FailedSmartServerResponse(('NotStacked',))
301
 
        except UnicodeError, e:
302
 
            # If it is a DecodeError, than most likely we are starting
303
 
            # with a plain string
304
 
            str_or_unicode = e.object
305
 
            if isinstance(str_or_unicode, unicode):
306
 
                # XXX: UTF-8 might have \x01 (our seperator byte) in it.  We
307
 
                # should escape it somehow.
308
 
                val = 'u:' + str_or_unicode.encode('utf-8')
309
 
            else:
310
 
                val = 's:' + str_or_unicode.encode('base64')
311
 
            # This handles UnicodeEncodeError or UnicodeDecodeError
312
 
            return FailedSmartServerResponse((e.__class__.__name__,
313
 
                    e.encoding, val, str(e.start), str(e.end), e.reason))
314
 
        except errors.TransportNotPossible, e:
315
 
            if e.msg == "readonly transport":
316
 
                return FailedSmartServerResponse(('ReadOnlyError', ))
317
 
            else:
318
 
                raise
 
281
        except (KeyboardInterrupt, SystemExit):
 
282
            raise
 
283
        except Exception, err:
 
284
            err_struct = _translate_error(err)
 
285
            return FailedSmartServerResponse(err_struct)
319
286
 
320
287
    def headers_received(self, headers):
321
288
        # Just a no-op at the moment.
331
298
        self._command = command(self._backing_transport)
332
299
        self._run_handler_code(self._command.execute, args, {})
333
300
 
334
 
    def prefixed_body_received(self, body_bytes):
335
 
        """No more body data will be received."""
336
 
        self._run_handler_code(self._command.do_body, (body_bytes,), {})
337
 
        # cannot read after this.
338
 
        self.finished_reading = True
339
 
 
340
 
    def body_chunk_received(self, chunk_bytes):
341
 
        self._run_handler_code(self._command.do_chunk, (chunk_bytes,), {})
342
 
 
343
301
    def end_received(self):
344
302
        self._run_handler_code(self._command.do_end, (), {})
345
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
    # Unserialisable error.  Log it, and return a generic error
 
348
    trace.log_exception_quietly()
 
349
    return ('error', str(err))
 
350
 
346
351
 
347
352
class HelloRequest(SmartServerRequest):
348
353
    """Answer a version request with the highest protocol version this server
383
388
request_handlers.register_lazy(
384
389
    'append', 'bzrlib.smart.vfs', 'AppendRequest')
385
390
request_handlers.register_lazy(
386
 
    'Branch.get_config_file', 'bzrlib.smart.branch', 'SmartServerBranchGetConfigFile')
 
391
    'Branch.get_config_file', 'bzrlib.smart.branch',
 
392
    'SmartServerBranchGetConfigFile')
 
393
request_handlers.register_lazy(
 
394
    'Branch.get_parent', 'bzrlib.smart.branch', 'SmartServerBranchGetParent')
 
395
request_handlers.register_lazy(
 
396
    'Branch.get_tags_bytes', 'bzrlib.smart.branch',
 
397
    'SmartServerBranchGetTagsBytes')
387
398
request_handlers.register_lazy(
388
399
    'Branch.get_stacked_on_url', 'bzrlib.smart.branch', 'SmartServerBranchRequestGetStackedOnURL')
389
400
request_handlers.register_lazy(
403
414
request_handlers.register_lazy(
404
415
    'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
405
416
request_handlers.register_lazy(
406
 
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepositoryV1')
407
 
request_handlers.register_lazy(
408
 
    'BzrDir.find_repositoryV2', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepositoryV2')
409
 
request_handlers.register_lazy(
410
 
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir', 'SmartServerRequestInitializeBzrDir')
411
 
request_handlers.register_lazy(
412
 
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBranch')
 
417
    'BzrDir.cloning_metadir', 'bzrlib.smart.bzrdir',
 
418
    'SmartServerBzrDirRequestCloningMetaDir')
 
419
request_handlers.register_lazy(
 
420
    'BzrDir.create_branch', 'bzrlib.smart.bzrdir',
 
421
    'SmartServerRequestCreateBranch')
 
422
request_handlers.register_lazy(
 
423
    'BzrDir.create_repository', 'bzrlib.smart.bzrdir',
 
424
    'SmartServerRequestCreateRepository')
 
425
request_handlers.register_lazy(
 
426
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir',
 
427
    'SmartServerRequestFindRepositoryV1')
 
428
request_handlers.register_lazy(
 
429
    'BzrDir.find_repositoryV2', 'bzrlib.smart.bzrdir',
 
430
    'SmartServerRequestFindRepositoryV2')
 
431
request_handlers.register_lazy(
 
432
    'BzrDir.find_repositoryV3', 'bzrlib.smart.bzrdir',
 
433
    'SmartServerRequestFindRepositoryV3')
 
434
request_handlers.register_lazy(
 
435
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir',
 
436
    'SmartServerRequestInitializeBzrDir')
 
437
request_handlers.register_lazy(
 
438
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir',
 
439
    'SmartServerRequestOpenBranch')
 
440
request_handlers.register_lazy(
 
441
    'BzrDir.open_branchV2', 'bzrlib.smart.bzrdir',
 
442
    'SmartServerRequestOpenBranchV2')
413
443
request_handlers.register_lazy(
414
444
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
415
445
request_handlers.register_lazy(
436
466
    'readv', 'bzrlib.smart.vfs', 'ReadvRequest')
437
467
request_handlers.register_lazy(
438
468
    'rename', 'bzrlib.smart.vfs', 'RenameRequest')
 
469
request_handlers.register_lazy(
 
470
    'PackRepository.autopack', 'bzrlib.smart.packrepository',
 
471
    'SmartServerPackRepositoryAutopack')
439
472
request_handlers.register_lazy('Repository.gather_stats',
440
473
                               'bzrlib.smart.repository',
441
474
                               'SmartServerRepositoryGatherStats')
447
480
request_handlers.register_lazy(
448
481
    'Repository.has_revision', 'bzrlib.smart.repository', 'SmartServerRequestHasRevision')
449
482
request_handlers.register_lazy(
 
483
    'Repository.insert_stream', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStream')
 
484
request_handlers.register_lazy(
450
485
    'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
451
486
request_handlers.register_lazy(
452
487
    'Repository.lock_write', 'bzrlib.smart.repository', 'SmartServerRepositoryLockWrite')
453
488
request_handlers.register_lazy(
 
489
    'Repository.set_make_working_trees', 'bzrlib.smart.repository',
 
490
    'SmartServerRepositorySetMakeWorkingTrees')
 
491
request_handlers.register_lazy(
454
492
    'Repository.unlock', 'bzrlib.smart.repository', 'SmartServerRepositoryUnlock')
455
493
request_handlers.register_lazy(
 
494
    'Repository.get_stream', 'bzrlib.smart.repository',
 
495
    'SmartServerRepositoryGetStream')
 
496
request_handlers.register_lazy(
456
497
    'Repository.tarball', 'bzrlib.smart.repository',
457
498
    'SmartServerRepositoryTarball')
458
499
request_handlers.register_lazy(