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