Files
caption-local/scripts/browser_capture_https.py
T

75 lines
4.3 KiB
Python

"""Controlled HTTPS-origin preview; assets intercepted locally, no public publishing."""
import json
from pathlib import Path
import subprocess
import sys
import time
import urllib.request
from urllib.parse import urlsplit
from playwright.sync_api import sync_playwright
from browser_support import browser_options
from browser_capture_connection import TOKEN, ROOT
from service_test_support import require_free_port
require_free_port(8778)
child=subprocess.Popen([sys.executable,'scripts/browser_capture_connection.py','--serve-https'],
creationflags=getattr(subprocess,'CREATE_NO_WINDOW',0))
report={'scope':'Intercepted HTTPS-origin page assets with real loopback HTTP requests and fake inference; not a deployed HTTPS site', 'scenarios':[]}
try:
for _ in range(100):
try:
urllib.request.urlopen('http://127.0.0.1:8778/capture-local.html',timeout=1).close(); break
except OSError: time.sleep(.1)
with sync_playwright() as p:
browser=p.chromium.launch(**browser_options(),headless=True,args=[
'--use-fake-device-for-media-stream',f'--use-file-for-fake-audio-capture={ROOT/"samples/jfk.wav"}'])
report['browser_version']=browser.version
for permission in ('denied','granted'):
context=browser.new_context(permissions=['microphone'])
page=context.new_page(); cdp=context.new_cdp_session(page)
context_id=cdp.send('Target.getTargetInfo')['targetInfo']['browserContextId']
cdp.send('Browser.setPermission',{'permission':{'name':'loopback-network'},
'setting':permission,'origin':'https://caption.ninja','browserContextId':context_id})
failures=[]
console=[]
page.on('console',lambda message: console.append(message.text) if message.type=='error' else None)
page.on('requestfailed',lambda request: failures.append({'url':request.url,'error':request.failure}))
def route(request):
url=request.request.url
if url.startswith('http://127.0.0.1:8778/'):
request.continue_(); return
if url.startswith('https://caption.ninja/'):
relative=urlsplit(url).path.lstrip('/')
source=(ROOT/'samples/captionninja'/relative).resolve()
base=(ROOT/'samples/captionninja').resolve()
if source.is_relative_to(base) and source.is_file():
request.fulfill(path=str(source),content_type='text/html' if source.suffix=='.html' else 'application/javascript'); return
request.abort()
context.route('**/*',route)
context.route_web_socket('**/*',lambda socket:socket.close())
page.goto('https://caption.ninja/capture-local.html')
page.fill('#endpoint','http://127.0.0.1:8778');page.fill('#connectionToken',TOKEN);page.click('#connect')
page.wait_for_function('() => !connection.checking')
ready=not page.locator('#start').is_disabled()
scenario={'permission':permission,'ready':ready,'request_failures':failures,'console':console}
report['scenarios'].append(scenario)
if ready:
page.click('#start')
try:
page.wait_for_function('() => transcript.length > 0',timeout=30000)
except Exception:
scenario['capture_state']=page.evaluate('({running,starting,failed,error:$("error").textContent})')
raise
page.click('#stop');page.wait_for_function('() => !processing && !running && !stopping && !pending',timeout=20000)
scenario['capture_drained']=page.evaluate('!failed && buffer.length <= 8000')
context.close()
browser.close()
report['passed']=not report['scenarios'][0]['ready'] and report['scenarios'][1].get('capture_drained',False)
except Exception as exc:
report['error']=repr(exc);report['passed']=False
finally:
child.terminate();child.wait(timeout=15)
output=ROOT/'evidence/gpu-validation/https-preview.json';output.parent.mkdir(parents=True,exist_ok=True)
output.write_text(json.dumps(report,indent=2)+'\n',encoding='utf-8');print(json.dumps(report,indent=2))
if not report['passed']: raise SystemExit('HTTPS preview validation incomplete; evidence retained')