/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: Martin Pool
  • Date: 2009-03-13 07:54:48 UTC
  • mfrom: (4144 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4189.
  • Revision ID: mbp@sourcefrog.net-20090313075448-jlz1t7baz7gzipqn
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
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 protocol v1 and v2 seperator
307
 
                # byte) in it, so this encoding could cause broken responses.
308
 
                # Newer clients use protocol v3, so will be fine.
309
 
                val = 'u:' + str_or_unicode.encode('utf-8')
310
 
            else:
311
 
                val = 's:' + str_or_unicode.encode('base64')
312
 
            # This handles UnicodeEncodeError or UnicodeDecodeError
313
 
            return FailedSmartServerResponse((e.__class__.__name__,
314
 
                    e.encoding, val, str(e.start), str(e.end), e.reason))
315
 
        except errors.TransportNotPossible, e:
316
 
            if e.msg == "readonly transport":
317
 
                return FailedSmartServerResponse(('ReadOnlyError', ))
318
 
            else:
319
 
                raise
320
 
        except errors.ReadError, e:
321
 
            # cannot read the file
322
 
            return FailedSmartServerResponse(('ReadError', e.path))
323
 
        except errors.PermissionDenied, e:
324
 
            return FailedSmartServerResponse(
325
 
                ('PermissionDenied', e.path, e.extra))
 
281
        except (KeyboardInterrupt, SystemExit):
 
282
            raise
 
283
        except Exception, err:
 
284
            err_struct = _translate_error(err)
 
285
            return FailedSmartServerResponse(err_struct)
326
286
 
327
287
    def headers_received(self, headers):
328
288
        # Just a no-op at the moment.
338
298
        self._command = command(self._backing_transport)
339
299
        self._run_handler_code(self._command.execute, args, {})
340
300
 
341
 
    def prefixed_body_received(self, body_bytes):
342
 
        """No more body data will be received."""
343
 
        self._run_handler_code(self._command.do_body, (body_bytes,), {})
344
 
        # cannot read after this.
345
 
        self.finished_reading = True
346
 
 
347
 
    def body_chunk_received(self, chunk_bytes):
348
 
        self._run_handler_code(self._command.do_chunk, (chunk_bytes,), {})
349
 
 
350
301
    def end_received(self):
351
302
        self._run_handler_code(self._command.do_end, (), {})
352
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
 
353
351
 
354
352
class HelloRequest(SmartServerRequest):
355
353
    """Answer a version request with the highest protocol version this server
390
388
request_handlers.register_lazy(
391
389
    'append', 'bzrlib.smart.vfs', 'AppendRequest')
392
390
request_handlers.register_lazy(
393
 
    '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')
394
398
request_handlers.register_lazy(
395
399
    'Branch.get_stacked_on_url', 'bzrlib.smart.branch', 'SmartServerBranchRequestGetStackedOnURL')
396
400
request_handlers.register_lazy(
410
414
request_handlers.register_lazy(
411
415
    'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
412
416
request_handlers.register_lazy(
413
 
    'BzrDir.find_repository', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepositoryV1')
414
 
request_handlers.register_lazy(
415
 
    'BzrDir.find_repositoryV2', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepositoryV2')
416
 
request_handlers.register_lazy(
417
 
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir', 'SmartServerRequestInitializeBzrDir')
418
 
request_handlers.register_lazy(
419
 
    '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')
420
443
request_handlers.register_lazy(
421
444
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
422
445
request_handlers.register_lazy(
457
480
request_handlers.register_lazy(
458
481
    'Repository.has_revision', 'bzrlib.smart.repository', 'SmartServerRequestHasRevision')
459
482
request_handlers.register_lazy(
 
483
    'Repository.insert_stream', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStream')
 
484
request_handlers.register_lazy(
460
485
    'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
461
486
request_handlers.register_lazy(
462
487
    'Repository.lock_write', 'bzrlib.smart.repository', 'SmartServerRepositoryLockWrite')
463
488
request_handlers.register_lazy(
 
489
    'Repository.set_make_working_trees', 'bzrlib.smart.repository',
 
490
    'SmartServerRepositorySetMakeWorkingTrees')
 
491
request_handlers.register_lazy(
464
492
    'Repository.unlock', 'bzrlib.smart.repository', 'SmartServerRepositoryUnlock')
465
493
request_handlers.register_lazy(
 
494
    'Repository.get_stream', 'bzrlib.smart.repository',
 
495
    'SmartServerRepositoryGetStream')
 
496
request_handlers.register_lazy(
466
497
    'Repository.tarball', 'bzrlib.smart.repository',
467
498
    'SmartServerRepositoryTarball')
468
499
request_handlers.register_lazy(