/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/transport/http/wsgi.py

  • Committer: Andrew Bennetts
  • Date: 2007-12-13 22:22:58 UTC
  • mto: This revision was merged to the branch mainline in revision 3320.
  • Revision ID: andrew.bennetts@canonical.com-20071213222258-mh7mq5gtxqn4yceb
All WSGI tests passing, and manual testing works too.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006 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
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
"""WSGI application for bzr HTTP smart server.
18
18
 
22
22
 
23
23
from cStringIO import StringIO
24
24
 
25
 
from bzrlib.smart import protocol, medium
 
25
from bzrlib.smart import protocol
26
26
from bzrlib.transport import chroot, get_transport
27
27
from bzrlib.urlutils import local_path_to_url
28
 
 
29
 
 
30
 
def make_app(root, prefix, path_var='REQUEST_URI', readonly=True,
31
 
    load_plugins=True, enable_logging=True):
 
28
    
 
29
 
 
30
def make_app(root, prefix, path_var, readonly=True):
32
31
    """Convenience function to construct a WSGI bzr smart server.
33
 
 
 
32
    
34
33
    :param root: a local path that requests will be relative to.
35
34
    :param prefix: See RelpathSetter.
36
35
    :param path_var: See RelpathSetter.
40
39
        base_transport = get_transport('readonly+' + local_url)
41
40
    else:
42
41
        base_transport = get_transport(local_url)
43
 
    if load_plugins:
44
 
        from bzrlib.plugin import load_plugins
45
 
        load_plugins()
46
 
    if enable_logging:
47
 
        import bzrlib.trace
48
 
        bzrlib.trace.enable_default_logging()
49
42
    app = SmartWSGIApp(base_transport, prefix)
50
43
    app = RelpathSetter(app, '', path_var)
51
44
    return app
53
46
 
54
47
class RelpathSetter(object):
55
48
    """WSGI middleware to set 'bzrlib.relpath' in the environ.
56
 
 
 
49
    
57
50
    Different servers can invoke a SmartWSGIApp in different ways.  This
58
51
    middleware allows an adminstrator to configure how to the SmartWSGIApp will
59
52
    determine what path it should be serving for a given request for many common
65
58
    prefix="/some/prefix/" and path_var="REQUEST_URI" will set that request's
66
59
    'bzrlib.relpath' variable to "repo/branch".
67
60
    """
68
 
 
 
61
    
69
62
    def __init__(self, app, prefix='', path_var='REQUEST_URI'):
70
63
        """Constructor.
71
64
 
106
99
        # e.g. consider a smart server request for "get /etc/passwd" or
107
100
        # something.
108
101
        self.chroot_server = chroot.ChrootServer(backing_transport)
109
 
        self.chroot_server.start_server()
 
102
        self.chroot_server.setUp()
110
103
        self.backing_transport = get_transport(self.chroot_server.get_url())
111
104
        self.root_client_path = root_client_path
112
105
        # While the chroot server can technically be torn down at this point,
113
 
        # as all it does is remove the scheme registration from transport's
114
 
        # protocol dictionary, we don't *just in case* there are parts of
 
106
        # as all it does is remove the scheme registration from transport's 
 
107
        # protocol dictionary, we don't *just in case* there are parts of 
115
108
        # bzrlib that will invoke 'get_transport' on urls rather than cloning
116
109
        # around the existing transport.
117
 
        #self.chroot_server.stop_server()
 
110
        #self.chroot_server.tearDown()
118
111
 
119
112
    def __call__(self, environ, start_response):
120
113
        """WSGI application callable."""
123
116
            return []
124
117
 
125
118
        relpath = environ['bzrlib.relpath']
 
119
        # 1. subtract HTTP path from rcp ("adjusted rcp")
 
120
        #    1a.  If HTTP path unconsumed, clone backing transport with
 
121
        #         remainder ("adjusted transport")
 
122
        # 2. feed HPSS request path + adjusted/backing transport + adjusted
 
123
        #    rcp?
126
124
 
127
125
        if not relpath.startswith('/'):
128
126
            relpath = '/' + relpath
129
127
        if not relpath.endswith('/'):
130
128
            relpath += '/'
131
129
 
132
 
        # Compare the HTTP path (relpath) and root_client_path, and calculate
133
 
        # new relpath and root_client_path accordingly, to be used to build the
134
 
        # request.
 
130
        # (relpath='/foo/bar/', rcp='/foo/' -> (rp 'bar', arcp '/')
 
131
        #  relpath='/foo/', rcp='/foo/bar/' -> (rp '/', arcp '/bar')
135
132
        if relpath.startswith(self.root_client_path):
136
 
            # The relpath traverses all of the mandatory root client path.
137
 
            # Remove the root_client_path from the relpath, and set
138
 
            # adjusted_tcp to None to tell the request handler that no further
139
 
            # path translation is required.
 
133
            # The entire root client path is part of the relpath.
140
134
            adjusted_rcp = None
 
135
            # Consume the relpath part we just subtracted
141
136
            adjusted_relpath = relpath[len(self.root_client_path):]
142
137
        elif self.root_client_path.startswith(relpath):
143
138
            # The relpath traverses some of the mandatory root client path.
144
 
            # Subtract the relpath from the root_client_path, and set the
145
 
            # relpath to '.'.
 
139
            # Subtract relpath from HTTP request
146
140
            adjusted_rcp = '/' + self.root_client_path[len(relpath):]
 
141
            # The entire relpath was consumed.
147
142
            adjusted_relpath = '.'
148
143
        else:
149
144
            adjusted_rcp = self.root_client_path
151
146
 
152
147
        if adjusted_relpath.startswith('/'):
153
148
            adjusted_relpath = adjusted_relpath[1:]
154
 
        if adjusted_relpath.startswith('/'):
155
 
            raise AssertionError(adjusted_relpath)
 
149
        assert not adjusted_relpath.startswith('/')
156
150
 
157
151
        transport = self.backing_transport.clone(adjusted_relpath)
 
152
 
 
153
 
 
154
        # Random Notes:
 
155
 
 
156
        #transport = self.backing_transport.clone(relpath)
158
157
        out_buffer = StringIO()
159
158
        request_data_length = int(environ['CONTENT_LENGTH'])
160
159
        request_data_bytes = environ['wsgi.input'].read(request_data_length)
174
173
        return [response_data]
175
174
 
176
175
    def make_request(self, transport, write_func, request_bytes, rcp):
177
 
        protocol_factory, unused_bytes = medium._get_protocol_factory_for_bytes(
178
 
            request_bytes)
179
 
        server_protocol = protocol_factory(
180
 
            transport, write_func, rcp, self.backing_transport)
181
 
        server_protocol.accept_bytes(unused_bytes)
 
176
        # XXX: This duplicates the logic in
 
177
        # SmartServerStreamMedium._build_protocol.
 
178
        if request_bytes.startswith(protocol.REQUEST_VERSION_TWO):
 
179
            protocol_class = protocol.SmartServerRequestProtocolTwo
 
180
            request_bytes = request_bytes[len(protocol.REQUEST_VERSION_TWO):]
 
181
        else:
 
182
            protocol_class = protocol.SmartServerRequestProtocolOne
 
183
        server_protocol = protocol_class(
 
184
            transport, write_func, rcp)
 
185
        server_protocol.accept_bytes(request_bytes)
182
186
        return server_protocol