This is an automated email from the ASF dual-hosted git repository.
maskit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 3c232f47f9 Add metrics for H2/H3 frames and a way to access them from
plugins (#10627)
3c232f47f9 is described below
commit 3c232f47f9b0c0f0c32b644ecb715dd1a7b7ce25
Author: Masakazu Kitajo <[email protected]>
AuthorDate: Mon Jan 29 15:53:33 2024 -0700
Add metrics for H2/H3 frames and a way to access them from plugins (#10627)
---
.../api/functions/TSHttpSsnInfoIntGet.en.rst | 55 +++++++
include/proxy/ProxySession.h | 3 +
include/proxy/http2/HTTP2.h | 13 ++
include/proxy/http2/Http2ClientSession.h | 2 +
include/proxy/http2/Http2CommonSession.h | 20 +++
include/proxy/http2/Http2ServerSession.h | 2 +
include/proxy/http3/Http3.h | 10 ++
.../proxy/http3/{Http3.h => Http3FrameCounter.h} | 49 ++++---
include/proxy/http3/Http3Session.h | 5 +
include/proxy/http3/Http3Types.h | 27 ++--
include/ts/apidefs.h.in | 9 ++
include/ts/ts.h | 13 ++
src/api/InkAPI.cc | 25 ++++
src/proxy/ProxySession.cc | 12 ++
src/proxy/http2/HTTP2.cc | 25 ++++
src/proxy/http2/Http2ClientSession.cc | 16 ++
src/proxy/http2/Http2CommonSession.cc | 13 ++
src/proxy/http2/Http2ServerSession.cc | 16 ++
src/proxy/http3/CMakeLists.txt | 1 +
src/proxy/http3/Http3.cc | 30 +++-
src/proxy/http3/Http3App.cc | 2 +
src/proxy/http3/Http3DebugNames.cc | 14 +-
src/proxy/http3/Http3FrameCounter.cc | 59 ++++++++
src/proxy/http3/Http3ProtocolEnforcer.cc | 9 +-
src/proxy/http3/Http3Session.cc | 18 +++
src/proxy/http3/Http3Transaction.cc | 1 +
tests/gold_tests/h2/gold/log.gold | 2 +
tests/gold_tests/pluginTest/tsapi/CMakeLists.txt | 1 +
.../pluginTest/tsapi/test_TSHttpSsnInfo.cc | 163 +++++++++++++++++++++
.../pluginTest/tsapi/test_TSHttpSsnInfo.test.py | 103 +++++++++++++
.../pluginTest/tsapi/test_TSHttpSsnInfo_curl0.gold | 1 +
.../tsapi/test_TSHttpSsnInfo_nghttp0.gold | 21 +++
.../tsapi/test_TSHttpSsnInfo_plugin_log.gold | 2 +
33 files changed, 693 insertions(+), 49 deletions(-)
diff --git a/doc/developer-guide/api/functions/TSHttpSsnInfoIntGet.en.rst
b/doc/developer-guide/api/functions/TSHttpSsnInfoIntGet.en.rst
new file mode 100644
index 0000000000..c8028d906a
--- /dev/null
+++ b/doc/developer-guide/api/functions/TSHttpSsnInfoIntGet.en.rst
@@ -0,0 +1,55 @@
+.. Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed
+ with this work for additional information regarding copyright
+ ownership. The ASF licenses this file to you under the Apache
+ License, Version 2.0 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of
+ the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ permissions and limitations under the License.
+
+
+TSHttpSsnInfoIntGet
+===================
+
+Synopsis
+--------
+
+.. code-block:: cpp
+
+ #include <ts/ts.h>
+
+.. c:function:: TSReturnCode TSHttpSsnInfoIntGet(TSHttpSsn ssnp,
TSHttpSsnInfoKey key, TSMgmtInt * value, uint64_t subkey = 0)
+
+Description
+-----------
+
+:c:func:`TSHttpSsnInfoIntGet` returns arbitrary integer-typed info about a
session as defined in
+:c:type:`TSHttpSsnInfoKey`. The API will be part of a generic API umbrella
that can support returning
+arbitrary info about a session using custom log tags.
+
+The :c:type:`TSHttpSsnInfoKey` currently supports the below integer-based info
about a transaction
+
+.. c:enum:: TSHttpSsnInfoKey
+
+ .. c:enumerator:: TS_SSN_INFO_TRANSACTION_COUNT
+
+ The value indicate the number of transactions made on the session.
+
+ .. c:enumerator:: TS_SSN_INFO_RECEIVED_FRAME_COUNT
+
+ The value indicate the number of HTTP/2 or HTTP/3 frames received on the
session.
+ A frame type must be specified by passing it to subkey.
+ You can use TS_SSN_INFO_RECEIVED_FRAME_COUNT_H2_UNKNOWN and
TS_SSN_INFO_RECEIVED_FRAME_COUNT_H3_UNKNOWN to get the value for
+ unknown frames.
+
+Return values
+-------------
+
+The API returns :c:data:`TS_SUCCESS`, if the requested info is supported,
:c:data:`TS_ERROR` otherwise.
diff --git a/include/proxy/ProxySession.h b/include/proxy/ProxySession.h
index 9ad9f644d6..a9f3e4a289 100644
--- a/include/proxy/ProxySession.h
+++ b/include/proxy/ProxySession.h
@@ -111,6 +111,9 @@ public:
virtual PoolableSession *get_server_session() const;
+ virtual bool is_protocol_framed() const;
+ virtual uint64_t get_received_frame_count(uint64_t type) const;
+
// Replicate NetVConnection API
virtual sockaddr const *get_remote_addr() const;
virtual sockaddr const *get_local_addr();
diff --git a/include/proxy/http2/HTTP2.h b/include/proxy/http2/HTTP2.h
index e71f5577f6..fbaa579e7c 100644
--- a/include/proxy/http2/HTTP2.h
+++ b/include/proxy/http2/HTTP2.h
@@ -106,6 +106,17 @@ struct Http2StatsBlock {
Metrics::Counter::AtomicType *insufficient_avg_window_update;
Metrics::Counter::AtomicType *max_concurrent_streams_exceeded_in;
Metrics::Counter::AtomicType *max_concurrent_streams_exceeded_out;
+ Metrics::Counter::AtomicType *data_frames_in;
+ Metrics::Counter::AtomicType *headers_frames_in;
+ Metrics::Counter::AtomicType *priority_frames_in;
+ Metrics::Counter::AtomicType *rst_stream_frames_in;
+ Metrics::Counter::AtomicType *settings_frames_in;
+ Metrics::Counter::AtomicType *push_promise_frames_in;
+ Metrics::Counter::AtomicType *ping_frames_in;
+ Metrics::Counter::AtomicType *goaway_frames_in;
+ Metrics::Counter::AtomicType *window_update_frames_in;
+ Metrics::Counter::AtomicType *continuation_frames_in;
+ Metrics::Counter::AtomicType *unknown_frames_in;
};
extern Http2StatsBlock http2_rsb;
@@ -166,6 +177,8 @@ enum Http2FrameType {
HTTP2_FRAME_TYPE_MAX,
};
+extern Metrics::Counter::AtomicType
*http2_frame_metrics_in[HTTP2_FRAME_TYPE_MAX + 1];
+
// [RFC 7540] 6.1. Data
enum Http2FrameFlagsData {
HTTP2_FLAGS_DATA_END_STREAM = 0x01,
diff --git a/include/proxy/http2/Http2ClientSession.h
b/include/proxy/http2/Http2ClientSession.h
index 8a550c02f4..3e3cea196a 100644
--- a/include/proxy/http2/Http2ClientSession.h
+++ b/include/proxy/http2/Http2ClientSession.h
@@ -49,6 +49,8 @@ public:
void destroy() override;
void release(ProxyTransaction *trans) override;
void free() override;
+ bool is_protocol_framed() const override;
+ uint64_t get_received_frame_count(uint64_t type) const override;
////////////////////
// Accessors
diff --git a/include/proxy/http2/Http2CommonSession.h
b/include/proxy/http2/Http2CommonSession.h
index 60e19be543..9f9b7e53b3 100644
--- a/include/proxy/http2/Http2CommonSession.h
+++ b/include/proxy/http2/Http2CommonSession.h
@@ -132,6 +132,11 @@ protected:
int do_start_frame_read(Http2ErrorCode &ret_error);
int do_complete_frame_read();
+ /**
+ * Count received frames
+ */
+ void _count_received_frames(uint32_t type);
+
bool _should_do_something_else();
////////
@@ -169,6 +174,21 @@ protected:
int64_t read_from_early_data = 0;
bool cur_frame_from_early_data = false;
+ // Counter for received frames
+ std::atomic<uint64_t> _frame_counts_in[HTTP2_FRAME_TYPE_MAX + 1] = {
+ 0, // DATA
+ 0, // HEADERS
+ 0, // PRIORITY
+ 0, // RST_STREAM
+ 0, // SETTINGS
+ 0, // PUSH_PROMISE
+ 0, // PING
+ 0, // GOAWAY
+ 0, // WINDOW_UPDATE
+ 0, // CONTINUATION
+ 0 // UNKNOWN
+ };
+
private:
bool _interrupt_reading_frames = false;
};
diff --git a/include/proxy/http2/Http2ServerSession.h
b/include/proxy/http2/Http2ServerSession.h
index 472c594700..c575314278 100644
--- a/include/proxy/http2/Http2ServerSession.h
+++ b/include/proxy/http2/Http2ServerSession.h
@@ -52,6 +52,8 @@ public:
void release(ProxyTransaction *trans) override;
void free() override;
ProxyTransaction *new_transaction() override;
+ bool is_protocol_framed() const override;
+ uint64_t get_received_frame_count(uint64_t type) const override;
void add_session() override;
void remove_session();
diff --git a/include/proxy/http3/Http3.h b/include/proxy/http3/Http3.h
index c1b1ff3205..f3777e5d38 100644
--- a/include/proxy/http3/Http3.h
+++ b/include/proxy/http3/Http3.h
@@ -25,6 +25,7 @@
#include "tscore/ink_defs.h"
#include "tsutil/Metrics.h"
+#include "Http3Types.h"
using ts::Metrics;
@@ -44,6 +45,15 @@ struct Http3StatsBlock {
// Example: Metrics::Counter::AtomicType *current_client_session_count;
// Once created, e.g.
// Metrics::Counter::increment(http3_rsb.current_client_session_count);
+ Metrics::Counter::AtomicType *data_frames_in;
+ Metrics::Counter::AtomicType *headers_frames_in;
+ Metrics::Counter::AtomicType *cancel_push_frames_in;
+ Metrics::Counter::AtomicType *settings_frames_in;
+ Metrics::Counter::AtomicType *push_promise_frames_in;
+ Metrics::Counter::AtomicType *goaway_frames_in;
+ Metrics::Counter::AtomicType *max_push_id;
+ Metrics::Counter::AtomicType *unknown_frames_in;
};
extern Http3StatsBlock http3_rsb; // Container for statistics.
+extern Metrics::Counter::AtomicType
*http3_frame_metrics_in[static_cast<int>(Http3FrameType::UNKNOWN) + 1];
diff --git a/include/proxy/http3/Http3.h
b/include/proxy/http3/Http3FrameCounter.h
similarity index 50%
copy from include/proxy/http3/Http3.h
copy to include/proxy/http3/Http3FrameCounter.h
index c1b1ff3205..b64b080dc4 100644
--- a/include/proxy/http3/Http3.h
+++ b/include/proxy/http3/Http3FrameCounter.h
@@ -23,27 +23,38 @@
#pragma once
-#include "tscore/ink_defs.h"
-#include "tsutil/Metrics.h"
+#include "proxy/http3/Http3Types.h"
+#include "proxy/http3/Http3FrameHandler.h"
-using ts::Metrics;
-
-extern const uint32_t HTTP3_DEFAULT_HEADER_TABLE_SIZE;
-extern const uint32_t HTTP3_DEFAULT_MAX_FIELD_SECTION_SIZE;
-extern const uint32_t HTTP3_DEFAULT_QPACK_BLOCKED_STREAMS;
-extern const uint32_t HTTP3_DEFAULT_NUM_PLACEHOLDERS;
-
-class Http3
+class Http3FrameCounter : public Http3FrameHandler
{
public:
- static void init();
-};
+ Http3FrameCounter(){};
-// Statistics
-struct Http3StatsBlock {
- // Example: Metrics::Counter::AtomicType *current_client_session_count;
- // Once created, e.g.
- // Metrics::Counter::increment(http3_rsb.current_client_session_count);
-};
+ // Http3FrameHandler
+ std::vector<Http3FrameType> interests() override;
+ Http3ErrorUPtr handle_frame(std::shared_ptr<const Http3Frame> frame, int32_t
frame_seq = -1,
+ Http3StreamType s_type =
Http3StreamType::UNKNOWN) override;
+
+ uint64_t get_count(uint64_t type) const;
-extern Http3StatsBlock http3_rsb; // Container for statistics.
+private:
+ // Counter for received frames
+ std::atomic<uint64_t>
_frame_counts_in[static_cast<int>(Http3FrameType::UNKNOWN) + 1] = {
+ 0, // DATA
+ 0, // HEADERS
+ 0, // X_RESERVED_1
+ 0, // CANCEL_PUSH
+ 0, // SETTINGS
+ 0, // PUSH_PROMISE
+ 0, // X_RESERVED_2
+ 0, // GOAWAY
+ 0, // X_RESERVED_3
+ 0, // X_RESERVED_4
+ 0, // UNDEFINED
+ 0, // UNDEFINED
+ 0, // UNDEFINED
+ 0, // MAX_PUSH_ID
+ 0 // UNKNOWN
+ };
+};
diff --git a/include/proxy/http3/Http3Session.h
b/include/proxy/http3/Http3Session.h
index 5bbb4ed795..cfcb7e7f76 100644
--- a/include/proxy/http3/Http3Session.h
+++ b/include/proxy/http3/Http3Session.h
@@ -25,6 +25,7 @@
#include "proxy/ProxySession.h"
#include "proxy/http3/Http3Transaction.h"
+#include "proxy/http3/Http3FrameCounter.h"
#include "proxy/http3/QPACK.h"
class HQSession : public ProxySession
@@ -76,16 +77,20 @@ public:
HTTPVersion get_version(HTTPHdr &hdr) const override;
void increment_current_active_connections_stat() override;
void decrement_current_active_connections_stat() override;
+ bool is_protocol_framed() const override;
+ uint64_t get_received_frame_count(uint64_t type) const override;
// Implement ProxySession interface
const char *get_protocol_string() const override;
QPACK *local_qpack();
QPACK *remote_qpack();
+ Http3FrameCounter *get_received_frame_counter();
private:
QPACK *_remote_qpack = nullptr; // QPACK for decoding
QPACK *_local_qpack = nullptr; // QPACK for encoding
+ Http3FrameCounter _received_frame_counter;
};
/**
diff --git a/include/proxy/http3/Http3Types.h b/include/proxy/http3/Http3Types.h
index 8986813d78..e5d54c6731 100644
--- a/include/proxy/http3/Http3Types.h
+++ b/include/proxy/http3/Http3Types.h
@@ -50,20 +50,19 @@ enum class Http3SettingsId : uint64_t {
// Update Http3Frame::type(const uint8_t *) too when you modify this list
enum class Http3FrameType : uint64_t {
- DATA = 0x00,
- HEADERS = 0x01,
- PRIORITY = 0x02,
- CANCEL_PUSH = 0x03,
- SETTINGS = 0x04,
- PUSH_PROMISE = 0x05,
- X_RESERVED_1 = 0x06,
- GOAWAY = 0x07,
- X_RESERVED_2 = 0x08,
- X_RESERVED_3 = 0x09,
- MAX_PUSH_ID = 0x0D,
- DUPLICATE_PUSH_ID = 0x0E,
- X_MAX_DEFINED = 0x0E,
- UNKNOWN = 0xFF,
+ DATA = 0x00,
+ HEADERS = 0x01,
+ X_RESERVED_1 = 0x02,
+ CANCEL_PUSH = 0x03,
+ SETTINGS = 0x04,
+ PUSH_PROMISE = 0x05,
+ X_RESERVED_2 = 0x06,
+ GOAWAY = 0x07,
+ X_RESERVED_3 = 0x08,
+ X_RESERVED_4 = 0x09,
+ MAX_PUSH_ID = 0x0D,
+ X_MAX_DEFINED = 0x0D,
+ UNKNOWN = 0x0E,
};
enum class Http3ErrorClass {
diff --git a/include/ts/apidefs.h.in b/include/ts/apidefs.h.in
index e2399b9768..9c9ae73823 100644
--- a/include/ts/apidefs.h.in
+++ b/include/ts/apidefs.h.in
@@ -647,6 +647,15 @@ char const TS_VERSION_STRING[] = "@TS_VERSION_STRING@";
TS_TXN_INFO_LAST_ENTRY,
};
+ enum TSHttpSsnInfoKey {
+ TS_SSN_INFO_NONE = -1,
+ TS_SSN_INFO_TRANSACTION_COUNT,
+ TS_SSN_INFO_RECEIVED_FRAME_COUNT,
+ };
+
+#define TS_SSN_INFO_RECEIVED_FRAME_COUNT_H2_UNKNOWN 999
+#define TS_SSN_INFO_RECEIVED_FRAME_COUNT_H3_UNKNOWN 0x21
+
enum TSVConnCloseFlags {
TS_VC_CLOSE_ABORT = -1,
TS_VC_CLOSE_NORMAL = 1,
diff --git a/include/ts/ts.h b/include/ts/ts.h
index e8fc431113..d414bd39f8 100644
--- a/include/ts/ts.h
+++ b/include/ts/ts.h
@@ -3036,6 +3036,19 @@ namespace c
*/
TSReturnCode TSHttpTxnInfoIntGet(TSHttpTxn txnp, TSHttpTxnInfoKey key,
TSMgmtInt *value);
+ /* Get Arbitrary Ssn info such as total transaction count etc as defined in
TSHttpSsnInfoKey */
+ /**
+ Return the particular ssn info requested.
+
+ @param ssnp the transaction pointer
+ @param key the requested ssn info.
+ @param TSMgmtInt a pointer to a integer where the return value is stored
+
+ @return @c TS_SUCCESS if the requested info is supported, TS_ERROR
otherwise
+
+ */
+ TSReturnCode TSHttpSsnInfoIntGet(TSHttpSsn ssnp, TSHttpSsnInfoKey key,
TSMgmtInt *value, uint64_t sub_key = 0);
+
/****************************************************************************
* TSHttpTxnCacheLookupCountGet
* Return: TS_SUCCESS/TS_ERROR
diff --git a/src/api/InkAPI.cc b/src/api/InkAPI.cc
index 47657c11f6..05ee4ce783 100644
--- a/src/api/InkAPI.cc
+++ b/src/api/InkAPI.cc
@@ -4933,6 +4933,31 @@ tsapi::c::TSHttpTxnInfoIntGet(TSHttpTxn txnp,
TSHttpTxnInfoKey key, TSMgmtInt *v
return TS_SUCCESS;
}
+TSReturnCode
+tsapi::c::TSHttpSsnInfoIntGet(TSHttpSsn ssnp, TSHttpSsnInfoKey key, TSMgmtInt
*value, uint64_t sub_key)
+{
+ sdk_assert(sdk_sanity_check_http_ssn(ssnp) == TS_SUCCESS);
+ sdk_assert(sdk_sanity_check_null_ptr((void *)value) == TS_SUCCESS);
+
+ ProxySession *ssn = reinterpret_cast<ProxySession *>(ssnp);
+
+ switch (key) {
+ case TS_SSN_INFO_TRANSACTION_COUNT:
+ *value = ssn->get_transact_count();
+ break;
+ case TS_SSN_INFO_RECEIVED_FRAME_COUNT:
+ if (!ssn->is_protocol_framed()) {
+ return TS_ERROR;
+ }
+ *value = ssn->get_received_frame_count(sub_key);
+ break;
+ default:
+ return TS_ERROR;
+ }
+
+ return TS_SUCCESS;
+}
+
int
tsapi::c::TSHttpTxnIsWebsocket(TSHttpTxn txnp)
{
diff --git a/src/proxy/ProxySession.cc b/src/proxy/ProxySession.cc
index dea84625a4..c2911ddd4f 100644
--- a/src/proxy/ProxySession.cc
+++ b/src/proxy/ProxySession.cc
@@ -215,6 +215,18 @@ ProxySession::get_server_session() const
return nullptr;
}
+bool
+ProxySession::is_protocol_framed() const
+{
+ return false;
+}
+
+uint64_t
+ProxySession::get_received_frame_count(uint64_t type) const
+{
+ return 0;
+}
+
void
ProxySession::set_active_timeout(ink_hrtime timeout_in)
{
diff --git a/src/proxy/http2/HTTP2.cc b/src/proxy/http2/HTTP2.cc
index 584d203a73..3bd504ee62 100644
--- a/src/proxy/http2/HTTP2.cc
+++ b/src/proxy/http2/HTTP2.cc
@@ -50,6 +50,8 @@ static VersionConverter hvc;
// Statistics
Http2StatsBlock http2_rsb;
+Metrics::Counter::AtomicType *http2_frame_metrics_in[HTTP2_FRAME_TYPE_MAX + 1];
+
union byte_pointer {
byte_pointer(void *p) : ptr(p) {}
void *ptr;
@@ -600,6 +602,29 @@ Http2::init()
Metrics::Counter::createPtr("proxy.process.http2.max_concurrent_streams_exceeded_in");
http2_rsb.max_concurrent_streams_exceeded_out =
Metrics::Counter::createPtr("proxy.process.http2.max_concurrent_streams_exceeded_out");
+ http2_rsb.data_frames_in =
Metrics::Counter::createPtr("proxy.process.http2.data_frames_in"),
+ http2_rsb.headers_frames_in =
Metrics::Counter::createPtr("proxy.process.http2.headers_frames_in"),
+ http2_rsb.priority_frames_in =
Metrics::Counter::createPtr("proxy.process.http2.priority_frames_in"),
+ http2_rsb.rst_stream_frames_in =
Metrics::Counter::createPtr("proxy.process.http2.rst_stream_frames_in"),
+ http2_rsb.settings_frames_in =
Metrics::Counter::createPtr("proxy.process.http2.settings_frames_in"),
+ http2_rsb.push_promise_frames_in =
Metrics::Counter::createPtr("proxy.process.http2.push_promise_frames_in"),
+ http2_rsb.ping_frames_in =
Metrics::Counter::createPtr("proxy.process.http2.ping_frames_in"),
+ http2_rsb.goaway_frames_in =
Metrics::Counter::createPtr("proxy.process.http2.goaway_frames_in"),
+ http2_rsb.window_update_frames_in =
Metrics::Counter::createPtr("proxy.process.http2.window_update_frames_in"),
+ http2_rsb.continuation_frames_in =
Metrics::Counter::createPtr("proxy.process.http2.continuation_frames_in"),
+ http2_rsb.unknown_frames_in =
Metrics::Counter::createPtr("proxy.process.http2.unknown_frames_in"),
+
+ http2_frame_metrics_in[0] = http2_rsb.data_frames_in;
+ http2_frame_metrics_in[1] = http2_rsb.headers_frames_in;
+ http2_frame_metrics_in[2] = http2_rsb.priority_frames_in;
+ http2_frame_metrics_in[3] = http2_rsb.rst_stream_frames_in;
+ http2_frame_metrics_in[4] = http2_rsb.settings_frames_in;
+ http2_frame_metrics_in[5] = http2_rsb.push_promise_frames_in;
+ http2_frame_metrics_in[6] = http2_rsb.ping_frames_in;
+ http2_frame_metrics_in[7] = http2_rsb.goaway_frames_in;
+ http2_frame_metrics_in[8] = http2_rsb.window_update_frames_in;
+ http2_frame_metrics_in[9] = http2_rsb.continuation_frames_in;
+ http2_frame_metrics_in[10] = http2_rsb.unknown_frames_in;
http2_init();
}
diff --git a/src/proxy/http2/Http2ClientSession.cc
b/src/proxy/http2/Http2ClientSession.cc
index f343a6b1c5..bf4732fb46 100644
--- a/src/proxy/http2/Http2ClientSession.cc
+++ b/src/proxy/http2/Http2ClientSession.cc
@@ -338,3 +338,19 @@ Http2ClientSession::get_version(HTTPHdr &hdr) const
{
return HTTP_2_0;
}
+
+bool
+Http2ClientSession::is_protocol_framed() const
+{
+ return true;
+}
+
+uint64_t
+Http2ClientSession::get_received_frame_count(uint64_t type) const
+{
+ if (type == 999) { // TS_SSN_INFO_RECEIVED_FRAME_COUNT_H2_UNKNOWN in
apidefs.h.in
+ return this->_frame_counts_in[HTTP2_FRAME_TYPE_MAX];
+ } else {
+ return this->_frame_counts_in[type];
+ }
+}
diff --git a/src/proxy/http2/Http2CommonSession.cc
b/src/proxy/http2/Http2CommonSession.cc
index cc4a357523..56c82e40ac 100644
--- a/src/proxy/http2/Http2CommonSession.cc
+++ b/src/proxy/http2/Http2CommonSession.cc
@@ -321,6 +321,7 @@ Http2CommonSession::do_complete_frame_read()
ink_release_assert(this->_read_buffer_reader->read_avail() >=
this->current_hdr.length);
Http2Frame frame(this->current_hdr, this->_read_buffer_reader,
this->cur_frame_from_early_data);
+ this->_count_received_frames(frame.header().type);
connection_state.rcv_frame(&frame);
// Check whether data is read from early data
@@ -467,3 +468,15 @@ Http2CommonSession::is_outbound() const
{
return false;
}
+
+void
+Http2CommonSession::_count_received_frames(uint32_t type)
+{
+ if (type > HTTP2_FRAME_TYPE_MAX) {
+ type = HTTP2_FRAME_TYPE_MAX;
+ }
+ // Global counter
+ Metrics::Counter::increment(http2_frame_metrics_in[type]);
+ // Local counter
+ this->_frame_counts_in[type]++;
+}
diff --git a/src/proxy/http2/Http2ServerSession.cc
b/src/proxy/http2/Http2ServerSession.cc
index 10896ac6aa..46b3767420 100644
--- a/src/proxy/http2/Http2ServerSession.cc
+++ b/src/proxy/http2/Http2ServerSession.cc
@@ -403,6 +403,22 @@ Http2ServerSession::get_remote_reader()
return _read_buffer_reader;
}
+bool
+Http2ServerSession::is_protocol_framed() const
+{
+ return true;
+}
+
+uint64_t
+Http2ServerSession::get_received_frame_count(uint64_t type) const
+{
+ if (type == 999) { // TS_SSN_INFO_RECEIVED_FRAME_COUNT_H2_UNKNOWN in
apidefs.h.in
+ return this->_frame_counts_in[HTTP2_FRAME_TYPE_MAX];
+ } else {
+ return this->_frame_counts_in[type];
+ }
+}
+
std::function<PoolableSession *()> create_h2_server_session = []() ->
PoolableSession * {
return http2ServerSessionAllocator.alloc();
};
diff --git a/src/proxy/http3/CMakeLists.txt b/src/proxy/http3/CMakeLists.txt
index 49e3d93d4b..37e00b918d 100644
--- a/src/proxy/http3/CMakeLists.txt
+++ b/src/proxy/http3/CMakeLists.txt
@@ -28,6 +28,7 @@ add_library(
Http3DebugNames.cc
Http3Frame.cc
Http3FrameCollector.cc
+ Http3FrameCounter.cc
Http3FrameDispatcher.cc
Http3HeaderFramer.cc
Http3DataFramer.cc
diff --git a/src/proxy/http3/Http3.cc b/src/proxy/http3/Http3.cc
index f46c1b933c..c9c8b4165d 100644
--- a/src/proxy/http3/Http3.cc
+++ b/src/proxy/http3/Http3.cc
@@ -22,6 +22,7 @@
*/
#include "proxy/http3/Http3.h"
+#include "proxy/http3/Http3Types.h"
// Default values of settings defined by specs (draft-17)
const uint32_t HTTP3_DEFAULT_HEADER_TABLE_SIZE = 0;
@@ -31,11 +32,34 @@ const uint32_t HTTP3_DEFAULT_NUM_PLACEHOLDERS = 0;
Http3StatsBlock http3_rsb;
+Metrics::Counter::AtomicType
*http3_frame_metrics_in[static_cast<int>(Http3FrameType::UNKNOWN) + 1];
+
void
Http3::init()
{
- // Example (remove comments here when addding)
- //
// Setup statistics
- // http3_rsb.current_client_session_count =
Metrics::Gauge::createPtr("proxy.process.http3.current_client_connections");
+ http3_rsb.data_frames_in =
Metrics::Counter::createPtr("proxy.process.http3.data_frames_in");
+ http3_rsb.headers_frames_in =
Metrics::Counter::createPtr("proxy.process.http3.headers_frames_in");
+ http3_rsb.cancel_push_frames_in =
Metrics::Counter::createPtr("proxy.process.http3.cancel_push_frames_in");
+ http3_rsb.settings_frames_in =
Metrics::Counter::createPtr("proxy.process.http3.settings_frames_in");
+ http3_rsb.push_promise_frames_in =
Metrics::Counter::createPtr("proxy.process.http3.push_promise_frames_in");
+ http3_rsb.goaway_frames_in =
Metrics::Counter::createPtr("proxy.process.http3.goaway_frames_in");
+ http3_rsb.max_push_id =
Metrics::Counter::createPtr("proxy.process.http3.max_push_id_frames_in");
+ http3_rsb.unknown_frames_in =
Metrics::Counter::createPtr("proxy.process.http3.unknown_frames_in");
+
+ http3_frame_metrics_in[static_cast<int>(Http3FrameType::DATA)] =
http3_rsb.data_frames_in;
+ http3_frame_metrics_in[static_cast<int>(Http3FrameType::HEADERS)] =
http3_rsb.headers_frames_in;
+ http3_frame_metrics_in[static_cast<int>(Http3FrameType::X_RESERVED_1)] =
http3_rsb.unknown_frames_in;
+ http3_frame_metrics_in[static_cast<int>(Http3FrameType::CANCEL_PUSH)] =
http3_rsb.cancel_push_frames_in;
+ http3_frame_metrics_in[static_cast<int>(Http3FrameType::SETTINGS)] =
http3_rsb.settings_frames_in;
+ http3_frame_metrics_in[static_cast<int>(Http3FrameType::PUSH_PROMISE)] =
http3_rsb.push_promise_frames_in;
+ http3_frame_metrics_in[static_cast<int>(Http3FrameType::X_RESERVED_2)] =
http3_rsb.unknown_frames_in;
+ http3_frame_metrics_in[static_cast<int>(Http3FrameType::GOAWAY)] =
http3_rsb.goaway_frames_in;
+ http3_frame_metrics_in[static_cast<int>(Http3FrameType::X_RESERVED_3)] =
http3_rsb.unknown_frames_in;
+ http3_frame_metrics_in[static_cast<int>(Http3FrameType::X_RESERVED_4)] =
http3_rsb.unknown_frames_in;
+ http3_frame_metrics_in[10] =
http3_rsb.unknown_frames_in;
+ http3_frame_metrics_in[11] =
http3_rsb.unknown_frames_in;
+ http3_frame_metrics_in[12] =
http3_rsb.unknown_frames_in;
+ http3_frame_metrics_in[static_cast<int>(Http3FrameType::MAX_PUSH_ID)] =
http3_rsb.max_push_id;
+ http3_frame_metrics_in[static_cast<int>(Http3FrameType::UNKNOWN)] =
http3_rsb.unknown_frames_in;
}
diff --git a/src/proxy/http3/Http3App.cc b/src/proxy/http3/Http3App.cc
index 5d0cf18b8a..c453950b6e 100644
--- a/src/proxy/http3/Http3App.cc
+++ b/src/proxy/http3/Http3App.cc
@@ -54,6 +54,8 @@ Http3App::Http3App(NetVConnection *client_vc, QUICConnection
*qc, IpAllow::ACL &
this->_qc->stream_manager()->set_default_application(this);
+
this->_control_stream_dispatcher.add_handler(this->_ssn->get_received_frame_counter());
+
this->_protocol_enforcer = new Http3ProtocolEnforcer();
this->_control_stream_dispatcher.add_handler(this->_protocol_enforcer);
diff --git a/src/proxy/http3/Http3DebugNames.cc
b/src/proxy/http3/Http3DebugNames.cc
index e3aebe5b84..4208f6c4a4 100644
--- a/src/proxy/http3/Http3DebugNames.cc
+++ b/src/proxy/http3/Http3DebugNames.cc
@@ -32,24 +32,22 @@ Http3DebugNames::frame_type(Http3FrameType type)
return "DATA";
case Http3FrameType::HEADERS:
return "HEADERS";
- case Http3FrameType::PRIORITY:
- return "PRIORITY";
+ case Http3FrameType::X_RESERVED_1:
+ return "X_RESERVED_1";
case Http3FrameType::CANCEL_PUSH:
return "CANCEL_PUSH";
case Http3FrameType::SETTINGS:
return "SETTINGS";
case Http3FrameType::PUSH_PROMISE:
return "PUSH_PROMISE";
- case Http3FrameType::GOAWAY:
- return "GOAWAY";
- case Http3FrameType::DUPLICATE_PUSH_ID:
- return "DUPLICATE_PUSH_ID";
- case Http3FrameType::X_RESERVED_1:
- return "X_RESERVED_1";
case Http3FrameType::X_RESERVED_2:
return "X_RESERVED_2";
+ case Http3FrameType::GOAWAY:
+ return "GOAWAY";
case Http3FrameType::X_RESERVED_3:
return "X_RESERVED_3";
+ case Http3FrameType::X_RESERVED_4:
+ return "X_RESERVED_4";
case Http3FrameType::UNKNOWN:
default:
return "UNKNOWN";
diff --git a/src/proxy/http3/Http3FrameCounter.cc
b/src/proxy/http3/Http3FrameCounter.cc
new file mode 100644
index 0000000000..8fee6afd42
--- /dev/null
+++ b/src/proxy/http3/Http3FrameCounter.cc
@@ -0,0 +1,59 @@
+/** @file
+ *
+ * A brief file description
+ *
+ * @section license License
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "tsutil/Metrics.h"
+#include "proxy/http3/Http3.h"
+#include "proxy/http3/Http3FrameCounter.h"
+
+std::vector<Http3FrameType>
+Http3FrameCounter::interests()
+{
+ return {Http3FrameType::DATA, Http3FrameType::HEADERS,
Http3FrameType::X_RESERVED_1, Http3FrameType::CANCEL_PUSH,
+ Http3FrameType::SETTINGS, Http3FrameType::PUSH_PROMISE,
Http3FrameType::X_RESERVED_2, Http3FrameType::GOAWAY,
+ Http3FrameType::X_RESERVED_3, Http3FrameType::X_RESERVED_4,
Http3FrameType::MAX_PUSH_ID, Http3FrameType::X_MAX_DEFINED,
+ Http3FrameType::UNKNOWN};
+}
+
+Http3ErrorUPtr
+Http3FrameCounter::handle_frame(std::shared_ptr<const Http3Frame> frame,
int32_t frame_seq, Http3StreamType s_type)
+{
+ Http3ErrorUPtr error = Http3ErrorUPtr(nullptr);
+ Http3FrameType f_type = frame->type();
+ if (f_type > Http3FrameType::X_MAX_DEFINED) {
+ f_type = Http3FrameType::X_MAX_DEFINED;
+ }
+ this->_frame_counts_in[static_cast<int>(f_type)]++;
+
Metrics::Counter::increment(http3_frame_metrics_in[static_cast<int>(f_type)]);
+
+ return error;
+}
+
+uint64_t
+Http3FrameCounter::get_count(uint64_t type) const
+{
+ if (type == 0x21) { // TS_SSN_INFO_RECEIVED_FRAME_COUNT_H3_UNKNOWN in
apidefs.h.in
+ return
this->_frame_counts_in[static_cast<int>(Http3FrameType::X_MAX_DEFINED)];
+ } else {
+ return this->_frame_counts_in[type];
+ }
+}
diff --git a/src/proxy/http3/Http3ProtocolEnforcer.cc
b/src/proxy/http3/Http3ProtocolEnforcer.cc
index 23ad112d79..ee2b26addf 100644
--- a/src/proxy/http3/Http3ProtocolEnforcer.cc
+++ b/src/proxy/http3/Http3ProtocolEnforcer.cc
@@ -27,11 +27,10 @@
std::vector<Http3FrameType>
Http3ProtocolEnforcer::interests()
{
- return {Http3FrameType::DATA, Http3FrameType::HEADERS,
Http3FrameType::PRIORITY,
- Http3FrameType::CANCEL_PUSH, Http3FrameType::SETTINGS,
Http3FrameType::PUSH_PROMISE,
- Http3FrameType::X_RESERVED_1, Http3FrameType::GOAWAY,
Http3FrameType::X_RESERVED_2,
- Http3FrameType::X_RESERVED_3, Http3FrameType::MAX_PUSH_ID,
Http3FrameType::DUPLICATE_PUSH_ID,
- Http3FrameType::X_MAX_DEFINED, Http3FrameType::UNKNOWN};
+ return {Http3FrameType::DATA, Http3FrameType::HEADERS,
Http3FrameType::X_RESERVED_1, Http3FrameType::CANCEL_PUSH,
+ Http3FrameType::SETTINGS, Http3FrameType::PUSH_PROMISE,
Http3FrameType::X_RESERVED_2, Http3FrameType::GOAWAY,
+ Http3FrameType::X_RESERVED_3, Http3FrameType::X_RESERVED_4,
Http3FrameType::MAX_PUSH_ID, Http3FrameType::X_MAX_DEFINED,
+ Http3FrameType::UNKNOWN};
}
Http3ErrorUPtr
diff --git a/src/proxy/http3/Http3Session.cc b/src/proxy/http3/Http3Session.cc
index 61a3ee4dce..eb31215572 100644
--- a/src/proxy/http3/Http3Session.cc
+++ b/src/proxy/http3/Http3Session.cc
@@ -25,6 +25,7 @@
#include "iocore/net/QUICSupport.h"
#include "proxy/http3/Http3.h"
+#include "proxy/http3/Http3Types.h"
//
// HQSession
@@ -220,6 +221,23 @@ Http3Session::remote_qpack()
return this->_remote_qpack;
}
+Http3FrameCounter *
+Http3Session::get_received_frame_counter()
+{
+ return &this->_received_frame_counter;
+}
+
+bool
+Http3Session::is_protocol_framed() const
+{
+ return true;
+}
+uint64_t
+Http3Session::get_received_frame_count(uint64_t type) const
+{
+ return this->_received_frame_counter.get_count(type);
+}
+
//
// Http09Session
//
diff --git a/src/proxy/http3/Http3Transaction.cc
b/src/proxy/http3/Http3Transaction.cc
index 2e544c41f9..1b2388fcc4 100644
--- a/src/proxy/http3/Http3Transaction.cc
+++ b/src/proxy/http3/Http3Transaction.cc
@@ -413,6 +413,7 @@ Http3Transaction::Http3Transaction(Http3Session *session,
QUICStreamVCAdapter::I
this->_header_handler = new Http3HeaderVIOAdaptor(&this->_read_vio,
http_type, session->remote_qpack(), stream_id);
this->_data_handler = new Http3StreamDataVIOAdaptor(&this->_read_vio);
+ this->_frame_dispatcher.add_handler(session->get_received_frame_counter());
this->_frame_dispatcher.add_handler(this->_header_handler);
this->_frame_dispatcher.add_handler(this->_data_handler);
diff --git a/tests/gold_tests/h2/gold/log.gold
b/tests/gold_tests/h2/gold/log.gold
new file mode 100644
index 0000000000..23b348f90c
--- /dev/null
+++ b/tests/gold_tests/h2/gold/log.gold
@@ -0,0 +1,2 @@
+Global: event=TS_EVENT_HTTP_SSN_CLOSE
+``
diff --git a/tests/gold_tests/pluginTest/tsapi/CMakeLists.txt
b/tests/gold_tests/pluginTest/tsapi/CMakeLists.txt
index b0ee883557..a03fa94037 100644
--- a/tests/gold_tests/pluginTest/tsapi/CMakeLists.txt
+++ b/tests/gold_tests/pluginTest/tsapi/CMakeLists.txt
@@ -17,3 +17,4 @@
add_autest_plugin(test_tsapi test_tsapi.cc)
add_autest_plugin(test_TSHttpTxnServerAddrSet test_TSHttpTxnServerAddrSet.cc)
+add_autest_plugin(test_TSHttpSsnInfo test_TSHttpSsnInfo.cc)
diff --git a/tests/gold_tests/pluginTest/tsapi/test_TSHttpSsnInfo.cc
b/tests/gold_tests/pluginTest/tsapi/test_TSHttpSsnInfo.cc
new file mode 100644
index 0000000000..129f1f17b6
--- /dev/null
+++ b/tests/gold_tests/pluginTest/tsapi/test_TSHttpSsnInfo.cc
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fstream>
+#include <cstdlib>
+
+#include <ts/ts.h>
+
+namespace
+{
+#define PINAME "test_TSHttpSsnInfo"
+char PIName[] = PINAME;
+
+DbgCtl dbg_ctl{PIName};
+
+// NOTE: It's important to flush this after writing so that a gold test using
this plugin can examine the log before TS
+// terminates.
+//
+std::fstream logFile;
+
+void
+handle_ssn_close(TSHttpSsn ssn)
+{
+ if (TSHttpSsnClientProtocolStackContains(ssn, "h2")) {
+ TSMgmtInt count[11];
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[0], 0);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[1], 1);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[2], 2);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[3], 3);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[4], 4);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[5], 5);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[6], 6);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[7], 7);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[8], 8);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[9], 9);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[10],
TS_SSN_INFO_RECEIVED_FRAME_COUNT_H2_UNKNOWN);
+
+ logFile << "H2 Frames Received:"
+ << "D" << count[0] << ","
+ << "H" << count[1] << ","
+ << "PR" << count[2] << ","
+ << "RS" << count[3] << ","
+ << "S" << count[4] << ","
+ << "PP" << count[5] << ","
+ << "P" << count[6] << ","
+ << "G" << count[7] << ","
+ << "WU" << count[8] << ","
+ << "C" << count[9] << ","
+ << "U" << count[10] << std::endl;
+ } else {
+ TSMgmtInt count[15];
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[0], 0);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[1], 1);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[2], 2);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[3], 3);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[4], 4);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[5], 5);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[6], 6);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[7], 7);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[8], 8);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[9], 9);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[10], 10);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[11], 11);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[12], 12);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[13], 13);
+ TSHttpSsnInfoIntGet(ssn, TS_SSN_INFO_RECEIVED_FRAME_COUNT, &count[14],
TS_SSN_INFO_RECEIVED_FRAME_COUNT_H2_UNKNOWN);
+
+ logFile << "H3 Frames Received:"
+ << "D" << count[0] << ","
+ << "H" << count[1] << ","
+ << "Ra" << count[2] << ","
+ << "CP" << count[3] << ","
+ << "S" << count[4] << ","
+ << "PP" << count[5] << ","
+ << "Rb" << count[6] << ","
+ << "G" << count[7] << ","
+ << "Rc" << count[8] << ","
+ << "Rd" << count[9] << ","
+ << "UND" << count[10] << ","
+ << "UND" << count[11] << ","
+ << "UND" << count[12] << ","
+ << "MPI" << count[13] << ","
+ << "U" << count[14] << std::endl;
+ }
+
+ TSHttpSsnReenable(ssn, TS_EVENT_HTTP_CONTINUE);
+}
+
+int
+globalContFunc(TSCont, TSEvent event, void *eventData)
+{
+ logFile << "Global: event=" << TSHttpEventNameLookup(event) << std::endl;
+
+ Dbg(dbg_ctl, "Global: event=%s(%d) eventData=%p",
TSHttpEventNameLookup(event), event, eventData);
+
+ switch (event) {
+ case TS_EVENT_HTTP_SSN_CLOSE:
+ handle_ssn_close(static_cast<TSHttpSsn>(eventData));
+ break;
+ default:
+ break;
+ } // end switch
+
+ return 0;
+}
+
+TSCont gCont;
+
+} // end anonymous namespace
+
+void
+TSPluginInit(int argc, const char *argv[])
+{
+ TSPluginRegistrationInfo info;
+
+ info.plugin_name = PIName;
+ info.vendor_name = "Apache Software Foundation";
+ info.support_email = "[email protected]";
+
+ if (TSPluginRegister(&info) != TS_SUCCESS) {
+ TSError(PINAME ": Plugin registration failed");
+
+ return;
+ }
+
+ const char *fileSpec = std::getenv("OUTPUT_FILE");
+
+ if (nullptr == fileSpec) {
+ TSError(PINAME ": Environment variable OUTPUT_FILE not found.");
+
+ return;
+ }
+
+ // Disable output buffering for logFile, so that explicit flushing is not
necessary.
+ logFile.rdbuf()->pubsetbuf(nullptr, 0);
+
+ logFile.open(fileSpec, std::ios::out);
+ if (!logFile.is_open()) {
+ TSError(PINAME ": could not open log file \"%s\"", fileSpec);
+
+ return;
+ }
+
+ // Mutex to protect the logFile object.
+ TSMutex mtx = TSMutexCreate();
+ gCont = TSContCreate(globalContFunc, mtx);
+ TSHttpHookAdd(TS_HTTP_SSN_CLOSE_HOOK, gCont);
+}
diff --git a/tests/gold_tests/pluginTest/tsapi/test_TSHttpSsnInfo.test.py
b/tests/gold_tests/pluginTest/tsapi/test_TSHttpSsnInfo.test.py
new file mode 100644
index 0000000000..421e2aa720
--- /dev/null
+++ b/tests/gold_tests/pluginTest/tsapi/test_TSHttpSsnInfo.test.py
@@ -0,0 +1,103 @@
+'''
+'''
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+Test.Summary = '''
+Test TS API to get H2 Session info and H2 metrics
+'''
+
+Test.SkipUnless(Condition.HasProgram("nghttp", "Nghttp need to be installed on
system for this test to work"),)
+Test.ContinueOnFail = True
+
+# ----
+# Setup Origin Server
+# ----
+httpbin = Test.MakeHttpBinServer("httpbin")
+
+# 128ytes
+post_body = "0123456789abcdef" * 8
+post_body_file = open(os.path.join(Test.RunDirectory, "post_body"), "w")
+post_body_file.write(post_body)
+post_body_file.close()
+
+# ----
+# Setup ATS
+# ----
+if Condition.HasATSFeature('TS_USE_QUIC') and
Condition.HasCurlFeature('http3'):
+ ts = Test.MakeATSProcess("ts", enable_tls=True, enable_quic=True)
+else:
+ ts = Test.MakeATSProcess("ts", enable_tls=True)
+
+# add ssl materials like key, certificates for the server
+ts.addDefaultSSLFiles()
+
+ts.Disk.remap_config.AddLines(['map /httpbin/
http://127.0.0.1:{0}/'.format(httpbin.Variables.Port)])
+
+ts.Disk.ssl_multicert_config.AddLine('dest_ip=* ssl_cert_name=server.pem
ssl_key_name=server.key')
+
+Test.PrepareTestPlugin(
+ os.path.join(Test.Variables.AtsBuildGoldTestsDir, 'pluginTest', 'tsapi',
'.libs', 'test_TSHttpSsnInfo.so'), ts)
+
+ts.Disk.records_config.update(
+ {
+ 'proxy.config.diags.debug.enabled': 1,
+ 'proxy.config.diags.debug.tags': 'http2|http3|quic|test_TSHttpSsnInfo',
+ 'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
+ 'proxy.config.ssl.server.private_key.path':
'{0}'.format(ts.Variables.SSLDir)
+ })
+
+# http2_info.so will output test logging to this file.
+log_path = os.path.join(ts.Variables.LOGDIR,
"test_TSHttpSsnInfo_plugin_log.txt")
+Test.Env["OUTPUT_FILE"] = log_path
+
+# ----
+# Test Cases
+# ----
+
+# H2 SETTINGS, PRIORITY, HEADERS, CONTINUATION, DATA, GOAWAY
+tr = Test.AddTestRun()
+tr.TimeOut = 10
+tr.Processes.Default.Command = f"nghttp -vn --continuation
'https://127.0.0.1:{ts.Variables.ssl_port}/httpbin/post' -d 'post_body'"
+tr.Processes.Default.ReturnCode = 0
+tr.Processes.Default.StartBefore(httpbin,
ready=When.PortOpen(httpbin.Variables.Port))
+tr.Processes.Default.StartBefore(Test.Processes.ts)
+tr.Processes.Default.Streams.stdout = "test_TSHttpSsnInfo_nghttp0.gold"
+tr.StillRunningAfter = httpbin
+tr.StillRunningAfter = ts
+
+# H3
+if Condition.HasATSFeature('TS_USE_QUIC') and
Condition.HasCurlFeature('http3'):
+ tr = Test.AddTestRun()
+ tr.TimeOut = 10
+ tr.Processes.Default.Command = f"curl -k --http3
'https://127.0.0.1:{ts.Variables.ssl_port}/httpbin/post' -d 'post_body'"
+ tr.Processes.Default.ReturnCode = 0
+ tr.Processes.Default.Streams.stdout = "test_TSHttpSsnInfo_curl0.gold"
+ tr.StillRunningAfter = httpbin
+ tr.StillRunningAfter = ts
+
+tr = Test.AddTestRun()
+tr.Processes.Default.Command = "echo check log"
+tr.Processes.Default.ReturnCode = 0
+f = tr.Disk.File(log_path)
+f.Content = "test_TSHttpSsnInfo_plugin_log.gold"
+f.Content += Testers.ContainsExpression(
+ "H2 Frames Received:D1,H1,PR5,RS0,S2,PP0,P0,G1,WU0,C1,U0", "Expected
numbers of frames should be received")
+# We cannot test this on H3 now because the test plugin does not work on H3
sessions
+# f.Content += Testers.ContainsExpression("H3 Frames
Received:D1,H1,Ra0,CP0,S1,PP0,Rb0,G0,Rc0,Rd0,UND0,UND0,UND0,MPI0,U0",
+# "Expected numbers of frames should be
received")
diff --git a/tests/gold_tests/pluginTest/tsapi/test_TSHttpSsnInfo_curl0.gold
b/tests/gold_tests/pluginTest/tsapi/test_TSHttpSsnInfo_curl0.gold
new file mode 100644
index 0000000000..cf637de588
--- /dev/null
+++ b/tests/gold_tests/pluginTest/tsapi/test_TSHttpSsnInfo_curl0.gold
@@ -0,0 +1 @@
+``
diff --git a/tests/gold_tests/pluginTest/tsapi/test_TSHttpSsnInfo_nghttp0.gold
b/tests/gold_tests/pluginTest/tsapi/test_TSHttpSsnInfo_nghttp0.gold
new file mode 100644
index 0000000000..8fc661cb94
--- /dev/null
+++ b/tests/gold_tests/pluginTest/tsapi/test_TSHttpSsnInfo_nghttp0.gold
@@ -0,0 +1,21 @@
+``
+[``] send PRIORITY frame <length=5, flags=0x00, stream_id=3>
+ (dep_stream_id=0, weight=201, exclusive=0)
+[``] send PRIORITY frame <length=5, flags=0x00, stream_id=5>
+ (dep_stream_id=0, weight=101, exclusive=0)
+[``] send PRIORITY frame <length=5, flags=0x00, stream_id=7>
+ (dep_stream_id=0, weight=1, exclusive=0)
+[``] send PRIORITY frame <length=5, flags=0x00, stream_id=9>
+ (dep_stream_id=7, weight=1, exclusive=0)
+[``] send PRIORITY frame <length=5, flags=0x00, stream_id=11>
+ (dep_stream_id=3, weight=1, exclusive=0)
+[``] send HEADERS frame <length=``, flags=0x24, stream_id=13>
+ ; END_HEADERS | PRIORITY
+``
+[``] send DATA frame <length=``, flags=0x01, stream_id=13>
+``; END_STREAM
+``
+[``] recv (stream_id=13) :status: 431
+``
+``; END_STREAM
+``
diff --git
a/tests/gold_tests/pluginTest/tsapi/test_TSHttpSsnInfo_plugin_log.gold
b/tests/gold_tests/pluginTest/tsapi/test_TSHttpSsnInfo_plugin_log.gold
new file mode 100644
index 0000000000..23b348f90c
--- /dev/null
+++ b/tests/gold_tests/pluginTest/tsapi/test_TSHttpSsnInfo_plugin_log.gold
@@ -0,0 +1,2 @@
+Global: event=TS_EVENT_HTTP_SSN_CLOSE
+``