@@ -508,6 +508,93 @@ def handler(reader, writer):
508
508
asyncio .ensure_future (stream_handler (reader , writer , ** vars (self ), ** args ))
509
509
return aioquic .asyncio .serve (self .host_name , self .port , configuration = self .quicserver , stream_handler = handler )
510
510
511
+ class ProxyH3 (ProxyQUIC ):
512
+ def get_stream (self , conn , stream_id ):
513
+ remote_addr = conn ._quic ._network_paths [0 ].addr
514
+ reader = asyncio .StreamReader ()
515
+ class StreamWriter ():
516
+ def __init__ (self ):
517
+ self .closed = False
518
+ self .headers = asyncio .get_event_loop ().create_future ()
519
+ def get_extra_info (self , key ):
520
+ return dict (peername = remote_addr , sockname = remote_addr ).get (key )
521
+ def write (self , data ):
522
+ conn .http .send_data (stream_id , data , False )
523
+ conn .transmit ()
524
+ async def drain (self ):
525
+ conn .transmit ()
526
+ def is_closing (self ):
527
+ return self .closed
528
+ def close (self ):
529
+ if not self .closed :
530
+ conn .http .send_data (stream_id , b'' , True )
531
+ conn .transmit ()
532
+ conn .close_stream (stream_id )
533
+ self .closed = True
534
+ def send_headers (self , headers ):
535
+ conn .http .send_headers (stream_id , [(i .encode (), j .encode ()) for i , j in headers ])
536
+ conn .transmit ()
537
+ return reader , StreamWriter ()
538
+ def get_protocol (self , server_side = False , handler = None ):
539
+ import aioquic .asyncio , aioquic .quic .events , aioquic .h3 .connection , aioquic .h3 .events
540
+ class Protocol (aioquic .asyncio .QuicConnectionProtocol ):
541
+ def __init__ (s , * args , ** kw ):
542
+ super ().__init__ (* args , ** kw )
543
+ s .http = aioquic .h3 .connection .H3Connection (s ._quic )
544
+ s .streams = {}
545
+ def quic_event_received (s , event ):
546
+ if not server_side :
547
+ if isinstance (event , aioquic .quic .events .HandshakeCompleted ):
548
+ self .handshake .set_result (s )
549
+ elif isinstance (event , aioquic .quic .events .ConnectionTerminated ):
550
+ self .handshake = None
551
+ self .quic_egress_acm = None
552
+ if s .http is not None :
553
+ for http_event in s .http .handle_event (event ):
554
+ s .http_event_received (http_event )
555
+ def http_event_received (s , event ):
556
+ if isinstance (event , aioquic .h3 .events .HeadersReceived ):
557
+ if event .stream_id not in s .streams and server_side :
558
+ reader , writer = s .create_stream (event .stream_id )
559
+ writer .headers .set_result (event .headers )
560
+ asyncio .ensure_future (handler (reader , writer ))
561
+ elif isinstance (event , aioquic .h3 .events .DataReceived ) and event .stream_id in s .streams :
562
+ reader , writer = s .streams [event .stream_id ]
563
+ if event .data :
564
+ reader .feed_data (event .data )
565
+ if event .stream_ended :
566
+ reader .feed_eof ()
567
+ s .close_stream (event .stream_id )
568
+ def create_stream (s , stream_id = None ):
569
+ if stream_id is None :
570
+ stream_id = s ._quic .get_next_available_stream_id (False )
571
+ s ._quic ._get_or_create_stream_for_send (stream_id )
572
+ reader , writer = self .get_stream (s , stream_id )
573
+ s .streams [stream_id ] = (reader , writer )
574
+ return reader , writer
575
+ def close_stream (s , stream_id ):
576
+ if stream_id in s .streams :
577
+ reader , writer = s .streams [stream_id ]
578
+ if reader .at_eof () and writer .is_closing ():
579
+ s .streams .pop (stream_id )
580
+ return Protocol
581
+ async def wait_h3_connection (self ):
582
+ if self .handshake is not None :
583
+ if not self .handshake .done ():
584
+ await self .handshake
585
+ else :
586
+ import aioquic .asyncio
587
+ self .handshake = asyncio .get_event_loop ().create_future ()
588
+ self .quic_egress_acm = aioquic .asyncio .connect (self .host_name , self .port , create_protocol = self .get_protocol (), configuration = self .quicclient )
589
+ conn = await self .quic_egress_acm .__aenter__ ()
590
+ await self .handshake
591
+ async def wait_open_connection (self , * args ):
592
+ await self .wait_h3_connection ()
593
+ return self .handshake .result ().create_stream ()
594
+ def start_server (self , args , stream_handler = stream_handler ):
595
+ import aioquic .asyncio
596
+ return aioquic .asyncio .serve (self .host_name , self .port , configuration = self .quicserver , create_protocol = self .get_protocol (True , functools .partial (stream_handler , ** vars (self ), ** args )))
597
+
511
598
class ProxySSH (ProxySimple ):
512
599
def __init__ (self , ** kw ):
513
600
super ().__init__ (** kw )
@@ -670,6 +757,7 @@ def proxy_by_uri(uri, jump):
670
757
url = urllib .parse .urlparse ('s://' + uri )
671
758
rawprotos = [i .lower () for i in scheme .split ('+' )]
672
759
err_str , protos = proto .get_protos (rawprotos )
760
+ protonames = [i .name for i in protos ]
673
761
if err_str :
674
762
raise argparse .ArgumentTypeError (err_str )
675
763
if 'ssl' in rawprotos or 'secure' in rawprotos :
@@ -683,7 +771,7 @@ def proxy_by_uri(uri, jump):
683
771
sslcontexts .append (sslclient )
684
772
else :
685
773
sslserver = sslclient = None
686
- if 'quic' in rawprotos :
774
+ if 'quic' in rawprotos or 'h3' in protonames :
687
775
try :
688
776
import ssl , aioquic .quic .configuration
689
777
except Exception :
@@ -698,7 +786,6 @@ def proxy_by_uri(uri, jump):
698
786
import h2
699
787
except Exception :
700
788
raise Exception ('Missing library: "pip3 install h2"' )
701
- protonames = [i .name for i in protos ]
702
789
urlpath , _ , plugins = url .path .partition (',' )
703
790
urlpath , _ , lbind = urlpath .partition ('@' )
704
791
plugins = plugins .split (',' ) if plugins else None
@@ -740,6 +827,8 @@ def proxy_by_uri(uri, jump):
740
827
host_name = host_name , port = port , unix = not loc , lbind = lbind , sslclient = sslclient , sslserver = sslserver )
741
828
if 'quic' in rawprotos :
742
829
proxy = ProxyQUIC (quicserver , quicclient , ** params )
830
+ elif 'h3' in protonames :
831
+ proxy = ProxyH3 (quicserver , quicclient , ** params )
743
832
elif 'h2' in protonames :
744
833
proxy = ProxyH2 (** params )
745
834
elif 'ssh' in protonames :
0 commit comments