blob: b90d0b0b1795e79c09ba691c7b3ed5064d6c69db [file] [log] [blame]
QUICHE team82dee2f2019-01-18 12:35:12 -05001// Copyright 2016 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/third_party/quiche/src/spdy/core/spdy_deframer_visitor.h"
6
7#include <stdlib.h>
8
9#include <algorithm>
10#include <limits>
11
QUICHE team82dee2f2019-01-18 12:35:12 -050012#include "net/third_party/quiche/src/http2/test_tools/http2_random.h"
QUICHE teamf3c80c92020-02-12 09:47:55 -080013#include "net/third_party/quiche/src/common/platform/api/quiche_test.h"
QUICHE team82dee2f2019-01-18 12:35:12 -050014#include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h"
15#include "net/third_party/quiche/src/spdy/core/mock_spdy_framer_visitor.h"
16#include "net/third_party/quiche/src/spdy/core/spdy_frame_builder.h"
17#include "net/third_party/quiche/src/spdy/core/spdy_frame_reader.h"
18#include "net/third_party/quiche/src/spdy/core/spdy_framer.h"
19#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
20#include "net/third_party/quiche/src/spdy/core/spdy_protocol_test_utils.h"
21#include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h"
QUICHE teamded03512019-03-07 14:45:11 -080022#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
QUICHE team82dee2f2019-01-18 12:35:12 -050023
24namespace spdy {
25namespace test {
26namespace {
27
QUICHE teamf3c80c92020-02-12 09:47:55 -080028class SpdyDeframerVisitorTest : public QuicheTest {
QUICHE team82dee2f2019-01-18 12:35:12 -050029 protected:
30 SpdyDeframerVisitorTest() : encoder_(SpdyFramer::ENABLE_COMPRESSION) {
31 decoder_.set_process_single_input_frame(true);
32 auto collector =
bnc463f2352019-10-10 04:49:34 -070033 std::make_unique<DeframerCallbackCollector>(&collected_frames_);
QUICHE team82dee2f2019-01-18 12:35:12 -050034 auto log_and_collect =
35 SpdyDeframerVisitorInterface::LogBeforeVisiting(std::move(collector));
36 deframer_ = SpdyTestDeframer::CreateConverter(std::move(log_and_collect));
37 decoder_.set_visitor(deframer_.get());
38 }
39
40 bool DeframeInput(const char* input, size_t size) {
41 size_t input_remaining = size;
42 while (input_remaining > 0 &&
43 decoder_.spdy_framer_error() ==
44 http2::Http2DecoderAdapter::SPDY_NO_ERROR) {
45 // To make the tests more interesting, we feed random (and small) chunks
46 // into the framer. This simulates getting strange-sized reads from
47 // the socket.
48 const size_t kMaxReadSize = 32;
49 size_t bytes_read =
50 (random_.Uniform(std::min(input_remaining, kMaxReadSize))) + 1;
51 size_t bytes_processed = decoder_.ProcessInput(input, bytes_read);
52 input_remaining -= bytes_processed;
53 input += bytes_processed;
54 if (decoder_.state() ==
55 http2::Http2DecoderAdapter::SPDY_READY_FOR_FRAME) {
56 deframer_->AtFrameEnd();
57 }
58 }
59 return (input_remaining == 0 &&
60 decoder_.spdy_framer_error() ==
61 http2::Http2DecoderAdapter::SPDY_NO_ERROR);
62 }
63
64 SpdyFramer encoder_;
65 http2::Http2DecoderAdapter decoder_;
66 std::vector<CollectedFrame> collected_frames_;
67 std::unique_ptr<SpdyTestDeframer> deframer_;
68
69 private:
70 http2::test::Http2Random random_;
71};
72
73TEST_F(SpdyDeframerVisitorTest, DataFrame) {
74 const char kFrameData[] = {
75 0x00, 0x00, 0x0d, // Length = 13.
76 0x00, // DATA
77 0x08, // PADDED
78 0x00, 0x00, 0x00, 0x01, // Stream 1
79 0x07, // Pad length field.
80 'h', 'e', 'l', 'l', // Data
81 'o', // More Data
82 0x00, 0x00, 0x00, 0x00, // Padding
83 0x00, 0x00, 0x00 // More Padding
84 };
85
86 EXPECT_TRUE(DeframeInput(kFrameData, sizeof kFrameData));
87 ASSERT_EQ(1u, collected_frames_.size());
88 const CollectedFrame& cf0 = collected_frames_[0];
89 ASSERT_NE(cf0.frame_ir, nullptr);
90
91 SpdyDataIR expected_ir(/* stream_id = */ 1, "hello");
92 expected_ir.set_padding_len(8);
93 EXPECT_TRUE(cf0.VerifyHasFrame(expected_ir));
94}
95
96TEST_F(SpdyDeframerVisitorTest, HeaderFrameWithContinuation) {
97 const char kFrameData[] = {
98 0x00, 0x00, 0x05, // Payload Length: 5
99 0x01, // Type: HEADERS
100 0x09, // Flags: PADDED | END_STREAM
101 0x00, 0x00, 0x00, 0x01, // Stream: 1
102 0x04, // Padding Length: 4
103 0x00, 0x00, 0x00, 0x00, // Padding
104 /* Second Frame */
105 0x00, 0x00, 0x12, // Payload Length: 18
106 0x09, // Type: CONTINUATION
107 0x04, // Flags: END_HEADERS
108 0x00, 0x00, 0x00, 0x01, // Stream: 1
109 0x00, // Unindexed, literal name & value
110 0x03, 0x62, 0x61, 0x72, // Name len and name (3, "bar")
111 0x03, 0x66, 0x6f, 0x6f, // Value len and value (3, "foo")
112 0x00, // Unindexed, literal name & value
113 0x03, 0x66, 0x6f, 0x6f, // Name len and name (3, "foo")
114 0x03, 0x62, 0x61, 0x72, // Value len and value (3, "bar")
115 };
116
117 EXPECT_TRUE(DeframeInput(kFrameData, sizeof kFrameData));
118 ASSERT_EQ(1u, collected_frames_.size());
119 const CollectedFrame& cf0 = collected_frames_[0];
120
121 StringPairVector headers;
122 headers.push_back({"bar", "foo"});
123 headers.push_back({"foo", "bar"});
124
125 EXPECT_TRUE(cf0.VerifyHasHeaders(headers));
126
127 SpdyHeadersIR expected_ir(/* stream_id = */ 1);
128 // Yet again SpdyFramerVisitorInterface is lossy: it doesn't call OnPadding
129 // for HEADERS, just for DATA. Sigh.
130 // expected_ir.set_padding_len(5);
131 expected_ir.set_fin(true);
132 for (const auto& nv : headers) {
133 expected_ir.SetHeader(nv.first, nv.second);
134 }
135
136 EXPECT_TRUE(cf0.VerifyHasFrame(expected_ir));
137
138 // Confirm that mismatches are also detected.
139 headers.push_back({"baz", "bing"});
140 EXPECT_FALSE(cf0.VerifyHasHeaders(headers));
141 EXPECT_TRUE(cf0.VerifyHasFrame(expected_ir));
142
143 headers.pop_back();
144 EXPECT_TRUE(cf0.VerifyHasHeaders(headers));
145 EXPECT_TRUE(cf0.VerifyHasFrame(expected_ir));
146
147 expected_ir.SetHeader("baz", "bing");
148 EXPECT_FALSE(cf0.VerifyHasFrame(expected_ir));
149 EXPECT_TRUE(cf0.VerifyHasHeaders(headers));
150}
151
152TEST_F(SpdyDeframerVisitorTest, PriorityFrame) {
153 const char kFrameData[] = {
154 0x00, 0x00, 0x05, // Length: 5
155 0x02, // Type: PRIORITY
156 0x00, // Flags: none
157 0x00, 0x00, 0x00, 0x65, // Stream: 101
158 '\x80', 0x00, 0x00, 0x01, // Parent: 1 (Exclusive)
159 0x10, // Weight: 17
160 };
161
162 EXPECT_TRUE(DeframeInput(kFrameData, sizeof kFrameData));
163 ASSERT_EQ(1u, collected_frames_.size());
164 const CollectedFrame& cf0 = collected_frames_[0];
165
166 SpdyPriorityIR expected_ir(/* stream_id = */ 101,
167 /* parent_stream_id = */ 1, /* weight = */ 17,
168 /* exclusive = */ true);
169 EXPECT_TRUE(cf0.VerifyHasFrame(expected_ir));
170
171 // Confirm that mismatches are also detected.
172 EXPECT_FALSE(cf0.VerifyHasFrame(SpdyPriorityIR(101, 1, 16, true)));
173 EXPECT_FALSE(cf0.VerifyHasFrame(SpdyPriorityIR(101, 50, 17, true)));
174 EXPECT_FALSE(cf0.VerifyHasFrame(SpdyPriorityIR(201, 1, 17, true)));
175 EXPECT_FALSE(cf0.VerifyHasFrame(SpdyPriorityIR(101, 1, 17, false)));
176}
177
178TEST_F(SpdyDeframerVisitorTest, DISABLED_RstStreamFrame) {
179 // TODO(jamessynge): Please implement.
180}
181
182TEST_F(SpdyDeframerVisitorTest, SettingsFrame) {
183 // Settings frame with two entries for the same parameter but with different
184 // values. The last one will be in the decoded SpdySettingsIR, but the vector
185 // of settings will have both, in the same order.
186 const char kFrameData[] = {
187 0x00, 0x00, 0x0c, // Length
188 0x04, // Type (SETTINGS)
189 0x00, // Flags
190 0x00, 0x00, 0x00, 0x00, // Stream id (must be zero)
191 0x00, 0x04, // Setting id (SETTINGS_INITIAL_WINDOW_SIZE)
192 0x0a, 0x0b, 0x0c, 0x0d, // Setting value
193 0x00, 0x04, // Setting id (SETTINGS_INITIAL_WINDOW_SIZE)
194 0x00, 0x00, 0x00, '\xff', // Setting value
195 };
196
197 EXPECT_TRUE(DeframeInput(kFrameData, sizeof kFrameData));
198 ASSERT_EQ(1u, collected_frames_.size());
199 const CollectedFrame& cf0 = collected_frames_[0];
200 ASSERT_NE(cf0.frame_ir, nullptr);
201
202 SpdySettingsIR expected_ir;
203 expected_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 255);
204 EXPECT_TRUE(cf0.VerifyHasFrame(expected_ir));
205
206 SettingVector expected_settings;
207 expected_settings.push_back({SETTINGS_INITIAL_WINDOW_SIZE, 0x0a0b0c0d});
208 expected_settings.push_back({SETTINGS_INITIAL_WINDOW_SIZE, 255});
209
210 EXPECT_TRUE(cf0.VerifyHasSettings(expected_settings));
211
212 // Confirm that mismatches are also detected.
213 expected_settings.push_back({SETTINGS_INITIAL_WINDOW_SIZE, 65536});
214 EXPECT_FALSE(cf0.VerifyHasSettings(expected_settings));
215
216 expected_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 65536);
217 EXPECT_FALSE(cf0.VerifyHasFrame(expected_ir));
218
219 SpdySettingsIR unexpected_ir;
220 unexpected_ir.set_is_ack(true);
221 EXPECT_FALSE(cf0.VerifyHasFrame(unexpected_ir));
222}
223
224TEST_F(SpdyDeframerVisitorTest, DISABLED_PushPromiseFrame) {
225 // TODO(jamessynge): Please implement.
226}
227
228TEST_F(SpdyDeframerVisitorTest, DISABLED_PingFrame) {
229 // TODO(jamessynge): Please implement.
230}
231
232TEST_F(SpdyDeframerVisitorTest, DISABLED_GoAwayFrame) {
233 // TODO(jamessynge): Please implement.
234}
235
236TEST_F(SpdyDeframerVisitorTest, DISABLED_WindowUpdateFrame) {
237 // TODO(jamessynge): Please implement.
238}
239
240TEST_F(SpdyDeframerVisitorTest, DISABLED_AltSvcFrame) {
241 // TODO(jamessynge): Please implement.
242}
243
244} // namespace
245} // namespace test
246} // namespace spdy