hsa: in http client print the response body only for text/* 72/43072/16
authorSemir Sionek <[email protected]>
Tue, 3 Jun 2025 15:07:28 +0000 (15:07 +0000)
committerFlorin Coras <[email protected]>
Fri, 13 Jun 2025 04:22:20 +0000 (04:22 +0000)
Type: improvement
Change-Id: I9a2e6b9344b64018b0c6872dbd051d7c39c7a0c3
Signed-off-by: Semir Sionek <[email protected]>
extras/hs-test/http_test.go
src/plugins/hs_apps/http_client.c

index 8b69072..88cb886 100644 (file)
@@ -38,7 +38,7 @@ func init() {
                HttpRequestLineTest, HttpClientGetTimeout, HttpStaticFileHandlerWrkTest, HttpStaticUrlHandlerWrkTest, HttpConnTimeoutTest,
                HttpClientGetRepeatTest, HttpClientPostRepeatTest, HttpIgnoreH2UpgradeTest, HttpInvalidAuthorityFormUriTest, HttpHeaderErrorConnectionDropTest,
                HttpClientInvalidHeaderNameTest, HttpStaticHttp1OnlyTest, HttpTimerSessionDisable, HttpClientBodySizeTest,
-               HttpStaticRedirectTest)
+               HttpStaticRedirectTest, HttpClientNoPrintTest)
        RegisterNoTopoSoloTests(HttpStaticPromTest, HttpGetTpsTest, HttpGetTpsInterruptModeTest, PromConcurrentConnectionsTest,
                PromMemLeakTest, HttpClientPostMemLeakTest, HttpInvalidClientRequestMemLeakTest, HttpPostTpsTest, HttpPostTpsInterruptModeTest,
                PromConsecutiveConnectionsTest, HttpGetTpsTlsTest, HttpPostTpsTlsTest)
@@ -459,6 +459,30 @@ func HttpClientPostFormTest(s *NoTopoSuite) {
        s.AssertContains(o, "200 OK")
 }
 
+func HttpClientNoPrintTest(s *NoTopoSuite) {
+       serverAddress := s.HostAddr() + ":" + s.Ports.Http
+       server := ghttp.NewUnstartedServer()
+       l, err := net.Listen("tcp", serverAddress)
+       s.AssertNil(err, fmt.Sprint(err))
+       server.HTTPTestServer.Listener = l
+       server.AppendHandlers(
+               ghttp.CombineHandlers(
+                       s.LogHttpReq(true),
+                       ghttp.VerifyRequest("GET", "/"),
+                       // Bogus header just for testing
+                       ghttp.RespondWith(http.StatusOK, "<html><body><p>Hello</p></body></html>", http.Header{"Content-Type": {"image/jpeg"}}),
+               ))
+       server.Start()
+       defer server.Close()
+       uri := "http://" + serverAddress
+       vpp := s.Containers.Vpp.VppInstance
+       o := vpp.Vppctl("http client verbose uri " + uri)
+
+       s.Log(o)
+       s.AssertContains(o, "* binary file, not printing!", "no warning message found!")
+       s.AssertNotContains(o, "</html>", "</html> found in the result!")
+}
+
 func HttpClientGetResponseBodyTest(s *NoTopoSuite) {
        response := "<body>hello world</body>"
        size := len(response)
index 7b86278..5787b45 100644 (file)
@@ -6,10 +6,23 @@
 #include <vnet/session/application_interface.h>
 #include <vnet/session/session.h>
 #include <http/http.h>
+#include <http/http_header_names.h>
 #include <http/http_content_types.h>
 #include <http/http_status_codes.h>
 #include <vppinfra/unix.h>
 
+#define foreach_hc_s_flag                                                     \
+  _ (1, IS_CLOSED)                                                            \
+  _ (2, PRINTABLE_BODY)                                                       \
+  _ (4, BODY_OVER_LIMIT)
+
+typedef enum hc_s_flag_
+{
+#define _(n, s) HC_S_FLAG_##s = n,
+  foreach_hc_s_flag
+#undef _
+} hc_s_flags;
+
 typedef struct
 {
   u64 req_per_wrk;
@@ -24,8 +37,7 @@ typedef struct
   u32 session_index;
   clib_thread_index_t thread_index;
   u64 to_recv;
-  u8 is_closed;
-  u8 body_over_limit;
+  u8 session_flags;
   hc_stats_t stats;
   u64 data_offset;
   u64 body_recv;
@@ -98,6 +110,19 @@ typedef enum
   HC_REPEAT_DONE,
 } hc_cli_signal_t;
 
+#define mime_printable_max_len 35
+const char mime_printable[][mime_printable_max_len] = {
+  "text/\0",
+  "application/json\0",
+  "application/javascript\0",
+  "application/x-yaml\0",
+  "application/x-www-form-urlencoded\0",
+  "application/xml\0",
+  "application/x-sh\0",
+  "application/x-tex\0",
+  "application/x-javascript\0",
+  "application/x-powershell\0"
+};
 static hc_main_t hc_main;
 static hc_stats_t hc_stats;
 
@@ -350,7 +375,7 @@ hc_session_reset_callback (session_t *s)
   int rv;
 
   hc_session = hc_session_get (s->opaque, s->thread_index);
-  hc_session->is_closed = 1;
+  hc_session->session_flags |= HC_S_FLAG_IS_CLOSED;
 
   a->handle = session_handle (s);
   a->app_index = hcm->app_index;
@@ -371,7 +396,7 @@ hc_rx_callback (session_t *s)
   session_error_t session_err = 0;
   int send_err = 0;
 
-  if (hc_session->is_closed)
+  if (hc_session->session_flags & HC_S_FLAG_IS_CLOSED)
     {
       clib_warning ("hc_session_index[%d] is closed", s->opaque);
       return -1;
@@ -401,17 +426,43 @@ hc_rx_callback (session_t *s)
            hc_session->response_status =
              format (0, "%U", format_http_status_code, msg.code);
 
+         http_header_table_t ht = HTTP_HEADER_TABLE_NULL;
+
          svm_fifo_dequeue_drop (s->rx_fifo, msg.data.headers_offset);
 
          vec_validate (hc_session->resp_headers, msg.data.headers_len - 1);
          vec_set_len (hc_session->resp_headers, msg.data.headers_len);
          rv = svm_fifo_dequeue (s->rx_fifo, msg.data.headers_len,
                                 hc_session->resp_headers);
+         ht.buf = hc_session->resp_headers;
 
          ASSERT (rv == msg.data.headers_len);
          HTTP_DBG (1, (char *) format (0, "%v", hc_session->resp_headers));
          msg.data.body_offset -=
            msg.data.headers_len + msg.data.headers_offset;
+
+         http_build_header_table (&ht, msg);
+         const http_token_t *content_type = http_get_header (
+           &ht, http_header_name_token (HTTP_HEADER_CONTENT_TYPE));
+         if (content_type)
+           {
+             for (u8 i = 0; i < sizeof (mime_printable) /
+                                  (sizeof (char) * mime_printable_max_len);
+                  i++)
+               {
+                 u8 mime_len =
+                   clib_strnlen (mime_printable[i], mime_printable_max_len);
+                 if (content_type->len >= mime_len &&
+                     clib_strncmp (content_type->base, mime_printable[i],
+                                   mime_len) == 0)
+                   {
+                     hc_session->session_flags |= HC_S_FLAG_PRINTABLE_BODY;
+                     break;
+                   }
+               }
+           }
+         ht.buf = NULL;
+         http_free_header_table (&ht);
        }
 
       if (msg.data.body_len == 0)
@@ -428,10 +479,11 @@ hc_rx_callback (session_t *s)
          goto done;
        }
       if (msg.data.body_len > hcm->max_body_size)
-       hc_session->body_over_limit = true;
+       hc_session->session_flags |= HC_S_FLAG_BODY_OVER_LIMIT;
       vec_validate (hc_session->http_response,
-                   (hc_session->body_over_limit ? hcm->rx_fifo_size - 1 :
-                                                  msg.data.body_len - 1));
+                   (hc_session->session_flags & HC_S_FLAG_BODY_OVER_LIMIT ?
+                      hcm->rx_fifo_size - 1 :
+                      msg.data.body_len - 1));
       vec_reset_length (hc_session->http_response);
     }
 
@@ -454,7 +506,7 @@ hc_rx_callback (session_t *s)
     }
 
   ASSERT (rv == n_deq);
-  if (!hc_session->body_over_limit)
+  if (!(hc_session->session_flags & HC_S_FLAG_BODY_OVER_LIMIT))
     vec_set_len (hc_session->http_response, curr + n_deq);
   ASSERT (hc_session->to_recv >= rv);
   hc_session->to_recv -= rv;
@@ -732,14 +784,21 @@ hc_get_event (vlib_main_t *vm)
        {
          wrk = hc_worker_get (hcm->worker_index);
          hc_session = hc_session_get (wrk->session_index, wrk->thread_index);
-         vlib_cli_output (vm, "< %v\n< %v\n", hc_session->response_status,
+         vlib_cli_output (vm, "< %v\n< %v\n%v", hc_session->response_status,
                           hc_session->resp_headers);
-         if (hc_session->body_over_limit)
+         if (hc_session->session_flags & HC_S_FLAG_BODY_OVER_LIMIT)
            vlib_cli_output (
              vm, "* message body over limit, read total %llu bytes",
              hc_session->body_recv);
          else
-           vlib_cli_output (vm, "%v", hc_session->http_response);
+           {
+             if (hc_session->session_flags & HC_S_FLAG_PRINTABLE_BODY)
+               vlib_cli_output (vm, "%v", hc_session->http_response);
+             else
+               vlib_cli_output (vm,
+                                "* binary file, not printing!\n* consider "
+                                "saving to file with the 'file' option");
+           }
        }
       break;
     case HC_REPEAT_DONE: