+ app_wrk = app_worker_get_if_valid (quic_session->app_wrk_index);
+ if (!app_wrk)
+ {
+ QUIC_DBG (1, "Invalid app worker :(");
+ return -1;
+ }
+
+ sctx_index = quic_ctx_alloc (quic_session->thread_index); /* Allocate before we get pointers */
+ sctx = quic_ctx_get (sctx_index, quic_session->thread_index);
+ qctx =
+ quic_ctx_get (quic_session->connection_index, quic_session->thread_index);
+ if (qctx->c_quic_ctx_id.is_stream)
+ {
+ QUIC_DBG (1, "session is a stream");
+ quic_ctx_free (sctx);
+ return -1;
+ }
+
+ sctx->c_quic_ctx_id.parent_app_wrk_id =
+ qctx->c_quic_ctx_id.parent_app_wrk_id;
+ sctx->c_quic_ctx_id.parent_app_id = qctx->c_quic_ctx_id.parent_app_id;
+ sctx->c_quic_ctx_id.quic_connection_ctx_id = qctx->c_c_index;
+ sctx->c_c_index = sctx_index;
+ sctx->c_quic_ctx_id.is_stream = 1;
+
+ conn = qctx->c_quic_ctx_id.conn;
+
+ if (!conn || !quicly_connection_is_ready (conn))
+ return -1;
+
+ if ((rv = quicly_open_stream (conn, &stream, 0 /* uni */ )))
+ {
+ QUIC_DBG (2, "Stream open failed with %d", rv);
+ return -1;
+ }
+ sctx->c_quic_ctx_id.stream = stream;
+
+ QUIC_DBG (2, "Opened stream %d, creating session", stream->stream_id);
+
+ stream_session = session_alloc (qctx->c_thread_index);
+ QUIC_DBG (2, "Allocated stream_session, id %u, thread %u ctx %u",
+ stream_session->session_index, stream_session->thread_index,
+ sctx_index);
+ stream_session->flags |= SESSION_F_QUIC_STREAM;
+ stream_session->app_wrk_index = app_wrk->wrk_index;
+ stream_session->connection_index = sctx_index;
+ stream_session->listener_index = qm->fake_app_listener_index;
+ stream_session->session_type =
+ session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC,
+ qctx->c_quic_ctx_id.udp_is_ip4);
+
+ sctx->c_s_index = stream_session->session_index;
+
+ if (app_worker_init_connected (app_wrk, stream_session))
+ {
+ QUIC_DBG (1, "failed to app_worker_init_connected");
+ quicly_reset_stream (stream, 0x30003);
+ session_free_w_fifos (stream_session);
+ quic_ctx_free (sctx);
+ return app_worker_connect_notify (app_wrk, NULL, sep->opaque);
+ }
+
+ stream_session->session_state = SESSION_STATE_READY;
+ if (app_worker_connect_notify (app_wrk, stream_session, sep->opaque))
+ {
+ QUIC_DBG (1, "failed to notify app");
+ quicly_reset_stream (stream, 0x30004);
+ session_free_w_fifos (stream_session);
+ quic_ctx_free (sctx);
+ return -1;
+ }
+ session_lookup_add_connection (&sctx->connection,
+ session_handle (stream_session));
+ stream_data = (quic_stream_data_t *) stream->data;
+ stream_data->ctx_id = sctx->c_c_index;
+ stream_data->thread_index = sctx->c_thread_index;
+ return 0;
+}
+
+static int
+quic_connect_new_connection (session_endpoint_cfg_t * sep)
+{
+ vnet_connect_args_t _cargs = { {}, }, *cargs = &_cargs;
+ quic_main_t *qm = &quic_main;
+ quic_ctx_t *ctx;
+ app_worker_t *app_wrk;
+ application_t *app;
+ u32 ctx_index;
+ int error;
+
+ ctx_index = quic_ctx_alloc (vlib_get_thread_index ());
+ ctx = quic_ctx_get (ctx_index, vlib_get_thread_index ());
+ ctx->c_quic_ctx_id.parent_app_wrk_id = sep->app_wrk_index;
+ ctx->c_s_index = QUIC_SESSION_INVALID;
+ ctx->c_c_index = ctx_index;
+ ctx->c_quic_ctx_id.udp_is_ip4 = sep->is_ip4;
+ ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
+ ctx->conn_state = QUIC_CONN_STATE_HANDSHAKE;
+ ctx->client_opaque = sep->opaque;
+ if (sep->hostname)
+ {
+ ctx->srv_hostname = format (0, "%v", sep->hostname);
+ vec_terminate_c_string (ctx->srv_hostname);
+ }
+ else
+ {
+ /* needed by quic for crypto + determining client / server */
+ ctx->srv_hostname =
+ format (0, "%U", format_ip46_address, &sep->ip, sep->is_ip4);
+ }
+
+ clib_memcpy (&cargs->sep, sep, sizeof (session_endpoint_cfg_t));
+ cargs->sep.transport_proto = TRANSPORT_PROTO_UDPC;
+ cargs->app_index = qm->app_index;
+ cargs->api_context = ctx_index;
+
+ app_wrk = app_worker_get (sep->app_wrk_index);
+ app = application_get (app_wrk->app_index);
+ ctx->c_quic_ctx_id.parent_app_id = app_wrk->app_index;
+ cargs->sep_ext.ns_index = app->ns_index;
+
+ allocate_quicly_ctx (app, 1 /* is client */ );
+
+ if ((error = vnet_connect (cargs)))
+ return error;