http: starting tls connection with alpn 90/42990/5
authorMatus Fabian <[email protected]>
Tue, 20 May 2025 14:42:39 +0000 (14:42 +0000)
committerFlorin Coras <[email protected]>
Thu, 22 May 2025 17:37:59 +0000 (17:37 +0000)
this patch also enable http/2

Type: improvement

Change-Id: I20532ff6e3c4f7c4a45e889714a96ee874848fe4
Signed-off-by: Matus Fabian <[email protected]>
extras/hs-test/http2_test.go
extras/hs-test/http_test.go
extras/hs-test/infra/suite_h2.go
src/plugins/http/CMakeLists.txt
src/plugins/http/http.c
src/plugins/http/http2/http2.c

index 747f517..8bcca16 100644 (file)
@@ -9,7 +9,7 @@ import (
 )
 
 func init() {
-       RegisterH2Tests(Http2TcpGetTest, Http2TcpPostTest, Http2MultiplexingTest, Http2MultiplexingMTTest)
+       RegisterH2Tests(Http2TcpGetTest, Http2TcpPostTest, Http2MultiplexingTest, Http2MultiplexingMTTest, Http2TlsTest)
 }
 
 func Http2TcpGetTest(s *H2Suite) {
@@ -92,3 +92,16 @@ func Http2MultiplexingMTTest(s *H2Suite) {
        s.AssertContains(o, " 0 errored")
        s.AssertContains(o, " 0 timeout")
 }
+
+func Http2TlsTest(s *H2Suite) {
+       vpp := s.Containers.Vpp.VppInstance
+       serverAddress := s.VppAddr()
+       s.Log(vpp.Vppctl("http static server uri tls://" + serverAddress + "/443 url-handlers debug"))
+
+       args := fmt.Sprintf("--max-time 10 --noproxy '*' -k https://%s:443/version.json", serverAddress)
+       writeOut, log := s.RunCurlContainer(s.Containers.Curl, args)
+       s.Log(vpp.Vppctl("show session verbose 2"))
+       s.AssertContains(log, "HTTP/2 200")
+       s.AssertContains(log, "ALPN: server accepted h2")
+       s.AssertContains(writeOut, "version")
+}
index 66f6ada..37bdecf 100644 (file)
@@ -1132,6 +1132,7 @@ func HttpStaticBuildInUrlGetVersionTest(s *NoTopoSuite) {
        defer resp.Body.Close()
        s.Log(DumpHttpResp(resp, true))
        s.AssertHttpStatus(resp, 200)
+       s.AssertEqual(1, resp.ProtoMajor)
        data, err := io.ReadAll(resp.Body)
        s.AssertNil(err, fmt.Sprint(err))
        s.AssertContains(string(data), "vpp_details")
index 7cf7a6b..04919ad 100644 (file)
@@ -76,7 +76,7 @@ func (s *H2Suite) VppAddr() string {
 }
 
 // Marked as pending since http plugin is not build with http/2 enabled by default
-var _ = Describe("Http2Suite", Pending, Ordered, ContinueOnFailure, func() {
+var _ = Describe("Http2Suite", Ordered, ContinueOnFailure, func() {
        var s H2Suite
        BeforeAll(func() {
                s.SetupSuite()
@@ -170,8 +170,7 @@ var hpackTests = []h2specTest{
 
 var http2Tests = []h2specTest{
        {desc: "http2/3.5/1"},
-       // TODO: need to be tested with TLS, otherwise we consider invalid preface as bogus HTTP/1 request
-       // {desc: "http2/3.5/2"},
+       {desc: "http2/3.5/2"},
        {desc: "http2/4.1/1"},
        {desc: "http2/4.1/2"},
        {desc: "http2/4.1/3"},
@@ -298,7 +297,7 @@ var specs = []struct {
 }
 
 // Marked as pending since http plugin is not build with http/2 enabled by default
-var _ = Describe("H2SpecSuite", Pending, Ordered, ContinueOnFailure, func() {
+var _ = Describe("H2SpecSuite", Ordered, ContinueOnFailure, func() {
        var s H2Suite
        BeforeAll(func() {
                s.SetupSuite()
@@ -321,15 +320,15 @@ var _ = Describe("H2SpecSuite", Pending, Ordered, ContinueOnFailure, func() {
                                s.Log(testName + ": BEGIN")
                                vpp := s.Containers.Vpp.VppInstance
                                serverAddress := s.VppAddr()
-                               s.Log(vpp.Vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug 2"))
+                               s.Log(vpp.Vppctl("http static server uri tls://" + serverAddress + "/443 url-handlers debug 2"))
                                s.Log(vpp.Vppctl("test-url-handler enable"))
                                conf := &config.Config{
                                        Host:         serverAddress,
-                                       Port:         80,
+                                       Port:         443,
                                        Path:         "/test1",
                                        Timeout:      time.Second * 5,
                                        MaxHeaderLen: 1024,
-                                       TLS:          false,
+                                       TLS:          true,
                                        Insecure:     true,
                                        Sections:     []string{test.desc},
                                        Verbose:      true,
index ca2c0a9..f5dabe2 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-option(VPP_ENABLE_HTTP_2 "Build http plugin with HTTP/2 enabled" OFF)
-if(VPP_ENABLE_HTTP_2)
-    add_compile_definitions(HTTP_2_ENABLE=1)
-endif()
-
 add_vpp_plugin(http
   SOURCES
   http2/hpack.c
index bf43ab0..876b7d2 100644 (file)
@@ -16,6 +16,7 @@
 #include <vpp/app/version.h>
 #include <vnet/session/application_interface.h>
 #include <vnet/session/application.h>
+#include <vnet/tls/tls_types.h>
 
 #include <http/http.h>
 #include <http/http_private.h>
@@ -438,6 +439,7 @@ http_ts_accept_callback (session_t *ts)
   u32 hc_index, thresh;
   http_conn_handle_t hc_handle;
   transport_proto_t tp;
+  tls_alpn_proto_t alpn_proto;
 
   ts_listener = listen_session_get_from_handle (ts->listener_handle);
   lhc = http_listener_get (ts_listener->opaque);
@@ -457,14 +459,30 @@ http_ts_accept_callback (session_t *ts)
   tp = session_get_transport_proto (ts);
   if (tp == TRANSPORT_PROTO_TLS)
     {
-      /* TODO: set by ALPN result */
-      hc->version = HTTP_VERSION_1;
+      alpn_proto = tls_get_alpn_selected (ts->connection_index);
+      HTTP_DBG (1, "ALPN selected: %U", format_tls_alpn_proto, alpn_proto);
+      switch (alpn_proto)
+       {
+       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:
+         hc->version = HTTP_VERSION_1;
+         break;
+       default:
+         ASSERT (0);
+         return -1;
+       }
     }
   else
     {
       /* going to decide in http_ts_rx_callback */
       hc->version = HTTP_VERSION_NA;
     }
+
+  HTTP_DBG (1, "identified HTTP/%u", hc->version == HTTP_VERSION_1 ? 1 : 2);
   hc_handle.version = hc->version;
   hc_handle.conn_index = hc_index;
   ts->opaque = hc_handle.as_u32;
@@ -492,6 +510,8 @@ http_ts_connected_callback (u32 http_app_index, u32 ho_hc_index, session_t *ts,
   http_conn_t *hc, *ho_hc;
   app_worker_t *app_wrk;
   http_conn_handle_t hc_handle;
+  transport_proto_t tp;
+  tls_alpn_proto_t alpn_proto;
   int rv;
 
   ho_hc = http_ho_conn_get (ho_hc_index);
@@ -524,7 +544,29 @@ 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;
-  /* TODO: TLS set by ALPN result, TCP: prior knowledge (set in ho) */
+  tp = session_get_transport_proto (ts);
+  /* TLS set by ALPN result, TCP: prior knowledge (set in ho) */
+  if (tp == TRANSPORT_PROTO_TLS)
+    {
+      alpn_proto = tls_get_alpn_selected (ts->connection_index);
+      HTTP_DBG (1, "ALPN selected: %U", format_tls_alpn_proto, alpn_proto);
+      switch (alpn_proto)
+       {
+       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:
+         hc->version = HTTP_VERSION_1;
+         break;
+       default:
+         ASSERT (0);
+         return -1;
+       }
+    }
+
+  HTTP_DBG (1, "identified HTTP/%u", hc->version == HTTP_VERSION_1 ? 1 : 2);
   hc_handle.version = hc->version;
   hc_handle.conn_index = new_hc_index;
   ts->opaque = hc_handle.as_u32;
@@ -617,14 +659,8 @@ http_ts_rx_callback (session_t *ts)
          if (memcmp (rx_buf, http2_conn_preface.base,
                      http2_conn_preface.len) == 0)
            {
-#if HTTP_2_ENABLE > 0
              hc->version = HTTP_VERSION_2;
              http_vfts[hc->version].conn_accept_callback (hc);
-#else
-             svm_fifo_dequeue_drop_all (ts->rx_fifo);
-             http_disconnect_transport (hc);
-             return 0;
-#endif
            }
          else
            hc->version = HTTP_VERSION_1;
@@ -920,6 +956,13 @@ http_start_listen (u32 app_listener_index, transport_endpoint_cfg_t *tep)
     {
       HTTP_DBG (1, "app set tls");
       tp = TRANSPORT_PROTO_TLS;
+      if (ext_cfg->crypto.alpn_protos[0] == TLS_ALPN_PROTO_NONE)
+       {
+         HTTP_DBG (1,
+                   "app do not set alpn list, using default (h2,http/1.1)");
+         ext_cfg->crypto.alpn_protos[0] = TLS_ALPN_PROTO_HTTP_2;
+         ext_cfg->crypto.alpn_protos[1] = TLS_ALPN_PROTO_HTTP_1_1;
+       }
     }
   args->sep_ext.transport_proto = tp;
 
index 0097421..38a3cca 100644 (file)
@@ -8,10 +8,6 @@
 #include <http/http_private.h>
 #include <http/http_timer.h>
 
-#ifndef HTTP_2_ENABLE
-#define HTTP_2_ENABLE 0
-#endif
-
 #define HTTP2_WIN_SIZE_MAX     0x7FFFFFFF
 #define HTTP2_INITIAL_WIN_SIZE 65535
 /* connection-level flow control window kind of mirrors TCP flow control */
@@ -1966,8 +1962,6 @@ http2_init (vlib_main_t *vm)
   return 0;
 }
 
-#if HTTP_2_ENABLE > 0
 VLIB_INIT_FUNCTION (http2_init) = {
   .runs_after = VLIB_INITS ("http_transport_init"),
 };
-#endif