"""Isolated Nginx header inheritance experiment; never reloads system Nginx.""" import argparse,json,subprocess,tempfile,socket,time,platform from pathlib import Path from urllib.request import urlopen from urllib.error import HTTPError from datetime import datetime,timezone p=argparse.ArgumentParser();p.add_argument('--output',type=Path,required=True);args=p.parse_args() if args.output.exists():raise SystemExit('Output already exists; choose a new path') with tempfile.TemporaryDirectory(prefix='punch-header-depth-') as folder: root=Path(folder) with socket.socket() as sock:sock.bind(('127.0.0.1',0));port=sock.getsockname()[1] conf=f'''pid {root}/nginx.pid; error_log {root}/error.log; events {{ worker_connections 32; }} http {{ access_log off; server {{ listen 127.0.0.1:{port}; add_header X-Content-Type-Options nosniff always; location = /parent-only {{ return 404; }} location = /child-always {{ add_header X-Lab-Route child always; return 404; }} location = /explicit-both {{ add_header X-Lab-Route child always; add_header X-Content-Type-Options nosniff always; return 404; }} }} }}''' path=root/'nginx.conf';path.write_text(conf) subprocess.run(['nginx','-t','-p',str(root)+'/', '-c',str(path)],check=True,capture_output=True) child=subprocess.Popen(['nginx','-p',str(root)+'/', '-c',str(path),'-g','daemon off; master_process off;'],stdout=subprocess.DEVNULL,stderr=subprocess.PIPE) rows=[] try: deadline=time.monotonic()+5 while True: try: with socket.create_connection(('127.0.0.1',port),timeout=.3):break except OSError: if child.poll() is not None or time.monotonic()>deadline:raise RuntimeError('Own Nginx child failed to listen') time.sleep(.05) for route in ['/parent-only','/child-always','/explicit-both']: try:r=urlopen('http://127.0.0.1:'+str(port)+route,timeout=3) except HTTPError as error:r=error with r:rows.append({'path':route,'status':r.code,'nosniff':r.headers.get('X-Content-Type-Options'),'child_header':r.headers.get('X-Lab-Route'),'body_bytes':len(r.read())}) assert [x['status'] for x in rows]==[404]*3 assert [x['nosniff'] for x in rows]==['nosniff',None,'nosniff'] finally: child.terminate() try:child.wait(timeout=3) except subprocess.TimeoutExpired:child.kill();child.wait(timeout=3) result={'observed_at_utc':datetime.now(timezone.utc).isoformat(),'python':platform.python_version(),'nginx':subprocess.run(['nginx','-v'],capture_output=True,text=True).stderr.strip(),'scope':'own loopback child only; no production config/reload/browser/CDN','configuration':conf.replace(str(root),'').replace(str(port),''),'results':rows} with args.output.open('x') as f:json.dump(result,f,ensure_ascii=False,indent=2) print(json.dumps(rows))