test_proxy.py (1730:44912af5b3e6) test_proxy.py (1735:a0e0d4f90e51)
1import re
2import socket
3import time
4
5import pytest
6
7from conftest import run_process
8from conftest import skip_alert
1import re
2import socket
3import time
4
5import pytest
6
7from conftest import run_process
8from conftest import skip_alert
9from conftest import waitforsocket
10from unit.applications.lang.python import TestApplicationPython
11from unit.option import option
9from unit.applications.lang.python import TestApplicationPython
10from unit.option import option
11from unit.utils import waitforsocket
12
13
14class TestProxy(TestApplicationPython):
15 prerequisites = {'modules': {'python': 'any'}}
16
17 SERVER_PORT = 7999
18
19 @staticmethod
20 def run_server(server_port):
21 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
22 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
23
24 server_address = ('', server_port)
25 sock.bind(server_address)
26 sock.listen(5)
27
28 def recvall(sock):
29 buff_size = 4096
30 data = b''
31 while True:
32 part = sock.recv(buff_size)
33 data += part
34 if len(part) < buff_size:
35 break
36 return data
37
38 req = b"""HTTP/1.1 200 OK
39Content-Length: 10
40
41"""
42
43 while True:
44 connection, client_address = sock.accept()
45
46 data = recvall(connection).decode()
47
48 to_send = req
49
50 m = re.search(r'X-Len: (\d+)', data)
51 if m:
52 to_send += b'X' * int(m.group(1))
53
54 connection.sendall(to_send)
55
56 connection.close()
57
58 def get_http10(self, *args, **kwargs):
59 return self.get(*args, http_10=True, **kwargs)
60
61 def post_http10(self, *args, **kwargs):
62 return self.post(*args, http_10=True, **kwargs)
63
64 def setup_method(self):
65 run_process(self.run_server, self.SERVER_PORT)
66 waitforsocket(self.SERVER_PORT)
67
68 assert 'success' in self.conf(
69 {
70 "listeners": {
71 "*:7080": {"pass": "routes"},
72 "*:7081": {"pass": "applications/mirror"},
73 },
74 "routes": [{"action": {"proxy": "http://127.0.0.1:7081"}}],
75 "applications": {
76 "mirror": {
77 "type": "python",
78 "processes": {"spare": 0},
79 "path": option.test_dir + "/python/mirror",
80 "working_directory": option.test_dir
81 + "/python/mirror",
82 "module": "wsgi",
83 },
84 "custom_header": {
85 "type": "python",
86 "processes": {"spare": 0},
87 "path": option.test_dir + "/python/custom_header",
88 "working_directory": option.test_dir
89 + "/python/custom_header",
90 "module": "wsgi",
91 },
92 "delayed": {
93 "type": "python",
94 "processes": {"spare": 0},
95 "path": option.test_dir + "/python/delayed",
96 "working_directory": option.test_dir
97 + "/python/delayed",
98 "module": "wsgi",
99 },
100 },
101 }
102 ), 'proxy initial configuration'
103
104 def test_proxy_http10(self):
105 for _ in range(10):
106 assert self.get_http10()['status'] == 200, 'status'
107
108 def test_proxy_chain(self):
109 assert 'success' in self.conf(
110 {
111 "listeners": {
112 "*:7080": {"pass": "routes/first"},
113 "*:7081": {"pass": "routes/second"},
114 "*:7082": {"pass": "routes/third"},
115 "*:7083": {"pass": "routes/fourth"},
116 "*:7084": {"pass": "routes/fifth"},
117 "*:7085": {"pass": "applications/mirror"},
118 },
119 "routes": {
120 "first": [{"action": {"proxy": "http://127.0.0.1:7081"}}],
121 "second": [{"action": {"proxy": "http://127.0.0.1:7082"}}],
122 "third": [{"action": {"proxy": "http://127.0.0.1:7083"}}],
123 "fourth": [{"action": {"proxy": "http://127.0.0.1:7084"}}],
124 "fifth": [{"action": {"proxy": "http://127.0.0.1:7085"}}],
125 },
126 "applications": {
127 "mirror": {
128 "type": "python",
129 "processes": {"spare": 0},
130 "path": option.test_dir + "/python/mirror",
131 "working_directory": option.test_dir
132 + "/python/mirror",
133 "module": "wsgi",
134 }
135 },
136 }
137 ), 'proxy chain configuration'
138
139 assert self.get_http10()['status'] == 200, 'status'
140
141 def test_proxy_body(self):
142 payload = '0123456789'
143 for _ in range(10):
144 resp = self.post_http10(body=payload)
145
146 assert resp['status'] == 200, 'status'
147 assert resp['body'] == payload, 'body'
148
149 payload = 'X' * 4096
150 for _ in range(10):
151 resp = self.post_http10(body=payload)
152
153 assert resp['status'] == 200, 'status'
154 assert resp['body'] == payload, 'body'
155
156 payload = 'X' * 4097
157 for _ in range(10):
158 resp = self.post_http10(body=payload)
159
160 assert resp['status'] == 200, 'status'
161 assert resp['body'] == payload, 'body'
162
163 payload = 'X' * 4096 * 256
164 for _ in range(10):
165 resp = self.post_http10(body=payload, read_buffer_size=4096 * 128)
166
167 assert resp['status'] == 200, 'status'
168 assert resp['body'] == payload, 'body'
169
170 payload = 'X' * 4096 * 257
171 for _ in range(10):
172 resp = self.post_http10(body=payload, read_buffer_size=4096 * 128)
173
174 assert resp['status'] == 200, 'status'
175 assert resp['body'] == payload, 'body'
176
177 self.conf({'http': {'max_body_size': 32 * 1024 * 1024}}, 'settings')
178
179 payload = '0123456789abcdef' * 32 * 64 * 1024
180 resp = self.post_http10(body=payload, read_buffer_size=1024 * 1024)
181 assert resp['status'] == 200, 'status'
182 assert resp['body'] == payload, 'body'
183
184 def test_proxy_parallel(self):
185 payload = 'X' * 4096 * 257
186 buff_size = 4096 * 258
187
188 socks = []
189 for i in range(10):
190 _, sock = self.post_http10(
191 body=payload + str(i),
192 start=True,
193 no_recv=True,
194 read_buffer_size=buff_size,
195 )
196 socks.append(sock)
197
198 for i in range(10):
199 resp = self.recvall(socks[i], buff_size=buff_size).decode()
200 socks[i].close()
201
202 resp = self._resp_to_dict(resp)
203
204 assert resp['status'] == 200, 'status'
205 assert resp['body'] == payload + str(i), 'body'
206
207 def test_proxy_header(self):
208 assert 'success' in self.conf(
209 {"pass": "applications/custom_header"}, 'listeners/*:7081'
210 ), 'custom_header configure'
211
212 header_value = 'blah'
213 assert (
214 self.get_http10(
215 headers={'Host': 'localhost', 'Custom-Header': header_value}
216 )['headers']['Custom-Header']
217 == header_value
218 ), 'custom header'
219
220 header_value = r'(),/:;<=>?@[\]{}\t !#$%&\'*+-.^_`|~'
221 assert (
222 self.get_http10(
223 headers={'Host': 'localhost', 'Custom-Header': header_value}
224 )['headers']['Custom-Header']
225 == header_value
226 ), 'custom header 2'
227
228 header_value = 'X' * 4096
229 assert (
230 self.get_http10(
231 headers={'Host': 'localhost', 'Custom-Header': header_value}
232 )['headers']['Custom-Header']
233 == header_value
234 ), 'custom header 3'
235
236 header_value = 'X' * 8191
237 assert (
238 self.get_http10(
239 headers={'Host': 'localhost', 'Custom-Header': header_value}
240 )['headers']['Custom-Header']
241 == header_value
242 ), 'custom header 4'
243
244 header_value = 'X' * 8192
245 assert (
246 self.get_http10(
247 headers={'Host': 'localhost', 'Custom-Header': header_value}
248 )['status']
249 == 431
250 ), 'custom header 5'
251
252 def test_proxy_fragmented(self):
253 _, sock = self.http(
254 b"""GET / HTT""", raw=True, start=True, no_recv=True
255 )
256
257 time.sleep(1)
258
259 sock.sendall("P/1.0\r\nHost: localhos".encode())
260
261 time.sleep(1)
262
263 sock.sendall("t\r\n\r\n".encode())
264
265 assert re.search(
266 '200 OK', self.recvall(sock).decode()
267 ), 'fragmented send'
268 sock.close()
269
270 def test_proxy_fragmented_close(self):
271 _, sock = self.http(
272 b"""GET / HTT""", raw=True, start=True, no_recv=True
273 )
274
275 time.sleep(1)
276
277 sock.sendall("P/1.0\r\nHo".encode())
278
279 sock.close()
280
281 def test_proxy_fragmented_body(self):
282 _, sock = self.http(
283 b"""GET / HTT""", raw=True, start=True, no_recv=True
284 )
285
286 time.sleep(1)
287
288 sock.sendall("P/1.0\r\nHost: localhost\r\n".encode())
289 sock.sendall("Content-Length: 30000\r\n".encode())
290
291 time.sleep(1)
292
293 sock.sendall("\r\n".encode())
294 sock.sendall(("X" * 10000).encode())
295
296 time.sleep(1)
297
298 sock.sendall(("X" * 10000).encode())
299
300 time.sleep(1)
301
302 sock.sendall(("X" * 10000).encode())
303
304 resp = self._resp_to_dict(self.recvall(sock).decode())
305 sock.close()
306
307 assert resp['status'] == 200, 'status'
308 assert resp['body'] == "X" * 30000, 'body'
309
310 def test_proxy_fragmented_body_close(self):
311 _, sock = self.http(
312 b"""GET / HTT""", raw=True, start=True, no_recv=True
313 )
314
315 time.sleep(1)
316
317 sock.sendall("P/1.0\r\nHost: localhost\r\n".encode())
318 sock.sendall("Content-Length: 30000\r\n".encode())
319
320 time.sleep(1)
321
322 sock.sendall("\r\n".encode())
323 sock.sendall(("X" * 10000).encode())
324
325 sock.close()
326
327 def test_proxy_nowhere(self):
328 assert 'success' in self.conf(
329 [{"action": {"proxy": "http://127.0.0.1:7082"}}], 'routes'
330 ), 'proxy path changed'
331
332 assert self.get_http10()['status'] == 502, 'status'
333
334 def test_proxy_ipv6(self):
335 assert 'success' in self.conf(
336 {
337 "*:7080": {"pass": "routes"},
338 "[::1]:7081": {'application': 'mirror'},
339 },
340 'listeners',
341 ), 'add ipv6 listener configure'
342
343 assert 'success' in self.conf(
344 [{"action": {"proxy": "http://[::1]:7081"}}], 'routes'
345 ), 'proxy ipv6 configure'
346
347 assert self.get_http10()['status'] == 200, 'status'
348
349 def test_proxy_unix(self, temp_dir):
350 addr = temp_dir + '/sock'
351
352 assert 'success' in self.conf(
353 {
354 "*:7080": {"pass": "routes"},
355 "unix:" + addr: {'application': 'mirror'},
356 },
357 'listeners',
358 ), 'add unix listener configure'
359
360 assert 'success' in self.conf(
361 [{"action": {"proxy": 'http://unix:' + addr}}], 'routes'
362 ), 'proxy unix configure'
363
364 assert self.get_http10()['status'] == 200, 'status'
365
366 def test_proxy_delayed(self):
367 assert 'success' in self.conf(
368 {"pass": "applications/delayed"}, 'listeners/*:7081'
369 ), 'delayed configure'
370
371 body = '0123456789' * 1000
372 resp = self.post_http10(
373 headers={
374 'Host': 'localhost',
375 'Content-Type': 'text/html',
376 'Content-Length': str(len(body)),
377 'X-Parts': '2',
378 'X-Delay': '1',
379 },
380 body=body,
381 )
382
383 assert resp['status'] == 200, 'status'
384 assert resp['body'] == body, 'body'
385
386 resp = self.post_http10(
387 headers={
388 'Host': 'localhost',
389 'Content-Type': 'text/html',
390 'Content-Length': str(len(body)),
391 'X-Parts': '2',
392 'X-Delay': '1',
393 },
394 body=body,
395 )
396
397 assert resp['status'] == 200, 'status'
398 assert resp['body'] == body, 'body'
399
400 def test_proxy_delayed_close(self):
401 assert 'success' in self.conf(
402 {"pass": "applications/delayed"}, 'listeners/*:7081'
403 ), 'delayed configure'
404
405 _, sock = self.post_http10(
406 headers={
407 'Host': 'localhost',
408 'Content-Type': 'text/html',
409 'Content-Length': '10000',
410 'X-Parts': '3',
411 'X-Delay': '1',
412 },
413 body='0123456789' * 1000,
414 start=True,
415 no_recv=True,
416 )
417
418 assert re.search('200 OK', sock.recv(100).decode()), 'first'
419 sock.close()
420
421 _, sock = self.post_http10(
422 headers={
423 'Host': 'localhost',
424 'Content-Type': 'text/html',
425 'Content-Length': '10000',
426 'X-Parts': '3',
427 'X-Delay': '1',
428 },
429 body='0123456789' * 1000,
430 start=True,
431 no_recv=True,
432 )
433
434 assert re.search('200 OK', sock.recv(100).decode()), 'second'
435 sock.close()
436
437 @pytest.mark.skip('not yet')
438 def test_proxy_content_length(self):
439 assert 'success' in self.conf(
440 [
441 {
442 "action": {
443 "proxy": "http://127.0.0.1:" + str(self.SERVER_PORT)
444 }
445 }
446 ],
447 'routes',
448 ), 'proxy backend configure'
449
450 resp = self.get_http10()
451 assert len(resp['body']) == 0, 'body lt Content-Length 0'
452
453 resp = self.get_http10(headers={'Host': 'localhost', 'X-Len': '5'})
454 assert len(resp['body']) == 5, 'body lt Content-Length 5'
455
456 resp = self.get_http10(headers={'Host': 'localhost', 'X-Len': '9'})
457 assert len(resp['body']) == 9, 'body lt Content-Length 9'
458
459 resp = self.get_http10(headers={'Host': 'localhost', 'X-Len': '11'})
460 assert len(resp['body']) == 10, 'body gt Content-Length 11'
461
462 resp = self.get_http10(headers={'Host': 'localhost', 'X-Len': '15'})
463 assert len(resp['body']) == 10, 'body gt Content-Length 15'
464
465 def test_proxy_invalid(self):
466 def check_proxy(proxy):
467 assert 'error' in \
468 self.conf([{"action": {"proxy": proxy}}], 'routes'), \
469 'proxy invalid'
470
471 check_proxy('blah')
472 check_proxy('/blah')
473 check_proxy('unix:/blah')
474 check_proxy('http://blah')
475 check_proxy('http://127.0.0.1')
476 check_proxy('http://127.0.0.1:')
477 check_proxy('http://127.0.0.1:blah')
478 check_proxy('http://127.0.0.1:-1')
479 check_proxy('http://127.0.0.1:7080b')
480 check_proxy('http://[]')
481 check_proxy('http://[]:7080')
482 check_proxy('http://[:]:7080')
483 check_proxy('http://[::7080')
484
485 def test_proxy_loop(self):
486 skip_alert(
487 r'socket.*failed',
488 r'accept.*failed',
489 r'new connections are not accepted',
490 )
491 self.conf(
492 {
493 "listeners": {
494 "*:7080": {"pass": "routes"},
495 "*:7081": {"pass": "applications/mirror"},
496 "*:7082": {"pass": "routes"},
497 },
498 "routes": [{"action": {"proxy": "http://127.0.0.1:7082"}}],
499 "applications": {
500 "mirror": {
501 "type": "python",
502 "processes": {"spare": 0},
503 "path": option.test_dir + "/python/mirror",
504 "working_directory": option.test_dir + "/python/mirror",
505 "module": "wsgi",
506 },
507 },
508 }
509 )
510
511 self.get_http10(no_recv=True)
512 self.get_http10(read_timeout=1)
12
13
14class TestProxy(TestApplicationPython):
15 prerequisites = {'modules': {'python': 'any'}}
16
17 SERVER_PORT = 7999
18
19 @staticmethod
20 def run_server(server_port):
21 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
22 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
23
24 server_address = ('', server_port)
25 sock.bind(server_address)
26 sock.listen(5)
27
28 def recvall(sock):
29 buff_size = 4096
30 data = b''
31 while True:
32 part = sock.recv(buff_size)
33 data += part
34 if len(part) < buff_size:
35 break
36 return data
37
38 req = b"""HTTP/1.1 200 OK
39Content-Length: 10
40
41"""
42
43 while True:
44 connection, client_address = sock.accept()
45
46 data = recvall(connection).decode()
47
48 to_send = req
49
50 m = re.search(r'X-Len: (\d+)', data)
51 if m:
52 to_send += b'X' * int(m.group(1))
53
54 connection.sendall(to_send)
55
56 connection.close()
57
58 def get_http10(self, *args, **kwargs):
59 return self.get(*args, http_10=True, **kwargs)
60
61 def post_http10(self, *args, **kwargs):
62 return self.post(*args, http_10=True, **kwargs)
63
64 def setup_method(self):
65 run_process(self.run_server, self.SERVER_PORT)
66 waitforsocket(self.SERVER_PORT)
67
68 assert 'success' in self.conf(
69 {
70 "listeners": {
71 "*:7080": {"pass": "routes"},
72 "*:7081": {"pass": "applications/mirror"},
73 },
74 "routes": [{"action": {"proxy": "http://127.0.0.1:7081"}}],
75 "applications": {
76 "mirror": {
77 "type": "python",
78 "processes": {"spare": 0},
79 "path": option.test_dir + "/python/mirror",
80 "working_directory": option.test_dir
81 + "/python/mirror",
82 "module": "wsgi",
83 },
84 "custom_header": {
85 "type": "python",
86 "processes": {"spare": 0},
87 "path": option.test_dir + "/python/custom_header",
88 "working_directory": option.test_dir
89 + "/python/custom_header",
90 "module": "wsgi",
91 },
92 "delayed": {
93 "type": "python",
94 "processes": {"spare": 0},
95 "path": option.test_dir + "/python/delayed",
96 "working_directory": option.test_dir
97 + "/python/delayed",
98 "module": "wsgi",
99 },
100 },
101 }
102 ), 'proxy initial configuration'
103
104 def test_proxy_http10(self):
105 for _ in range(10):
106 assert self.get_http10()['status'] == 200, 'status'
107
108 def test_proxy_chain(self):
109 assert 'success' in self.conf(
110 {
111 "listeners": {
112 "*:7080": {"pass": "routes/first"},
113 "*:7081": {"pass": "routes/second"},
114 "*:7082": {"pass": "routes/third"},
115 "*:7083": {"pass": "routes/fourth"},
116 "*:7084": {"pass": "routes/fifth"},
117 "*:7085": {"pass": "applications/mirror"},
118 },
119 "routes": {
120 "first": [{"action": {"proxy": "http://127.0.0.1:7081"}}],
121 "second": [{"action": {"proxy": "http://127.0.0.1:7082"}}],
122 "third": [{"action": {"proxy": "http://127.0.0.1:7083"}}],
123 "fourth": [{"action": {"proxy": "http://127.0.0.1:7084"}}],
124 "fifth": [{"action": {"proxy": "http://127.0.0.1:7085"}}],
125 },
126 "applications": {
127 "mirror": {
128 "type": "python",
129 "processes": {"spare": 0},
130 "path": option.test_dir + "/python/mirror",
131 "working_directory": option.test_dir
132 + "/python/mirror",
133 "module": "wsgi",
134 }
135 },
136 }
137 ), 'proxy chain configuration'
138
139 assert self.get_http10()['status'] == 200, 'status'
140
141 def test_proxy_body(self):
142 payload = '0123456789'
143 for _ in range(10):
144 resp = self.post_http10(body=payload)
145
146 assert resp['status'] == 200, 'status'
147 assert resp['body'] == payload, 'body'
148
149 payload = 'X' * 4096
150 for _ in range(10):
151 resp = self.post_http10(body=payload)
152
153 assert resp['status'] == 200, 'status'
154 assert resp['body'] == payload, 'body'
155
156 payload = 'X' * 4097
157 for _ in range(10):
158 resp = self.post_http10(body=payload)
159
160 assert resp['status'] == 200, 'status'
161 assert resp['body'] == payload, 'body'
162
163 payload = 'X' * 4096 * 256
164 for _ in range(10):
165 resp = self.post_http10(body=payload, read_buffer_size=4096 * 128)
166
167 assert resp['status'] == 200, 'status'
168 assert resp['body'] == payload, 'body'
169
170 payload = 'X' * 4096 * 257
171 for _ in range(10):
172 resp = self.post_http10(body=payload, read_buffer_size=4096 * 128)
173
174 assert resp['status'] == 200, 'status'
175 assert resp['body'] == payload, 'body'
176
177 self.conf({'http': {'max_body_size': 32 * 1024 * 1024}}, 'settings')
178
179 payload = '0123456789abcdef' * 32 * 64 * 1024
180 resp = self.post_http10(body=payload, read_buffer_size=1024 * 1024)
181 assert resp['status'] == 200, 'status'
182 assert resp['body'] == payload, 'body'
183
184 def test_proxy_parallel(self):
185 payload = 'X' * 4096 * 257
186 buff_size = 4096 * 258
187
188 socks = []
189 for i in range(10):
190 _, sock = self.post_http10(
191 body=payload + str(i),
192 start=True,
193 no_recv=True,
194 read_buffer_size=buff_size,
195 )
196 socks.append(sock)
197
198 for i in range(10):
199 resp = self.recvall(socks[i], buff_size=buff_size).decode()
200 socks[i].close()
201
202 resp = self._resp_to_dict(resp)
203
204 assert resp['status'] == 200, 'status'
205 assert resp['body'] == payload + str(i), 'body'
206
207 def test_proxy_header(self):
208 assert 'success' in self.conf(
209 {"pass": "applications/custom_header"}, 'listeners/*:7081'
210 ), 'custom_header configure'
211
212 header_value = 'blah'
213 assert (
214 self.get_http10(
215 headers={'Host': 'localhost', 'Custom-Header': header_value}
216 )['headers']['Custom-Header']
217 == header_value
218 ), 'custom header'
219
220 header_value = r'(),/:;<=>?@[\]{}\t !#$%&\'*+-.^_`|~'
221 assert (
222 self.get_http10(
223 headers={'Host': 'localhost', 'Custom-Header': header_value}
224 )['headers']['Custom-Header']
225 == header_value
226 ), 'custom header 2'
227
228 header_value = 'X' * 4096
229 assert (
230 self.get_http10(
231 headers={'Host': 'localhost', 'Custom-Header': header_value}
232 )['headers']['Custom-Header']
233 == header_value
234 ), 'custom header 3'
235
236 header_value = 'X' * 8191
237 assert (
238 self.get_http10(
239 headers={'Host': 'localhost', 'Custom-Header': header_value}
240 )['headers']['Custom-Header']
241 == header_value
242 ), 'custom header 4'
243
244 header_value = 'X' * 8192
245 assert (
246 self.get_http10(
247 headers={'Host': 'localhost', 'Custom-Header': header_value}
248 )['status']
249 == 431
250 ), 'custom header 5'
251
252 def test_proxy_fragmented(self):
253 _, sock = self.http(
254 b"""GET / HTT""", raw=True, start=True, no_recv=True
255 )
256
257 time.sleep(1)
258
259 sock.sendall("P/1.0\r\nHost: localhos".encode())
260
261 time.sleep(1)
262
263 sock.sendall("t\r\n\r\n".encode())
264
265 assert re.search(
266 '200 OK', self.recvall(sock).decode()
267 ), 'fragmented send'
268 sock.close()
269
270 def test_proxy_fragmented_close(self):
271 _, sock = self.http(
272 b"""GET / HTT""", raw=True, start=True, no_recv=True
273 )
274
275 time.sleep(1)
276
277 sock.sendall("P/1.0\r\nHo".encode())
278
279 sock.close()
280
281 def test_proxy_fragmented_body(self):
282 _, sock = self.http(
283 b"""GET / HTT""", raw=True, start=True, no_recv=True
284 )
285
286 time.sleep(1)
287
288 sock.sendall("P/1.0\r\nHost: localhost\r\n".encode())
289 sock.sendall("Content-Length: 30000\r\n".encode())
290
291 time.sleep(1)
292
293 sock.sendall("\r\n".encode())
294 sock.sendall(("X" * 10000).encode())
295
296 time.sleep(1)
297
298 sock.sendall(("X" * 10000).encode())
299
300 time.sleep(1)
301
302 sock.sendall(("X" * 10000).encode())
303
304 resp = self._resp_to_dict(self.recvall(sock).decode())
305 sock.close()
306
307 assert resp['status'] == 200, 'status'
308 assert resp['body'] == "X" * 30000, 'body'
309
310 def test_proxy_fragmented_body_close(self):
311 _, sock = self.http(
312 b"""GET / HTT""", raw=True, start=True, no_recv=True
313 )
314
315 time.sleep(1)
316
317 sock.sendall("P/1.0\r\nHost: localhost\r\n".encode())
318 sock.sendall("Content-Length: 30000\r\n".encode())
319
320 time.sleep(1)
321
322 sock.sendall("\r\n".encode())
323 sock.sendall(("X" * 10000).encode())
324
325 sock.close()
326
327 def test_proxy_nowhere(self):
328 assert 'success' in self.conf(
329 [{"action": {"proxy": "http://127.0.0.1:7082"}}], 'routes'
330 ), 'proxy path changed'
331
332 assert self.get_http10()['status'] == 502, 'status'
333
334 def test_proxy_ipv6(self):
335 assert 'success' in self.conf(
336 {
337 "*:7080": {"pass": "routes"},
338 "[::1]:7081": {'application': 'mirror'},
339 },
340 'listeners',
341 ), 'add ipv6 listener configure'
342
343 assert 'success' in self.conf(
344 [{"action": {"proxy": "http://[::1]:7081"}}], 'routes'
345 ), 'proxy ipv6 configure'
346
347 assert self.get_http10()['status'] == 200, 'status'
348
349 def test_proxy_unix(self, temp_dir):
350 addr = temp_dir + '/sock'
351
352 assert 'success' in self.conf(
353 {
354 "*:7080": {"pass": "routes"},
355 "unix:" + addr: {'application': 'mirror'},
356 },
357 'listeners',
358 ), 'add unix listener configure'
359
360 assert 'success' in self.conf(
361 [{"action": {"proxy": 'http://unix:' + addr}}], 'routes'
362 ), 'proxy unix configure'
363
364 assert self.get_http10()['status'] == 200, 'status'
365
366 def test_proxy_delayed(self):
367 assert 'success' in self.conf(
368 {"pass": "applications/delayed"}, 'listeners/*:7081'
369 ), 'delayed configure'
370
371 body = '0123456789' * 1000
372 resp = self.post_http10(
373 headers={
374 'Host': 'localhost',
375 'Content-Type': 'text/html',
376 'Content-Length': str(len(body)),
377 'X-Parts': '2',
378 'X-Delay': '1',
379 },
380 body=body,
381 )
382
383 assert resp['status'] == 200, 'status'
384 assert resp['body'] == body, 'body'
385
386 resp = self.post_http10(
387 headers={
388 'Host': 'localhost',
389 'Content-Type': 'text/html',
390 'Content-Length': str(len(body)),
391 'X-Parts': '2',
392 'X-Delay': '1',
393 },
394 body=body,
395 )
396
397 assert resp['status'] == 200, 'status'
398 assert resp['body'] == body, 'body'
399
400 def test_proxy_delayed_close(self):
401 assert 'success' in self.conf(
402 {"pass": "applications/delayed"}, 'listeners/*:7081'
403 ), 'delayed configure'
404
405 _, sock = self.post_http10(
406 headers={
407 'Host': 'localhost',
408 'Content-Type': 'text/html',
409 'Content-Length': '10000',
410 'X-Parts': '3',
411 'X-Delay': '1',
412 },
413 body='0123456789' * 1000,
414 start=True,
415 no_recv=True,
416 )
417
418 assert re.search('200 OK', sock.recv(100).decode()), 'first'
419 sock.close()
420
421 _, sock = self.post_http10(
422 headers={
423 'Host': 'localhost',
424 'Content-Type': 'text/html',
425 'Content-Length': '10000',
426 'X-Parts': '3',
427 'X-Delay': '1',
428 },
429 body='0123456789' * 1000,
430 start=True,
431 no_recv=True,
432 )
433
434 assert re.search('200 OK', sock.recv(100).decode()), 'second'
435 sock.close()
436
437 @pytest.mark.skip('not yet')
438 def test_proxy_content_length(self):
439 assert 'success' in self.conf(
440 [
441 {
442 "action": {
443 "proxy": "http://127.0.0.1:" + str(self.SERVER_PORT)
444 }
445 }
446 ],
447 'routes',
448 ), 'proxy backend configure'
449
450 resp = self.get_http10()
451 assert len(resp['body']) == 0, 'body lt Content-Length 0'
452
453 resp = self.get_http10(headers={'Host': 'localhost', 'X-Len': '5'})
454 assert len(resp['body']) == 5, 'body lt Content-Length 5'
455
456 resp = self.get_http10(headers={'Host': 'localhost', 'X-Len': '9'})
457 assert len(resp['body']) == 9, 'body lt Content-Length 9'
458
459 resp = self.get_http10(headers={'Host': 'localhost', 'X-Len': '11'})
460 assert len(resp['body']) == 10, 'body gt Content-Length 11'
461
462 resp = self.get_http10(headers={'Host': 'localhost', 'X-Len': '15'})
463 assert len(resp['body']) == 10, 'body gt Content-Length 15'
464
465 def test_proxy_invalid(self):
466 def check_proxy(proxy):
467 assert 'error' in \
468 self.conf([{"action": {"proxy": proxy}}], 'routes'), \
469 'proxy invalid'
470
471 check_proxy('blah')
472 check_proxy('/blah')
473 check_proxy('unix:/blah')
474 check_proxy('http://blah')
475 check_proxy('http://127.0.0.1')
476 check_proxy('http://127.0.0.1:')
477 check_proxy('http://127.0.0.1:blah')
478 check_proxy('http://127.0.0.1:-1')
479 check_proxy('http://127.0.0.1:7080b')
480 check_proxy('http://[]')
481 check_proxy('http://[]:7080')
482 check_proxy('http://[:]:7080')
483 check_proxy('http://[::7080')
484
485 def test_proxy_loop(self):
486 skip_alert(
487 r'socket.*failed',
488 r'accept.*failed',
489 r'new connections are not accepted',
490 )
491 self.conf(
492 {
493 "listeners": {
494 "*:7080": {"pass": "routes"},
495 "*:7081": {"pass": "applications/mirror"},
496 "*:7082": {"pass": "routes"},
497 },
498 "routes": [{"action": {"proxy": "http://127.0.0.1:7082"}}],
499 "applications": {
500 "mirror": {
501 "type": "python",
502 "processes": {"spare": 0},
503 "path": option.test_dir + "/python/mirror",
504 "working_directory": option.test_dir + "/python/mirror",
505 "module": "wsgi",
506 },
507 },
508 }
509 )
510
511 self.get_http10(no_recv=True)
512 self.get_http10(read_timeout=1)