/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: Sabin Iacob
  • Date: 2009-03-23 14:59:43 UTC
  • mto: (4189.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4193.
  • Revision ID: iacobs@m0n5t3r.info-20090323145943-3s3p1px5q1rkh2e5
update FSF mailing address

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
"""Infrastructure for server-side request handlers.
18
 
 
19
 
Interesting module attributes:
20
 
    * The request_handlers registry maps verb names to SmartServerRequest
21
 
      classes.
22
 
    * The jail_info threading.local() object is used to prevent accidental
23
 
      opening of BzrDirs outside of the backing transport, or any other
24
 
      transports placed in jail_info.transports.  The jail_info is reset on
25
 
      every call into a request handler (which can happen an arbitrary number
26
 
      of times during a request).
 
17
"""Basic server-side logic for dealing with requests.
 
18
 
 
19
**XXX**:
 
20
 
 
21
The class names are a little confusing: the protocol will instantiate a
 
22
SmartServerRequestHandler, whose dispatch_command method creates an instance of
 
23
a SmartServerRequest subclass.
 
24
 
 
25
The request_handlers registry tracks SmartServerRequest classes (rather than
 
26
SmartServerRequestHandler).
27
27
"""
28
28
 
29
 
# XXX: The class names are a little confusing: the protocol will instantiate a
30
 
# SmartServerRequestHandler, whose dispatch_command method creates an instance
31
 
# of a SmartServerRequest subclass.
32
 
 
33
 
 
34
 
import threading
 
29
import tempfile
35
30
 
36
31
from bzrlib import (
37
32
    bzrdir,
38
 
    debug,
39
33
    errors,
40
 
    osutils,
41
34
    registry,
42
35
    revision,
43
36
    trace,
46
39
from bzrlib.lazy_import import lazy_import
47
40
lazy_import(globals(), """
48
41
from bzrlib.bundle import serializer
49
 
 
50
 
import tempfile
51
 
import thread
52
42
""")
53
43
 
54
44
 
55
 
jail_info = threading.local()
56
 
jail_info.transports = None
57
 
 
58
 
 
59
 
def _install_hook():
60
 
    bzrdir.BzrDir.hooks.install_named_hook(
61
 
        'pre_open', _pre_open_hook, 'checking server jail')
62
 
 
63
 
 
64
 
def _pre_open_hook(transport):
65
 
    allowed_transports = getattr(jail_info, 'transports', None)
66
 
    if allowed_transports is None:
67
 
        return
68
 
    abspath = transport.base
69
 
    for allowed_transport in allowed_transports:
70
 
        try:
71
 
            allowed_transport.relpath(abspath)
72
 
        except errors.PathNotChild:
73
 
            continue
74
 
        else:
75
 
            return
76
 
    raise errors.JailBreak(abspath)
77
 
 
78
 
 
79
 
_install_hook()
80
 
 
81
 
 
82
45
class SmartServerRequest(object):
83
46
    """Base class for request handlers.
84
47
 
90
53
    # XXX: rename this class to BaseSmartServerRequestHandler ?  A request
91
54
    # *handler* is a different concept to the request.
92
55
 
93
 
    def __init__(self, backing_transport, root_client_path='/', jail_root=None):
 
56
    def __init__(self, backing_transport, root_client_path='/'):
94
57
        """Constructor.
95
58
 
96
59
        :param backing_transport: the base transport to be used when performing
100
63
            from the client.  Clients will not be able to refer to paths above
101
64
            this root.  If root_client_path is None, then no translation will
102
65
            be performed on client paths.  Default is '/'.
103
 
        :param jail_root: if specified, the root of the BzrDir.open jail to use
104
 
            instead of backing_transport.
105
66
        """
106
67
        self._backing_transport = backing_transport
107
 
        if jail_root is None:
108
 
            jail_root = backing_transport
109
 
        self._jail_root = jail_root
110
68
        if root_client_path is not None:
111
69
            if not root_client_path.startswith('/'):
112
70
                root_client_path = '/' + root_client_path
163
121
        self._body_chunks = None
164
122
        return self.do_body(body_bytes)
165
123
 
166
 
    def setup_jail(self):
167
 
        jail_info.transports = [self._jail_root]
168
 
 
169
 
    def teardown_jail(self):
170
 
        jail_info.transports = None
171
 
 
172
124
    def translate_client_path(self, client_path):
173
125
        """Translate a path received from a network client into a local
174
126
        relpath.
185
137
            return client_path
186
138
        if not client_path.startswith('/'):
187
139
            client_path = '/' + client_path
188
 
        if client_path + '/' == self._root_client_path:
189
 
            return '.'
190
140
        if client_path.startswith(self._root_client_path):
191
141
            path = client_path[len(self._root_client_path):]
192
142
            relpath = urlutils.joinpath('/', path)
193
143
            if not relpath.startswith('/'):
194
144
                raise ValueError(relpath)
195
 
            return urlutils.escape('.' + relpath)
 
145
            return '.' + relpath
196
146
        else:
197
147
            raise errors.PathNotChild(client_path, self._root_client_path)
198
148
 
274
224
    # TODO: Better way of representing the body for commands that take it,
275
225
    # and allow it to be streamed into the server.
276
226
 
277
 
    def __init__(self, backing_transport, commands, root_client_path,
278
 
        jail_root=None):
 
227
    def __init__(self, backing_transport, commands, root_client_path):
279
228
        """Constructor.
280
229
 
281
230
        :param backing_transport: a Transport to handle requests for.
285
234
        self._backing_transport = backing_transport
286
235
        self._root_client_path = root_client_path
287
236
        self._commands = commands
288
 
        if jail_root is None:
289
 
            jail_root = backing_transport
290
 
        self._jail_root = jail_root
291
237
        self.response = None
292
238
        self.finished_reading = False
293
239
        self._command = None
294
 
        if 'hpss' in debug.debug_flags:
295
 
            self._request_start_time = osutils.timer_func()
296
 
            self._thread_id = thread.get_ident()
297
 
 
298
 
    def _trace(self, action, message, extra_bytes=None, include_time=False):
299
 
        # It is a bit of a shame that this functionality overlaps with that of 
300
 
        # ProtocolThreeRequester._trace. However, there is enough difference
301
 
        # that just putting it in a helper doesn't help a lot. And some state
302
 
        # is taken from the instance.
303
 
        if include_time:
304
 
            t = '%5.3fs ' % (osutils.timer_func() - self._request_start_time)
305
 
        else:
306
 
            t = ''
307
 
        if extra_bytes is None:
308
 
            extra = ''
309
 
        else:
310
 
            extra = ' ' + repr(extra_bytes[:40])
311
 
            if len(extra) > 33:
312
 
                extra = extra[:29] + extra[-1] + '...'
313
 
        trace.mutter('%12s: [%s] %s%s%s'
314
 
                     % (action, self._thread_id, t, message, extra))
315
240
 
316
241
    def accept_body(self, bytes):
317
242
        """Accept body data."""
318
 
        if self._command is None:
319
 
            # no active command object, so ignore the event.
320
 
            return
321
243
        self._run_handler_code(self._command.do_chunk, (bytes,), {})
322
 
        if 'hpss' in debug.debug_flags:
323
 
            self._trace('accept body',
324
 
                        '%d bytes' % (len(bytes),), bytes)
325
244
 
326
245
    def end_of_body(self):
327
246
        """No more body data will be received."""
328
247
        self._run_handler_code(self._command.do_end, (), {})
329
248
        # cannot read after this.
330
249
        self.finished_reading = True
331
 
        if 'hpss' in debug.debug_flags:
332
 
            self._trace('end of body', '', include_time=True)
 
250
 
 
251
    def dispatch_command(self, cmd, args):
 
252
        """Deprecated compatibility method.""" # XXX XXX
 
253
        try:
 
254
            command = self._commands.get(cmd)
 
255
        except LookupError:
 
256
            raise errors.UnknownSmartMethod(cmd)
 
257
        self._command = command(self._backing_transport, self._root_client_path)
 
258
        self._run_handler_code(self._command.execute, args, {})
333
259
 
334
260
    def _run_handler_code(self, callable, args, kwargs):
335
261
        """Run some handler specific code 'callable'.
351
277
        # XXX: most of this error conversion is VFS-related, and thus ought to
352
278
        # be in SmartServerVFSRequestHandler somewhere.
353
279
        try:
354
 
            self._command.setup_jail()
355
 
            try:
356
 
                return callable(*args, **kwargs)
357
 
            finally:
358
 
                self._command.teardown_jail()
 
280
            return callable(*args, **kwargs)
359
281
        except (KeyboardInterrupt, SystemExit):
360
282
            raise
361
283
        except Exception, err:
364
286
 
365
287
    def headers_received(self, headers):
366
288
        # Just a no-op at the moment.
367
 
        if 'hpss' in debug.debug_flags:
368
 
            self._trace('headers', repr(headers))
 
289
        pass
369
290
 
370
291
    def args_received(self, args):
371
292
        cmd = args[0]
373
294
        try:
374
295
            command = self._commands.get(cmd)
375
296
        except LookupError:
376
 
            if 'hpss' in debug.debug_flags:
377
 
                self._trace('hpss unknown request', 
378
 
                            cmd, repr(args)[1:-1])
379
297
            raise errors.UnknownSmartMethod(cmd)
380
 
        if 'hpss' in debug.debug_flags:
381
 
            from bzrlib.smart import vfs
382
 
            if issubclass(command, vfs.VfsRequest):
383
 
                action = 'hpss vfs req'
384
 
            else:
385
 
                action = 'hpss request'
386
 
            self._trace(action, 
387
 
                        '%s %s' % (cmd, repr(args)[1:-1]))
388
 
        self._command = command(
389
 
            self._backing_transport, self._root_client_path, self._jail_root)
 
298
        self._command = command(self._backing_transport)
390
299
        self._run_handler_code(self._command.execute, args, {})
391
300
 
392
301
    def end_received(self):
393
 
        if self._command is None:
394
 
            # no active command object, so ignore the event.
395
 
            return
396
302
        self._run_handler_code(self._command.do_end, (), {})
397
 
        if 'hpss' in debug.debug_flags:
398
 
            self._trace('end', '', include_time=True)
399
303
 
400
304
    def post_body_error_received(self, error_args):
401
305
        # Just a no-op at the moment.
409
313
        return ('FileExists', err.path)
410
314
    elif isinstance(err, errors.DirectoryNotEmpty):
411
315
        return ('DirectoryNotEmpty', err.path)
412
 
    elif isinstance(err, errors.IncompatibleRepositories):
413
 
        return ('IncompatibleRepositories', str(err.source), str(err.target),
414
 
            str(err.details))
415
316
    elif isinstance(err, errors.ShortReadvError):
416
317
        return ('ShortReadvError', err.path, str(err.offset), str(err.length),
417
318
                str(err.actual))
446
347
    elif isinstance(err, errors.TokenMismatch):
447
348
        return ('TokenMismatch', err.given_token, err.lock_token)
448
349
    elif isinstance(err, errors.LockContention):
449
 
        return ('LockContention',)
450
 
    elif isinstance(err, MemoryError):
451
 
        # GZ 2011-02-24: Copy bzrlib.trace -Dmem_dump functionality here?
452
 
        return ('MemoryError',)
 
350
        return ('LockContention', err.lock, err.msg)
453
351
    # Unserialisable error.  Log it, and return a generic error
454
352
    trace.log_exception_quietly()
455
 
    return ('error', trace._qualified_exception_name(err.__class__, True),
456
 
        str(err))
 
353
    return ('error', str(err))
457
354
 
458
355
 
459
356
class HelloRequest(SmartServerRequest):
503
400
    'Branch.get_tags_bytes', 'bzrlib.smart.branch',
504
401
    'SmartServerBranchGetTagsBytes')
505
402
request_handlers.register_lazy(
506
 
    'Branch.set_tags_bytes', 'bzrlib.smart.branch',
507
 
    'SmartServerBranchSetTagsBytes')
508
 
request_handlers.register_lazy(
509
 
    'Branch.heads_to_fetch', 'bzrlib.smart.branch',
510
 
    'SmartServerBranchHeadsToFetch')
511
 
request_handlers.register_lazy(
512
403
    'Branch.get_stacked_on_url', 'bzrlib.smart.branch', 'SmartServerBranchRequestGetStackedOnURL')
513
404
request_handlers.register_lazy(
514
405
    'Branch.last_revision_info', 'bzrlib.smart.branch', 'SmartServerBranchRequestLastRevisionInfo')
515
406
request_handlers.register_lazy(
516
407
    'Branch.lock_write', 'bzrlib.smart.branch', 'SmartServerBranchRequestLockWrite')
517
 
request_handlers.register_lazy( 'Branch.revision_history',
518
 
    'bzrlib.smart.branch', 'SmartServerRequestRevisionHistory')
519
 
request_handlers.register_lazy( 'Branch.set_config_option',
520
 
    'bzrlib.smart.branch', 'SmartServerBranchRequestSetConfigOption')
521
 
request_handlers.register_lazy( 'Branch.set_config_option_dict',
522
 
    'bzrlib.smart.branch', 'SmartServerBranchRequestSetConfigOptionDict')
523
 
request_handlers.register_lazy( 'Branch.set_last_revision',
524
 
    'bzrlib.smart.branch', 'SmartServerBranchRequestSetLastRevision')
 
408
request_handlers.register_lazy(
 
409
    'Branch.revision_history', 'bzrlib.smart.branch', 'SmartServerRequestRevisionHistory')
 
410
request_handlers.register_lazy(
 
411
    'Branch.set_last_revision', 'bzrlib.smart.branch', 'SmartServerBranchRequestSetLastRevision')
525
412
request_handlers.register_lazy(
526
413
    'Branch.set_last_revision_info', 'bzrlib.smart.branch',
527
414
    'SmartServerBranchRequestSetLastRevisionInfo')
529
416
    'Branch.set_last_revision_ex', 'bzrlib.smart.branch',
530
417
    'SmartServerBranchRequestSetLastRevisionEx')
531
418
request_handlers.register_lazy(
532
 
    'Branch.set_parent_location', 'bzrlib.smart.branch',
533
 
    'SmartServerBranchRequestSetParentLocation')
534
 
request_handlers.register_lazy(
535
419
    'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
536
420
request_handlers.register_lazy(
537
421
    'BzrDir.cloning_metadir', 'bzrlib.smart.bzrdir',
552
436
    'BzrDir.find_repositoryV3', 'bzrlib.smart.bzrdir',
553
437
    'SmartServerRequestFindRepositoryV3')
554
438
request_handlers.register_lazy(
555
 
    'BzrDir.get_config_file', 'bzrlib.smart.bzrdir',
556
 
    'SmartServerBzrDirRequestConfigFile')
557
 
request_handlers.register_lazy(
558
439
    'BzrDirFormat.initialize', 'bzrlib.smart.bzrdir',
559
440
    'SmartServerRequestInitializeBzrDir')
560
441
request_handlers.register_lazy(
561
 
    'BzrDirFormat.initialize_ex_1.16', 'bzrlib.smart.bzrdir',
562
 
    'SmartServerRequestBzrDirInitializeEx')
563
 
request_handlers.register_lazy(
564
 
    'BzrDir.open', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBzrDir')
565
 
request_handlers.register_lazy(
566
 
    'BzrDir.open_2.1', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBzrDir_2_1')
567
 
request_handlers.register_lazy(
568
442
    'BzrDir.open_branch', 'bzrlib.smart.bzrdir',
569
443
    'SmartServerRequestOpenBranch')
570
444
request_handlers.register_lazy(
571
445
    'BzrDir.open_branchV2', 'bzrlib.smart.bzrdir',
572
446
    'SmartServerRequestOpenBranchV2')
573
447
request_handlers.register_lazy(
574
 
    'BzrDir.open_branchV3', 'bzrlib.smart.bzrdir',
575
 
    'SmartServerRequestOpenBranchV3')
576
 
request_handlers.register_lazy(
577
448
    'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
578
449
request_handlers.register_lazy(
579
450
    'get', 'bzrlib.smart.vfs', 'GetRequest')
615
486
request_handlers.register_lazy(
616
487
    'Repository.insert_stream', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStream')
617
488
request_handlers.register_lazy(
618
 
    'Repository.insert_stream_1.19', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStream_1_19')
619
 
request_handlers.register_lazy(
620
489
    'Repository.insert_stream_locked', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStreamLocked')
621
490
request_handlers.register_lazy(
622
491
    'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
628
497
request_handlers.register_lazy(
629
498
    'Repository.unlock', 'bzrlib.smart.repository', 'SmartServerRepositoryUnlock')
630
499
request_handlers.register_lazy(
631
 
    'Repository.get_rev_id_for_revno', 'bzrlib.smart.repository',
632
 
    'SmartServerRepositoryGetRevIdForRevno')
633
 
request_handlers.register_lazy(
634
500
    'Repository.get_stream', 'bzrlib.smart.repository',
635
501
    'SmartServerRepositoryGetStream')
636
502
request_handlers.register_lazy(
637
 
    'Repository.get_stream_1.19', 'bzrlib.smart.repository',
638
 
    'SmartServerRepositoryGetStream_1_19')
639
 
request_handlers.register_lazy(
640
503
    'Repository.tarball', 'bzrlib.smart.repository',
641
504
    'SmartServerRepositoryTarball')
642
505
request_handlers.register_lazy(
645
508
    'stat', 'bzrlib.smart.vfs', 'StatRequest')
646
509
request_handlers.register_lazy(
647
510
    'Transport.is_readonly', 'bzrlib.smart.request', 'SmartServerIsReadonly')
 
511
request_handlers.register_lazy(
 
512
    'BzrDir.open', 'bzrlib.smart.bzrdir', 'SmartServerRequestOpenBzrDir')