http: http_transport_connect TLS support 61/42461/2
authorMatus Fabian <[email protected]>
Fri, 7 Mar 2025 09:34:59 +0000 (04:34 -0500)
committerFlorin Coras <[email protected]>
Mon, 10 Mar 2025 20:37:16 +0000 (20:37 +0000)
enable HTTPS for client apps

Type: improvement

Change-Id: I2ca8b926771a350863cca81729102faf6ee9c874
Signed-off-by: Matus Fabian <[email protected]>
extras/hs-test/http_test.go
src/plugins/hs_apps/http_client_cli.c
src/plugins/http/http.c
src/plugins/http/http1.c
src/plugins/http/http_private.h
src/plugins/http/http_timer.h

index 91d0823..ad43f9d 100644 (file)
@@ -22,7 +22,7 @@ import (
 )
 
 func init() {
-       RegisterVethTests(HttpCliTest, HttpCliConnectErrorTest)
+       RegisterVethTests(HttpCliTest, HttpCliConnectErrorTest, HttpCliTlsTest)
        RegisterSoloVethTests(HttpClientGetMemLeakTest)
        RegisterNoTopoTests(HeaderServerTest, HttpPersistentConnectionTest, HttpPipeliningTest,
                HttpStaticMovedTest, HttpStaticNotFoundTest, HttpCliMethodNotAllowedTest, HttpAbsoluteFormUriTest,
@@ -241,6 +241,25 @@ func HttpCliTest(s *VethsSuite) {
        s.AssertContains(o, "</html>", "</html> not found in the result!")
 }
 
+func HttpCliTlsTest(s *VethsSuite) {
+       uri := "tls://" + s.Interfaces.Server.Ip4AddressString() + "/443"
+
+       s.Containers.ServerVpp.VppInstance.Vppctl("http cli server uri " + uri)
+
+       o := s.Containers.ClientVpp.VppInstance.Vppctl("http cli client" +
+               " uri " + uri + " query /show/version")
+       s.Log(o)
+       s.AssertContains(o, "<html>", "<html> not found in the result!")
+       s.AssertContains(o, "</html>", "</html> not found in the result!")
+
+       /* second request to test postponed ho-cleanup */
+       o = s.Containers.ClientVpp.VppInstance.Vppctl("http cli client" +
+               " uri " + uri + " query /show/version")
+       s.Log(o)
+       s.AssertContains(o, "<html>", "<html> not found in the result!")
+       s.AssertContains(o, "</html>", "</html> not found in the result!")
+}
+
 func HttpCliConnectErrorTest(s *VethsSuite) {
        uri := "http://" + s.Interfaces.Server.Ip4AddressString() + "/80"
 
index 4ee3b49..8df5bfd 100644 (file)
@@ -62,6 +62,8 @@ typedef struct
   u8 *http_response;
   u8 *appns_id;
   u64 appns_secret;
+  u32 ckpair_index;
+  u8 need_crypto;
 } hcc_main_t;
 
 typedef enum
@@ -333,6 +335,7 @@ hcc_attach ()
   vnet_app_attach_args_t _a, *a = &_a;
   u64 options[18];
   u32 segment_size = 128 << 20;
+  vnet_app_add_cert_key_pair_args_t _ck_pair, *ck_pair = &_ck_pair;
   int rv;
 
   if (hcm->private_segment_size)
@@ -353,6 +356,7 @@ hcc_attach ()
     hcm->fifo_size ? hcm->fifo_size : 32 << 10;
   a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
   a->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = hcm->prealloc_fifos;
+  a->options[APP_OPTIONS_TLS_ENGINE] = CRYPTO_ENGINE_OPENSSL;
   if (hcm->appns_id)
     {
       a->namespace_id = hcm->appns_id;
@@ -365,6 +369,15 @@ hcc_attach ()
   hcm->app_index = a->app_index;
   vec_free (a->name);
   hcm->test_client_attached = 1;
+
+  clib_memset (ck_pair, 0, sizeof (*ck_pair));
+  ck_pair->cert = (u8 *) test_srv_crt_rsa;
+  ck_pair->key = (u8 *) test_srv_key_rsa;
+  ck_pair->cert_len = test_srv_crt_rsa_len;
+  ck_pair->key_len = test_srv_key_rsa_len;
+  vnet_app_add_cert_key_pair (ck_pair);
+  hcm->ckpair_index = ck_pair->index;
+
   return 0;
 }
 
@@ -411,6 +424,14 @@ hcc_connect ()
     &a->sep_ext, TRANSPORT_ENDPT_EXT_CFG_HTTP, sizeof (http_cfg));
   clib_memcpy (ext_cfg->data, &http_cfg, sizeof (http_cfg));
 
+  if (hcm->need_crypto)
+    {
+      ext_cfg = session_endpoint_add_ext_cfg (
+       &a->sep_ext, TRANSPORT_ENDPT_EXT_CFG_CRYPTO,
+       sizeof (transport_endpt_crypto_cfg_t));
+      ext_cfg->crypto.ckpair_index = hcm->ckpair_index;
+    }
+
   /* allocate http session on main thread */
   wrk = hcc_worker_get (0);
   hs = hcc_session_alloc (wrk);
@@ -581,6 +602,8 @@ hcc_command_fn (vlib_main_t *vm, unformat_input_t *input,
       err = clib_error_return (0, "Uri parse error: %d", rv);
       goto done;
     }
+  hcm->need_crypto = hcm->connect_sep.transport_proto == TRANSPORT_PROTO_TLS;
+  hcm->connect_sep.transport_proto = TRANSPORT_PROTO_HTTP;
 
   session_enable_disable_args_t args = { .is_en = 1,
                                         .rt_engine_type =
index c995c99..fc5b7d5 100644 (file)
@@ -179,6 +179,13 @@ http_conn_free (http_conn_t *hc)
   pool_put (wrk->conn_pool, hc);
 }
 
+static void
+http_add_postponed_ho_cleanups (u32 ho_hc_index)
+{
+  http_main_t *hm = &http_main;
+  vec_add1 (hm->postponed_ho_free, ho_hc_index);
+}
+
 static inline http_conn_t *
 http_ho_conn_get (u32 ho_hc_index)
 {
@@ -195,12 +202,49 @@ http_ho_conn_free (http_conn_t *ho_hc)
   pool_put (hm->ho_conn_pool, ho_hc);
 }
 
+static void
+http_ho_try_free (u32 ho_hc_index)
+{
+  http_conn_t *ho_hc;
+  HTTP_DBG (1, "half open: %x", ho_hc_index);
+  ho_hc = http_ho_conn_get (ho_hc_index);
+  if (!(ho_hc->flags & HTTP_CONN_F_HO_DONE))
+    {
+      HTTP_DBG (1, "postponed cleanup");
+      ho_hc->h_tc_session_handle = SESSION_INVALID_HANDLE;
+      http_add_postponed_ho_cleanups (ho_hc_index);
+      return;
+    }
+  if (!(ho_hc->flags & HTTP_CONN_F_NO_APP_SESSION))
+    session_half_open_delete_notify (&ho_hc->connection);
+  http_ho_conn_free (ho_hc);
+}
+
+static void
+http_flush_postponed_ho_cleanups ()
+{
+  http_main_t *hm = &http_main;
+  u32 *ho_indexp, *tmp;
+
+  tmp = hm->postponed_ho_free;
+  hm->postponed_ho_free = hm->ho_free_list;
+  hm->ho_free_list = tmp;
+
+  vec_foreach (ho_indexp, hm->ho_free_list)
+    http_ho_try_free (*ho_indexp);
+
+  vec_reset_length (hm->ho_free_list);
+}
+
 static inline u32
 http_ho_conn_alloc (void)
 {
   http_main_t *hm = &http_main;
   http_conn_t *hc;
 
+  if (vec_len (hm->postponed_ho_free))
+    http_flush_postponed_ho_cleanups ();
+
   pool_get_aligned_safe (hm->ho_conn_pool, hc, CLIB_CACHE_LINE_BYTES);
   clib_memset (hc, 0, sizeof (*hc));
   hc->h_hc_index = hc - hm->ho_conn_pool;
@@ -361,7 +405,7 @@ http_conn_invalidate_timer_cb (u32 hs_handle)
     }
 
   hc->timer_handle = HTTP_TIMER_HANDLE_INVALID;
-  hc->pending_timer = 1;
+  hc->flags |= HTTP_CONN_F_PENDING_TIMER;
 }
 
 static void
@@ -381,7 +425,7 @@ http_conn_timeout_cb (void *hc_handlep)
       return;
     }
 
-  if (!hc->pending_timer)
+  if (!(hc->flags & HTTP_CONN_F_PENDING_TIMER))
     {
       HTTP_DBG (1, "timer not pending");
       return;
@@ -492,6 +536,7 @@ http_ts_connected_callback (u32 http_app_index, u32 ho_hc_index, session_t *ts,
     {
       clib_warning ("half-open hc index %d, error: %U", ho_hc_index,
                    format_session_error, err);
+      ho_hc->flags |= HTTP_CONN_F_HO_DONE;
       app_wrk = app_worker_get_if_valid (ho_hc->h_pa_wrk_index);
       if (app_wrk)
        app_worker_connect_notify (app_wrk, 0, err, ho_hc->h_pa_app_api_ctx);
@@ -503,6 +548,9 @@ http_ts_connected_callback (u32 http_app_index, u32 ho_hc_index, session_t *ts,
 
   clib_memcpy_fast (hc, ho_hc, sizeof (*hc));
 
+  /* in chain with TLS there is race on half-open cleanup */
+  __atomic_fetch_or (&ho_hc->flags, HTTP_CONN_F_HO_DONE, __ATOMIC_RELEASE);
+
   hc->timer_handle = HTTP_TIMER_HANDLE_INVALID;
   hc->c_thread_index = ts->thread_index;
   hc->h_tc_session_handle = session_handle (ts);
@@ -642,12 +690,12 @@ http_ts_cleanup_callback (session_t *ts, session_cleanup_ntf_t ntf)
     }
   pool_free (hc->req_pool);
 
-  if (hc->pending_timer == 0)
+  if (!(hc->flags & HTTP_CONN_F_PENDING_TIMER))
     http_conn_timer_stop (hc);
 
   session_transport_delete_notify (&hc->connection);
 
-  if (!hc->is_server)
+  if (!(hc->flags & HTTP_CONN_F_IS_SERVER))
     {
       vec_free (hc->app_name);
       vec_free (hc->host);
@@ -658,12 +706,9 @@ http_ts_cleanup_callback (session_t *ts, session_cleanup_ntf_t ntf)
 static void
 http_ts_ho_cleanup_callback (session_t *ts)
 {
-  http_conn_t *ho_hc;
   u32 ho_hc_index = http_conn_index_from_handle (ts->opaque);
   HTTP_DBG (1, "half open: %x", ho_hc_index);
-  ho_hc = http_ho_conn_get (ho_hc_index);
-  session_half_open_delete_notify (&ho_hc->connection);
-  http_ho_conn_free (ho_hc);
+  http_ho_try_free (ho_hc_index);
 }
 
 int
@@ -803,7 +848,12 @@ http_transport_connect (transport_endpoint_cfg_t *tep)
       hc->timeout = http_cfg->timeout;
     }
 
-  hc->is_server = 0;
+  ext_cfg = session_endpoint_get_ext_cfg (sep, TRANSPORT_ENDPT_EXT_CFG_CRYPTO);
+  if (ext_cfg)
+    {
+      HTTP_DBG (1, "app set tls");
+      cargs->sep.transport_proto = TRANSPORT_PROTO_TLS;
+    }
 
   if (vec_len (app->name))
     hc->app_name = vec_dup (app->name);
@@ -895,7 +945,7 @@ http_start_listen (u32 app_listener_index, transport_endpoint_cfg_t *tep)
   lhc->c_s_index = app_listener_index;
   lhc->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
 
-  lhc->is_server = 1;
+  lhc->flags |= HTTP_CONN_F_IS_SERVER;
 
   if (vec_len (app->name))
     lhc->app_name = vec_dup (app->name);
@@ -1142,6 +1192,12 @@ http_transport_cleanup_ho (u32 ho_hc_index)
 
   HTTP_DBG (1, "half open: %x", ho_hc_index);
   ho_hc = http_ho_conn_get (ho_hc_index);
+  if (ho_hc->h_tc_session_handle == SESSION_INVALID_HANDLE)
+    {
+      HTTP_DBG (1, "already pending cleanup");
+      ho_hc->flags |= HTTP_CONN_F_NO_APP_SESSION;
+      return;
+    }
   session_cleanup_half_open (ho_hc->h_tc_session_handle);
   http_ho_conn_free (ho_hc);
 }
index 44dd099..c152956 100644 (file)
@@ -1028,7 +1028,7 @@ http1_req_state_transport_io_more_data (http_conn_t *hc, http_req_t *req,
    * server back to HTTP_REQ_STATE_WAIT_APP_REPLY
    * client to HTTP_REQ_STATE_WAIT_APP_METHOD */
   if (req->to_recv == 0)
-    http_req_state_change (req, hc->is_server ?
+    http_req_state_change (req, (hc->flags & HTTP_CONN_F_IS_SERVER) ?
                                  HTTP_REQ_STATE_WAIT_APP_REPLY :
                                  HTTP_REQ_STATE_WAIT_APP_METHOD);
 
@@ -1456,7 +1456,7 @@ http1_req_state_app_io_more_data (http_conn_t *hc, http_req_t *req,
       /* Finished transaction:
        * server back to HTTP_REQ_STATE_WAIT_TRANSPORT_METHOD
        * client to HTTP_REQ_STATE_WAIT_TRANSPORT_REPLY */
-      http_req_state_change (req, hc->is_server ?
+      http_req_state_change (req, (hc->flags & HTTP_CONN_F_IS_SERVER) ?
                                    HTTP_REQ_STATE_WAIT_TRANSPORT_METHOD :
                                    HTTP_REQ_STATE_WAIT_TRANSPORT_REPLY);
       http_buffer_free (hb);
@@ -1635,7 +1635,8 @@ http1_app_tx_callback (http_conn_t *hc, transport_send_params_t *sp)
     {
       /* Sometimes the server apps can send the response earlier
        * than expected (e.g when rejecting a bad request)*/
-      if (req->state == HTTP_REQ_STATE_TRANSPORT_IO_MORE_DATA && hc->is_server)
+      if (req->state == HTTP_REQ_STATE_TRANSPORT_IO_MORE_DATA &&
+         (hc->flags & HTTP_CONN_F_IS_SERVER))
        {
          http_io_ts_drain_all (hc);
          http_req_state_change (req, HTTP_REQ_STATE_WAIT_APP_REPLY);
index 187d1fe..154a63d 100644 (file)
@@ -138,6 +138,26 @@ typedef struct http_req_
   http_upgrade_proto_t upgrade_proto;
 } http_req_t;
 
+#define foreach_http_conn_flags                                               \
+  _ (HO_DONE, "ho-done")                                                      \
+  _ (NO_APP_SESSION, "no-app-session")                                        \
+  _ (PENDING_TIMER, "pending-timer")                                          \
+  _ (IS_SERVER, "is-server")
+
+typedef enum http_conn_flags_bit_
+{
+#define _(sym, str) HTTP_CONN_F_BIT_##sym,
+  foreach_http_conn_flags
+#undef _
+} http_conn_flags_bit_t;
+
+typedef enum http_conn_flags_
+{
+#define _(sym, str) HTTP_CONN_F_##sym = 1 << HTTP_CONN_F_BIT_##sym,
+  foreach_http_conn_flags
+#undef _
+} __clib_packed http_conn_flags_t;
+
 typedef struct http_tc_
 {
   union
@@ -155,10 +175,9 @@ typedef struct http_tc_
   http_conn_state_t state;
   u32 timer_handle;
   u32 timeout;
-  u8 pending_timer;
   u8 *app_name;
   u8 *host;
-  u8 is_server;
+  http_conn_flags_t flags;
   http_udp_tunnel_mode_t udp_tunnel_mode;
 
   http_req_t *req_pool; /* multiplexing => request per stream */
@@ -174,6 +193,8 @@ typedef struct http_main_
   http_worker_t *wrk;
   http_conn_t *listener_pool;
   http_conn_t *ho_conn_pool;
+  u32 *postponed_ho_free;
+  u32 *ho_free_list;
   u32 app_index;
 
   u8 **rx_bufs;
index 3bd12f7..50f634c 100644 (file)
@@ -59,7 +59,7 @@ http_conn_timer_stop (http_conn_t *hc)
 {
   http_tw_ctx_t *twc = &http_tw_ctx;
 
-  hc->pending_timer = 0;
+  hc->flags &= ~HTTP_CONN_F_PENDING_TIMER;
   if (hc->timer_handle == HTTP_TIMER_HANDLE_INVALID)
     return;