s.Containers.ServerVpp.VppInstance.Vppctl(cliServerCmd + " listener del")
o = s.Containers.ServerVpp.VppInstance.Vppctl("show session verbose proto http")
s.AssertNotContains(o, "LISTEN")
+
+ o = s.Containers.ClientVpp.VppInstance.Vppctl("show http stats")
+ s.Log(o)
+ s.AssertContains(o, "1 connections established")
+ s.AssertContains(o, "1 requests sent")
+ s.AssertContains(o, "1 responses received")
+ o = s.Containers.ServerVpp.VppInstance.Vppctl("show http stats")
+ s.Log(o)
+ s.AssertContains(o, "1 connections accepted")
+ s.AssertContains(o, "1 requests received")
+ s.AssertContains(o, "1 responses sent")
}
func HttpCliTlsTest(s *VethsSuite) {
}
s.AssertEqual(true, tcpSessionCleanupDone, "TCP session not cleanup")
s.AssertEqual(true, httpCleanupDone, "HTTP not cleanup")
+ o = vpp.Vppctl("show http stats")
+ s.Log(o)
+ s.AssertContains(o, "1 connections protocol error")
}
func HttpClientErrRespTest(s *Http1Suite) {
reply = make([]byte, 1024)
_, err = conn.Read(reply)
s.AssertMatchError(err, io.EOF, "connection not closed by server")
+ o := vpp.Vppctl("show http stats")
+ s.Log(o)
+ s.AssertContains(o, "1 connections timeout")
}
func HttpIgnoreH2UpgradeTest(s *Http1Suite) {
s.Log(o)
s.AssertContains(o, "<html>", "<html> not found in the result!")
s.AssertContains(o, "</html>", "</html> not found in the result!")
+ s.Containers.ClientVpp.VppInstance.Vppctl("clear http stats")
/* second request to test postponed ho-cleanup */
o = s.Containers.ClientVpp.VppInstance.Vppctl("http cli client" +
s.Log(o)
s.AssertContains(o, "<html>", "<html> not found in the result!")
s.AssertContains(o, "</html>", "</html> not found in the result!")
+
+ o = s.Containers.ClientVpp.VppInstance.Vppctl("show http stats")
+ s.Log(o)
+ s.AssertContains(o, "1 connections established")
+ s.AssertContains(o, "1 requests sent")
+ s.AssertContains(o, "1 responses received")
+ s.AssertContains(o, "1 application streams opened")
+ s.AssertContains(o, "1 application streams closed")
+ o = s.Containers.ServerVpp.VppInstance.Vppctl("show http stats")
+ s.Log(o)
+ s.AssertContains(o, "2 connections accepted")
+ s.AssertContains(o, "2 requests received")
+ s.AssertContains(o, "2 responses sent")
+ s.AssertContains(o, "2 application streams opened")
+ s.AssertContains(o, "2 application streams closed")
}
func Http2ClientGetTest(s *Http2Suite) {
func (s *Http1Suite) TeardownTest() {
defer s.HstSuite.TeardownTest()
+ vpp := s.Containers.Vpp.VppInstance
+ if CurrentSpecReport().Failed() {
+ s.Log(vpp.Vppctl("show session verbose 2"))
+ s.Log(vpp.Vppctl("show error"))
+ s.Log(vpp.Vppctl("show http stats"))
+ }
}
// Creates container and config.
if CurrentSpecReport().Failed() {
s.Log(vpp.Vppctl("show session verbose 2"))
s.Log(vpp.Vppctl("show error"))
+ s.Log(vpp.Vppctl("show http stats"))
}
}
#include <http/http_private.h>
#include <http/http_timer.h>
-static http_main_t http_main;
+http_main_t http_main;
+
static http_engine_vft_t *http_vfts;
const http_buffer_type_t msg_to_buf_type[] = {
return format (s, "%U", format_clib_timebase_time, now);
}
-static inline http_worker_t *
-http_worker_get (clib_thread_index_t thread_index)
-{
- return &http_main.wrk[thread_index];
-}
-
static inline u32
http_conn_alloc_w_thread (clib_thread_index_t thread_index)
{
if (PREDICT_FALSE (hc->version != HTTP_VERSION_NA))
http_vfts[hc->version].transport_close_callback (hc);
http_disconnect_transport (hc);
+ http_stats_connections_timeout_inc (hs_handle >> 24);
}
/*************************/
vec_foreach (wrk, hm->wrk)
{
clib_spinlock_init (&wrk->pending_stream_connects_lock);
+ clib_memset (&wrk->stats, 0, sizeof (wrk->stats));
}
vec_validate (hm->rx_bufs, num_threads - 1);
vec_validate (hm->tx_bufs, num_threads - 1);
VLIB_INIT_FUNCTION (http_transport_init);
+static clib_error_t *
+show_http_stats_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ vlib_thread_main_t *vtm = vlib_get_thread_main ();
+ http_main_t *hm = &http_main;
+ http_worker_t *wrk;
+ u32 num_threads, i;
+
+ if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ return clib_error_return (0, "unknown input `%U'", format_unformat_error,
+ input);
+
+ if (!hm->is_init)
+ return clib_error_return (0, "http transport disabled");
+
+ num_threads = 1 /* main thread */ + vtm->n_threads;
+
+ for (i = 0; i < num_threads; i++)
+ {
+ wrk = http_worker_get (i);
+ vlib_cli_output (vm, "Thread %u:\n", i);
+
+#define _(name, str) \
+ if (wrk->stats.name) \
+ vlib_cli_output (vm, " %lu %s", wrk->stats.name, str);
+ foreach_http_wrk_stat
+#undef _
+ }
+
+ return 0;
+}
+
+VLIB_CLI_COMMAND (show_http_stats_command, static) = {
+ .path = "show http stats",
+ .short_help = "show http stats",
+ .function = show_http_stats_fn,
+};
+
+static clib_error_t *
+clear_http_stats_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ vlib_thread_main_t *vtm = vlib_get_thread_main ();
+ http_main_t *hm = &http_main;
+ http_worker_t *wrk;
+ u32 num_threads, i;
+
+ if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ return clib_error_return (0, "unknown input `%U'", format_unformat_error,
+ input);
+
+ if (!hm->is_init)
+ return clib_error_return (0, "http transport disabled");
+
+ num_threads = 1 /* main thread */ + vtm->n_threads;
+
+ for (i = 0; i < num_threads; i++)
+ {
+ wrk = http_worker_get (i);
+ clib_memset (&wrk->stats, 0, sizeof (wrk->stats));
+ }
+
+ return 0;
+}
+
+VLIB_CLI_COMMAND (clear_http_stats_command, static) = {
+ .path = "clear http stats",
+ .short_help = "clear http stats",
+ .function = clear_http_stats_fn,
+};
+
static uword
unformat_http_version_cfg (unformat_input_t *input, va_list *va)
{
}
HTTP_DBG (3, "%v", rx_buf);
+ http_stats_responses_received_inc (hc->c_thread_index);
if (vec_len (rx_buf) < 8)
{
session_transport_closing_notify (&req->connection);
session_transport_closed_notify (&req->connection);
http_disconnect_transport (hc);
+ http_stats_proto_errors_inc (hc->c_thread_index);
return HTTP_SM_ERROR;
}
return HTTP_SM_STOP;
HTTP_DBG (3, "%v", rx_buf);
+ http_stats_requests_received_inc (hc->c_thread_index);
if (vec_len (rx_buf) < 8)
{
http_io_ts_after_read (hc, 1);
http1_send_error (hc, ec, 0);
session_transport_closing_notify (&req->connection);
+ http_stats_proto_errors_inc (hc->c_thread_index);
http_disconnect_transport (hc);
return HTTP_SM_ERROR;
clib_warning ("http protocol error: received more data than expected");
session_transport_closing_notify (&req->connection);
http_disconnect_transport (hc);
+ http_stats_proto_errors_inc (hc->c_thread_index);
http_req_state_change (req, HTTP_REQ_STATE_WAIT_APP_METHOD);
return HTTP_SM_ERROR;
}
session_transport_closing_notify (&req->connection);
session_transport_closed_notify (&req->connection);
http_disconnect_transport (hc);
+ http_stats_proto_errors_inc (hc->c_thread_index);
return HTTP_SM_STOP;
}
else
http_req_state_change (req, next_state);
http_io_ts_after_write (hc, 0);
+ http_stats_responses_sent_inc (hc->c_thread_index);
return sm_result;
error:
http1_send_error (hc, sc, sp);
session_transport_closing_notify (&req->connection);
http_disconnect_transport (hc);
+ http_stats_proto_errors_inc (hc->c_thread_index);
return HTTP_SM_STOP;
}
http_req_state_change (req, next_state);
http_io_ts_after_write (hc, 0);
+ http_stats_requests_sent_inc (hc->c_thread_index);
goto done;
error:
session_transport_closing_notify (&req->connection);
session_transport_closed_notify (&req->connection);
http_disconnect_transport (hc);
+ http_stats_proto_errors_inc (hc->c_thread_index);
done:
return sm_result;
req = http1_req_get (req_index, thread_index);
session_transport_closed_notify (&req->connection);
http_disconnect_transport (hc);
+ http_stats_connections_reset_by_app_inc (hc->c_thread_index);
}
static int
req = http1_conn_alloc_req (hc);
http_req_state_change (req, HTTP_REQ_STATE_WAIT_APP_METHOD);
+ http_stats_connections_established_inc (hc->c_thread_index);
return http_conn_established (hc, req, hc->hc_pa_app_api_ctx, 0);
}
/* first request - create request ctx and notify app about new conn */
req = http1_conn_alloc_req (hc);
http_conn_accept_request (hc, req);
+ http_stats_connections_accepted_inc (hc->c_thread_index);
http_req_state_change (req, HTTP_REQ_STATE_WAIT_TRANSPORT_METHOD);
hc->flags &= ~HTTP_CONN_F_NO_APP_SESSION;
}
return;
http_req_t *req = http1_conn_get_req (hc);
session_transport_reset_notify (&req->connection);
+ http_stats_connections_reset_by_peer_inc (hc->c_thread_index);
}
static void
req->flags |= HTTP2_REQ_F_IS_PARENT;
h2c->parent_req_index = req_index;
}
+ http_stats_app_streams_opened_inc (hc->c_thread_index);
return req;
}
memset (req, 0xba, sizeof (*req));
pool_put (wrk->req_pool, req);
h2c->req_num--;
+ http_stats_app_streams_closed_inc (thread_index);
}
static inline void
if (clib_llist_elt_is_linked (h2c, sched_list))
clib_llist_remove (wrk->conn_pool, sched_list, h2c);
http_shutdown_transport (hc);
+ http_stats_proto_errors_inc (hc->c_thread_index);
}
always_inline void
n_written = http_io_ts_write_segs (hc, segs, 2, 0);
ASSERT (n_written == (HTTP2_FRAME_HEADER_SIZE + headers_len));
http_io_ts_after_write (hc, 0);
+ http_stats_responses_sent_inc (hc->c_thread_index);
}
static void
n_written = http_io_ts_write_segs (hc, segs, 2, 0);
ASSERT (n_written == (HTTP2_FRAME_HEADER_SIZE + headers_len));
http_io_ts_after_write (hc, 0);
+ http_stats_requests_sent_inc (hc->c_thread_index);
}
static void
http2_worker_ctx_t *wrk = http2_get_worker (hc->c_thread_index);
http_req_state_t new_state = HTTP_REQ_STATE_WAIT_APP_METHOD;
+ http_stats_responses_received_inc (hc->c_thread_index);
+
h2c = http2_conn_ctx_get_w_thread (hc);
vec_reset_length (req->base.headers);
http_req_state_t new_state = HTTP_REQ_STATE_WAIT_APP_REPLY;
http2_worker_ctx_t *wrk = http2_get_worker (hc->c_thread_index);
+ http_stats_requests_received_inc (hc->c_thread_index);
+
h2c = http2_conn_ctx_get_w_thread (hc);
*error =
&h2c->decoder_dynamic_table,
http2_default_conn_settings.header_table_size);
http_req_state_change (&req->base, HTTP_REQ_STATE_WAIT_APP_METHOD);
+ http_stats_connections_established_inc (hc->c_thread_index);
if (http_conn_established (hc, &req->base, hc->hc_pa_app_api_ctx, 0))
return HTTP2_ERROR_INTERNAL_ERROR;
}
}
else
{
+ http_stats_connections_reset_by_peer_inc (hc->c_thread_index);
if (fh->length > HTTP2_GOAWAY_MIN_SIZE)
clib_warning ("additional debug data: %U", format_http_bytes,
rx_buf + HTTP2_GOAWAY_MIN_SIZE,
HTTP2_ERROR_INTERNAL_ERROR,
0);
session_transport_delete_notify (&req->base.connection);
+ if (req->flags & HTTP2_REQ_F_IS_PARENT)
+ {
+ HTTP_DBG (1, "client app closed parent, closing connection");
+ ASSERT (!(hc->flags & HTTP_CONN_F_IS_SERVER));
+ http_disconnect_transport (hc);
+ http_stats_connections_reset_by_app_inc (thread_index);
+ }
h2c = http2_conn_ctx_get_w_thread (hc);
http2_conn_free_req (h2c, req, hc->c_thread_index);
}
{
HTTP_DBG (1, "to_deq %u is less than conn preface size", to_deq);
http_disconnect_transport (hc);
+ http_stats_proto_errors_inc (hc->c_thread_index);
return;
}
+ http_stats_connections_accepted_inc (hc->c_thread_index);
if (http2_expect_preface (hc, h2c))
{
HTTP_DBG (1, "conn preface verification failed");
http_disconnect_transport (hc);
+ http_stats_proto_errors_inc (hc->c_thread_index);
return;
}
http2_send_server_preface (hc);
static const http_token_t http2_conn_preface = { http_token_lit (
"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") };
+#define foreach_http_wrk_stat \
+ _ (proto_errors, "connections protocol error") \
+ _ (connections_timeout, "connections timeout") \
+ _ (connections_accepted, "connections accepted") \
+ _ (connections_established, "connections established") \
+ _ (connections_reset_by_peer, "connections reset by peer") \
+ _ (connections_reset_by_app, "connections reset by app") \
+ _ (app_streams_opened, "application streams opened") \
+ _ (app_streams_closed, "application streams closed") \
+ _ (requests_received, "requests received") \
+ _ (requests_sent, "requests sent") \
+ _ (responses_received, "responses received") \
+ _ (responses_sent, "responses sent")
+
+typedef struct
+{
+#define _(name, str) u64 name;
+ foreach_http_wrk_stat
+#undef _
+} http_wrk_stats_t;
+
typedef union
{
struct
clib_spinlock_t pending_stream_connects_lock;
http_pending_connect_stream_t *pending_connect_streams;
http_pending_connect_stream_t *burst_connect_streams;
+ http_wrk_stats_t stats;
} http_worker_t;
typedef struct http_main_
return 0;
}
+extern http_main_t http_main;
+
+static_always_inline http_worker_t *
+http_worker_get (clib_thread_index_t thread_index)
+{
+ return &http_main.wrk[thread_index];
+}
+
+#define _(name, str) \
+ static_always_inline void http_stats_##name##_inc ( \
+ clib_thread_index_t thread_index) \
+ { \
+ http_worker_t *wrk = http_worker_get (thread_index); \
+ wrk->stats.name++; \
+ }
+foreach_http_wrk_stat
+#undef _
+
#endif /* SRC_PLUGINS_HTTP_HTTP_PRIVATE_H_ */