| #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_seconds=" << altsvc.max_age_seconds << " 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 |