web.py (2726B)
1 #!/usr/bin/env python3 2 """Repeated requests in one PHP process exercise request-scoped FFI teardown.""" 3 import os 4 from pathlib import Path 5 import socket 6 import subprocess 7 import sys 8 import tempfile 9 import time 10 import urllib.request 11 import urllib.error 12 13 php, public, fixture = sys.argv[1:] 14 with socket.socket() as sock: 15 sock.bind(('127.0.0.1',0)) 16 port=sock.getsockname()[1] 17 with tempfile.TemporaryFile() as log: 18 server=subprocess.Popen([php,'-d','ffi.enable=true','-S',f'127.0.0.1:{port}','-t',public],stdout=log,stderr=log, 19 env={k:v for k,v in os.environ.items() if k!='PHP_CLI_SERVER_WORKERS'}) 20 def post(bundle,name='id',format='string',argument='hello 🌳\x00world'): 21 boundary='tricu-binding-test-boundary' 22 fields={'export':name,'format':format,'arg0':argument} 23 body=b'' 24 for key,value in fields.items(): 25 body+=f'--{boundary}\r\nContent-Disposition: form-data; name="{key}"\r\n\r\n'.encode()+value.encode()+b'\r\n' 26 body+=f'--{boundary}\r\nContent-Disposition: form-data; name="bundle"; filename="test.arboricx"\r\nContent-Type: application/octet-stream\r\n\r\n'.encode() 27 body+=bundle+f'\r\n--{boundary}--\r\n'.encode() 28 request=urllib.request.Request(f'http://127.0.0.1:{port}/eval.php',body, 29 {'Content-Type':f'multipart/form-data; boundary={boundary}'}) 30 try: 31 with urllib.request.urlopen(request,timeout=5) as response: 32 return response.status,response.read() 33 except urllib.error.HTTPError as error: 34 return error.code,error.read() 35 try: 36 for _ in range(100): 37 if server.poll() is not None: raise RuntimeError('PHP server exited during startup') 38 try: 39 with socket.create_connection(('127.0.0.1',port),timeout=.1): break 40 except OSError: time.sleep(.02) 41 else: raise RuntimeError('PHP server startup timed out') 42 bundle=Path(fixture).read_bytes() 43 for i in range(12): 44 assert post(bundle)==(200,'hello 🌳\x00world'.encode()) 45 assert post(bundle,name='missing')[0]==400 46 assert post(b'bad bundle')[0]==400 47 assert post(bundle,format='invalid')[0]==400 48 assert server.poll() is None, 'PHP process died between requests' 49 assert post(bundle)==(200,'hello 🌳\x00world'.encode()) 50 print('PHP request lifecycle passed in one persistent process') 51 except BaseException: 52 log.seek(0); sys.stderr.write(log.read().decode(errors='replace')); raise 53 finally: 54 server.terminate() 55 try: server.wait(timeout=5) 56 except subprocess.TimeoutExpired: server.kill(); server.wait()