Add PrintTo() methods to BinaryHttpMessage and related classes
These are called by gUnit to produce human readable error messages.
Alternatively we could define operator<<, but it might be best to reserve the
operator for a machine-meaningful serialized output. DebugString() output is
not intended for machine consumption.
PiperOrigin-RevId: 474864994
diff --git a/quiche/binary_http/binary_http_message.cc b/quiche/binary_http/binary_http_message.cc
index 1a6a149..93d25bf 100644
--- a/quiche/binary_http/binary_http_message.cc
+++ b/quiche/binary_http/binary_http_message.cc
@@ -13,6 +13,7 @@
#include "absl/status/statusor.h"
#include "absl/strings/ascii.h"
#include "absl/strings/str_cat.h"
+#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
#include "quiche/common/quiche_data_reader.h"
#include "quiche/common/quiche_data_writer.h"
@@ -388,4 +389,48 @@
absl::StrCat("Unsupported framing type ", framing));
}
+std::string BinaryHttpMessage::DebugString() const {
+ std::vector<std::string> headers;
+ for (const auto& field : GetHeaderFields()) {
+ headers.emplace_back(field.DebugString());
+ }
+ return absl::StrCat("BinaryHttpMessage{Headers{",
+ absl::StrJoin(headers, ";;"), "}Body{", body(), "}}");
+}
+
+std::string BinaryHttpMessage::Field::DebugString() const {
+ return absl::StrCat(name, "=", value);
+}
+
+std::string BinaryHttpResponse::InformationalResponse::DebugString() const {
+ std::vector<std::string> fs;
+ for (const auto& field : fields()) {
+ fs.emplace_back(field.DebugString());
+ }
+ return absl::StrCat("InformationalResponse{", absl::StrJoin(fs, ";;"), "}");
+}
+
+std::string BinaryHttpResponse::DebugString() const {
+ std::vector<std::string> irs;
+ for (const auto& ir : informational_responses()) {
+ irs.emplace_back(ir.DebugString());
+ }
+ return absl::StrCat("BinaryHttpResponse(", status_code_, "){",
+ BinaryHttpMessage::DebugString(),
+ absl::StrJoin(irs, ";;"), "}");
+}
+
+std::string BinaryHttpRequest::DebugString() const {
+ return absl::StrCat("BinaryHttpRequest{", BinaryHttpMessage::DebugString(),
+ "}");
+}
+
+void PrintTo(const BinaryHttpRequest& msg, std::ostream* os) {
+ *os << msg.DebugString();
+}
+
+void PrintTo(const BinaryHttpResponse& msg, std::ostream* os) {
+ *os << msg.DebugString();
+}
+
} // namespace quiche
diff --git a/quiche/binary_http/binary_http_message.h b/quiche/binary_http/binary_http_message.h
index e49b6a9..88906e7 100644
--- a/quiche/binary_http/binary_http_message.h
+++ b/quiche/binary_http/binary_http_message.h
@@ -29,6 +29,8 @@
bool operator==(const BinaryHttpMessage::Field& rhs) const {
return name == rhs.name && value == rhs.value;
}
+
+ std::string DebugString() const;
};
virtual ~BinaryHttpMessage() = default;
@@ -50,6 +52,9 @@
virtual absl::StatusOr<std::string> Serialize() const = 0;
// TODO(bschneider): Add AddTrailerField for chunked messages
// TODO(bschneider): Add SetBodyCallback() for chunked messages
+
+ virtual std::string DebugString() const;
+
protected:
class Fields {
public:
@@ -117,6 +122,8 @@
absl::StatusOr<std::string> Serialize() const override;
const ControlData& control_data() const { return control_data_; }
+ virtual std::string DebugString() const override;
+
private:
absl::Status EncodeControlData(quiche::QuicheDataWriter& writer) const;
@@ -130,6 +137,8 @@
const ControlData control_data_;
};
+void PrintTo(const BinaryHttpRequest& msg, std::ostream* os);
+
class QUICHE_EXPORT_PRIVATE BinaryHttpResponse : public BinaryHttpMessage {
public:
// https://www.ietf.org/archive/id/draft-ietf-httpbis-binary-message-06.html#name-response-control-data
@@ -163,6 +172,8 @@
uint16_t status_code() const { return status_code_; }
+ std::string DebugString() const;
+
private:
// Give BinaryHttpResponse access to Encoding functionality.
friend class BinaryHttpResponse;
@@ -196,6 +207,8 @@
return informational_response_control_data_;
}
+ virtual std::string DebugString() const override;
+
private:
// Returns Binary Http known length request formatted response.
absl::StatusOr<std::string> EncodeAsKnownLength() const;
@@ -205,6 +218,8 @@
std::vector<InformationalResponse> informational_response_control_data_;
const uint16_t status_code_;
};
+
+void PrintTo(const BinaryHttpResponse& msg, std::ostream* os);
} // namespace quiche
#endif // QUICHE_BINARY_HTTP_BINARY_HTTP_MESSAGE_H_
diff --git a/quiche/binary_http/binary_http_message_test.cc b/quiche/binary_http/binary_http_message_test.cc
index 7d22bf3..c747505 100644
--- a/quiche/binary_http/binary_http_message_test.cc
+++ b/quiche/binary_http/binary_http_message_test.cc
@@ -10,6 +10,7 @@
using ::testing::ContainerEq;
using ::testing::FieldsAre;
+using ::testing::StrEq;
namespace quiche {
namespace {
@@ -20,6 +21,12 @@
static_cast<uint8_t>(word >> 8), static_cast<uint8_t>(word)});
}
+template <class T>
+void TestPrintTo(const T& resp) {
+ std::ostringstream os;
+ PrintTo(resp, &os);
+ EXPECT_EQ(os.str(), resp.DebugString());
+}
} // namespace
// Test examples from
// https://www.ietf.org/archive/id/draft-ietf-httpbis-binary-message-06.html
@@ -65,6 +72,13 @@
const auto result = request.Serialize();
ASSERT_TRUE(result.ok());
ASSERT_EQ(*result, expected);
+ EXPECT_THAT(
+ request.DebugString(),
+ StrEq(
+ "BinaryHttpRequest{BinaryHttpMessage{Headers{user-agent=curl/7.16.3 "
+ "libcurl/7.16.3 OpenSSL/0.9.7l "
+ "zlib/1.2.3;;host=www.example.com;;accept-language=en, mi}Body{}}}"));
+ TestPrintTo(request);
}
TEST(BinaryHttpRequest, DecodeGetNoBody) {
@@ -90,6 +104,13 @@
{"accept-language", "en, mi"}};
ASSERT_THAT(request.GetHeaderFields(), ContainerEq(expected_fields));
ASSERT_EQ(request.body(), "");
+ EXPECT_THAT(
+ request.DebugString(),
+ StrEq(
+ "BinaryHttpRequest{BinaryHttpMessage{Headers{user-agent=curl/7.16.3 "
+ "libcurl/7.16.3 OpenSSL/0.9.7l "
+ "zlib/1.2.3;;host=www.example.com;;accept-language=en, mi}Body{}}}"));
+ TestPrintTo(request);
}
TEST(BinaryHttpRequest, EncodeGetWithAuthority) {
@@ -128,6 +149,11 @@
const auto result = request.Serialize();
ASSERT_TRUE(result.ok());
ASSERT_EQ(*result, expected);
+ EXPECT_THAT(
+ request.DebugString(),
+ StrEq("BinaryHttpRequest{BinaryHttpMessage{Headers{user-agent=curl/"
+ "7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l "
+ "zlib/1.2.3;;accept-language=en, mi}Body{}}}"));
}
TEST(BinaryHttpRequest, DecodeGetWithAuthority) {
@@ -152,6 +178,11 @@
{"accept-language", "en, mi"}};
ASSERT_THAT(request.GetHeaderFields(), ContainerEq(expected_fields));
ASSERT_EQ(request.body(), "");
+ EXPECT_THAT(
+ request.DebugString(),
+ StrEq("BinaryHttpRequest{BinaryHttpMessage{Headers{user-agent=curl/"
+ "7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l "
+ "zlib/1.2.3;;accept-language=en, mi}Body{}}}"));
}
TEST(BinaryHttpRequest, EncodePostBody) {
@@ -194,6 +225,11 @@
const auto result = request.Serialize();
ASSERT_TRUE(result.ok());
ASSERT_EQ(*result, expected);
+ EXPECT_THAT(
+ request.DebugString(),
+ StrEq("BinaryHttpRequest{BinaryHttpMessage{Headers{user-agent=not/"
+ "telling;;host=www.example.com;;accept-language=en}Body{Some "
+ "body that I used to post.\r\n}}}"));
}
TEST(BinaryHttpRequest, DecodePostBody) {
@@ -219,6 +255,11 @@
{"accept-language", "en"}};
ASSERT_THAT(request.GetHeaderFields(), ContainerEq(expected_fields));
ASSERT_EQ(request.body(), "Some body that I used to post.\r\n");
+ EXPECT_THAT(
+ request.DebugString(),
+ StrEq("BinaryHttpRequest{BinaryHttpMessage{Headers{user-agent=not/"
+ "telling;;host=www.example.com;;accept-language=en}Body{Some "
+ "body that I used to post.\r\n}}}"));
}
TEST(BinaryHttpResponse, EncodeNoBody) {
@@ -243,6 +284,9 @@
const auto result = response.Serialize();
ASSERT_TRUE(result.ok());
ASSERT_EQ(*result, expected);
+ EXPECT_THAT(response.DebugString(),
+ StrEq("BinaryHttpResponse(404){BinaryHttpMessage{Headers{server="
+ "Apache}Body{}}}"));
}
TEST(BinaryHttpResponse, DecodeNoBody) {
@@ -265,6 +309,9 @@
ASSERT_THAT(response.GetHeaderFields(), ContainerEq(expected_fields));
ASSERT_EQ(response.body(), "");
ASSERT_TRUE(response.informational_responses().empty());
+ EXPECT_THAT(response.DebugString(),
+ StrEq("BinaryHttpResponse(404){BinaryHttpMessage{Headers{server="
+ "Apache}Body{}}}"));
}
TEST(BinaryHttpResponse, EncodeBody) {
@@ -295,6 +342,9 @@
const auto result = response.Serialize();
ASSERT_TRUE(result.ok());
ASSERT_EQ(*result, expected);
+ EXPECT_THAT(response.DebugString(),
+ StrEq("BinaryHttpResponse(200){BinaryHttpMessage{Headers{server="
+ "Apache}Body{Hello, world!\r\n}}}"));
}
TEST(BinaryHttpResponse, DecodeBody) {
@@ -319,6 +369,9 @@
ASSERT_THAT(response.GetHeaderFields(), ContainerEq(expected_fields));
ASSERT_EQ(response.body(), "Hello, world!\r\n");
ASSERT_TRUE(response.informational_responses().empty());
+ EXPECT_THAT(response.DebugString(),
+ StrEq("BinaryHttpResponse(200){BinaryHttpMessage{Headers{server="
+ "Apache}Body{Hello, world!\r\n}}}"));
}
TEST(BHttpResponse, AddBadInformationalResponseCode) {
@@ -416,6 +469,18 @@
const auto result = response.Serialize();
ASSERT_TRUE(result.ok());
ASSERT_EQ(*result, expected);
+ EXPECT_THAT(
+ response.DebugString(),
+ StrEq("BinaryHttpResponse(200){BinaryHttpMessage{Headers{date=Mon, "
+ "27 Jul 2009 12:28:53 GMT;;server=Apache;;last-modified=Wed, "
+ "22 Jul 2009 19:15:56 "
+ "GMT;;etag=\"34aa387-d-1568eb00\";;accept-ranges=bytes;;"
+ "content-length=51;;vary=Accept-Encoding;;content-type=text/"
+ "plain}Body{Hello World! My content includes a trailing "
+ "CRLF.\r\n}}InformationalResponse{running=\"sleep "
+ "15\"};;InformationalResponse{link=</style.css>; rel=preload; "
+ "as=style;;link=</script.js>; rel=preload; as=script}}"));
+ TestPrintTo(response);
}
TEST(BinaryHttpResponse, DecodeMultiInformationalWithBody) {
@@ -484,6 +549,18 @@
{102, header102}, {103, header103}};
ASSERT_THAT(response.informational_responses(),
ContainerEq(expected_control));
+ EXPECT_THAT(
+ response.DebugString(),
+ StrEq("BinaryHttpResponse(200){BinaryHttpMessage{Headers{date=Mon, "
+ "27 Jul 2009 12:28:53 GMT;;server=Apache;;last-modified=Wed, "
+ "22 Jul 2009 19:15:56 "
+ "GMT;;etag=\"34aa387-d-1568eb00\";;accept-ranges=bytes;;"
+ "content-length=51;;vary=Accept-Encoding;;content-type=text/"
+ "plain}Body{Hello World! My content includes a trailing "
+ "CRLF.\r\n}}InformationalResponse{running=\"sleep "
+ "15\"};;InformationalResponse{link=</style.css>; rel=preload; "
+ "as=style;;link=</script.js>; rel=preload; as=script}}"));
+ TestPrintTo(response);
}
} // namespace quiche