~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: John Arbash Meinel
  • Date: 2007-03-01 19:58:38 UTC
  • mto: (2255.7.84 dirstate)
  • mto: This revision was merged to the branch mainline in revision 2322.
  • Revision ID: john@arbash-meinel.com-20070301195838-7p053os20qwr6qf7
Update WorkingTree4 so that it doesn't use a HashCache,
instead caching the sha values and stat fingerprint in the 'current'
section.

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
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
 
 
29
 
 
30
 
def make_app(root, prefix, path_var='REQUEST_URI', readonly=True,
31
 
    load_plugins=True, enable_logging=True):
 
27
    
 
28
 
 
29
def make_app(root, prefix, path_var, readonly=True):
32
30
    """Convenience function to construct a WSGI bzr smart server.
33
 
 
 
31
    
34
32
    :param root: a local path that requests will be relative to.
35
33
    :param prefix: See RelpathSetter.
36
34
    :param path_var: See RelpathSetter.
40
38
        base_transport = get_transport('readonly+' + local_url)
41
39
    else:
42
40
        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)
 
41
    app = SmartWSGIApp(base_transport)
 
42
    app = RelpathSetter(app, prefix, path_var)
51
43
    return app
52
44
 
53
45
 
54
46
class RelpathSetter(object):
55
47
    """WSGI middleware to set 'bzrlib.relpath' in the environ.
56
 
 
 
48
    
57
49
    Different servers can invoke a SmartWSGIApp in different ways.  This
58
50
    middleware allows an adminstrator to configure how to the SmartWSGIApp will
59
51
    determine what path it should be serving for a given request for many common
65
57
    prefix="/some/prefix/" and path_var="REQUEST_URI" will set that request's
66
58
    'bzrlib.relpath' variable to "repo/branch".
67
59
    """
68
 
 
 
60
    
69
61
    def __init__(self, app, prefix='', path_var='REQUEST_URI'):
70
62
        """Constructor.
71
63
 
83
75
        path = environ[self.path_var]
84
76
        suffix = '/.bzr/smart'
85
77
        if not (path.startswith(self.prefix) and path.endswith(suffix)):
86
 
            start_response('404 Not Found', [])
 
78
            start_response('404 Not Found', {})
87
79
            return []
88
80
        environ['bzrlib.relpath'] = path[len(self.prefix):-len(suffix)]
89
81
        return self.app(environ, start_response)
92
84
class SmartWSGIApp(object):
93
85
    """A WSGI application for the bzr smart server."""
94
86
 
95
 
    def __init__(self, backing_transport, root_client_path='/'):
 
87
    def __init__(self, backing_transport):
96
88
        """Constructor.
97
89
 
98
90
        :param backing_transport: a transport.  Requests will be processed
99
91
            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
92
        """
104
93
        # Use a ChrootTransportDecorator so that this web application won't
105
94
        # accidentally let people access locations they shouldn't.
106
95
        # e.g. consider a smart server request for "get /etc/passwd" or
107
96
        # something.
108
 
        self.chroot_server = chroot.ChrootServer(backing_transport)
109
 
        self.chroot_server.setUp()
110
 
        self.backing_transport = get_transport(self.chroot_server.get_url())
111
 
        self.root_client_path = root_client_path
112
 
        # 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
115
 
        # bzrlib that will invoke 'get_transport' on urls rather than cloning
116
 
        # around the existing transport.
117
 
        #self.chroot_server.tearDown()
 
97
        self.backing_transport = chroot.ChrootTransportDecorator(
 
98
            'chroot+' + backing_transport.base, _decorated=backing_transport)
118
99
 
119
100
    def __call__(self, environ, start_response):
120
101
        """WSGI application callable."""
123
104
            return []
124
105
 
125
106
        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)
 
107
        transport = self.backing_transport.clone(relpath)
158
108
        out_buffer = StringIO()
 
109
        smart_protocol_request = self.make_request(transport, out_buffer.write)
159
110
        request_data_length = int(environ['CONTENT_LENGTH'])
160
111
        request_data_bytes = environ['wsgi.input'].read(request_data_length)
161
 
        smart_protocol_request = self.make_request(
162
 
            transport, out_buffer.write, request_data_bytes, adjusted_rcp)
 
112
        smart_protocol_request.accept_bytes(request_data_bytes)
163
113
        if smart_protocol_request.next_read_size() != 0:
164
114
            # The request appears to be incomplete, or perhaps it's just a
165
115
            # newer version we don't understand.  Regardless, all we can do
173
123
        start_response('200 OK', headers)
174
124
        return [response_data]
175
125
 
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(transport, write_func, rcp)
180
 
        server_protocol.accept_bytes(unused_bytes)
181
 
        return server_protocol
 
126
    def make_request(self, transport, write_func):
 
127
        return smart.SmartServerRequestProtocolOne(transport, write_func)