17
17
"""Tests for WSGI application"""
20
from ..sixish import (
23
from ..bzr.smart import medium, protocol
24
from ..transport.http import wsgi
25
from ..transport import chroot, memory
19
from cStringIO import StringIO
21
from bzrlib import tests
22
from bzrlib.smart import medium, message, protocol
23
from bzrlib.transport.http import wsgi
24
from bzrlib.transport import chroot, memory
28
27
class WSGITestMixin(object):
43
42
'SERVER_PROTOCOL': 'HTTP/1.0',
45
44
# Required WSGI variables
46
'wsgi.version': (1, 0),
45
'wsgi.version': (1,0),
47
46
'wsgi.url_scheme': 'http',
48
'wsgi.input': BytesIO(b''),
49
'wsgi.errors': BytesIO(),
47
'wsgi.input': StringIO(''),
48
'wsgi.errors': StringIO(),
50
49
'wsgi.multithread': False,
51
50
'wsgi.multiprocess': False,
52
51
'wsgi.run_once': True,
96
95
def test_smart_wsgi_app_uses_given_relpath(self):
97
# The SmartWSGIApp should use the "breezy.relpath" field from the
96
# The SmartWSGIApp should use the "bzrlib.relpath" field from the
98
97
# WSGI environ to clone from its backing transport to get a specific
99
98
# transport for this request.
100
99
transport = FakeTransport()
101
100
wsgi_app = wsgi.SmartWSGIApp(transport)
102
101
wsgi_app.backing_transport = transport
103
102
wsgi_app.make_request = self._fake_make_request
104
fake_input = BytesIO(b'fake request')
103
fake_input = StringIO('fake request')
105
104
environ = self.build_environ({
106
105
'REQUEST_METHOD': 'POST',
107
106
'CONTENT_LENGTH': len(fake_input.getvalue()),
108
107
'wsgi.input': fake_input,
109
'breezy.relpath': 'foo/bar',
108
'bzrlib.relpath': 'foo/bar',
111
110
iterable = wsgi_app(environ, self.start_response)
112
111
response = self.read_response(iterable)
113
self.assertEqual([('clone', 'foo/bar/')], transport.calls)
112
self.assertEqual([('clone', 'foo/bar/')] , transport.calls)
115
114
def test_smart_wsgi_app_request_and_response(self):
116
115
# SmartWSGIApp reads the smart request from the 'wsgi.input' file-like
117
116
# object in the environ dict, and returns the response via the iterable
118
117
# returned to the WSGI handler.
119
118
transport = memory.MemoryTransport()
120
transport.put_bytes('foo', b'some bytes')
119
transport.put_bytes('foo', 'some bytes')
121
120
wsgi_app = wsgi.SmartWSGIApp(transport)
122
121
wsgi_app.make_request = self._fake_make_request
123
fake_input = BytesIO(b'fake request')
122
fake_input = StringIO('fake request')
124
123
environ = self.build_environ({
125
124
'REQUEST_METHOD': 'POST',
126
125
'CONTENT_LENGTH': len(fake_input.getvalue()),
127
126
'wsgi.input': fake_input,
128
'breezy.relpath': 'foo',
127
'bzrlib.relpath': 'foo',
130
129
iterable = wsgi_app(environ, self.start_response)
131
130
response = self.read_response(iterable)
132
131
self.assertEqual('200 OK', self.status)
133
self.assertEqual(b'got bytes: fake request', response)
132
self.assertEqual('got bytes: fake request', response)
135
134
def test_relpath_setter(self):
136
# wsgi.RelpathSetter is WSGI "middleware" to set the 'breezy.relpath'
135
# wsgi.RelpathSetter is WSGI "middleware" to set the 'bzrlib.relpath'
140
138
def fake_app(environ, start_response):
141
calls.append(environ['breezy.relpath'])
139
calls.append(environ['bzrlib.relpath'])
142
140
wrapped_app = wsgi.RelpathSetter(
143
141
fake_app, prefix='/abc/', path_var='FOO')
144
142
wrapped_app({'FOO': '/abc/xyz/.bzr/smart'}, None)
199
196
wsgi_app.make_request = make_request
201
fake_input = BytesIO(b'incomplete request')
198
fake_input = StringIO('incomplete request')
202
199
environ = self.build_environ({
203
200
'REQUEST_METHOD': 'POST',
204
201
'CONTENT_LENGTH': len(fake_input.getvalue()),
205
202
'wsgi.input': fake_input,
206
'breezy.relpath': 'foo/bar',
203
'bzrlib.relpath': 'foo/bar',
208
205
iterable = wsgi_app(environ, self.start_response)
209
206
response = self.read_response(iterable)
210
207
self.assertEqual('200 OK', self.status)
211
self.assertEqual(b'error\x01incomplete request\n', response)
208
self.assertEqual('error\x01incomplete request\n', response)
213
210
def test_protocol_version_detection_one(self):
214
211
# SmartWSGIApp detects requests that don't start with
215
212
# REQUEST_VERSION_TWO as version one.
216
213
transport = memory.MemoryTransport()
217
214
wsgi_app = wsgi.SmartWSGIApp(transport)
218
fake_input = BytesIO(b'hello\n')
215
fake_input = StringIO('hello\n')
219
216
environ = self.build_environ({
220
217
'REQUEST_METHOD': 'POST',
221
218
'CONTENT_LENGTH': len(fake_input.getvalue()),
222
219
'wsgi.input': fake_input,
223
'breezy.relpath': 'foo',
220
'bzrlib.relpath': 'foo',
225
222
iterable = wsgi_app(environ, self.start_response)
226
223
response = self.read_response(iterable)
227
224
self.assertEqual('200 OK', self.status)
228
225
# Expect a version 1-encoded response.
229
self.assertEqual(b'ok\x012\n', response)
226
self.assertEqual('ok\x012\n', response)
231
228
def test_protocol_version_detection_two(self):
232
229
# SmartWSGIApp detects requests that start with REQUEST_VERSION_TWO
233
230
# as version two.
234
231
transport = memory.MemoryTransport()
235
232
wsgi_app = wsgi.SmartWSGIApp(transport)
236
fake_input = BytesIO(protocol.REQUEST_VERSION_TWO + b'hello\n')
233
fake_input = StringIO(protocol.REQUEST_VERSION_TWO + 'hello\n')
237
234
environ = self.build_environ({
238
235
'REQUEST_METHOD': 'POST',
239
236
'CONTENT_LENGTH': len(fake_input.getvalue()),
240
237
'wsgi.input': fake_input,
241
'breezy.relpath': 'foo',
238
'bzrlib.relpath': 'foo',
243
240
iterable = wsgi_app(environ, self.start_response)
244
241
response = self.read_response(iterable)
245
242
self.assertEqual('200 OK', self.status)
246
243
# Expect a version 2-encoded response.
247
244
self.assertEqual(
248
protocol.RESPONSE_VERSION_TWO + b'success\nok\x012\n', response)
245
protocol.RESPONSE_VERSION_TWO + 'success\nok\x012\n', response)
251
248
class TestWSGIJail(tests.TestCaseWithMemoryTransport, WSGITestMixin):
253
250
def make_hpss_wsgi_request(self, wsgi_relpath, *args):
254
write_buf = BytesIO()
251
write_buf = StringIO()
255
252
request_medium = medium.SmartSimplePipesClientMedium(
256
253
None, write_buf, 'fake:' + wsgi_relpath)
257
254
request_encoder = protocol.ProtocolThreeRequester(
274
271
# make a branch in a shared repo
275
272
self.make_repository('repo', shared=True)
276
branch = self.make_controldir('repo/branch').create_branch()
273
branch = self.make_bzrdir('repo/branch').create_branch()
277
274
# serve the repo via bzr+http WSGI
278
275
wsgi_app = wsgi.SmartWSGIApp(self.get_transport())
279
276
# send a request to /repo/branch that will have to access /repo.
280
277
environ = self.make_hpss_wsgi_request(
281
'/repo/branch', b'BzrDir.open_branchV2', b'.')
278
'/repo/branch', 'BzrDir.open_branchV2', '.')
282
279
iterable = wsgi_app(environ, self.start_response)
283
280
response_bytes = self.read_response(iterable)
284
281
self.assertEqual('200 OK', self.status)
285
282
# expect a successful response, rather than a jail break error
286
from breezy.tests.test_smart_transport import LoggingMessageHandler
283
from bzrlib.tests.test_smart_transport import LoggingMessageHandler
287
284
message_handler = LoggingMessageHandler()
288
285
decoder = protocol.ProtocolThreeDecoder(
289
286
message_handler, expect_version_marker=True)
290
287
decoder.accept_bytes(response_bytes)
292
('structure', (b'branch', branch._format.network_name()))
289
('structure', ('branch', branch._format.network_name()))
293
290
in message_handler.event_log)
298
295
def __init__(self, transport, write_func):
299
296
self.transport = transport
300
297
self.write_func = write_func
301
self.accepted_bytes = b''
298
self.accepted_bytes = ''
303
300
def accept_bytes(self, bytes):
304
301
self.accepted_bytes = bytes
305
self.write_func(b'got bytes: ' + bytes)
302
self.write_func('got bytes: ' + bytes)
307
304
def next_read_size(self):