1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree.
7  */
8 
9 #include <proxygen/lib/http/session/test/HQSessionTestCommon.h>
10 #include <quic/codec/QuicInteger.h>
11 
12 #include <folly/Random.h>
13 #include <folly/String.h>
14 
15 using namespace proxygen;
16 using namespace proxygen::hq;
17 
encodeQuicIntegerWithAtLeast(uint64_t value,uint8_t atLeast,folly::io::QueueAppender & appender)18 size_t encodeQuicIntegerWithAtLeast(uint64_t value,
19                                     uint8_t atLeast,
20                                     folly::io::QueueAppender& appender) {
21   CHECK(atLeast == 1 || atLeast == 2 || atLeast == 4 || atLeast == 8);
22 
23   CHECK_LE(value, quic::kEightByteLimit);
24   uint8_t numBytes = 0;
25   if (value <= quic::kOneByteLimit) {
26     numBytes = 1;
27   } else if (value <= quic::kTwoByteLimit) {
28     numBytes = 2;
29   } else if (value <= quic::kFourByteLimit) {
30     numBytes = 4;
31   } else if (value <= quic::kEightByteLimit) {
32     numBytes = 8;
33   }
34   CHECK_NE(numBytes, 0);
35   numBytes = std::max(numBytes, atLeast);
36   CHECK(numBytes == 1 || numBytes == 2 || numBytes == 4 || numBytes == 8);
37   if (numBytes == 1) {
38     uint8_t modified = static_cast<uint8_t>(value);
39     appender.writeBE(modified);
40     return sizeof(modified);
41   } else if (numBytes == 2) {
42     uint16_t reduced = static_cast<uint16_t>(value);
43     uint16_t modified = reduced | 0x4000;
44     appender.writeBE(modified);
45     return sizeof(modified);
46   } else if (numBytes == 4) {
47     uint32_t reduced = static_cast<uint32_t>(value);
48     uint32_t modified = reduced | 0x80000000;
49     appender.writeBE(modified);
50     return sizeof(modified);
51   } else if (numBytes == 8) {
52     uint64_t modified = value | 0xC000000000000000;
53     appender.writeBE(modified);
54     return sizeof(modified);
55   }
56   CHECK(false);
57 }
58 
generateStreamPreface(folly::IOBufQueue & writeBuf,UnidirectionalStreamType type)59 size_t generateStreamPreface(folly::IOBufQueue& writeBuf,
60                              UnidirectionalStreamType type) {
61   folly::io::QueueAppender appender(&writeBuf, 8);
62   uint8_t size = 1 << (folly::Random::rand32() % 4);
63   auto bytesWritten = encodeQuicIntegerWithAtLeast(
64       static_cast<hq::StreamTypeType>(type), size, appender);
65   CHECK_GE(bytesWritten, size);
66   return bytesWritten;
67 }
68 
paramsToTestName(const testing::TestParamInfo<TestParams> & info)69 std::string paramsToTestName(const testing::TestParamInfo<TestParams>& info) {
70   std::vector<std::string> paramsV;
71   folly::split("-", info.param.alpn_, paramsV);
72   if (info.param.numBytesOnPushStream < kUnlimited) {
73     paramsV.push_back("_" +
74                       folly::to<std::string>(info.param.numBytesOnPushStream));
75   }
76   if (info.param.unidirectionalStreamsCredit != kDefaultUnidirStreamCredit) {
77     paramsV.push_back(
78         "_" + folly::to<std::string>(info.param.unidirectionalStreamsCredit));
79   }
80   if (!info.param.createQPACKStreams_) {
81     paramsV.push_back("_noqpack");
82   }
83   if (info.param.datagrams_) {
84     paramsV.push_back("_datagrams");
85   }
86   return folly::join("", paramsV);
87 }
88 
parseStreamPreface(folly::io::Cursor & cursor,std::string alpn)89 folly::Optional<std::pair<UnidirectionalStreamType, size_t>> parseStreamPreface(
90     folly::io::Cursor& cursor, std::string alpn) {
91   CHECK(!ALPN_H1Q_FB_V1);
92   auto res = quic::decodeQuicInteger(cursor);
93   if (!res) {
94     return folly::none;
95   }
96   auto prefaceEnum = UnidirectionalStreamType(res->first);
97   switch (prefaceEnum) {
98     case UnidirectionalStreamType::H1Q_CONTROL:
99       if (ALPN_H1Q_FB_V2) {
100         return std::make_pair(prefaceEnum, res->second);
101       } else {
102         return folly::none;
103       }
104       break;
105     case UnidirectionalStreamType::CONTROL:
106     case UnidirectionalStreamType::PUSH:
107     case UnidirectionalStreamType::QPACK_ENCODER:
108     case UnidirectionalStreamType::QPACK_DECODER:
109       if (ALPN_HQ) {
110         return std::make_pair(prefaceEnum, res->second);
111       } else {
112         return folly::none;
113       }
114       break;
115     default:
116       break;
117   }
118   return folly::none;
119 }
120 
parseReadData(HQUnidirectionalCodec * codec,folly::IOBufQueue & readBuf,std::unique_ptr<folly::IOBuf> buf)121 void parseReadData(HQUnidirectionalCodec* codec,
122                    folly::IOBufQueue& readBuf,
123                    std::unique_ptr<folly::IOBuf> buf) {
124   readBuf.append(std::move(buf));
125   auto ret = codec->onUnidirectionalIngress(readBuf.move());
126   readBuf.append(std::move(ret));
127 }
128 
createControlStream(quic::MockQuicSocketDriver * socketDriver,quic::StreamId id,UnidirectionalStreamType streamType)129 void createControlStream(quic::MockQuicSocketDriver* socketDriver,
130                          quic::StreamId id,
131                          UnidirectionalStreamType streamType) {
132   folly::IOBufQueue writeBuf{folly::IOBufQueue::cacheChainLength()};
133   auto length = generateStreamPreface(writeBuf, streamType);
134   CHECK_EQ(length, writeBuf.chainLength());
135   socketDriver->sock_->setControlStream(id);
136   for (size_t i = 0; i < length; i++) {
137     socketDriver->addReadEvent(
138         id, writeBuf.splitAtMost(1), std::chrono::milliseconds(0));
139   }
140 }
141