1 #include "quiche/http2/adapter/nghttp2_session.h"
2
3 #include "quiche/http2/adapter/mock_http2_visitor.h"
4 #include "quiche/http2/adapter/nghttp2_callbacks.h"
5 #include "quiche/http2/adapter/nghttp2_util.h"
6 #include "quiche/http2/adapter/test_frame_sequence.h"
7 #include "quiche/http2/adapter/test_utils.h"
8 #include "quiche/common/platform/api/quiche_expect_bug.h"
9 #include "quiche/common/platform/api/quiche_test.h"
10
11 namespace http2 {
12 namespace adapter {
13 namespace test {
14 namespace {
15
16 using testing::_;
17
18 enum FrameType {
19 DATA,
20 HEADERS,
21 PRIORITY,
22 RST_STREAM,
23 SETTINGS,
24 PUSH_PROMISE,
25 PING,
26 GOAWAY,
27 WINDOW_UPDATE,
28 };
29
30 class NgHttp2SessionTest : public quiche::test::QuicheTest {
31 public:
SetUp()32 void SetUp() override {
33 nghttp2_option_new(&options_);
34 nghttp2_option_set_no_auto_window_update(options_, 1);
35 }
36
TearDown()37 void TearDown() override { nghttp2_option_del(options_); }
38
CreateCallbacks()39 nghttp2_session_callbacks_unique_ptr CreateCallbacks() {
40 nghttp2_session_callbacks_unique_ptr callbacks = callbacks::Create();
41 return callbacks;
42 }
43
44 DataSavingVisitor visitor_;
45 nghttp2_option* options_ = nullptr;
46 };
47
TEST_F(NgHttp2SessionTest,ClientConstruction)48 TEST_F(NgHttp2SessionTest, ClientConstruction) {
49 NgHttp2Session session(Perspective::kClient, CreateCallbacks(), options_,
50 &visitor_);
51 EXPECT_TRUE(session.want_read());
52 EXPECT_FALSE(session.want_write());
53 EXPECT_EQ(session.GetRemoteWindowSize(), kInitialFlowControlWindowSize);
54 EXPECT_NE(session.raw_ptr(), nullptr);
55 }
56
TEST_F(NgHttp2SessionTest,ClientHandlesFrames)57 TEST_F(NgHttp2SessionTest, ClientHandlesFrames) {
58 NgHttp2Session session(Perspective::kClient, CreateCallbacks(), options_,
59 &visitor_);
60
61 ASSERT_EQ(0, nghttp2_session_send(session.raw_ptr()));
62 ASSERT_GT(visitor_.data().size(), 0);
63
64 const std::string initial_frames = TestFrameSequence()
65 .ServerPreface()
66 .Ping(42)
67 .WindowUpdate(0, 1000)
68 .Serialize();
69 testing::InSequence s;
70
71 // Server preface (empty SETTINGS)
72 EXPECT_CALL(visitor_, OnFrameHeader(0, 0, SETTINGS, 0));
73 EXPECT_CALL(visitor_, OnSettingsStart());
74 EXPECT_CALL(visitor_, OnSettingsEnd());
75
76 EXPECT_CALL(visitor_, OnFrameHeader(0, 8, PING, 0));
77 EXPECT_CALL(visitor_, OnPing(42, false));
78 EXPECT_CALL(visitor_, OnFrameHeader(0, 4, WINDOW_UPDATE, 0));
79 EXPECT_CALL(visitor_, OnWindowUpdate(0, 1000));
80
81 const int64_t initial_result = session.ProcessBytes(initial_frames);
82 EXPECT_EQ(initial_frames.size(), initial_result);
83
84 EXPECT_EQ(session.GetRemoteWindowSize(),
85 kInitialFlowControlWindowSize + 1000);
86
87 EXPECT_CALL(visitor_, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
88 EXPECT_CALL(visitor_, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
89 EXPECT_CALL(visitor_, OnBeforeFrameSent(PING, 0, 8, 0x1));
90 EXPECT_CALL(visitor_, OnFrameSent(PING, 0, 8, 0x1, 0));
91
92 ASSERT_EQ(0, nghttp2_session_send(session.raw_ptr()));
93 // Some bytes should have been serialized.
94 absl::string_view serialized = visitor_.data();
95 ASSERT_THAT(serialized,
96 testing::StartsWith(spdy::kHttp2ConnectionHeaderPrefix));
97 serialized.remove_prefix(strlen(spdy::kHttp2ConnectionHeaderPrefix));
98 EXPECT_THAT(serialized, EqualsFrames({spdy::SpdyFrameType::SETTINGS,
99 spdy::SpdyFrameType::PING}));
100 visitor_.Clear();
101
102 const std::vector<Header> headers1 =
103 ToHeaders({{":method", "GET"},
104 {":scheme", "http"},
105 {":authority", "example.com"},
106 {":path", "/this/is/request/one"}});
107 const auto nvs1 = GetNghttp2Nvs(headers1);
108
109 const std::vector<Header> headers2 =
110 ToHeaders({{":method", "GET"},
111 {":scheme", "http"},
112 {":authority", "example.com"},
113 {":path", "/this/is/request/two"}});
114 const auto nvs2 = GetNghttp2Nvs(headers2);
115
116 const std::vector<Header> headers3 =
117 ToHeaders({{":method", "GET"},
118 {":scheme", "http"},
119 {":authority", "example.com"},
120 {":path", "/this/is/request/three"}});
121 const auto nvs3 = GetNghttp2Nvs(headers3);
122
123 const int32_t stream_id1 = nghttp2_submit_request(
124 session.raw_ptr(), nullptr, nvs1.data(), nvs1.size(), nullptr, nullptr);
125 ASSERT_GT(stream_id1, 0);
126 QUICHE_LOG(INFO) << "Created stream: " << stream_id1;
127
128 const int32_t stream_id2 = nghttp2_submit_request(
129 session.raw_ptr(), nullptr, nvs2.data(), nvs2.size(), nullptr, nullptr);
130 ASSERT_GT(stream_id2, 0);
131 QUICHE_LOG(INFO) << "Created stream: " << stream_id2;
132
133 const int32_t stream_id3 = nghttp2_submit_request(
134 session.raw_ptr(), nullptr, nvs3.data(), nvs3.size(), nullptr, nullptr);
135 ASSERT_GT(stream_id3, 0);
136 QUICHE_LOG(INFO) << "Created stream: " << stream_id3;
137
138 EXPECT_CALL(visitor_, OnBeforeFrameSent(HEADERS, 1, _, 0x5));
139 EXPECT_CALL(visitor_, OnFrameSent(HEADERS, 1, _, 0x5, 0));
140 EXPECT_CALL(visitor_, OnBeforeFrameSent(HEADERS, 3, _, 0x5));
141 EXPECT_CALL(visitor_, OnFrameSent(HEADERS, 3, _, 0x5, 0));
142 EXPECT_CALL(visitor_, OnBeforeFrameSent(HEADERS, 5, _, 0x5));
143 EXPECT_CALL(visitor_, OnFrameSent(HEADERS, 5, _, 0x5, 0));
144
145 ASSERT_EQ(0, nghttp2_session_send(session.raw_ptr()));
146 serialized = visitor_.data();
147 EXPECT_THAT(serialized, EqualsFrames({spdy::SpdyFrameType::HEADERS,
148 spdy::SpdyFrameType::HEADERS,
149 spdy::SpdyFrameType::HEADERS}));
150 visitor_.Clear();
151
152 const std::string stream_frames =
153 TestFrameSequence()
154 .Headers(1,
155 {{":status", "200"},
156 {"server", "my-fake-server"},
157 {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
158 /*fin=*/false)
159 .Data(1, "This is the response body.")
160 .RstStream(3, Http2ErrorCode::INTERNAL_ERROR)
161 .GoAway(5, Http2ErrorCode::ENHANCE_YOUR_CALM, "calm down!!")
162 .Serialize();
163
164 EXPECT_CALL(visitor_, OnFrameHeader(1, _, HEADERS, 4));
165 EXPECT_CALL(visitor_, OnBeginHeadersForStream(1));
166 EXPECT_CALL(visitor_, OnHeaderForStream(1, ":status", "200"));
167 EXPECT_CALL(visitor_, OnHeaderForStream(1, "server", "my-fake-server"));
168 EXPECT_CALL(visitor_,
169 OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT"));
170 EXPECT_CALL(visitor_, OnEndHeadersForStream(1));
171 EXPECT_CALL(visitor_, OnFrameHeader(1, 26, DATA, 0));
172 EXPECT_CALL(visitor_, OnBeginDataForStream(1, 26));
173 EXPECT_CALL(visitor_, OnDataForStream(1, "This is the response body."));
174 EXPECT_CALL(visitor_, OnFrameHeader(3, 4, RST_STREAM, 0));
175 EXPECT_CALL(visitor_, OnRstStream(3, Http2ErrorCode::INTERNAL_ERROR));
176 EXPECT_CALL(visitor_, OnCloseStream(3, Http2ErrorCode::INTERNAL_ERROR));
177 EXPECT_CALL(visitor_, OnFrameHeader(0, 19, GOAWAY, 0));
178 EXPECT_CALL(visitor_,
179 OnGoAway(5, Http2ErrorCode::ENHANCE_YOUR_CALM, "calm down!!"));
180 const int64_t stream_result = session.ProcessBytes(stream_frames);
181 EXPECT_EQ(stream_frames.size(), stream_result);
182
183 // Even though the client recieved a GOAWAY, streams 1 and 5 are still active.
184 EXPECT_TRUE(session.want_read());
185
186 EXPECT_CALL(visitor_, OnFrameHeader(1, 0, DATA, 1));
187 EXPECT_CALL(visitor_, OnBeginDataForStream(1, 0));
188 EXPECT_CALL(visitor_, OnEndStream(1));
189 EXPECT_CALL(visitor_, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR));
190 EXPECT_CALL(visitor_, OnFrameHeader(5, 4, RST_STREAM, 0));
191 EXPECT_CALL(visitor_, OnRstStream(5, Http2ErrorCode::REFUSED_STREAM));
192 EXPECT_CALL(visitor_, OnCloseStream(5, Http2ErrorCode::REFUSED_STREAM));
193 session.ProcessBytes(TestFrameSequence()
194 .Data(1, "", true)
195 .RstStream(5, Http2ErrorCode::REFUSED_STREAM)
196 .Serialize());
197 // After receiving END_STREAM for 1 and RST_STREAM for 5, the session no
198 // longer expects reads.
199 EXPECT_FALSE(session.want_read());
200
201 // Client will not have anything else to write.
202 EXPECT_FALSE(session.want_write());
203 ASSERT_EQ(0, nghttp2_session_send(session.raw_ptr()));
204 serialized = visitor_.data();
205 EXPECT_EQ(serialized.size(), 0);
206 }
207
TEST_F(NgHttp2SessionTest,ServerConstruction)208 TEST_F(NgHttp2SessionTest, ServerConstruction) {
209 NgHttp2Session session(Perspective::kServer, CreateCallbacks(), options_,
210 &visitor_);
211 EXPECT_TRUE(session.want_read());
212 EXPECT_FALSE(session.want_write());
213 EXPECT_EQ(session.GetRemoteWindowSize(), kInitialFlowControlWindowSize);
214 EXPECT_NE(session.raw_ptr(), nullptr);
215 }
216
TEST_F(NgHttp2SessionTest,ServerHandlesFrames)217 TEST_F(NgHttp2SessionTest, ServerHandlesFrames) {
218 NgHttp2Session session(Perspective::kServer, CreateCallbacks(), options_,
219 &visitor_);
220
221 const std::string frames = TestFrameSequence()
222 .ClientPreface()
223 .Ping(42)
224 .WindowUpdate(0, 1000)
225 .Headers(1,
226 {{":method", "POST"},
227 {":scheme", "https"},
228 {":authority", "example.com"},
229 {":path", "/this/is/request/one"}},
230 /*fin=*/false)
231 .WindowUpdate(1, 2000)
232 .Data(1, "This is the request body.")
233 .Headers(3,
234 {{":method", "GET"},
235 {":scheme", "http"},
236 {":authority", "example.com"},
237 {":path", "/this/is/request/two"}},
238 /*fin=*/true)
239 .RstStream(3, Http2ErrorCode::CANCEL)
240 .Ping(47)
241 .Serialize();
242 testing::InSequence s;
243
244 // Client preface (empty SETTINGS)
245 EXPECT_CALL(visitor_, OnFrameHeader(0, 0, SETTINGS, 0));
246 EXPECT_CALL(visitor_, OnSettingsStart());
247 EXPECT_CALL(visitor_, OnSettingsEnd());
248
249 EXPECT_CALL(visitor_, OnFrameHeader(0, 8, PING, 0));
250 EXPECT_CALL(visitor_, OnPing(42, false));
251 EXPECT_CALL(visitor_, OnFrameHeader(0, 4, WINDOW_UPDATE, 0));
252 EXPECT_CALL(visitor_, OnWindowUpdate(0, 1000));
253 EXPECT_CALL(visitor_, OnFrameHeader(1, _, HEADERS, 4));
254 EXPECT_CALL(visitor_, OnBeginHeadersForStream(1));
255 EXPECT_CALL(visitor_, OnHeaderForStream(1, ":method", "POST"));
256 EXPECT_CALL(visitor_, OnHeaderForStream(1, ":scheme", "https"));
257 EXPECT_CALL(visitor_, OnHeaderForStream(1, ":authority", "example.com"));
258 EXPECT_CALL(visitor_, OnHeaderForStream(1, ":path", "/this/is/request/one"));
259 EXPECT_CALL(visitor_, OnEndHeadersForStream(1));
260 EXPECT_CALL(visitor_, OnFrameHeader(1, 4, WINDOW_UPDATE, 0));
261 EXPECT_CALL(visitor_, OnWindowUpdate(1, 2000));
262 EXPECT_CALL(visitor_, OnFrameHeader(1, 25, DATA, 0));
263 EXPECT_CALL(visitor_, OnBeginDataForStream(1, 25));
264 EXPECT_CALL(visitor_, OnDataForStream(1, "This is the request body."));
265 EXPECT_CALL(visitor_, OnFrameHeader(3, _, HEADERS, 5));
266 EXPECT_CALL(visitor_, OnBeginHeadersForStream(3));
267 EXPECT_CALL(visitor_, OnHeaderForStream(3, ":method", "GET"));
268 EXPECT_CALL(visitor_, OnHeaderForStream(3, ":scheme", "http"));
269 EXPECT_CALL(visitor_, OnHeaderForStream(3, ":authority", "example.com"));
270 EXPECT_CALL(visitor_, OnHeaderForStream(3, ":path", "/this/is/request/two"));
271 EXPECT_CALL(visitor_, OnEndHeadersForStream(3));
272 EXPECT_CALL(visitor_, OnEndStream(3));
273 EXPECT_CALL(visitor_, OnFrameHeader(3, 4, RST_STREAM, 0));
274 EXPECT_CALL(visitor_, OnRstStream(3, Http2ErrorCode::CANCEL));
275 EXPECT_CALL(visitor_, OnCloseStream(3, Http2ErrorCode::CANCEL));
276 EXPECT_CALL(visitor_, OnFrameHeader(0, 8, PING, 0));
277 EXPECT_CALL(visitor_, OnPing(47, false));
278
279 const int64_t result = session.ProcessBytes(frames);
280 EXPECT_EQ(frames.size(), result);
281
282 EXPECT_EQ(session.GetRemoteWindowSize(),
283 kInitialFlowControlWindowSize + 1000);
284
285 EXPECT_CALL(visitor_, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
286 EXPECT_CALL(visitor_, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
287 EXPECT_CALL(visitor_, OnBeforeFrameSent(PING, 0, 8, 0x1));
288 EXPECT_CALL(visitor_, OnFrameSent(PING, 0, 8, 0x1, 0));
289 EXPECT_CALL(visitor_, OnBeforeFrameSent(PING, 0, 8, 0x1));
290 EXPECT_CALL(visitor_, OnFrameSent(PING, 0, 8, 0x1, 0));
291
292 EXPECT_TRUE(session.want_write());
293 ASSERT_EQ(0, nghttp2_session_send(session.raw_ptr()));
294 // Some bytes should have been serialized.
295 absl::string_view serialized = visitor_.data();
296 // SETTINGS ack, two PING acks.
297 EXPECT_THAT(serialized, EqualsFrames({spdy::SpdyFrameType::SETTINGS,
298 spdy::SpdyFrameType::PING,
299 spdy::SpdyFrameType::PING}));
300 }
301
302 // Verifies that a null payload is caught by the OnPackExtensionCallback
303 // implementation.
TEST_F(NgHttp2SessionTest,NullPayload)304 TEST_F(NgHttp2SessionTest, NullPayload) {
305 NgHttp2Session session(Perspective::kClient, CreateCallbacks(), options_,
306 &visitor_);
307
308 void* payload = nullptr;
309 const int result = nghttp2_submit_extension(
310 session.raw_ptr(), kMetadataFrameType, 0, 1, payload);
311 ASSERT_EQ(0, result);
312 EXPECT_TRUE(session.want_write());
313 int send_result = -1;
314 EXPECT_QUICHE_BUG(
315 {
316 send_result = nghttp2_session_send(session.raw_ptr());
317 EXPECT_EQ(NGHTTP2_ERR_CALLBACK_FAILURE, send_result);
318 },
319 "Extension frame payload for stream 1 is null!");
320 }
321
TEST_F(NgHttp2SessionTest,ServerSeesErrorOnEndStream)322 TEST_F(NgHttp2SessionTest, ServerSeesErrorOnEndStream) {
323 NgHttp2Session session(Perspective::kServer, CreateCallbacks(), options_,
324 &visitor_);
325
326 const std::string frames = TestFrameSequence()
327 .ClientPreface()
328 .Headers(1,
329 {{":method", "POST"},
330 {":scheme", "https"},
331 {":authority", "example.com"},
332 {":path", "/"}},
333 /*fin=*/false)
334 .Data(1, "Request body", true)
335 .Serialize();
336 testing::InSequence s;
337
338 // Client preface (empty SETTINGS)
339 EXPECT_CALL(visitor_, OnFrameHeader(0, 0, SETTINGS, 0));
340 EXPECT_CALL(visitor_, OnSettingsStart());
341 EXPECT_CALL(visitor_, OnSettingsEnd());
342 // Stream 1
343 EXPECT_CALL(visitor_, OnFrameHeader(1, _, HEADERS, 0x4));
344 EXPECT_CALL(visitor_, OnBeginHeadersForStream(1));
345 EXPECT_CALL(visitor_, OnHeaderForStream(1, ":method", "POST"));
346 EXPECT_CALL(visitor_, OnHeaderForStream(1, ":scheme", "https"));
347 EXPECT_CALL(visitor_, OnHeaderForStream(1, ":authority", "example.com"));
348 EXPECT_CALL(visitor_, OnHeaderForStream(1, ":path", "/"));
349 EXPECT_CALL(visitor_, OnEndHeadersForStream(1));
350
351 EXPECT_CALL(visitor_, OnFrameHeader(1, _, DATA, 0x1));
352 EXPECT_CALL(visitor_, OnBeginDataForStream(1, _));
353 EXPECT_CALL(visitor_, OnDataForStream(1, "Request body"));
354 EXPECT_CALL(visitor_, OnEndStream(1)).WillOnce(testing::Return(false));
355
356 const int64_t result = session.ProcessBytes(frames);
357 EXPECT_EQ(NGHTTP2_ERR_CALLBACK_FAILURE, result);
358
359 EXPECT_TRUE(session.want_write());
360
361 EXPECT_CALL(visitor_, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
362 EXPECT_CALL(visitor_, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
363
364 ASSERT_EQ(0, nghttp2_session_send(session.raw_ptr()));
365 EXPECT_THAT(visitor_.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
366 visitor_.Clear();
367
368 EXPECT_FALSE(session.want_write());
369 }
370
371 } // namespace
372 } // namespace test
373 } // namespace adapter
374 } // namespace http2
375