@@ -253,7 +253,7 @@ impl Builder {
253253 /// Sets the [ALPN] protocols that this endpoint will accept on incoming connections.
254254 ///
255255 /// Not setting this will still allow creating connections, but to accept incoming
256- /// connections the [ALPN] must be set.
256+ /// connections at least one [ALPN] must be set.
257257 ///
258258 /// [ALPN]: https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation
259259 pub fn alpns ( mut self , alpn_protocols : Vec < Vec < u8 > > ) -> Self {
@@ -752,7 +752,8 @@ impl Endpoint {
752752 "Attempting connection..."
753753 ) ;
754754 let client_config = {
755- let alpn_protocols = vec ! [ alpn. to_vec( ) ] ;
755+ let mut alpn_protocols = vec ! [ alpn. to_vec( ) ] ;
756+ alpn_protocols. extend ( options. additional_alpns ) ;
756757 let quic_client_config = self . static_config . tls_auth . make_client_config (
757758 & self . static_config . secret_key ,
758759 node_id,
@@ -1263,6 +1264,7 @@ impl Endpoint {
12631264#[ derive( Default , Debug , Clone ) ]
12641265pub struct ConnectOptions {
12651266 transport_config : Option < Arc < TransportConfig > > ,
1267+ additional_alpns : Vec < Vec < u8 > > ,
12661268}
12671269
12681270impl ConnectOptions {
@@ -1279,6 +1281,31 @@ impl ConnectOptions {
12791281 self . transport_config = Some ( transport_config) ;
12801282 self
12811283 }
1284+
1285+ /// Sets [ALPN] identifiers that should be signaled as supported on connection, *in
1286+ /// addition* to the main [ALPN] identifier used in [`Endpoint::connect_with_opts`].
1287+ ///
1288+ /// This allows connecting to servers that may only support older versions of your
1289+ /// protocol. In this case, you would add the older [ALPN] identifiers with this
1290+ /// function.
1291+ ///
1292+ /// You'll know the final negotiated [ALPN] identifier once your connection was
1293+ /// established using [`Connection::alpn`], or even slightly earlier in the
1294+ /// handshake by using [`Connecting::alpn`].
1295+ /// The negotiated [ALPN] identifier may be any of the [ALPN] identifiers in this
1296+ /// list or the main [ALPN] used in [`Endpoint::connect_with_opts`].
1297+ ///
1298+ /// The [ALPN] identifier order on the connect side doesn't matter, since it's the
1299+ /// accept side that determines the protocol.
1300+ ///
1301+ /// For setting the supported [ALPN] identifiers on the accept side, see the endpoint
1302+ /// builder's [`Builder::alpns`] function.
1303+ ///
1304+ /// [ALPN]: https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation
1305+ pub fn with_additional_alpns ( mut self , alpns : Vec < Vec < u8 > > ) -> Self {
1306+ self . additional_alpns = alpns;
1307+ self
1308+ }
12821309}
12831310
12841311/// Future produced by [`Endpoint::accept`].
@@ -2779,4 +2806,102 @@ mod tests {
27792806
27802807 Ok ( ( ) )
27812808 }
2809+
2810+ /// Configures the accept side to take `accept_alpns` ALPNs, then connects to it with `primary_connect_alpn`
2811+ /// with `secondary_connect_alpns` set, and finally returns the negotiated ALPN.
2812+ async fn alpn_connection_test (
2813+ accept_alpns : Vec < Vec < u8 > > ,
2814+ primary_connect_alpn : & [ u8 ] ,
2815+ secondary_connect_alpns : Vec < Vec < u8 > > ,
2816+ ) -> testresult:: TestResult < Option < Vec < u8 > > > {
2817+ let client = Endpoint :: builder ( )
2818+ . relay_mode ( RelayMode :: Disabled )
2819+ . bind ( )
2820+ . await ?;
2821+ let server = Endpoint :: builder ( )
2822+ . relay_mode ( RelayMode :: Disabled )
2823+ . alpns ( accept_alpns)
2824+ . bind ( )
2825+ . await ?;
2826+ let server_addr = server. node_addr ( ) . await ?;
2827+ let server_task = tokio:: spawn ( {
2828+ let server = server. clone ( ) ;
2829+ async move {
2830+ let incoming = server. accept ( ) . await . unwrap ( ) ;
2831+ let conn = incoming. await ?;
2832+ conn. close ( 0u32 . into ( ) , b"bye!" ) ;
2833+ testresult:: TestResult :: Ok ( conn. alpn ( ) )
2834+ }
2835+ } ) ;
2836+
2837+ let conn = client
2838+ . connect_with_opts (
2839+ server_addr,
2840+ primary_connect_alpn,
2841+ ConnectOptions :: new ( ) . with_additional_alpns ( secondary_connect_alpns) ,
2842+ )
2843+ . await ?;
2844+ let conn = conn. await ?;
2845+ let client_alpn = conn. alpn ( ) ;
2846+ conn. closed ( ) . await ;
2847+ client. close ( ) . await ;
2848+ server. close ( ) . await ;
2849+
2850+ let server_alpn = server_task. await ??;
2851+
2852+ assert_eq ! ( client_alpn, server_alpn) ;
2853+
2854+ Ok ( server_alpn)
2855+ }
2856+
2857+ #[ tokio:: test]
2858+ #[ traced_test]
2859+ async fn connect_multiple_alpn_negotiated ( ) -> testresult:: TestResult {
2860+ const ALPN_ONE : & [ u8 ] = b"alpn/1" ;
2861+ const ALPN_TWO : & [ u8 ] = b"alpn/2" ;
2862+
2863+ assert_eq ! (
2864+ alpn_connection_test(
2865+ // Prefer version 2 over version 1 on the accept side
2866+ vec![ ALPN_TWO . to_vec( ) , ALPN_ONE . to_vec( ) ] ,
2867+ ALPN_TWO ,
2868+ vec![ ALPN_ONE . to_vec( ) ] ,
2869+ )
2870+ . await ?,
2871+ Some ( ALPN_TWO . to_vec( ) ) ,
2872+ "accept side prefers version 2 over 1"
2873+ ) ;
2874+
2875+ assert_eq ! (
2876+ alpn_connection_test(
2877+ // Only support the old version
2878+ vec![ ALPN_ONE . to_vec( ) ] ,
2879+ ALPN_TWO ,
2880+ vec![ ALPN_ONE . to_vec( ) ] ,
2881+ )
2882+ . await ?,
2883+ Some ( ALPN_ONE . to_vec( ) ) ,
2884+ "accept side only supports the old version"
2885+ ) ;
2886+
2887+ assert_eq ! (
2888+ alpn_connection_test(
2889+ vec![ ALPN_TWO . to_vec( ) , ALPN_ONE . to_vec( ) ] ,
2890+ ALPN_ONE ,
2891+ vec![ ALPN_TWO . to_vec( ) ] ,
2892+ )
2893+ . await ?,
2894+ Some ( ALPN_TWO . to_vec( ) ) ,
2895+ "connect side ALPN order doesn't matter"
2896+ ) ;
2897+
2898+ assert_eq ! (
2899+ alpn_connection_test( vec![ ALPN_TWO . to_vec( ) , ALPN_ONE . to_vec( ) ] , ALPN_ONE , vec![ ] , )
2900+ . await ?,
2901+ Some ( ALPN_ONE . to_vec( ) ) ,
2902+ "connect side only supports the old version"
2903+ ) ;
2904+
2905+ Ok ( ( ) )
2906+ }
27822907}
0 commit comments