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 <mas...@apache.org> 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 = "d...@trafficserver.apache.org"; + + 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 +``