diff options
-rw-r--r-- | extras/hs-test/http_test.go | 21 | ||||
-rw-r--r-- | src/plugins/hs_apps/http_client_cli.c | 23 | ||||
-rw-r--r-- | src/plugins/http/http.c | 76 | ||||
-rw-r--r-- | src/plugins/http/http1.c | 7 | ||||
-rw-r--r-- | src/plugins/http/http_private.h | 25 | ||||
-rw-r--r-- | src/plugins/http/http_timer.h | 2 |
6 files changed, 137 insertions, 17 deletions
diff --git a/extras/hs-test/http_test.go b/extras/hs-test/http_test.go index 91d0823d9c3..ad43f9d032e 100644 --- a/extras/hs-test/http_test.go +++ b/extras/hs-test/http_test.go @@ -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" diff --git a/src/plugins/hs_apps/http_client_cli.c b/src/plugins/hs_apps/http_client_cli.c index 4ee3b49444c..8df5bfd10b8 100644 --- a/src/plugins/hs_apps/http_client_cli.c +++ b/src/plugins/hs_apps/http_client_cli.c @@ -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 = diff --git a/src/plugins/http/http.c b/src/plugins/http/http.c index c995c996433..fc5b7d5d72d 100644 --- a/src/plugins/http/http.c +++ b/src/plugins/http/http.c @@ -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); } diff --git a/src/plugins/http/http1.c b/src/plugins/http/http1.c index 44dd099ccee..c152956e43c 100644 --- a/src/plugins/http/http1.c +++ b/src/plugins/http/http1.c @@ -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); diff --git a/src/plugins/http/http_private.h b/src/plugins/http/http_private.h index 187d1fe8215..154a63d2402 100644 --- a/src/plugins/http/http_private.h +++ b/src/plugins/http/http_private.h @@ -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; diff --git a/src/plugins/http/http_timer.h b/src/plugins/http/http_timer.h index 3bd12f7821d..50f634c5397 100644 --- a/src/plugins/http/http_timer.h +++ b/src/plugins/http/http_timer.h @@ -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; |