+ int connect_stream = 0;
+
+ sep = (session_endpoint_cfg_t *) tep;
+
+ if (sep->port == 0)
+ {
+ /* TODO: better logic to detect if this is a stream or a connection request */
+ connect_stream = 1;
+ }
+
+ if (connect_stream)
+ {
+ return quic_connect_new_stream (sep);
+ }
+ else
+ {
+ return quic_connect_new_connection (sep);
+ }
+}
+
+static int
+quic_connect_new_stream (session_endpoint_cfg_t * sep)
+{
+ uint64_t quic_session_handle;
+ session_t *quic_session, *stream_session;
+ quic_stream_data_t *stream_data;
+ quicly_stream_t *stream;
+ quicly_conn_t *conn;
+ app_worker_t *app_wrk;
+ quic_ctx_t *qctx, *sctx;
+ u32 sctx_index;
+ quic_main_t *qm = &quic_main;
+ int rv;
+
+ /* Find base session to which the user want to attach a stream */
+ quic_session_handle = sep->transport_opts;
+ QUIC_DBG (2, "Opening new stream (qsession %u)", sep->transport_opts);
+ quic_session = session_get_from_handle (quic_session_handle);
+
+ if (quic_session->session_type !=
+ session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC, sep->is_ip4))
+ {
+ QUIC_DBG (1, "received incompatible session");
+ return -1;
+ }
+
+ 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;