From: Matus Fabian Date: Fri, 25 Jul 2025 09:39:44 +0000 (-0400) Subject: http: starting http/2 with prior knowledge X-Git-Tag: v26.02-rc0~152 X-Git-Url: https://gerrit.fd.io/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F78%2F43478%2F2;p=vpp.git http: starting http/2 with prior knowledge Added flags member to transport_endpt_cfg_http_t where client app can set HTTP_ENDPT_CFG_F_HTTP2_PRIOR_KNOWLEDGE when it want to use HTTP/2 connection over cleartext TCP. Type: improvement Change-Id: Ib904a5cbdd34c6838d029a46c388e31a3329d399 Signed-off-by: Matus Fabian --- diff --git a/extras/hs-test/http2_test.go b/extras/hs-test/http2_test.go index 26605f2140a..3cc0304d8f9 100644 --- a/extras/hs-test/http2_test.go +++ b/extras/hs-test/http2_test.go @@ -13,7 +13,8 @@ import ( func init() { RegisterH2Tests(Http2TcpGetTest, Http2TcpPostTest, Http2MultiplexingTest, Http2TlsTest, Http2ContinuationTxTest, Http2ServerMemLeakTest, - Http2ClientGetTest, Http2ClientPostTest, Http2ClientPostPtrTest, Http2ClientGetRepeatTest, Http2ClientMultiplexingTest) + Http2ClientGetTest, Http2ClientPostTest, Http2ClientPostPtrTest, Http2ClientGetRepeatTest, Http2ClientMultiplexingTest, + Http2ClientH2cTest) RegisterH2MWTests(Http2MultiplexingMWTest, Http2ClientMultiplexingMWTest) RegisterVethTests(Http2CliTlsTest, Http2ClientContinuationTest) } @@ -191,6 +192,32 @@ func Http2ClientGetTest(s *Http2Suite) { s.Log(o) s.AssertContains(o, "HTTP/2 200 OK") s.AssertContains(o, "10000000 bytes saved to file") + + logPath := s.Containers.NginxServer.GetHostWorkDir() + "/" + s.Containers.NginxServer.Name + "-access.log" + logContents, err := exechelper.Output("cat " + logPath) + s.AssertNil(err) + s.AssertContains(string(logContents), "HTTP/2") + s.AssertContains(string(logContents), "scheme=https conn=") +} + +func Http2ClientH2cTest(s *Http2Suite) { + vpp := s.Containers.Vpp.VppInstance + serverAddress := s.HostAddr() + ":" + s.Ports.Port1 + + s.CreateNginxServer() + s.AssertNil(s.Containers.NginxServer.Start()) + + uri := "http://" + serverAddress + "/httpTestFile" + o := vpp.Vppctl("http client http2 save-to response.txt verbose uri " + uri) + s.Log(o) + s.AssertContains(o, "HTTP/2 200 OK") + s.AssertContains(o, "10000000 bytes saved to file") + + logPath := s.Containers.NginxServer.GetHostWorkDir() + "/" + s.Containers.NginxServer.Name + "-access.log" + logContents, err := exechelper.Output("cat " + logPath) + s.AssertNil(err) + s.AssertContains(string(logContents), "HTTP/2") + s.AssertContains(string(logContents), "scheme=http conn=") } func http2ClientPostFile(s *Http2Suite, usePtr bool, fileSize int) { diff --git a/extras/hs-test/resources/nginx/nginx_server.conf b/extras/hs-test/resources/nginx/nginx_server.conf index d161e3c4164..f5e4f9e4857 100644 --- a/extras/hs-test/resources/nginx/nginx_server.conf +++ b/extras/hs-test/resources/nginx/nginx_server.conf @@ -15,7 +15,8 @@ events { http { log_format access_log_fmt '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' - '"$http_referer" "$http_user_agent" conn=$connection conn_reqs=$connection_requests'; + '"$http_referer" "$http_user_agent" ' + 'scheme=$scheme conn=$connection conn_reqs=$connection_requests'; keepalive_timeout 300s; keepalive_requests 1000000; client_body_timeout {{.Timeout}}s; diff --git a/src/plugins/hs_apps/http_client.c b/src/plugins/hs_apps/http_client.c index 40eb1d8c514..8a047fc9c36 100644 --- a/src/plugins/hs_apps/http_client.c +++ b/src/plugins/hs_apps/http_client.c @@ -839,17 +839,13 @@ hc_connect () hc_main_t *hcm = &hc_main; vnet_connect_args_t *a = 0; transport_endpt_ext_cfg_t *ext_cfg; - transport_endpt_cfg_http_t http_cfg = { (u32) hcm->timeout, 0 }; + transport_endpt_cfg_http_t http_cfg = { (u32) hcm->timeout, 0, 0 }; vec_validate (a, 0); clib_memset (a, 0, sizeof (a[0])); clib_memcpy (&a->sep_ext, &hcm->connect_sep, sizeof (hcm->connect_sep)); a->app_index = hcm->app_index; - ext_cfg = session_endpoint_add_ext_cfg ( - &a->sep_ext, TRANSPORT_ENDPT_EXT_CFG_HTTP, sizeof (http_cfg)); - clib_memcpy (ext_cfg->data, &http_cfg, sizeof (http_cfg)); - if (hcm->connect_sep.flags & SESSION_ENDPT_CFG_F_SECURE) { ext_cfg = session_endpoint_add_ext_cfg ( @@ -868,6 +864,15 @@ hc_connect () break; } } + else + { + if (hcm->http_version == HTTP_VERSION_2) + http_cfg.flags |= HTTP_ENDPT_CFG_F_HTTP2_PRIOR_KNOWLEDGE; + } + + ext_cfg = session_endpoint_add_ext_cfg ( + &a->sep_ext, TRANSPORT_ENDPT_EXT_CFG_HTTP, sizeof (http_cfg)); + clib_memcpy (ext_cfg->data, &http_cfg, sizeof (http_cfg)); session_send_rpc_evt_to_thread_force (transport_cl_thread (), hc_connect_rpc, a); diff --git a/src/plugins/http/http.c b/src/plugins/http/http.c index fc1fc81ff22..b387b61ae00 100644 --- a/src/plugins/http/http.c +++ b/src/plugins/http/http.c @@ -163,7 +163,7 @@ http_add_postponed_ho_cleanups (u32 ho_hc_index) vec_add1 (hm->postponed_ho_free, ho_hc_index); } -static inline http_conn_t * +http_conn_t * http_ho_conn_get (u32 ho_hc_index) { http_main_t *hm = &http_main; @@ -535,9 +535,6 @@ 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->hc_tc_session_handle = session_handle (ts); @@ -546,6 +543,7 @@ http_ts_connected_callback (u32 http_app_index, u32 ho_hc_index, session_t *ts, hc->state = HTTP_CONN_STATE_ESTABLISHED; ts->session_state = SESSION_STATE_READY; hc->flags |= HTTP_CONN_F_NO_APP_SESSION; + hc->ho_index = ho_hc_index; tp = session_get_transport_proto (ts); /* TLS set by ALPN result, TCP: prior knowledge (set in ho) */ if (tp == TRANSPORT_PROTO_TLS) @@ -556,7 +554,6 @@ http_ts_connected_callback (u32 http_app_index, u32 ho_hc_index, session_t *ts, { case TLS_ALPN_PROTO_HTTP_2: hc->version = HTTP_VERSION_2; - http_vfts[hc->version].conn_accept_callback (hc); break; case TLS_ALPN_PROTO_HTTP_1_1: case TLS_ALPN_PROTO_NONE: @@ -579,6 +576,7 @@ http_ts_connected_callback (u32 http_app_index, u32 ho_hc_index, session_t *ts, if ((rv = http_vfts[hc->version].transport_connected_callback (hc))) { clib_warning ("transport_connected_callback failed, rv=%d", rv); + __atomic_fetch_or (&ho_hc->flags, HTTP_CONN_F_HO_DONE, __ATOMIC_RELEASE); return rv; } @@ -898,6 +896,11 @@ http_connect_connection (session_endpoint_cfg_t *sep) (transport_endpt_cfg_http_t *) ext_cfg->data; HTTP_DBG (1, "app set timeout %u", http_cfg->timeout); hc->timeout = http_cfg->timeout; + if (http_cfg->flags & HTTP_ENDPT_CFG_F_HTTP2_PRIOR_KNOWLEDGE) + { + HTTP_DBG (1, "app want http2 with prior knowledge"); + hc->version = HTTP_VERSION_2; + } } ext_cfg = session_endpoint_get_ext_cfg (sep, TRANSPORT_ENDPT_EXT_CFG_CRYPTO); diff --git a/src/plugins/http/http.h b/src/plugins/http/http.h index 61c387bf781..c106038806f 100644 --- a/src/plugins/http/http.h +++ b/src/plugins/http/http.h @@ -46,10 +46,28 @@ typedef enum http_udp_tunnel_mode_ HTTP_UDP_TUNNEL_DGRAM, /**< convert capsule to datagram (zc proxy) */ } http_udp_tunnel_mode_t; +#define foreach_http_endpt_cfg_flags \ + _ (HTTP2_PRIOR_KNOWLEDGE) /**< HTTP/2 connections over cleartext TCP */ + +typedef enum http_endpt_cfg_flags_bit_ +{ +#define _(sym) HTTP_ENDPT_CFG_F_BIT_##sym, + foreach_http_endpt_cfg_flags +#undef _ +} http_endpt_cfg_flags_bit_t; + +typedef enum http_endpt_cfg_flags_ +{ +#define _(sym) HTTP_ENDPT_CFG_F_##sym = 1 << HTTP_ENDPT_CFG_F_BIT_##sym, + foreach_http_endpt_cfg_flags +#undef _ +} __clib_packed http_endpt_cfg_flags_t; + typedef struct transport_endpt_cfg_http { u32 timeout; /**< HTTP session timeout in seconds */ http_udp_tunnel_mode_t udp_tunnel_mode; /**< connect-udp mode */ + u8 flags; } transport_endpt_cfg_http_t; typedef struct diff --git a/src/plugins/http/http_private.h b/src/plugins/http/http_private.h index f6666af81e1..b76f4f6b50c 100644 --- a/src/plugins/http/http_private.h +++ b/src/plugins/http/http_private.h @@ -201,6 +201,7 @@ typedef struct http_tc_ u32 timer_handle; u32 timeout; u32 app_rx_fifo_size; + u32 ho_index; u8 *app_name; u8 *host; http_conn_flags_t flags; @@ -325,6 +326,8 @@ u8 *format_http_time_now (u8 *s, va_list *args); http_conn_t *http_conn_get_w_thread (u32 hc_index, clib_thread_index_t thread_index); +http_conn_t *http_ho_conn_get (u32 ho_hc_index); + /** * @brief Find the first occurrence of the string in the vector. * @@ -884,8 +887,13 @@ http_conn_established (http_conn_t *hc, http_req_t *req, session_t *as; app_worker_t *app_wrk; session_t *ts; + http_conn_t *ho_hc; int rv; + ho_hc = http_ho_conn_get (hc->ho_index); + /* in chain with TLS there is race on half-open cleanup */ + __atomic_fetch_or (&ho_hc->flags, HTTP_CONN_F_HO_DONE, __ATOMIC_RELEASE); + /* allocate app session and initialize */ as = session_alloc (hc->c_thread_index); HTTP_DBG (1, "allocated session 0x%lx", session_handle (as));