Moves Http2TraceLogger to QUICHE for reuse in open source. This required writing a small replacement for a gtl logging utility. PiperOrigin-RevId: 395291428
diff --git a/http2/core/http2_trace_logging.cc b/http2/core/http2_trace_logging.cc new file mode 100644 index 0000000..d2ef702 --- /dev/null +++ b/http2/core/http2_trace_logging.cc
@@ -0,0 +1,454 @@ +#include "http2/core/http2_trace_logging.h" + +#include <cstdint> + +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "common/platform/api/quiche_bug_tracker.h" +#include "common/platform/api/quiche_logging.h" +#include "spdy/core/spdy_protocol.h" + +// Convenience macros for printing function arguments in log lines in the +// format arg_name=value. +#define FORMAT_ARG(arg) " " #arg "=" << arg +#define FORMAT_INT_ARG(arg) " " #arg "=" << static_cast<int>(arg) + +// Convenience macros for printing Spdy*IR attributes in log lines in the +// format attrib_name=value. +#define FORMAT_ATTR(ir, attrib) " " #attrib "=" << ir.attrib() +#define FORMAT_INT_ATTR(ir, attrib) \ + " " #attrib "=" << static_cast<int>(ir.attrib()) + +namespace { + +// Logs a container, using a user-provided object to log each individual item. +template <typename T, typename ItemLogger> +struct ContainerLogger { + explicit ContainerLogger(const T& c, ItemLogger l) + : container(c), item_logger(l) {} + + friend std::ostream& operator<<(std::ostream& out, + const ContainerLogger& logger) { + out << "["; + auto begin = logger.container.begin(); + for (auto it = begin; it != logger.container.end(); ++it) { + if (it != begin) { + out << ", "; + } + logger.item_logger.Log(out, *it); + } + out << "]"; + return out; + } + const T& container; + ItemLogger item_logger; +}; + +// Returns a ContainerLogger that will log |container| using |item_logger|. +template <typename T, typename ItemLogger> +auto LogContainer(const T& container, ItemLogger item_logger) + -> decltype(ContainerLogger<T, ItemLogger>(container, item_logger)) { + return ContainerLogger<T, ItemLogger>(container, item_logger); +} + +} // anonymous namespace + +#define FORMAT_HEADER_BLOCK(ir) \ + " header_block=" << LogContainer(ir.header_block(), LogHeaderBlockEntry()) + +namespace http2 { + +using spdy::SettingsMap; +using spdy::SpdyAltSvcIR; +using spdy::SpdyContinuationIR; +using spdy::SpdyDataIR; +using spdy::SpdyGoAwayIR; +using spdy::SpdyHeaderBlock; +using spdy::SpdyHeadersIR; +using spdy::SpdyPingIR; +using spdy::SpdyPriorityIR; +using spdy::SpdyPushPromiseIR; +using spdy::SpdyRstStreamIR; +using spdy::SpdySettingsIR; +using spdy::SpdyStreamId; +using spdy::SpdyUnknownIR; +using spdy::SpdyWindowUpdateIR; + +namespace { + +// Defines how elements of SpdyHeaderBlocks are logged. +struct LogHeaderBlockEntry { + void Log(std::ostream& out, + const SpdyHeaderBlock::value_type& entry) const { // NOLINT + out << "\"" << entry.first << "\": \"" << entry.second << "\""; + } +}; + +// Defines how elements of SettingsMap are logged. +struct LogSettingsEntry { + void Log(std::ostream& out, + const SettingsMap::value_type& entry) const { // NOLINT + out << spdy::SettingsIdToString(entry.first) << ": " << entry.second; + } +}; + +// Defines how elements of AlternativeServiceVector are logged. +struct LogAlternativeService { + void Log(std::ostream& out, + const spdy::SpdyAltSvcWireFormat::AlternativeService& altsvc) + const { // NOLINT + out << "{" + << "protocol_id=" << altsvc.protocol_id << " host=" << altsvc.host + << " port=" << altsvc.port << " max_age=" << altsvc.max_age + << " version="; + for (auto v : altsvc.version) { + out << v << ","; + } + out << "}"; + } +}; + +} // anonymous namespace + +Http2TraceLogger::Http2TraceLogger(SpdyFramerVisitorInterface* parent, + absl::string_view perspective, + std::function<bool()> is_enabled, + const void* connection_id) + : wrapped_(parent), + perspective_(perspective), + is_enabled_(std::move(is_enabled)), + connection_id_(connection_id) {} + +Http2TraceLogger::~Http2TraceLogger() { + if (recording_headers_handler_ != nullptr && + !recording_headers_handler_->decoded_block().empty()) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "connection_id=" << connection_id_ + << " Received headers that were never logged! keys/values:" + << recording_headers_handler_->decoded_block().DebugString(); + } +} + +void Http2TraceLogger::OnError(Http2DecoderAdapter::SpdyFramerError error, + std::string detailed_error) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnError:" << FORMAT_ARG(connection_id_) + << ", error=" << Http2DecoderAdapter::SpdyFramerErrorToString(error); + wrapped_->OnError(error, detailed_error); +} + +void Http2TraceLogger::OnCommonHeader(SpdyStreamId stream_id, size_t length, + uint8_t type, uint8_t flags) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnCommonHeader:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id) << FORMAT_ARG(length) << FORMAT_INT_ARG(type) + << FORMAT_INT_ARG(flags); + wrapped_->OnCommonHeader(stream_id, length, type, flags); +} + +void Http2TraceLogger::OnDataFrameHeader(SpdyStreamId stream_id, size_t length, + bool fin) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnDataFrameHeader:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id) << FORMAT_ARG(length) << FORMAT_ARG(fin); + wrapped_->OnDataFrameHeader(stream_id, length, fin); +} + +void Http2TraceLogger::OnStreamFrameData(SpdyStreamId stream_id, + const char* data, size_t len) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnStreamFrameData:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id) << FORMAT_ARG(len); + wrapped_->OnStreamFrameData(stream_id, data, len); +} + +void Http2TraceLogger::OnStreamEnd(SpdyStreamId stream_id) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnStreamEnd:" << FORMAT_ARG(connection_id_) << FORMAT_ARG(stream_id); + wrapped_->OnStreamEnd(stream_id); +} + +void Http2TraceLogger::OnStreamPadLength(SpdyStreamId stream_id, size_t value) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnStreamPadLength:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id) << FORMAT_ARG(value); + wrapped_->OnStreamPadLength(stream_id, value); +} + +void Http2TraceLogger::OnStreamPadding(SpdyStreamId stream_id, size_t len) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnStreamPadding:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id) << FORMAT_ARG(len); + wrapped_->OnStreamPadding(stream_id, len); +} + +spdy::SpdyHeadersHandlerInterface* Http2TraceLogger::OnHeaderFrameStart( + SpdyStreamId stream_id) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnHeaderFrameStart:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id); + spdy::SpdyHeadersHandlerInterface* result = + wrapped_->OnHeaderFrameStart(stream_id); + recording_headers_handler_ = + absl::make_unique<spdy::RecordingHeadersHandler>(result); + result = recording_headers_handler_.get(); + return result; +} + +void Http2TraceLogger::OnHeaderFrameEnd(SpdyStreamId stream_id) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnHeaderFrameEnd:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id); + LogReceivedHeaders(); + wrapped_->OnHeaderFrameEnd(stream_id); + recording_headers_handler_ = nullptr; +} + +void Http2TraceLogger::OnRstStream(SpdyStreamId stream_id, + SpdyErrorCode error_code) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnRstStream:" << FORMAT_ARG(connection_id_) << FORMAT_ARG(stream_id) + << " error_code=" << spdy::ErrorCodeToString(error_code); + wrapped_->OnRstStream(stream_id, error_code); +} + +void Http2TraceLogger::OnSettings() { wrapped_->OnSettings(); } + +void Http2TraceLogger::OnSetting(SpdySettingsId id, uint32_t value) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnSetting:" << FORMAT_ARG(connection_id_) + << " id=" << spdy::SettingsIdToString(id) << FORMAT_ARG(value); + wrapped_->OnSetting(id, value); +} + +void Http2TraceLogger::OnSettingsEnd() { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnSettingsEnd:" << FORMAT_ARG(connection_id_); + wrapped_->OnSettingsEnd(); +} + +void Http2TraceLogger::OnSettingsAck() { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnSettingsAck:" << FORMAT_ARG(connection_id_); + wrapped_->OnSettingsAck(); +} + +void Http2TraceLogger::OnPing(SpdyPingId unique_id, bool is_ack) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnPing:" << FORMAT_ARG(connection_id_) << FORMAT_ARG(unique_id) + << FORMAT_ARG(is_ack); + wrapped_->OnPing(unique_id, is_ack); +} + +void Http2TraceLogger::OnGoAway(SpdyStreamId last_accepted_stream_id, + SpdyErrorCode error_code) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnGoAway:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(last_accepted_stream_id) + << " error_code=" << spdy::ErrorCodeToString(error_code); + wrapped_->OnGoAway(last_accepted_stream_id, error_code); +} + +bool Http2TraceLogger::OnGoAwayFrameData(const char* goaway_data, size_t len) { + return wrapped_->OnGoAwayFrameData(goaway_data, len); +} + +void Http2TraceLogger::OnHeaders(SpdyStreamId stream_id, bool has_priority, + int weight, SpdyStreamId parent_stream_id, + bool exclusive, bool fin, bool end) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnHeaders:" << FORMAT_ARG(connection_id_) << FORMAT_ARG(stream_id) + << FORMAT_ARG(has_priority) << FORMAT_INT_ARG(weight) + << FORMAT_ARG(parent_stream_id) << FORMAT_ARG(exclusive) + << FORMAT_ARG(fin) << FORMAT_ARG(end); + wrapped_->OnHeaders(stream_id, has_priority, weight, parent_stream_id, + exclusive, fin, end); +} + +void Http2TraceLogger::OnWindowUpdate(SpdyStreamId stream_id, + int delta_window_size) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnWindowUpdate:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id) << FORMAT_ARG(delta_window_size); + wrapped_->OnWindowUpdate(stream_id, delta_window_size); +} + +void Http2TraceLogger::OnPushPromise(SpdyStreamId original_stream_id, + SpdyStreamId promised_stream_id, bool end) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnPushPromise:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(original_stream_id) << FORMAT_ARG(promised_stream_id) + << FORMAT_ARG(end); + wrapped_->OnPushPromise(original_stream_id, promised_stream_id, end); +} + +void Http2TraceLogger::OnContinuation(SpdyStreamId stream_id, bool end) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnContinuation:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id) << FORMAT_ARG(end); + wrapped_->OnContinuation(stream_id, end); +} + +void Http2TraceLogger::OnAltSvc( + SpdyStreamId stream_id, absl::string_view origin, + const SpdyAltSvcWireFormat::AlternativeServiceVector& altsvc_vector) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnAltSvc:" << FORMAT_ARG(connection_id_) << FORMAT_ARG(stream_id) + << FORMAT_ARG(origin) << " altsvc_vector=" + << LogContainer(altsvc_vector, LogAlternativeService()); + wrapped_->OnAltSvc(stream_id, origin, altsvc_vector); +} + +void Http2TraceLogger::OnPriority(SpdyStreamId stream_id, + SpdyStreamId parent_stream_id, int weight, + bool exclusive) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnPriority:" << FORMAT_ARG(connection_id_) << FORMAT_ARG(stream_id) + << FORMAT_ARG(parent_stream_id) << FORMAT_INT_ARG(weight) + << FORMAT_ARG(exclusive); + wrapped_->OnPriority(stream_id, parent_stream_id, weight, exclusive); +} + +void Http2TraceLogger::OnPriorityUpdate( + SpdyStreamId prioritized_stream_id, + absl::string_view priority_field_value) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnPriorityUpdate:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(prioritized_stream_id) << FORMAT_ARG(priority_field_value); + wrapped_->OnPriorityUpdate(prioritized_stream_id, priority_field_value); +} + +bool Http2TraceLogger::OnUnknownFrame(SpdyStreamId stream_id, + uint8_t frame_type) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "OnUnknownFrame:" << FORMAT_ARG(connection_id_) + << FORMAT_ARG(stream_id) << FORMAT_INT_ARG(frame_type); + return wrapped_->OnUnknownFrame(stream_id, frame_type); +} + +void Http2TraceLogger::LogReceivedHeaders() const { + if (recording_headers_handler_ == nullptr) { + QUICHE_BUG(bug_2794_1) << "Cannot log headers before creating handler."; + return; + } + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Received headers;" << FORMAT_ARG(connection_id_) << " keys/values:" + << recording_headers_handler_->decoded_block().DebugString() + << " compressed_bytes=" + << recording_headers_handler_->compressed_header_bytes() + << " uncompressed_bytes=" + << recording_headers_handler_->uncompressed_header_bytes(); +} + +void Http2FrameLogger::VisitRstStream(const SpdyRstStreamIR& rst_stream) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyRstStreamIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(rst_stream, stream_id) + << " error_code=" << spdy::ErrorCodeToString(rst_stream.error_code()); +} + +void Http2FrameLogger::VisitSettings(const SpdySettingsIR& settings) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdySettingsIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(settings, is_ack) + << " values=" << LogContainer(settings.values(), LogSettingsEntry()); +} + +void Http2FrameLogger::VisitPing(const SpdyPingIR& ping) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyPingIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(ping, id) << FORMAT_ATTR(ping, is_ack); +} + +void Http2FrameLogger::VisitGoAway(const SpdyGoAwayIR& goaway) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyGoAwayIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(goaway, last_good_stream_id) + << " error_code=" << spdy::ErrorCodeToString(goaway.error_code()) + << FORMAT_ATTR(goaway, description); +} + +void Http2FrameLogger::VisitHeaders(const SpdyHeadersIR& headers) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyHeadersIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(headers, stream_id) << FORMAT_ATTR(headers, fin) + << FORMAT_ATTR(headers, has_priority) << FORMAT_INT_ATTR(headers, weight) + << FORMAT_ATTR(headers, parent_stream_id) + << FORMAT_ATTR(headers, exclusive) << FORMAT_ATTR(headers, padded) + << FORMAT_ATTR(headers, padding_payload_len) + << FORMAT_HEADER_BLOCK(headers); +} + +void Http2FrameLogger::VisitWindowUpdate( + const SpdyWindowUpdateIR& window_update) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyWindowUpdateIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(window_update, stream_id) + << FORMAT_ATTR(window_update, delta); +} + +void Http2FrameLogger::VisitPushPromise(const SpdyPushPromiseIR& push_promise) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyPushPromiseIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(push_promise, stream_id) << FORMAT_ATTR(push_promise, fin) + << FORMAT_ATTR(push_promise, promised_stream_id) + << FORMAT_ATTR(push_promise, padded) + << FORMAT_ATTR(push_promise, padding_payload_len) + << FORMAT_HEADER_BLOCK(push_promise); +} + +void Http2FrameLogger::VisitContinuation( + const SpdyContinuationIR& continuation) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyContinuationIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(continuation, stream_id) + << FORMAT_ATTR(continuation, end_headers); +} + +void Http2FrameLogger::VisitAltSvc(const SpdyAltSvcIR& altsvc) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyAltSvcIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(altsvc, stream_id) << FORMAT_ATTR(altsvc, origin) + << " altsvc_vector=" + << LogContainer(altsvc.altsvc_vector(), LogAlternativeService()); +} + +void Http2FrameLogger::VisitPriority(const SpdyPriorityIR& priority) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyPriorityIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(priority, stream_id) + << FORMAT_ATTR(priority, parent_stream_id) + << FORMAT_INT_ATTR(priority, weight) << FORMAT_ATTR(priority, exclusive); +} + +void Http2FrameLogger::VisitData(const SpdyDataIR& data) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyDataIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(data, stream_id) << FORMAT_ATTR(data, fin) + << " data_len=" << data.data_len() << FORMAT_ATTR(data, padded) + << FORMAT_ATTR(data, padding_payload_len); +} + +void Http2FrameLogger::VisitPriorityUpdate( + const spdy::SpdyPriorityUpdateIR& priority_update) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyPriorityUpdateIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(priority_update, stream_id) + << FORMAT_ATTR(priority_update, prioritized_stream_id) + << FORMAT_ATTR(priority_update, priority_field_value); +} + +void Http2FrameLogger::VisitAcceptCh( + const spdy::SpdyAcceptChIR& /*accept_ch*/) { + QUICHE_BUG(bug_2794_2) + << "Sending ACCEPT_CH frames is currently unimplemented."; +} + +void Http2FrameLogger::VisitUnknown(const SpdyUnknownIR& ir) { + HTTP2_TRACE_LOG(perspective_, is_enabled_) + << "Wrote SpdyUnknownIR:" << FORMAT_ARG(connection_id_) + << FORMAT_ATTR(ir, stream_id) << FORMAT_INT_ATTR(ir, type) + << FORMAT_INT_ATTR(ir, flags) << FORMAT_ATTR(ir, length); +} + +} // namespace http2
diff --git a/http2/core/http2_trace_logging.h b/http2/core/http2_trace_logging.h new file mode 100644 index 0000000..e1185bf --- /dev/null +++ b/http2/core/http2_trace_logging.h
@@ -0,0 +1,140 @@ +// Classes and utilities for supporting HTTP/2 trace logging, which logs +// information about all control and data frames sent and received over +// HTTP/2 connections. + +#ifndef QUICHE_HTTP2_CORE_HTTP2_TRACE_LOGGING_H_ +#define QUICHE_HTTP2_CORE_HTTP2_TRACE_LOGGING_H_ + +#include <cstdint> + +#include "absl/strings/string_view.h" +#include "common/platform/api/quiche_export.h" +#include "common/platform/api/quiche_logging.h" +#include "spdy/core/http2_frame_decoder_adapter.h" +#include "spdy/core/recording_headers_handler.h" +#include "spdy/core/spdy_headers_handler_interface.h" +#include "spdy/core/spdy_protocol.h" + +// Logging macro to use for all HTTP/2 trace logging. Iff trace logging is +// enabled, logs at level INFO with a common prefix prepended (to facilitate +// post-hoc filtering of trace logging output). +#define HTTP2_TRACE_LOG(perspective, is_enabled) \ + QUICHE_LOG_IF(INFO, is_enabled()) << "[HTTP2_TRACE " << perspective << "] " + +namespace http2 { + +// Intercepts deframing events to provide detailed logs. Intended to be used for +// manual debugging. +// +// Note any new methods in SpdyFramerVisitorInterface MUST be overridden here to +// properly forward the event. This could be ensured by making every event in +// SpdyFramerVisitorInterface a pure virtual. +class QUICHE_EXPORT_PRIVATE Http2TraceLogger + : public spdy::SpdyFramerVisitorInterface { + public: + typedef spdy::SpdyAltSvcWireFormat SpdyAltSvcWireFormat; + typedef spdy::SpdyErrorCode SpdyErrorCode; + typedef spdy::SpdyFramerVisitorInterface SpdyFramerVisitorInterface; + typedef spdy::SpdyPingId SpdyPingId; + typedef spdy::SpdyPriority SpdyPriority; + typedef spdy::SpdySettingsId SpdySettingsId; + typedef spdy::SpdyStreamId SpdyStreamId; + + Http2TraceLogger(SpdyFramerVisitorInterface* parent, + absl::string_view perspective, + std::function<bool()> is_enabled, const void* connection_id); + ~Http2TraceLogger() override; + + Http2TraceLogger(const Http2TraceLogger&) = delete; + Http2TraceLogger& operator=(const Http2TraceLogger&) = delete; + + void OnError(http2::Http2DecoderAdapter::SpdyFramerError error, + std::string detailed_error) override; + void OnCommonHeader(SpdyStreamId stream_id, size_t length, uint8_t type, + uint8_t flags) override; + spdy::SpdyHeadersHandlerInterface* OnHeaderFrameStart( + SpdyStreamId stream_id) override; + void OnHeaderFrameEnd(SpdyStreamId stream_id) override; + void OnDataFrameHeader(SpdyStreamId stream_id, size_t length, + bool fin) override; + void OnStreamFrameData(SpdyStreamId stream_id, const char* data, + size_t len) override; + void OnStreamEnd(SpdyStreamId stream_id) override; + void OnStreamPadLength(SpdyStreamId stream_id, size_t value) override; + void OnStreamPadding(SpdyStreamId stream_id, size_t len) override; + void OnRstStream(SpdyStreamId stream_id, SpdyErrorCode error_code) override; + void OnSetting(spdy::SpdySettingsId id, uint32_t value) override; + void OnPing(SpdyPingId unique_id, bool is_ack) override; + void OnSettings() override; + void OnSettingsEnd() override; + void OnSettingsAck() override; + void OnGoAway(SpdyStreamId last_accepted_stream_id, + SpdyErrorCode error_code) override; + bool OnGoAwayFrameData(const char* goaway_data, size_t len) override; + void OnHeaders(SpdyStreamId stream_id, bool has_priority, int weight, + SpdyStreamId parent_stream_id, bool exclusive, bool fin, + bool end) override; + void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) override; + void OnPushPromise(SpdyStreamId stream_id, SpdyStreamId promised_stream_id, + bool end) override; + void OnContinuation(SpdyStreamId stream_id, bool end) override; + void OnAltSvc(SpdyStreamId stream_id, absl::string_view origin, + const SpdyAltSvcWireFormat::AlternativeServiceVector& + altsvc_vector) override; + void OnPriority(SpdyStreamId stream_id, SpdyStreamId parent_stream_id, + int weight, bool exclusive) override; + void OnPriorityUpdate(SpdyStreamId prioritized_stream_id, + absl::string_view priority_field_value) override; + bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override; + + private: + void LogReceivedHeaders() const; + + std::unique_ptr<spdy::RecordingHeadersHandler> recording_headers_handler_; + + SpdyFramerVisitorInterface* wrapped_; + const absl::string_view perspective_; + const std::function<bool()> is_enabled_; + const void* connection_id_; +}; + +// Visitor to log control frames that have been written. +class QUICHE_EXPORT_PRIVATE Http2FrameLogger : public spdy::SpdyFrameVisitor { + public: + // This class will preface all of its log messages with the value of + // |connection_id| in hexadecimal. + Http2FrameLogger(absl::string_view perspective, + std::function<bool()> is_enabled, const void* connection_id) + : perspective_(perspective), + is_enabled_(std::move(is_enabled)), + connection_id_(connection_id) {} + + Http2FrameLogger(const Http2FrameLogger&) = delete; + Http2FrameLogger& operator=(const Http2FrameLogger&) = delete; + + void VisitRstStream(const spdy::SpdyRstStreamIR& rst_stream) override; + void VisitSettings(const spdy::SpdySettingsIR& settings) override; + void VisitPing(const spdy::SpdyPingIR& ping) override; + void VisitGoAway(const spdy::SpdyGoAwayIR& goaway) override; + void VisitHeaders(const spdy::SpdyHeadersIR& headers) override; + void VisitWindowUpdate( + const spdy::SpdyWindowUpdateIR& window_update) override; + void VisitPushPromise(const spdy::SpdyPushPromiseIR& push_promise) override; + void VisitContinuation(const spdy::SpdyContinuationIR& continuation) override; + void VisitAltSvc(const spdy::SpdyAltSvcIR& altsvc) override; + void VisitPriority(const spdy::SpdyPriorityIR& priority) override; + void VisitData(const spdy::SpdyDataIR& data) override; + void VisitPriorityUpdate( + const spdy::SpdyPriorityUpdateIR& priority_update) override; + void VisitAcceptCh(const spdy::SpdyAcceptChIR& accept_ch) override; + void VisitUnknown(const spdy::SpdyUnknownIR& ir) override; + + private: + const absl::string_view perspective_; + const std::function<bool()> is_enabled_; + const void* connection_id_; +}; + +} // namespace http2 + +#endif // QUICHE_HTTP2_CORE_HTTP2_TRACE_LOGGING_H_