/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: Jelmer Vernooij
  • Date: 2009-03-22 00:24:37 UTC
  • mfrom: (4180 +trunk)
  • mto: (3920.2.35 dpush)
  • mto: This revision was merged to the branch mainline in revision 4281.
  • Revision ID: jelmer@samba.org-20090322002437-0vlyqnz29isqeozo
Merge bzr.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 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
    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
 
353
355
 
354
356
class HelloRequest(SmartServerRequest):
355
357
    """Answer a version request with the highest protocol version this server
390
392
request_handlers.register_lazy(
391
393
    'append', 'bzrlib.smart.vfs', 'AppendRequest')
392
394
request_handlers.register_lazy(
393
 
    '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')
394
402
request_handlers.register_lazy(
395
403
    'Branch.get_stacked_on_url', 'bzrlib.smart.branch', 'SmartServerBranchRequestGetStackedOnURL')
396
404
request_handlers.register_lazy(
410
418
request_handlers.register_lazy(
411
419
    'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
412
420
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')
 
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')
420
447
request_handlers.register_lazy(
421
448
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
422
449
request_handlers.register_lazy(
457
484
request_handlers.register_lazy(
458
485
    'Repository.has_revision', 'bzrlib.smart.repository', 'SmartServerRequestHasRevision')
459
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(
460
491
    'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
461
492
request_handlers.register_lazy(
462
493
    'Repository.lock_write', 'bzrlib.smart.repository', 'SmartServerRepositoryLockWrite')
463
494
request_handlers.register_lazy(
 
495
    'Repository.set_make_working_trees', 'bzrlib.smart.repository',
 
496
    'SmartServerRepositorySetMakeWorkingTrees')
 
497
request_handlers.register_lazy(
464
498
    'Repository.unlock', 'bzrlib.smart.repository', 'SmartServerRepositoryUnlock')
465
499
request_handlers.register_lazy(
 
500
    'Repository.get_stream', 'bzrlib.smart.repository',
 
501
    'SmartServerRepositoryGetStream')
 
502
request_handlers.register_lazy(
466
503
    'Repository.tarball', 'bzrlib.smart.repository',
467
504
    'SmartServerRepositoryTarball')
468
505
request_handlers.register_lazy(