http: authority-form target parsing/serializing

Type: improvement
Change-Id: Ifb90818a3526d3d4030a66b1ef7eebedfe97978f
Signed-off-by: Matus Fabian <matfabia@cisco.com>
diff --git a/extras/hs-test/http_test.go b/extras/hs-test/http_test.go
index c45f7c2..8f8946f 100644
--- a/extras/hs-test/http_test.go
+++ b/extras/hs-test/http_test.go
@@ -29,7 +29,7 @@
 		HttpStaticMacTimeTest, HttpStaticBuildInUrlGetVersionVerboseTest, HttpVersionNotSupportedTest,
 		HttpInvalidContentLengthTest, HttpInvalidTargetSyntaxTest, HttpStaticPathTraversalTest, HttpUriDecodeTest,
 		HttpHeadersTest, HttpStaticFileHandlerTest, HttpClientTest, HttpClientErrRespTest, HttpClientPostFormTest,
-		HttpClientPostFileTest, HttpClientPostFilePtrTest)
+		HttpClientPostFileTest, HttpClientPostFilePtrTest, AuthorityFormTargetTest)
 	RegisterNoTopoSoloTests(HttpStaticPromTest, HttpTpsTest, HttpTpsInterruptModeTest, PromConcurrentConnectionsTest,
 		PromMemLeakTest, HttpClientPostMemLeakTest)
 }
@@ -305,6 +305,26 @@
 	httpClientPostFile(s, true, 131072)
 }
 
+func cliTestAuthority(s *NoTopoSuite, authority string) {
+	o := s.GetContainerByName("vpp").VppInstance.Vppctl("test http authority-form " + authority)
+	s.AssertNotContains(o, "error")
+	s.AssertContains(o, authority)
+}
+
+func cliTestAuthorityError(s *NoTopoSuite, authority string) {
+	o := s.GetContainerByName("vpp").VppInstance.Vppctl("test http authority-form " + authority)
+	s.AssertContains(o, "error")
+}
+
+func AuthorityFormTargetTest(s *NoTopoSuite) {
+	cliTestAuthority(s, "10.10.2.45:20")
+	cliTestAuthority(s, "[dead:beef::1234]:443")
+	cliTestAuthorityError(s, "example.com:80")
+	cliTestAuthorityError(s, "10.10.2.45")
+	cliTestAuthorityError(s, "1000.10.2.45:20")
+	cliTestAuthorityError(s, "[xyz0::1234]:443")
+}
+
 func HttpStaticPromTest(s *NoTopoSuite) {
 	query := "stats.prom"
 	vpp := s.GetContainerByName("vpp").VppInstance
diff --git a/src/plugins/http/CMakeLists.txt b/src/plugins/http/CMakeLists.txt
index d9cd84a..c51a7dc 100644
--- a/src/plugins/http/CMakeLists.txt
+++ b/src/plugins/http/CMakeLists.txt
@@ -16,4 +16,5 @@
   http.c
   http_buffer.c
   http_timer.c
+  http_test.c
 )
diff --git a/src/plugins/http/http.h b/src/plugins/http/http.h
index 63d0586..1914790 100644
--- a/src/plugins/http/http.h
+++ b/src/plugins/http/http.h
@@ -921,6 +921,58 @@
   return headers_buf;
 }
 
+typedef struct
+{
+  ip46_address_t ip;
+  u16 port;
+  u8 is_ip4;
+} http_uri_t;
+
+always_inline int
+http_parse_authority_form_target (u8 *target, http_uri_t *authority)
+{
+  unformat_input_t input;
+  u32 port;
+  int rv = 0;
+
+  unformat_init_vector (&input, vec_dup (target));
+  if (unformat (&input, "[%U]:%d", unformat_ip6_address, &authority->ip.ip6,
+		&port))
+    {
+      authority->port = clib_host_to_net_u16 (port);
+      authority->is_ip4 = 0;
+    }
+  else if (unformat (&input, "%U:%d", unformat_ip4_address, &authority->ip.ip4,
+		     &port))
+    {
+      authority->port = clib_host_to_net_u16 (port);
+      authority->is_ip4 = 1;
+    }
+  /* TODO reg-name resolution */
+  else
+    {
+      clib_warning ("unsupported format '%v'", target);
+      rv = -1;
+    }
+  unformat_free (&input);
+  return rv;
+}
+
+always_inline u8 *
+http_serialize_authority_form_target (http_uri_t *authority)
+{
+  u8 *s;
+
+  if (authority->is_ip4)
+    s = format (0, "%U:%d", format_ip4_address, &authority->ip.ip4,
+		clib_net_to_host_u16 (authority->port));
+  else
+    s = format (0, "[%U]:%d", format_ip6_address, &authority->ip.ip6,
+		clib_net_to_host_u16 (authority->port));
+
+  return s;
+}
+
 #endif /* SRC_PLUGINS_HTTP_HTTP_H_ */
 
 /*
diff --git a/src/plugins/http/http_test.c b/src/plugins/http/http_test.c
new file mode 100644
index 0000000..1f2f21d
--- /dev/null
+++ b/src/plugins/http/http_test.c
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2024 Cisco Systems, Inc.
+ */
+
+#include <http/http.h>
+
+static clib_error_t *
+test_http_authority_command_fn (vlib_main_t *vm, unformat_input_t *input,
+				vlib_cli_command_t *cmd)
+{
+  u8 *target = 0;
+  http_uri_t authority;
+  int rv;
+
+  if (!unformat (input, "%v", &target))
+    return clib_error_return (0, "error: no input provided");
+
+  rv = http_parse_authority_form_target (target, &authority);
+  vec_free (target);
+  if (rv)
+    return clib_error_return (0, "error: parsing failed");
+
+  target = http_serialize_authority_form_target (&authority);
+  vlib_cli_output (vm, "%v", target);
+  vec_free (target);
+
+  return 0;
+}
+
+VLIB_CLI_COMMAND (test_http_authority_command) = {
+  .path = "test http authority-form",
+  .short_help = "test dns authority-form",
+  .function = test_http_authority_command_fn,
+};