/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include using namespace apache::thrift; using namespace folly; using namespace apache::thrift::transport; namespace apache { namespace thrift { namespace transport { TEST(THeaderTest, largetransform) { THeader header; header.setTransform(THeader::ZLIB_TRANSFORM); // ZLib flag size_t buf_size = 10000000; std::unique_ptr buf(IOBuf::create(buf_size)); // Make the data compressible, but not totally random for (size_t i = 0; i < buf_size / 4; i++) { buf->writableData()[i] = (int8_t)folly::Random::rand32(256); buf->writableData()[i + 1] = buf->writableData()[i]; buf->writableData()[i + 2] = buf->writableData()[i]; buf->writableData()[i + 3] = (int8_t)folly::Random::rand32(256); } buf->append(buf_size); buf = header.addHeader(std::move(buf)); buf_size = buf->computeChainDataLength(); buf->gather(buf_size); std::unique_ptr queue(new IOBufQueue); std::unique_ptr queue2(new IOBufQueue); queue->append(std::move(buf)); queue2->append(queue->split(buf_size / 4)); queue2->append(queue->split(buf_size / 4)); queue2->append(IOBuf::create(0)); // Empty buffer should work queue2->append(queue->split(buf_size / 4)); queue2->append(queue->move()); size_t needed; THeader::StringToStringMap persistentHeaders; buf = header.removeHeader(queue2.get(), needed, persistentHeaders); EXPECT_EQ(buf->computeChainDataLength(), 10000000); } TEST(THeaderTest, http_clear_header) { THeader header; header.setClientType(THRIFT_HTTP_CLIENT_TYPE); auto parser = std::make_shared( "testhost", "testuri"); header.setHttpClientParser(parser); header.setHeader("WriteHeader", "foo"); size_t buf_size = 1000000; std::unique_ptr buf(IOBuf::create(buf_size)); buf = header.addHeader(std::move(buf)); EXPECT_TRUE(header.isWriteHeadersEmpty()); } TEST(THeaderTest, transform) { // Simple test for TRANSFORMS enum to string conversion EXPECT_EQ( THeader::getStringTransform(THeader::TRANSFORMS::ZLIB_TRANSFORM), "zlib"); } TEST(THeaderTest, eraseReadHeader) { THeader header; header.setReadHeaders({{"foo", "v"}, {"bar", "v"}, {"moo", "v"}}); EXPECT_EQ(3, header.getHeaders().size()); header.eraseReadHeader("bar"); EXPECT_EQ(2, header.getHeaders().size()); } TEST(THeaderTest, removeHeaderNullptrQueue) { THeader header; size_t needed; THeader::StringToStringMap persistentHeaders; EXPECT_EQ(nullptr, header.removeHeader(nullptr, needed, persistentHeaders)); EXPECT_EQ(4, needed); } TEST(THeaderTest, removeHeaderEmptyQueue) { THeader header; size_t needed; THeader::StringToStringMap persistentHeaders; IOBufQueue queue(IOBufQueue::cacheChainLength()); EXPECT_EQ(nullptr, header.removeHeader(&queue, needed, persistentHeaders)); EXPECT_EQ(4, needed); } TEST(THeaderTest, explicitTimeoutAndPriority) { THeader header; header.setClientTimeout(std::chrono::milliseconds(100)); header.setClientQueueTimeout(std::chrono::milliseconds(200)); header.setCallPriority(concurrency::PRIORITY::IMPORTANT); THeader::StringToStringMap headerMap; headerMap[THeader::CLIENT_TIMEOUT_HEADER] = "10"; headerMap[THeader::QUEUE_TIMEOUT_HEADER] = "20"; headerMap[THeader::PRIORITY_HEADER] = "3"; header.setReadHeaders(std::move(headerMap)); EXPECT_EQ(std::chrono::milliseconds(100), header.getClientTimeout()); EXPECT_EQ(std::chrono::milliseconds(200), header.getClientQueueTimeout()); EXPECT_EQ(concurrency::PRIORITY::IMPORTANT, header.getCallPriority()); } TEST(THeaderTest, headerTimeoutAndPriority) { THeader header; THeader::StringToStringMap headerMap; headerMap[THeader::CLIENT_TIMEOUT_HEADER] = "10"; headerMap[THeader::QUEUE_TIMEOUT_HEADER] = "20"; headerMap[THeader::PRIORITY_HEADER] = "3"; header.setReadHeaders(std::move(headerMap)); EXPECT_EQ(std::chrono::milliseconds(10), header.getClientTimeout()); EXPECT_EQ(std::chrono::milliseconds(20), header.getClientQueueTimeout()); EXPECT_EQ(concurrency::PRIORITY::NORMAL, header.getCallPriority()); } TEST(THeaderTest, defaultTimeoutAndPriority) { THeader header; EXPECT_EQ(std::chrono::milliseconds(0), header.getClientTimeout()); EXPECT_EQ(std::chrono::milliseconds(0), header.getClientQueueTimeout()); EXPECT_EQ(concurrency::PRIORITY::N_PRIORITIES, header.getCallPriority()); } TEST(THeaderTest, serverQueueingMetadataTest) { static constexpr std::chrono::milliseconds kQueueTimeout(10); static constexpr std::chrono::milliseconds kTimeQueued(6); THeader hdr1, hdr2; hdr1.setServerQueueTimeout(kQueueTimeout); hdr1.setProcessDelay(kTimeQueued); EXPECT_EQ(kQueueTimeout, hdr1.getServerQueueTimeout().value()); EXPECT_EQ(kTimeQueued, hdr1.getProcessDelay().value()); EXPECT_FALSE(hdr2.getServerQueueTimeout().hasValue()); EXPECT_FALSE(hdr2.getProcessDelay().hasValue()); } void testAsciiHeaderData(const std::string& data, const std::string& expected) { auto buf = folly::IOBuf::copyBuffer(data); THeader::StringToStringMap persistentHeaders; THeader header; std::unique_ptr queue(new IOBufQueue); queue->append(std::move(buf)); size_t needed; try { buf = header.removeHeader(queue.get(), needed, persistentHeaders); ASSERT_TRUE(false); } catch (TTransportException& e) { ASSERT_EQ(e.what(), expected); } } TEST(THeaderTest, asciiData1) { std::string data = "llocations statistics!"; auto expected = "The Thrift server received an ASCII request '" + data + "' and safely ignored it. In all likelihood, " "this isn't the reason of your problem (probably a local daemon " "sending HTTP content to all listening ports)."; testAsciiHeaderData(data, expected); } TEST(THeaderTest, asciiData2) { std::string data = "ap Here for more details"; auto expected = "The Thrift server received an ASCII request '" + data + "' and safely ignored it. In all likelihood, " "this isn't the reason of your problem (probably a local daemon " "sending HTTP content to all listening ports)."; testAsciiHeaderData(data, expected); } TEST(THeaderTest, asciiData3) { auto expected = "Header transport frame is too large: 1918987876 " "(hex 0x72616e64, ascii 'rand')"; testAsciiHeaderData("random data", expected); } TEST(THeaderTest, clientLoggingEnabled0) { THeader header; ASSERT_FALSE(header.isClientLoggingEnabled()); } TEST(THeaderTest, clientLoggingEnabled1) { THeader header; auto& ctx = header.loggingContext().emplace(); ctx.logSampleRatio() = 1000; ASSERT_TRUE(header.isClientLoggingEnabled()); } TEST(THeaderTest, clientLoggingEnabled2) { THeader header; auto& ctx = header.loggingContext().emplace(); ctx.logErrorSampleRatio() = 1000; ASSERT_TRUE(header.isClientLoggingEnabled()); } TEST(THeaderTest, clientLoggingEnabled3) { THeader header; auto& ctx = header.loggingContext().emplace(); ctx.logSampleRatio() = 0; ctx.logErrorSampleRatio() = 0; ASSERT_FALSE(header.isClientLoggingEnabled()); } } // namespace transport } // namespace thrift } // namespace apache