From e8d698ea28599ccd12c5cbabcb952112546442b4 Mon Sep 17 00:00:00 2001 From: Shiv Kushwah Date: Mon, 4 Nov 2024 12:33:01 -0800 Subject: [PATCH] [8/x Add Indeterminate support for generating body Summary: Context: https://docs.google.com/document/d/1rtw4JJJojO9NpUKlIUO0_oCRjWftusTXbajEJ0Zf8mo/edit In this diff, I add indeterminate mode support to the Binary HTTP codec (see https://www.rfc-editor.org/rfc/rfc9292.html#name-indeterminate-length-messag). The final step is to add support for a indeterminate mode when we are generating the body. Reviewed By: lnicco Differential Revision: D64720137 fbshipit-source-id: e982882b26e1de355a35ca8a38a9579b0bb75e92 --- proxygen/lib/http/codec/HTTPBinaryCodec.cpp | 5 + .../http/codec/test/HTTPBinaryCodecTest.cpp | 139 +++++++++++++++++- 2 files changed, 142 insertions(+), 2 deletions(-) diff --git a/proxygen/lib/http/codec/HTTPBinaryCodec.cpp b/proxygen/lib/http/codec/HTTPBinaryCodec.cpp index 08daf7ff42..18260f8278 100644 --- a/proxygen/lib/http/codec/HTTPBinaryCodec.cpp +++ b/proxygen/lib/http/codec/HTTPBinaryCodec.cpp @@ -677,6 +677,11 @@ size_t HTTPBinaryCodec::generatePadding(folly::IOBufQueue& writeBuf, } size_t HTTPBinaryCodec::generateEOM(folly::IOBufQueue& writeBuf, StreamID txn) { + if (!knownLength_) { + folly::io::QueueAppender appender(&writeBuf, queueAppenderMaxGrowth); + encodeInteger(0, appender); + return 1; + } return 0; } diff --git a/proxygen/lib/http/codec/test/HTTPBinaryCodecTest.cpp b/proxygen/lib/http/codec/test/HTTPBinaryCodecTest.cpp index 98c2b39ac8..0de6f4eac0 100644 --- a/proxygen/lib/http/codec/test/HTTPBinaryCodecTest.cpp +++ b/proxygen/lib/http/codec/test/HTTPBinaryCodecTest.cpp @@ -801,7 +801,7 @@ TEST_F(HTTPBinaryCodecTest, testGenerateIndeterminateLengthHeaders) { }); } -TEST_F(HTTPBinaryCodecTest, testGenerateBody) { +TEST_F(HTTPBinaryCodecTest, testGenerateKnownLengthBody) { // Create Test Body and encode std::string body = "Sample Test Body!"; std::unique_ptr testBody = @@ -821,7 +821,29 @@ TEST_F(HTTPBinaryCodecTest, testGenerateBody) { "Sample Test Body!"); } -TEST_F(HTTPBinaryCodecTest, testEncodeAndDecodeRequest) { +TEST_F(HTTPBinaryCodecTest, testGenerateIndeterminateLengthBody) { + // Create Test Body and encode + std::string body = "Sample Test Body!"; + std::unique_ptr testBody = + folly::IOBuf::wrapBuffer(body.data(), body.size()); + + folly::IOBufQueue writeBuffer; + upstreamBinaryCodecIndeterminateLength_->generateBody( + writeBuffer, 0, std::move(testBody), folly::none, true); + + // Decode Test Body and check + folly::io::Cursor cursor(writeBuffer.front()); + HTTPMessage msg; + // 18 bytes + 1 for the Content Terminator + EXPECT_EQ(upstreamBinaryCodecIndeterminateLength_->parseContent(cursor, 19) + .bytesParsed_, + 19); + EXPECT_EQ( + upstreamBinaryCodecIndeterminateLength_->getMsgBody().to(), + "Sample Test Body!"); +} + +TEST_F(HTTPBinaryCodecTest, testEncodeAndDecodeKnownLengthRequest) { // Create full request encode it to a buffer folly::IOBufQueue writeBuffer; @@ -931,4 +953,117 @@ TEST_F(HTTPBinaryCodecTest, testEncodeAndDecodeRequestEmptyBody) { "test-trailer-value"); } +TEST_F(HTTPBinaryCodecTest, testEncodeAndDecodeIndeterminateLengthRequest) { + // Create full request encode it to a buffer + folly::IOBufQueue writeBuffer; + + // Encode Framing Indicator, Control Data, and Headers + HTTPMessage msgEncoded; + msgEncoded.setMethod("POST"); + msgEncoded.setSecure(false); + msgEncoded.setURL("/hello.txt"); + HTTPHeaders& headersEncoded = msgEncoded.getHeaders(); + headersEncoded.set("user-agent", + "curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3"); + headersEncoded.set("host", "www.example.com"); + headersEncoded.set("accept-language", "en, mi"); + upstreamBinaryCodecIndeterminateLength_->generateHeader( + writeBuffer, 0, msgEncoded); + + // Encode Body + std::string body = "Sample Test Body!"; + std::unique_ptr testBody = + folly::IOBuf::wrapBuffer(body.data(), body.size()); + upstreamBinaryCodecIndeterminateLength_->generateBody( + writeBuffer, 0, std::move(testBody), folly::none, true); + + // Encode Trailing Headers + std::unique_ptr trailersEncoded = + std::make_unique(); + trailersEncoded->set("test-trailer", "test-trailer-value"); + msgEncoded.setTrailers(std::move(trailersEncoded)); + upstreamBinaryCodecIndeterminateLength_->generateTrailers( + writeBuffer, 0, *msgEncoded.getTrailers()); + + // Now, decode the request and check values + FakeHTTPCodecCallback callback; + upstreamBinaryCodecIndeterminateLength_->setCallback(&callback); + upstreamBinaryCodecIndeterminateLength_->onIngress(*writeBuffer.front()); + upstreamBinaryCodecIndeterminateLength_->onIngressEOF(); + + EXPECT_EQ(callback.msg->getMethod(), msgEncoded.getMethod()); + EXPECT_EQ(callback.msg->isSecure(), msgEncoded.isSecure()); + EXPECT_EQ(callback.msg->getURL(), msgEncoded.getURL()); + auto headersDecoded = callback.msg->getHeaders(); + EXPECT_EQ(headersDecoded.size(), headersEncoded.size()); + headersEncoded.forEach([&headersDecoded](const std::string& headerName, + const std::string& headerValue) { + EXPECT_EQ(headersDecoded.exists(headerName), true); + EXPECT_EQ(headersDecoded.getSingleOrEmpty(headerName), headerValue); + }); + + EXPECT_EQ(callback.data_.move()->to(), "Sample Test Body!"); + + auto trailersDecoded = *callback.msg->getTrailers(); + EXPECT_EQ(trailersDecoded.size(), 1); + EXPECT_EQ(trailersDecoded.exists("test-trailer"), true); + EXPECT_EQ(trailersDecoded.getSingleOrEmpty("test-trailer"), + "test-trailer-value"); +} + +TEST_F(HTTPBinaryCodecTest, + testEncodeAndDecodeIndeterminateLengthRequestEmptyBody) { + // Create full request encode it to a buffer + folly::IOBufQueue writeBuffer; + + // Encode Framing Indicator, Control Data, and Headers + HTTPMessage msgEncoded; + msgEncoded.setMethod("POST"); + msgEncoded.setSecure(false); + msgEncoded.setURL("/hello.txt"); + HTTPHeaders& headersEncoded = msgEncoded.getHeaders(); + headersEncoded.set("user-agent", + "curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3"); + headersEncoded.set("host", "www.example.com"); + headersEncoded.set("accept-language", "en, mi"); + upstreamBinaryCodecIndeterminateLength_->generateHeader( + writeBuffer, 0, msgEncoded); + + // Encode Empty Body + std::unique_ptr emptyBody; + upstreamBinaryCodecIndeterminateLength_->generateBody( + writeBuffer, 0, std::move(emptyBody), folly::none, true); + + // Encode Trailing Headers + std::unique_ptr trailersEncoded = + std::make_unique(); + trailersEncoded->set("test-trailer", "test-trailer-value"); + msgEncoded.setTrailers(std::move(trailersEncoded)); + upstreamBinaryCodecIndeterminateLength_->generateTrailers( + writeBuffer, 0, *msgEncoded.getTrailers()); + + // Now, decode the request and check values + FakeHTTPCodecCallback callback; + upstreamBinaryCodecIndeterminateLength_->setCallback(&callback); + upstreamBinaryCodecIndeterminateLength_->onIngress(*writeBuffer.front()); + upstreamBinaryCodecIndeterminateLength_->onIngressEOF(); + + EXPECT_EQ(callback.msg->getMethod(), msgEncoded.getMethod()); + EXPECT_EQ(callback.msg->isSecure(), msgEncoded.isSecure()); + EXPECT_EQ(callback.msg->getURL(), msgEncoded.getURL()); + auto headersDecoded = callback.msg->getHeaders(); + EXPECT_EQ(headersDecoded.size(), headersEncoded.size()); + headersEncoded.forEach([&headersDecoded](const std::string& headerName, + const std::string& headerValue) { + EXPECT_EQ(headersDecoded.exists(headerName), true); + EXPECT_EQ(headersDecoded.getSingleOrEmpty(headerName), headerValue); + }); + + auto trailersDecoded = *callback.msg->getTrailers(); + EXPECT_EQ(trailersDecoded.size(), 1); + EXPECT_EQ(trailersDecoded.exists("test-trailer"), true); + EXPECT_EQ(trailersDecoded.getSingleOrEmpty("test-trailer"), + "test-trailer-value"); +} + } // namespace proxygen::test