1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <folly/portability/GTest.h>
18 
19 #include <folly/ExceptionWrapper.h>
20 #include <folly/Singleton.h>
21 #include <folly/io/IOBuf.h>
22 #include <folly/io/async/DelayedDestruction.h>
23 #include <thrift/lib/cpp2/transport/rocket/framing/Parser.h>
24 
25 namespace apache {
26 namespace thrift {
27 namespace rocket {
28 
29 class FakeOwner : public folly::DelayedDestruction {
30  public:
handleFrame(std::unique_ptr<folly::IOBuf>)31   void handleFrame(std::unique_ptr<folly::IOBuf>) {}
close(folly::exception_wrapper)32   void close(folly::exception_wrapper) noexcept {}
scheduleTimeout(folly::HHWheelTimer::Callback *,const std::chrono::milliseconds &)33   void scheduleTimeout(
34       folly::HHWheelTimer::Callback*, const std::chrono::milliseconds&) {}
incMemoryUsage(uint32_t)35   bool incMemoryUsage(uint32_t) { return true; }
decMemoryUsage(uint32_t)36   void decMemoryUsage(uint32_t) {}
37 
customAlloc(size_t)38   std::unique_ptr<folly::IOBuf> customAlloc(size_t) { return nullptr; }
39 };
40 
41 // TODO: This should be removed once the new buffer logic controlled by
42 // THRIFT_FLAG(rocket_parser_dont_hold_buffer_enabled) is stable.
TEST(ParserTest,resizeBufferTest)43 TEST(ParserTest, resizeBufferTest) {
44   FakeOwner owner;
45   Parser<FakeOwner> parser(owner, std::chrono::milliseconds(0));
46   parser.setReadBufferSize(Parser<FakeOwner>::kMaxBufferSize * 2);
47 
48   folly::IOBuf iobuf{folly::IOBuf::CreateOp(), parser.getReadBufferSize()};
49   // pretend there is something written into the buffer
50   iobuf.append(Parser<FakeOwner>::kMinBufferSize);
51 
52   parser.setReadBuffer(std::move(iobuf));
53   parser.resizeBuffer();
54 
55   EXPECT_EQ(parser.getReadBufferSize(), Parser<FakeOwner>::kMaxBufferSize);
56 }
57 
58 // TODO: This should be removed once the new buffer logic controlled by
59 // THRIFT_FLAG(rocket_parser_dont_hold_buffer_enabled) is stable.
TEST(ParserTest,noResizeBufferReadBufGtMaxTest)60 TEST(ParserTest, noResizeBufferReadBufGtMaxTest) {
61   FakeOwner owner;
62   Parser<FakeOwner> parser(owner, std::chrono::milliseconds(0));
63   parser.setReadBufferSize(Parser<FakeOwner>::kMaxBufferSize * 2);
64 
65   folly::IOBuf iobuf{folly::IOBuf::CreateOp(), parser.getReadBufferSize()};
66   // pretend there is something written into the buffer, but with size > max
67   iobuf.append(Parser<FakeOwner>::kMaxBufferSize * 1.5);
68 
69   parser.setReadBuffer(std::move(iobuf));
70   parser.resizeBuffer();
71 
72   EXPECT_EQ(parser.getReadBufferSize(), Parser<FakeOwner>::kMaxBufferSize * 2);
73 }
74 
75 // TODO: This should be removed once the new buffer logic controlled by
76 // THRIFT_FLAG(rocket_parser_dont_hold_buffer_enabled) is stable.
TEST(ParserTest,noResizeBufferReadBufEqMaxTest)77 TEST(ParserTest, noResizeBufferReadBufEqMaxTest) {
78   FakeOwner owner;
79   Parser<FakeOwner> parser(owner, std::chrono::milliseconds(0));
80   parser.setReadBufferSize(Parser<FakeOwner>::kMaxBufferSize * 2);
81 
82   folly::IOBuf iobuf{folly::IOBuf::CreateOp(), parser.getReadBufferSize()};
83   // pretend there is something written into the buffer, but with size = max
84   iobuf.append(Parser<FakeOwner>::kMaxBufferSize);
85 
86   parser.setReadBuffer(std::move(iobuf));
87   parser.resizeBuffer();
88 
89   EXPECT_EQ(parser.getReadBufferSize(), Parser<FakeOwner>::kMaxBufferSize * 2);
90 }
91 
92 // TODO: This should be removed once the new buffer logic controlled by
93 // THRIFT_FLAG(rocket_parser_dont_hold_buffer_enabled) is stable.
TEST(ParserTest,AlignmentTest)94 TEST(ParserTest, AlignmentTest) {
95   std::string s = "1234567890";
96   auto iobuf = folly::IOBuf::copyBuffer(s);
97   auto res = alignTo4k(
98       *iobuf, 4 /* 4 should be the first on 4k aligned address */, 1000);
99   EXPECT_TRUE(res);
100   EXPECT_EQ(reinterpret_cast<std::uintptr_t>(iobuf->data() + 4) % 4096u, 0);
101   EXPECT_EQ(iobuf->length() + iobuf->tailroom(), 1000);
102   EXPECT_EQ(folly::StringPiece(iobuf->coalesce()), s);
103 
104   iobuf = folly::IOBuf::copyBuffer(s);
105   // it is possible the aligned part has not been received yet
106   res = alignTo4k(*iobuf, 256, 1000);
107   EXPECT_TRUE(res);
108   EXPECT_EQ(reinterpret_cast<std::uintptr_t>(iobuf->data() + 256) % 4096u, 0);
109   EXPECT_EQ(iobuf->length() + iobuf->tailroom(), 1000);
110   EXPECT_EQ(folly::StringPiece(iobuf->coalesce()), s);
111 
112   iobuf = folly::IOBuf::copyBuffer(s);
113   // it is also possible the frame is smaller than received data
114   res = alignTo4k(*iobuf, 4, 7);
115   EXPECT_EQ(reinterpret_cast<std::uintptr_t>(iobuf->data() + 4) % 4096u, 0);
116   EXPECT_EQ(iobuf->length() + iobuf->tailroom(), 10);
117   EXPECT_EQ(iobuf->tailroom(), 0);
118   EXPECT_EQ(folly::StringPiece(iobuf->coalesce()), s);
119 }
120 
TEST(ParserTest,AlignmentIOBufQueueTest)121 TEST(ParserTest, AlignmentIOBufQueueTest) {
122   std::string s = "1234567890";
123   folly::IOBufQueue iobufQueue{folly::IOBufQueue::cacheChainLength()};
124   iobufQueue.append(folly::IOBuf::copyBuffer(s));
125   auto res = alignTo4kBufQueue(
126       iobufQueue, 4 /* 4 should be the first on 4k aligned address */, 1000);
127   EXPECT_TRUE(res);
128   auto iobuf = iobufQueue.move();
129   EXPECT_EQ(reinterpret_cast<std::uintptr_t>(iobuf->data() + 4) % 4096u, 0);
130   EXPECT_EQ(iobuf->length() + iobuf->tailroom(), 1000);
131   EXPECT_EQ(folly::StringPiece(iobuf->coalesce()), s);
132 
133   iobufQueue.reset();
134   iobufQueue.append(folly::IOBuf::copyBuffer(s));
135   // it is possible the aligned part has not been received yet
136   res = alignTo4kBufQueue(iobufQueue, 256, 1000);
137   EXPECT_TRUE(res);
138   iobuf = iobufQueue.move();
139   EXPECT_EQ(reinterpret_cast<std::uintptr_t>(iobuf->data() + 256) % 4096u, 0);
140   EXPECT_EQ(iobuf->length() + iobuf->tailroom(), 1000);
141   EXPECT_EQ(folly::StringPiece(iobuf->coalesce()), s);
142 
143   iobufQueue.reset();
144   iobufQueue.append(folly::IOBuf::copyBuffer(s));
145   // it is also possible the frame is smaller than received data
146   res = alignTo4kBufQueue(iobufQueue, 4, 7);
147   EXPECT_TRUE(res);
148   iobuf = iobufQueue.move();
149   EXPECT_EQ(reinterpret_cast<std::uintptr_t>(iobuf->data() + 4) % 4096u, 0);
150   EXPECT_EQ(iobuf->length() + iobuf->tailroom(), 10);
151   EXPECT_EQ(iobuf->tailroom(), 0);
152   EXPECT_EQ(folly::StringPiece(iobuf->coalesce()), s);
153 }
154 
TEST(ParserTest,BufferIsChainedAlignmentTest)155 TEST(ParserTest, BufferIsChainedAlignmentTest) {
156   std::string s = "1234567890";
157   auto iobuf = folly::IOBuf::copyBuffer(s);
158   auto iobuf2 = folly::IOBuf::copyBuffer(s);
159   iobuf->appendChain(std::move(iobuf2));
160   EXPECT_TRUE(iobuf->isChained());
161 
162   folly::IOBufQueue iobufQueue{folly::IOBufQueue::cacheChainLength()};
163   iobufQueue.append(std::move(iobuf));
164   auto res = alignTo4kBufQueue(
165       iobufQueue, 4 /* 4 should be the first on 4k aligned address */, 1000);
166   EXPECT_TRUE(res);
167   auto buf = iobufQueue.move();
168   EXPECT_EQ(reinterpret_cast<std::uintptr_t>(buf->data() + 4) % 4096u, 0);
169   EXPECT_EQ(buf->length() + buf->tailroom(), 1000);
170   EXPECT_EQ(folly::StringPiece(buf->coalesce()), s + s);
171 }
172 
TEST(ParserTest,PageAlignedBufferTest)173 TEST(ParserTest, PageAlignedBufferTest) {
174   // total number of (usable) bytes to be allocated
175   constexpr size_t kNumBytes = 32;
176   // portion of kNumBytes to be placed before page boundary
177   constexpr size_t kStartOffset = 13;
178   // final length of the iobuf (must be <= kNumBytes)
179   size_t trimLength = 0;
180   auto buf = get4kAlignedBuf(kNumBytes, kStartOffset, trimLength);
181   ASSERT_NE(nullptr, buf.get());
182   EXPECT_EQ(
183       reinterpret_cast<std::uintptr_t>(buf->data() + kStartOffset) % 4096u, 0);
184   EXPECT_EQ(trimLength, buf->length());
185 
186   trimLength = kStartOffset;
187   buf = get4kAlignedBuf(kNumBytes, kStartOffset, trimLength);
188   ASSERT_NE(nullptr, buf.get());
189   EXPECT_EQ(
190       reinterpret_cast<std::uintptr_t>(buf->data() + kStartOffset) % 4096u, 0);
191   EXPECT_EQ(trimLength, buf->length());
192 }
193 
194 } // namespace rocket
195 } // namespace thrift
196 } // namespace apache
197 
main(int argc,char ** argv)198 int main(int argc, char** argv) {
199   // Enable glog logging to stderr by default.
200   FLAGS_logtostderr = true;
201 
202   ::testing::InitGoogleTest(&argc, argv);
203   folly::SingletonVault::singleton()->registrationComplete();
204 
205   return RUN_ALL_TESTS();
206 }
207