@@ -78,6 +78,25 @@ lws_client_h3_grace_cb(lws_sorted_usec_list_t *sul)
7878{
7979 struct lws * wsi = lws_container_of (sul , struct lws , sul_h3_grace );
8080
81+ if (!wsi -> role_ops || strcmp (wsi -> role_ops -> name , "quic" ) != 0 )
82+ return ;
83+
84+ int first_valid = -1 ;
85+ for (int i = 0 ; i < wsi -> parallel_count ; i ++ ) {
86+ if (wsi -> parallel_conns [i ].is_valid ) {
87+ first_valid = i ;
88+ break ;
89+ }
90+ }
91+
92+ if (first_valid == -1 && !wsi -> dns_sorted_list .count ) {
93+ /* There are no parallel TCP sockets, and no more DNS results to try!
94+ * We cannot fallback to TCP. Do not abort the QUIC race, just
95+ * let it run until the overall connect timeout expires. */
96+ lwsl_wsi_notice (wsi , "H3 grace timer expired, but no TCP fallback available. Keeping QUIC." );
97+ return ;
98+ }
99+
81100 lwsl_wsi_notice (wsi , "H3 grace timer expired, abandoning QUIC race" );
82101
83102 /* Mark H3 as FAILED in cache with 5s TTL */
@@ -91,41 +110,32 @@ lws_client_h3_grace_cb(lws_sorted_usec_list_t *sul)
91110 }
92111
93112 /* Abort QUIC and revert to TCP */
94- if (wsi -> role_ops && strcmp (wsi -> role_ops -> name , "quic" ) == 0 ) {
95- if (lws_socket_is_valid (wsi -> desc .sockfd )) {
96- struct lws_context_per_thread * pt = & wsi -> a .context -> pt [(int )wsi -> tsi ];
97- lws_pt_lock (pt , __func__ );
98- __remove_wsi_socket_from_fds (wsi );
99- lws_pt_unlock (pt );
100- compatible_close (wsi -> desc .sockfd );
101- wsi -> desc .sockfd = LWS_SOCK_INVALID ;
102- }
113+ if (lws_socket_is_valid (wsi -> desc .sockfd )) {
114+ struct lws_context_per_thread * pt = & wsi -> a .context -> pt [(int )wsi -> tsi ];
115+ lws_pt_lock (pt , __func__ );
116+ __remove_wsi_socket_from_fds (wsi );
117+ lws_pt_unlock (pt );
118+ compatible_close (wsi -> desc .sockfd );
119+ wsi -> desc .sockfd = LWS_SOCK_INVALID ;
120+ }
103121
104- if (lws_rops_fidx (wsi -> role_ops , LWS_ROPS_close_kill_connection ))
105- lws_rops_func_fidx (wsi -> role_ops , LWS_ROPS_close_kill_connection ).close_kill_connection (wsi , LWS_CLOSE_STATUS_NOSTATUS );
122+ if (lws_rops_fidx (wsi -> role_ops , LWS_ROPS_close_kill_connection ))
123+ lws_rops_func_fidx (wsi -> role_ops , LWS_ROPS_close_kill_connection ).close_kill_connection (wsi , LWS_CLOSE_STATUS_NOSTATUS );
106124
107- /* Promote the first parallel TCP connection */
108- int first_valid = -1 ;
109- for (int i = 0 ; i < wsi -> parallel_count ; i ++ ) {
110- if (wsi -> parallel_conns [i ].is_valid ) {
111- first_valid = i ;
112- break ;
113- }
114- }
115- if (first_valid != -1 ) {
116- const struct lws_role_ops * r = lws_role_by_name ("h2" );
117- if (!r ) r = lws_role_by_name ("h1" );
118- if (r ) {
119- lws_role_transition (wsi , LWSIFR_CLIENT , LRS_WAITING_CONNECT , r );
120- }
121- wsi -> desc .sockfd = wsi -> parallel_conns [first_valid ].desc .sockfd ;
122- wsi -> position_in_fds_table = wsi -> parallel_conns [first_valid ].position_in_fds_table ;
123- wsi -> parallel_conns [first_valid ].is_valid = 0 ;
124- /* We changed the primary fd, the event loop will trigger POLLOUT if it's connected */
125- } else {
126- /* No TCP sockets survived? Fail connection. */
127- lws_client_connect_3_connect (wsi , NULL , NULL , 0 , NULL );
125+ /* Promote the first parallel TCP connection */
126+ if (first_valid != -1 ) {
127+ const struct lws_role_ops * r = lws_role_by_name ("h2" );
128+ if (!r ) r = lws_role_by_name ("h1" );
129+ if (r ) {
130+ lws_role_transition (wsi , LWSIFR_CLIENT , LRS_WAITING_CONNECT , r );
128131 }
132+ wsi -> desc .sockfd = wsi -> parallel_conns [first_valid ].desc .sockfd ;
133+ wsi -> position_in_fds_table = wsi -> parallel_conns [first_valid ].position_in_fds_table ;
134+ wsi -> parallel_conns [first_valid ].is_valid = 0 ;
135+ /* We changed the primary fd, the event loop will trigger POLLOUT if it's connected */
136+ } else {
137+ /* No TCP sockets survived? Fail connection. */
138+ lws_client_connect_3_connect (wsi , NULL , NULL , 0 , NULL );
129139 }
130140}
131141
0 commit comments