~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Blake Winton
  • Date: 2007-10-16 16:02:01 UTC
  • mto: This revision was merged to the branch mainline in revision 2921.
  • Revision ID: bwinton@latte.ca-20071016160201-os2bci2ujf7in7an
Change 'print >> f,'s to 'f.write('s.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
    app = SmartWSGIApp(base_transport, prefix)
50
 
    app = RelpathSetter(app, '', path_var)
 
42
    app = SmartWSGIApp(base_transport)
 
43
    app = RelpathSetter(app, prefix, path_var)
51
44
    return app
52
45
 
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
 
92
85
class SmartWSGIApp(object):
93
86
    """A WSGI application for the bzr smart server."""
94
87
 
95
 
    def __init__(self, backing_transport, root_client_path='/'):
 
88
    def __init__(self, backing_transport):
96
89
        """Constructor.
97
90
 
98
91
        :param backing_transport: a transport.  Requests will be processed
99
92
            relative to this transport.
100
 
        :param root_client_path: the client path that maps to the root of
101
 
            backing_transport.  This is used to interpret relpaths received from
102
 
            the client.
103
93
        """
104
94
        # Use a ChrootTransportDecorator so that this web application won't
105
95
        # accidentally let people access locations they shouldn't.
106
96
        # e.g. consider a smart server request for "get /etc/passwd" or
107
97
        # something.
108
98
        self.chroot_server = chroot.ChrootServer(backing_transport)
109
 
        self.chroot_server.start_server()
 
99
        self.chroot_server.setUp()
110
100
        self.backing_transport = get_transport(self.chroot_server.get_url())
111
 
        self.root_client_path = root_client_path
112
101
        # 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
 
102
        # as all it does is remove the scheme registration from transport's 
 
103
        # protocol dictionary, we don't *just in case* there are parts of 
115
104
        # bzrlib that will invoke 'get_transport' on urls rather than cloning
116
105
        # around the existing transport.
117
 
        #self.chroot_server.stop_server()
 
106
        #self.chroot_server.tearDown()
118
107
 
119
108
    def __call__(self, environ, start_response):
120
109
        """WSGI application callable."""
123
112
            return []
124
113
 
125
114
        relpath = environ['bzrlib.relpath']
126
 
 
127
 
        if not relpath.startswith('/'):
128
 
            relpath = '/' + relpath
129
 
        if not relpath.endswith('/'):
130
 
            relpath += '/'
131
 
 
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.
135
 
        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.
140
 
            adjusted_rcp = None
141
 
            adjusted_relpath = relpath[len(self.root_client_path):]
142
 
        elif self.root_client_path.startswith(relpath):
143
 
            # 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 '.'.
146
 
            adjusted_rcp = '/' + self.root_client_path[len(relpath):]
147
 
            adjusted_relpath = '.'
148
 
        else:
149
 
            adjusted_rcp = self.root_client_path
150
 
            adjusted_relpath = relpath
151
 
 
152
 
        if adjusted_relpath.startswith('/'):
153
 
            adjusted_relpath = adjusted_relpath[1:]
154
 
        if adjusted_relpath.startswith('/'):
155
 
            raise AssertionError(adjusted_relpath)
156
 
 
157
 
        transport = self.backing_transport.clone(adjusted_relpath)
 
115
        transport = self.backing_transport.clone(relpath)
158
116
        out_buffer = StringIO()
159
117
        request_data_length = int(environ['CONTENT_LENGTH'])
160
118
        request_data_bytes = environ['wsgi.input'].read(request_data_length)
161
119
        smart_protocol_request = self.make_request(
162
 
            transport, out_buffer.write, request_data_bytes, adjusted_rcp)
 
120
            transport, out_buffer.write, request_data_bytes)
163
121
        if smart_protocol_request.next_read_size() != 0:
164
122
            # The request appears to be incomplete, or perhaps it's just a
165
123
            # newer version we don't understand.  Regardless, all we can do
173
131
        start_response('200 OK', headers)
174
132
        return [response_data]
175
133
 
176
 
    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)
 
134
    def make_request(self, transport, write_func, request_bytes):
 
135
        # XXX: This duplicates the logic in
 
136
        # SmartServerStreamMedium._build_protocol.
 
137
        if request_bytes.startswith(protocol.REQUEST_VERSION_TWO):
 
138
            protocol_class = protocol.SmartServerRequestProtocolTwo
 
139
            request_bytes = request_bytes[len(protocol.REQUEST_VERSION_TWO):]
 
140
        else:
 
141
            protocol_class = protocol.SmartServerRequestProtocolOne
 
142
        server_protocol = protocol_class(transport, write_func)
 
143
        server_protocol.accept_bytes(request_bytes)
182
144
        return server_protocol