1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "gtest/gtest.h"
17#include "frame_builder.h"
18
19using namespace OHOS::ArkCompiler::Toolchain;
20
21namespace panda::test {
22class FrameBuilderTest : public testing::Test {
23public:
24    // final message, ping-frame opcode
25    static constexpr char PING_EXPECTED_FIRST_BYTE = 0x89;
26
27    static constexpr uint8_t MASKING_KEY[WebSocketFrame::MASK_LEN] = {0xab};
28
29    static constexpr size_t SHORT_MSG_SIZE = 10;
30    static constexpr size_t LONG_MSG_SIZE = 1000;
31    static constexpr size_t LONG_LONG_MSG_SIZE = 0xfffff;
32
33    static const std::string SHORT_MSG;
34    static const std::string LONG_MSG;
35    static const std::string LONG_LONG_MSG;
36};
37
38const std::string FrameBuilderTest::SHORT_MSG      = std::string(SHORT_MSG_SIZE, 'f');
39const std::string FrameBuilderTest::LONG_MSG       = std::string(LONG_MSG_SIZE, 'f');
40const std::string FrameBuilderTest::LONG_LONG_MSG  = std::string(LONG_LONG_MSG_SIZE, 'f');
41
42HWTEST_F(FrameBuilderTest, TestNoPayload, testing::ext::TestSize.Level0)
43{
44    ServerFrameBuilder frameBuilder(true, FrameType::PING);
45    auto message = frameBuilder.Build();
46
47    constexpr size_t EXPECTED_MESSAGE_SIZE = 2;
48
49    ASSERT_EQ(message.size(), EXPECTED_MESSAGE_SIZE);
50    ASSERT_EQ(message[0], PING_EXPECTED_FIRST_BYTE);
51    // unmasked, zero length
52    ASSERT_EQ(message[1], 0);
53}
54
55HWTEST_F(FrameBuilderTest, TestShortPayload, testing::ext::TestSize.Level0)
56{
57    ServerFrameBuilder frameBuilder(true, FrameType::PING);
58    auto message = frameBuilder
59        .SetPayload(SHORT_MSG)
60        .Build();
61
62    constexpr size_t HEADER_LENGTH = 2;
63    constexpr size_t EXPECTED_MESSAGE_SIZE = HEADER_LENGTH + SHORT_MSG_SIZE;
64
65    ASSERT_EQ(message.size(), EXPECTED_MESSAGE_SIZE);
66    ASSERT_EQ(message[0], PING_EXPECTED_FIRST_BYTE);
67    // length fits into [0, 126) range
68    ASSERT_EQ(message[1], static_cast<char>(SHORT_MSG_SIZE));
69    for (size_t i = HEADER_LENGTH; i < message.size(); ++i) {
70        ASSERT_EQ(message[i], SHORT_MSG[i - HEADER_LENGTH]);
71    }
72}
73
74HWTEST_F(FrameBuilderTest, TestLongPayload, testing::ext::TestSize.Level0)
75{
76    ServerFrameBuilder frameBuilder(true, FrameType::PING);
77    auto message = frameBuilder
78        .SetPayload(LONG_MSG)
79        .Build();
80
81    constexpr size_t HEADER_LENGTH = 2 + 2;
82    constexpr size_t EXPECTED_MESSAGE_SIZE = HEADER_LENGTH + LONG_MSG_SIZE;
83
84    ASSERT_EQ(message.size(), EXPECTED_MESSAGE_SIZE);
85    ASSERT_EQ(message[0], PING_EXPECTED_FIRST_BYTE);
86    // length fits into [125, 65536) range - encoded with 126
87    ASSERT_EQ(message[1], 126);
88    // everything is encoded as big-endian
89    ASSERT_EQ(message[2], static_cast<char>((LONG_MSG_SIZE >> 8) & 0xff));
90    ASSERT_EQ(message[3], static_cast<char>(LONG_MSG_SIZE & 0xff));
91    for (size_t i = HEADER_LENGTH; i < message.size(); ++i) {
92        ASSERT_EQ(message[i], LONG_MSG[i - HEADER_LENGTH]);
93    }
94}
95
96HWTEST_F(FrameBuilderTest, TestLongLongPayload, testing::ext::TestSize.Level0)
97{
98    ServerFrameBuilder frameBuilder(true, FrameType::PING);
99    auto message = frameBuilder
100        .SetPayload(LONG_LONG_MSG)
101        .Build();
102
103    constexpr size_t HEADER_LENGTH = 2 + 8;
104    constexpr size_t EXPECTED_MESSAGE_SIZE = HEADER_LENGTH + LONG_LONG_MSG_SIZE;
105
106    ASSERT_EQ(message.size(), EXPECTED_MESSAGE_SIZE);
107    ASSERT_EQ(message[0], PING_EXPECTED_FIRST_BYTE);
108    // length is bigger than 65536 - encoded with 127
109    ASSERT_EQ(message[1], 127);
110    // everything is encoded as big-endian
111    for (size_t idx = 2, shiftCount = 8 * (sizeof(uint64_t) - 1); idx < HEADER_LENGTH; ++idx, shiftCount -= 8) {
112        ASSERT_EQ(message[idx], static_cast<char>((LONG_LONG_MSG_SIZE >> shiftCount) & 0xff));
113    }
114    for (size_t i = HEADER_LENGTH; i < message.size(); ++i) {
115        ASSERT_EQ(message[i], LONG_LONG_MSG[i - HEADER_LENGTH]);
116    }
117}
118
119HWTEST_F(FrameBuilderTest, TestAppendPayload, testing::ext::TestSize.Level0)
120{
121    ServerFrameBuilder frameBuilder(true, FrameType::PING);
122    auto message = frameBuilder
123        .SetPayload(SHORT_MSG)
124        .AppendPayload(SHORT_MSG)
125        .Build();
126
127    constexpr size_t HEADER_LENGTH = 2;
128    constexpr size_t PAYLOAD_SIZE = SHORT_MSG_SIZE * 2;
129    constexpr size_t EXPECTED_MESSAGE_SIZE = HEADER_LENGTH + PAYLOAD_SIZE;
130
131    ASSERT_EQ(message.size(), EXPECTED_MESSAGE_SIZE);
132    ASSERT_EQ(message[0], PING_EXPECTED_FIRST_BYTE);
133    // length fits into [0, 126) range
134    ASSERT_EQ(message[1], static_cast<char>(PAYLOAD_SIZE));
135    for (size_t i = HEADER_LENGTH; i < message.size(); ++i) {
136        ASSERT_EQ(message[i], SHORT_MSG[(i - HEADER_LENGTH) % SHORT_MSG_SIZE]);
137    }
138}
139
140HWTEST_F(FrameBuilderTest, TestClientNoPayload, testing::ext::TestSize.Level0)
141{
142    ClientFrameBuilder frameBuilder(true, FrameType::PING, MASKING_KEY);
143    auto message = frameBuilder.Build();
144
145    constexpr size_t EXPECTED_MESSAGE_SIZE = 2 + WebSocketFrame::MASK_LEN;
146
147    ASSERT_EQ(message.size(), EXPECTED_MESSAGE_SIZE);
148    ASSERT_EQ(message[0], PING_EXPECTED_FIRST_BYTE);
149    // masked, even if no payload provided
150    ASSERT_EQ(message[1], static_cast<char>(0x80));
151}
152
153HWTEST_F(FrameBuilderTest, TestClientMasking, testing::ext::TestSize.Level0)
154{
155    ClientFrameBuilder frameBuilder(true, FrameType::PING, MASKING_KEY);
156    auto message = frameBuilder
157        .SetPayload(LONG_MSG)
158        .Build();
159
160    constexpr size_t HEADER_LENGTH = 2 + 2 + WebSocketFrame::MASK_LEN;
161    constexpr size_t EXPECTED_MESSAGE_SIZE = HEADER_LENGTH + LONG_MSG_SIZE;
162
163    ASSERT_EQ(message.size(), EXPECTED_MESSAGE_SIZE);
164    ASSERT_EQ(message[0], PING_EXPECTED_FIRST_BYTE);
165    // masked, length fits into [125, 65536) range - encoded with 126
166    ASSERT_EQ(message[1], static_cast<char>(0x80 | 126));
167    // everything is encoded as big-endian
168    ASSERT_EQ(message[2], static_cast<char>((LONG_MSG_SIZE >> 8) & 0xff));
169    ASSERT_EQ(message[3], static_cast<char>(LONG_MSG_SIZE & 0xff));
170    // message must be masked
171    for (size_t i = HEADER_LENGTH; i < message.size(); ++i) {
172        ASSERT_EQ(static_cast<uint8_t>(message[i] ^ MASKING_KEY[i % WebSocketFrame::MASK_LEN]),
173                  static_cast<uint8_t>(LONG_MSG[i - HEADER_LENGTH]));
174    }
175}
176}  // namespace panda::test
177