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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
"""Tests for WSGI application"""
19
19
from cStringIO import StringIO
21
21
from bzrlib import tests
22
from bzrlib.smart import protocol
22
from bzrlib.smart import medium, protocol
23
23
from bzrlib.transport.http import wsgi
24
24
from bzrlib.transport import chroot, memory
27
class TestWSGI(tests.TestCase):
30
tests.TestCase.setUp(self)
27
class WSGITestMixin(object):
34
29
def build_environ(self, updates=None):
35
30
"""Builds an environ dict with all fields required by PEP 333.
37
32
:param updates: a dict to that will be incorporated into the returned
38
33
dict using dict.update(updates).
82
85
self.read_response(iterable)
83
86
self.assertEqual('405 Method not allowed', self.status)
84
87
self.assertTrue(('Allow', 'POST') in self.headers)
86
def _fake_make_request(self, transport, write_func, bytes):
89
def _fake_make_request(self, transport, write_func, bytes, rcp):
87
90
request = FakeRequest(transport, write_func)
88
91
request.accept_bytes(bytes)
89
92
self.request = request
92
95
def test_smart_wsgi_app_uses_given_relpath(self):
93
96
# The SmartWSGIApp should use the "bzrlib.relpath" field from the
94
97
# WSGI environ to clone from its backing transport to get a specific
107
110
iterable = wsgi_app(environ, self.start_response)
108
111
response = self.read_response(iterable)
109
self.assertEqual([('clone', 'foo/bar')] , transport.calls)
112
self.assertEqual([('clone', 'foo/bar/')] , transport.calls)
111
114
def test_smart_wsgi_app_request_and_response(self):
112
115
# SmartWSGIApp reads the smart request from the 'wsgi.input' file-like
138
141
fake_app, prefix='/abc/', path_var='FOO')
139
142
wrapped_app({'FOO': '/abc/xyz/.bzr/smart'}, None)
140
143
self.assertEqual(['xyz'], calls)
142
145
def test_relpath_setter_bad_path_prefix(self):
143
146
# wsgi.RelpathSetter will reject paths with that don't match the prefix
144
147
# with a 404. This is probably a sign of misconfiguration; a server
151
154
{'FOO': 'AAA/abc/xyz/.bzr/smart'}, self.start_response)
152
155
self.read_response(iterable)
153
156
self.assertTrue(self.status.startswith('404'))
155
158
def test_relpath_setter_bad_path_suffix(self):
156
159
# Similar to test_relpath_setter_bad_path_prefix: wsgi.RelpathSetter
157
160
# will reject paths with that don't match the suffix '.bzr/smart' with a
179
182
backing_transport = app.app.backing_transport
180
183
chroot_backing_transport = backing_transport.server.backing_transport
181
184
self.assertEndsWith(chroot_backing_transport.base, 'a%20root/')
182
self.assertEqual(app.prefix, 'a prefix')
185
self.assertEqual(app.app.root_client_path, 'a prefix')
183
186
self.assertEqual(app.path_var, 'a path_var')
185
188
def test_incomplete_request(self):
186
189
transport = FakeTransport()
187
190
wsgi_app = wsgi.SmartWSGIApp(transport)
188
def make_request(transport, write_func, bytes):
191
def make_request(transport, write_func, bytes, root_client_path):
189
192
request = IncompleteRequest(transport, write_func)
190
193
request.accept_bytes(bytes)
191
194
self.request = request
242
245
protocol.RESPONSE_VERSION_TWO + 'success\nok\x012\n', response)
248
class TestWSGIJail(tests.TestCaseWithMemoryTransport, WSGITestMixin):
250
def make_hpss_wsgi_request(self, wsgi_relpath, *args):
251
write_buf = StringIO()
252
request_medium = medium.SmartSimplePipesClientMedium(
253
None, write_buf, 'fake:' + wsgi_relpath)
254
request_encoder = protocol.ProtocolThreeRequester(
255
request_medium.get_request())
256
request_encoder.call(*args)
258
environ = self.build_environ({
259
'REQUEST_METHOD': 'POST',
260
'CONTENT_LENGTH': len(write_buf.getvalue()),
261
'wsgi.input': write_buf,
262
'bzrlib.relpath': wsgi_relpath,
266
def test_jail_root(self):
267
"""The WSGI HPSS glue allows access to the whole WSGI backing
268
transport, regardless of which HTTP path the request was delivered
271
# make a branch in a shared repo
272
self.make_repository('repo', shared=True)
273
branch = self.make_bzrdir('repo/branch').create_branch()
274
# serve the repo via bzr+http WSGI
275
wsgi_app = wsgi.SmartWSGIApp(self.get_transport())
276
# send a request to /repo/branch that will have to access /repo.
277
environ = self.make_hpss_wsgi_request(
278
'/repo/branch', 'BzrDir.open_branchV2', '.')
279
iterable = wsgi_app(environ, self.start_response)
280
response_bytes = self.read_response(iterable)
281
self.assertEqual('200 OK', self.status)
282
# expect a successful response, rather than a jail break error
283
from bzrlib.tests.test_smart_transport import LoggingMessageHandler
284
message_handler = LoggingMessageHandler()
285
decoder = protocol.ProtocolThreeDecoder(
286
message_handler, expect_version_marker=True)
287
decoder.accept_bytes(response_bytes)
289
('structure', ('branch', branch._format.network_name()))
290
in message_handler.event_log)
245
293
class FakeRequest(object):
247
295
def __init__(self, transport, write_func):
248
296
self.transport = transport
249
297
self.write_func = write_func