blob: ac0a82746d40cfcaad243039c1edc846fe7d6cff [file] [log] [blame]
#include "quiche/http2/core/http2_trace_logging.h"
#include <cstdint>
#include <memory>
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "quiche/common/platform/api/quiche_bug_tracker.h"
#include "quiche/common/platform/api/quiche_logging.h"
#include "quiche/spdy/core/http2_header_block.h"
#include "quiche/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::Http2HeaderBlock;
using spdy::SettingsMap;
using spdy::SpdyAltSvcIR;
using spdy::SpdyContinuationIR;
using spdy::SpdyDataIR;
using spdy::SpdyGoAwayIR;
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 Http2HeaderBlocks are logged.
struct LogHeaderBlockEntry {
void Log(std::ostream& out,
const Http2HeaderBlock::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);
if (is_enabled_()) {
recording_headers_handler_ =
std::make_unique<spdy::RecordingHeadersHandler>(result);
result = recording_headers_handler_.get();
} else {
recording_headers_handler_ = nullptr;
}
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, size_t payload_length,
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(payload_length) << 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, payload_length, 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,
size_t payload_length, bool end) {
HTTP2_TRACE_LOG(perspective_, is_enabled_)
<< "OnContinuation:" << FORMAT_ARG(connection_id_)
<< FORMAT_ARG(stream_id) << FORMAT_ARG(payload_length) << FORMAT_ARG(end);
wrapped_->OnContinuation(stream_id, payload_length, 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::OnUnknownFrameStart(spdy::SpdyStreamId stream_id,
size_t length, uint8_t type,
uint8_t flags) {
HTTP2_TRACE_LOG(perspective_, is_enabled_)
<< "OnUnknownFrameStart:" << FORMAT_ARG(connection_id_)
<< FORMAT_ARG(stream_id) << FORMAT_ARG(length) << FORMAT_INT_ARG(type)
<< FORMAT_INT_ARG(flags);
wrapped_->OnUnknownFrameStart(stream_id, length, type, flags);
}
void Http2TraceLogger::OnUnknownFramePayload(spdy::SpdyStreamId stream_id,
absl::string_view payload) {
HTTP2_TRACE_LOG(perspective_, is_enabled_)
<< "OnUnknownFramePayload:" << FORMAT_ARG(connection_id_)
<< FORMAT_ARG(stream_id) << " length=" << payload.size();
wrapped_->OnUnknownFramePayload(stream_id, payload);
}
void Http2TraceLogger::LogReceivedHeaders() const {
if (recording_headers_handler_ == nullptr) {
// Trace logging was not enabled when the start of the header block was
// received.
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