~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/http/wsgi.py

  • Committer: Aaron Bentley
  • Date: 2007-02-06 14:52:16 UTC
  • mfrom: (2266 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2268.
  • Revision ID: abentley@panoramicfeedback.com-20070206145216-fcpi8o3ufvuzwbp9
Merge bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 
23
23
from cStringIO import StringIO
24
24
 
25
 
from bzrlib.smart import protocol, medium
26
 
from bzrlib.transport import chroot, get_transport
 
25
from bzrlib.transport import chroot, get_transport, smart
27
26
from bzrlib.urlutils import local_path_to_url
28
27
    
29
28
 
30
 
def make_app(root, prefix, path_var='REQUEST_URI', readonly=True):
 
29
def make_app(root, prefix, path_var, readonly=True):
31
30
    """Convenience function to construct a WSGI bzr smart server.
32
31
    
33
32
    :param root: a local path that requests will be relative to.
39
38
        base_transport = get_transport('readonly+' + local_url)
40
39
    else:
41
40
        base_transport = get_transport(local_url)
42
 
    app = SmartWSGIApp(base_transport, prefix)
43
 
    app = RelpathSetter(app, '', path_var)
 
41
    app = SmartWSGIApp(base_transport)
 
42
    app = RelpathSetter(app, prefix, path_var)
44
43
    return app
45
44
 
46
45
 
76
75
        path = environ[self.path_var]
77
76
        suffix = '/.bzr/smart'
78
77
        if not (path.startswith(self.prefix) and path.endswith(suffix)):
79
 
            start_response('404 Not Found', [])
 
78
            start_response('404 Not Found', {})
80
79
            return []
81
80
        environ['bzrlib.relpath'] = path[len(self.prefix):-len(suffix)]
82
81
        return self.app(environ, start_response)
85
84
class SmartWSGIApp(object):
86
85
    """A WSGI application for the bzr smart server."""
87
86
 
88
 
    def __init__(self, backing_transport, root_client_path='/'):
 
87
    def __init__(self, backing_transport):
89
88
        """Constructor.
90
89
 
91
90
        :param backing_transport: a transport.  Requests will be processed
92
91
            relative to this transport.
93
 
        :param root_client_path: the client path that maps to the root of
94
 
            backing_transport.  This is used to interpret relpaths received from
95
 
            the client.
96
92
        """
97
93
        # Use a ChrootTransportDecorator so that this web application won't
98
94
        # accidentally let people access locations they shouldn't.
99
95
        # e.g. consider a smart server request for "get /etc/passwd" or
100
96
        # something.
101
 
        self.chroot_server = chroot.ChrootServer(backing_transport)
102
 
        self.chroot_server.setUp()
103
 
        self.backing_transport = get_transport(self.chroot_server.get_url())
104
 
        self.root_client_path = root_client_path
105
 
        # While the chroot server can technically be torn down at this point,
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 
108
 
        # bzrlib that will invoke 'get_transport' on urls rather than cloning
109
 
        # around the existing transport.
110
 
        #self.chroot_server.tearDown()
 
97
        self.backing_transport = chroot.ChrootTransportDecorator(
 
98
            'chroot+' + backing_transport.base, _decorated=backing_transport)
111
99
 
112
100
    def __call__(self, environ, start_response):
113
101
        """WSGI application callable."""
116
104
            return []
117
105
 
118
106
        relpath = environ['bzrlib.relpath']
119
 
 
120
 
        if not relpath.startswith('/'):
121
 
            relpath = '/' + relpath
122
 
        if not relpath.endswith('/'):
123
 
            relpath += '/'
124
 
 
125
 
        # Compare the HTTP path (relpath) and root_client_path, and calculate
126
 
        # new relpath and root_client_path accordingly, to be used to build the
127
 
        # request.
128
 
        if relpath.startswith(self.root_client_path):
129
 
            # The relpath traverses all of the mandatory root client path.
130
 
            # Remove the root_client_path from the relpath, and set
131
 
            # adjusted_tcp to None to tell the request handler that no further
132
 
            # path translation is required.
133
 
            adjusted_rcp = None
134
 
            adjusted_relpath = relpath[len(self.root_client_path):]
135
 
        elif self.root_client_path.startswith(relpath):
136
 
            # The relpath traverses some of the mandatory root client path.
137
 
            # Subtract the relpath from the root_client_path, and set the
138
 
            # relpath to '.'.
139
 
            adjusted_rcp = '/' + self.root_client_path[len(relpath):]
140
 
            adjusted_relpath = '.'
141
 
        else:
142
 
            adjusted_rcp = self.root_client_path
143
 
            adjusted_relpath = relpath
144
 
 
145
 
        if adjusted_relpath.startswith('/'):
146
 
            adjusted_relpath = adjusted_relpath[1:]
147
 
        if adjusted_relpath.startswith('/'):
148
 
            raise AssertionError(adjusted_relpath)
149
 
 
150
 
        transport = self.backing_transport.clone(adjusted_relpath)
 
107
        transport = self.backing_transport.clone(relpath)
151
108
        out_buffer = StringIO()
 
109
        smart_protocol_request = self.make_request(transport, out_buffer.write)
152
110
        request_data_length = int(environ['CONTENT_LENGTH'])
153
111
        request_data_bytes = environ['wsgi.input'].read(request_data_length)
154
 
        smart_protocol_request = self.make_request(
155
 
            transport, out_buffer.write, request_data_bytes, adjusted_rcp)
 
112
        smart_protocol_request.accept_bytes(request_data_bytes)
156
113
        if smart_protocol_request.next_read_size() != 0:
157
114
            # The request appears to be incomplete, or perhaps it's just a
158
115
            # newer version we don't understand.  Regardless, all we can do
166
123
        start_response('200 OK', headers)
167
124
        return [response_data]
168
125
 
169
 
    def make_request(self, transport, write_func, request_bytes, rcp):
170
 
        protocol_factory, unused_bytes = medium._get_protocol_factory_for_bytes(
171
 
            request_bytes)
172
 
        server_protocol = protocol_factory(transport, write_func, rcp)
173
 
        server_protocol.accept_bytes(unused_bytes)
174
 
        return server_protocol
 
126
    def make_request(self, transport, write_func):
 
127
        return smart.SmartServerRequestProtocolOne(transport, write_func)