tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

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()