OLD | NEW |
| (Empty) |
1 // Copyright 2013 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/tools/flip_server/spdy_interface.h" | |
6 | |
7 #include <list> | |
8 #include <memory> | |
9 | |
10 #include "base/strings/string_piece.h" | |
11 #include "net/spdy/buffered_spdy_framer.h" | |
12 #include "net/tools/balsa/balsa_enums.h" | |
13 #include "net/tools/balsa/balsa_headers.h" | |
14 #include "net/tools/flip_server/flip_config.h" | |
15 #include "net/tools/flip_server/flip_test_utils.h" | |
16 #include "net/tools/flip_server/mem_cache.h" | |
17 #include "testing/gmock/include/gmock/gmock.h" | |
18 #include "testing/gtest/include/gtest/gtest.h" | |
19 | |
20 namespace net { | |
21 | |
22 using ::base::StringPiece; | |
23 using ::testing::_; | |
24 using ::testing::InSequence; | |
25 using ::testing::InvokeWithoutArgs; | |
26 using ::testing::Return; | |
27 using ::testing::SaveArg; | |
28 using ::testing::Values; | |
29 | |
30 inline bool operator==(StringPiece x, | |
31 const SpdyHeaderBlock::StringPieceProxy& y) { | |
32 return x == y.operator StringPiece(); | |
33 } | |
34 | |
35 namespace { | |
36 | |
37 struct StringSaver { | |
38 public: | |
39 StringSaver() : data(NULL), size(0) {} | |
40 void Save() { string = std::string(data, size); } | |
41 | |
42 const char* data; | |
43 size_t size; | |
44 std::string string; | |
45 }; | |
46 | |
47 class SpdyFramerVisitor : public BufferedSpdyFramerVisitorInterface { | |
48 public: | |
49 virtual ~SpdyFramerVisitor() {} | |
50 MOCK_METHOD1(OnError, void(SpdyFramer::SpdyError)); | |
51 MOCK_METHOD2(OnStreamError, void(SpdyStreamId, const std::string&)); | |
52 // SaveArg cannot be used on non-copyable types like SpdyHeaderBlock. | |
53 void OnHeaders(SpdyStreamId stream_id, | |
54 bool has_priority, | |
55 int weight, | |
56 SpdyStreamId parent_stream_id, | |
57 bool exclusive, | |
58 bool fin, | |
59 const SpdyHeaderBlock& headers) override { | |
60 actual_header_block_ = headers.Clone(); | |
61 OnHeadersMock(stream_id, has_priority, weight, parent_stream_id, exclusive, | |
62 fin, headers); | |
63 } | |
64 MOCK_METHOD7(OnHeadersMock, | |
65 void(SpdyStreamId stream_id, | |
66 bool has_priority, | |
67 int weight, | |
68 SpdyStreamId parent_stream_id, | |
69 bool exclusive, | |
70 bool fin, | |
71 const SpdyHeaderBlock& headers)); | |
72 MOCK_METHOD3(OnDataFrameHeader, void(SpdyStreamId, size_t, bool)); | |
73 MOCK_METHOD3(OnStreamFrameData, void(SpdyStreamId, const char*, size_t)); | |
74 MOCK_METHOD1(OnStreamEnd, void(SpdyStreamId)); | |
75 MOCK_METHOD2(OnStreamPadding, void(SpdyStreamId, size_t)); | |
76 MOCK_METHOD1(OnHeaderFrameStart, | |
77 SpdyHeadersHandlerInterface*(SpdyStreamId stream_id)); | |
78 MOCK_METHOD2(OnHeaderFrameEnd, | |
79 void(SpdyStreamId stream_id, bool end_headers)); | |
80 MOCK_METHOD1(OnSettings, void(bool clear_persisted)); | |
81 MOCK_METHOD3(OnSetting, void(SpdySettingsIds, uint8_t, uint32_t)); | |
82 MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack)); | |
83 MOCK_METHOD2(OnRstStream, void(SpdyStreamId, SpdyRstStreamStatus)); | |
84 MOCK_METHOD3(OnGoAway, void(SpdyStreamId, SpdyGoAwayStatus, StringPiece)); | |
85 MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId, int)); | |
86 MOCK_METHOD3(OnPushPromise, | |
87 void(SpdyStreamId, SpdyStreamId, const SpdyHeaderBlock&)); | |
88 MOCK_METHOD3(OnAltSvc, | |
89 void(SpdyStreamId, | |
90 base::StringPiece, | |
91 const SpdyAltSvcWireFormat::AlternativeServiceVector&)); | |
92 MOCK_METHOD2(OnUnknownFrame, bool(SpdyStreamId stream_id, int frame_type)); | |
93 | |
94 SpdyHeaderBlock actual_header_block_; | |
95 }; | |
96 | |
97 class FakeSMConnection : public SMConnection { | |
98 public: | |
99 FakeSMConnection(EpollServer* epoll_server, | |
100 SSLState* ssl_state, | |
101 MemoryCache* memory_cache, | |
102 FlipAcceptor* acceptor, | |
103 std::string log_prefix) | |
104 : SMConnection(epoll_server, | |
105 ssl_state, | |
106 memory_cache, | |
107 acceptor, | |
108 log_prefix) {} | |
109 | |
110 MOCK_METHOD0(Cleanup, void()); | |
111 MOCK_METHOD8(InitSMConnection, | |
112 void(SMConnectionPoolInterface*, | |
113 SMInterface*, | |
114 EpollServer*, | |
115 int, | |
116 std::string, | |
117 std::string, | |
118 std::string, | |
119 bool)); | |
120 }; | |
121 | |
122 // This class is almost SpdySM, except one function. | |
123 // This class is the test target of tests in this file. | |
124 class TestSpdySM : public SpdySM { | |
125 public: | |
126 virtual ~TestSpdySM() {} | |
127 TestSpdySM(SMConnection* connection, | |
128 SMInterface* sm_http_interface, | |
129 EpollServer* epoll_server, | |
130 MemoryCache* memory_cache, | |
131 FlipAcceptor* acceptor) | |
132 : SpdySM(connection, | |
133 sm_http_interface, | |
134 epoll_server, | |
135 memory_cache, | |
136 acceptor) {} | |
137 | |
138 MOCK_METHOD2(FindOrMakeNewSMConnectionInterface, | |
139 SMInterface*(const std::string&, const std::string&)); | |
140 }; | |
141 | |
142 class SpdySMTestBase : public ::testing::Test { | |
143 public: | |
144 explicit SpdySMTestBase(FlipHandlerType type) { | |
145 SSLState* ssl_state = NULL; | |
146 mock_another_interface_.reset(new MockSMInterface); | |
147 memory_cache_.reset(new MemoryCache); | |
148 acceptor_.reset(new FlipAcceptor(type, | |
149 "127.0.0.1", | |
150 "8941", | |
151 "ssl_cert_filename", | |
152 "ssl_key_filename", | |
153 "127.0.0.1", | |
154 "8942", | |
155 "127.0.0.1", | |
156 "8943", | |
157 1, | |
158 0, | |
159 true, | |
160 1, | |
161 false, | |
162 true, | |
163 NULL)); | |
164 epoll_server_.reset(new EpollServer); | |
165 connection_.reset(new FakeSMConnection(epoll_server_.get(), | |
166 ssl_state, | |
167 memory_cache_.get(), | |
168 acceptor_.get(), | |
169 "log_prefix")); | |
170 | |
171 interface_.reset(new TestSpdySM( | |
172 connection_.get(), mock_another_interface_.get(), epoll_server_.get(), | |
173 memory_cache_.get(), acceptor_.get())); | |
174 | |
175 spdy_framer_.reset(new BufferedSpdyFramer()); | |
176 spdy_framer_visitor_.reset(new SpdyFramerVisitor); | |
177 spdy_framer_->set_visitor(spdy_framer_visitor_.get()); | |
178 } | |
179 | |
180 ~SpdySMTestBase() override { | |
181 if (acceptor_->listen_fd_ >= 0) { | |
182 epoll_server_->UnregisterFD(acceptor_->listen_fd_); | |
183 close(acceptor_->listen_fd_); | |
184 acceptor_->listen_fd_ = -1; | |
185 } | |
186 OutputList& output_list = *connection_->output_list(); | |
187 for (OutputList::const_iterator i = output_list.begin(); | |
188 i != output_list.end(); | |
189 ++i) { | |
190 delete *i; | |
191 } | |
192 output_list.clear(); | |
193 } | |
194 | |
195 bool HasStream(uint32_t stream_id) { | |
196 return interface_->output_ordering().ExistsInPriorityMaps(stream_id); | |
197 } | |
198 | |
199 protected: | |
200 std::unique_ptr<MockSMInterface> mock_another_interface_; | |
201 std::unique_ptr<MemoryCache> memory_cache_; | |
202 std::unique_ptr<FlipAcceptor> acceptor_; | |
203 std::unique_ptr<EpollServer> epoll_server_; | |
204 std::unique_ptr<FakeSMConnection> connection_; | |
205 std::unique_ptr<TestSpdySM> interface_; | |
206 std::unique_ptr<BufferedSpdyFramer> spdy_framer_; | |
207 std::unique_ptr<SpdyFramerVisitor> spdy_framer_visitor_; | |
208 }; | |
209 | |
210 class SpdySMProxyTest : public SpdySMTestBase { | |
211 public: | |
212 SpdySMProxyTest() : SpdySMTestBase(FLIP_HANDLER_PROXY) {} | |
213 ~SpdySMProxyTest() override {} | |
214 }; | |
215 | |
216 class SpdySMServerTest : public SpdySMTestBase { | |
217 public: | |
218 SpdySMServerTest() : SpdySMTestBase(FLIP_HANDLER_SPDY_SERVER) {} | |
219 ~SpdySMServerTest() override {} | |
220 }; | |
221 | |
222 TEST_F(SpdySMProxyTest, InitSMConnection) { | |
223 { | |
224 InSequence s; | |
225 EXPECT_CALL(*connection_, InitSMConnection(_, _, _, _, _, _, _, _)); | |
226 } | |
227 interface_->InitSMConnection( | |
228 NULL, NULL, epoll_server_.get(), -1, "", "", "", false); | |
229 } | |
230 | |
231 TEST_F(SpdySMProxyTest, OnRstStream) { | |
232 BufferedSpdyFramerVisitorInterface* visitor = interface_.get(); | |
233 uint32_t stream_id = 82; | |
234 MemCacheIter mci; | |
235 mci.stream_id = stream_id; | |
236 | |
237 { | |
238 BalsaHeaders headers; | |
239 std::string filename = "foobar"; | |
240 memory_cache_->InsertFile(&headers, filename, ""); | |
241 mci.file_data = memory_cache_->GetFileData(filename); | |
242 } | |
243 | |
244 interface_->AddToOutputOrder(mci); | |
245 ASSERT_TRUE(HasStream(stream_id)); | |
246 visitor->OnRstStream(stream_id, RST_STREAM_INVALID); | |
247 ASSERT_FALSE(HasStream(stream_id)); | |
248 } | |
249 | |
250 TEST_F(SpdySMProxyTest, ProcessReadInput) { | |
251 ASSERT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, | |
252 interface_->spdy_framer()->state()); | |
253 interface_->ProcessReadInput("", 1); | |
254 ASSERT_EQ(SpdyFramer::SPDY_READING_COMMON_HEADER, | |
255 interface_->spdy_framer()->state()); | |
256 } | |
257 | |
258 TEST_F(SpdySMProxyTest, ResetForNewConnection) { | |
259 uint32_t stream_id = 13; | |
260 MemCacheIter mci; | |
261 mci.stream_id = stream_id; | |
262 // incomplete input | |
263 const char input[] = {'\0', '\0', '\0'}; | |
264 | |
265 { | |
266 BalsaHeaders headers; | |
267 std::string filename = "foobar"; | |
268 memory_cache_->InsertFile(&headers, filename, ""); | |
269 mci.file_data = memory_cache_->GetFileData(filename); | |
270 } | |
271 | |
272 interface_->AddToOutputOrder(mci); | |
273 ASSERT_TRUE(HasStream(stream_id)); | |
274 interface_->ProcessReadInput(input, sizeof(input)); | |
275 ASSERT_NE(SpdyFramer::SPDY_READY_FOR_FRAME, | |
276 interface_->spdy_framer()->state()); | |
277 | |
278 interface_->ResetForNewConnection(); | |
279 ASSERT_FALSE(HasStream(stream_id)); | |
280 ASSERT_TRUE(interface_->spdy_framer() == NULL); | |
281 } | |
282 | |
283 TEST_F(SpdySMProxyTest, CreateFramer) { | |
284 interface_->ResetForNewConnection(); | |
285 interface_->CreateFramer(); | |
286 ASSERT_TRUE(interface_->spdy_framer()); | |
287 } | |
288 | |
289 TEST_F(SpdySMProxyTest, PostAcceptHook) { | |
290 interface_->PostAcceptHook(); | |
291 | |
292 ASSERT_EQ(1u, connection_->output_list()->size()); | |
293 std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); | |
294 DataFrame* df = *i++; | |
295 | |
296 { | |
297 InSequence s; | |
298 EXPECT_CALL(*spdy_framer_visitor_, OnSettings(false)); | |
299 EXPECT_CALL(*spdy_framer_visitor_, | |
300 OnSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 0u, 100u)); | |
301 } | |
302 spdy_framer_->ProcessInput(df->data, df->size); | |
303 } | |
304 | |
305 TEST_F(SpdySMProxyTest, NewStream) { | |
306 // TODO(yhirano): SpdySM::NewStream leads to crash when | |
307 // acceptor_->flip_handler_type_ != FLIP_HANDLER_SPDY_SERVER. | |
308 // It should be fixed though I don't know the solution now. | |
309 } | |
310 | |
311 TEST_F(SpdySMProxyTest, AddToOutputOrder) { | |
312 uint32_t stream_id = 13; | |
313 MemCacheIter mci; | |
314 mci.stream_id = stream_id; | |
315 | |
316 { | |
317 BalsaHeaders headers; | |
318 std::string filename = "foobar"; | |
319 memory_cache_->InsertFile(&headers, filename, ""); | |
320 mci.file_data = memory_cache_->GetFileData(filename); | |
321 } | |
322 | |
323 interface_->AddToOutputOrder(mci); | |
324 ASSERT_TRUE(HasStream(stream_id)); | |
325 } | |
326 | |
327 TEST_F(SpdySMProxyTest, SendErrorNotFound) { | |
328 uint32_t stream_id = 82; | |
329 const char* actual_data; | |
330 size_t actual_size; | |
331 testing::MockFunction<void(int)> checkpoint; // NOLINT | |
332 | |
333 interface_->SendErrorNotFound(stream_id); | |
334 | |
335 ASSERT_EQ(2u, connection_->output_list()->size()); | |
336 | |
337 { | |
338 InSequence s; | |
339 EXPECT_CALL(*spdy_framer_visitor_, | |
340 OnHeadersMock(stream_id, /*has_priority=*/false, _, _, _, | |
341 /*fin=*/false, _)); | |
342 EXPECT_CALL(checkpoint, Call(0)); | |
343 EXPECT_CALL(*spdy_framer_visitor_, | |
344 OnDataFrameHeader(stream_id, _, true)); | |
345 EXPECT_CALL(*spdy_framer_visitor_, OnStreamFrameData(stream_id, _, _)) | |
346 .Times(1) | |
347 .WillOnce(DoAll(SaveArg<1>(&actual_data), SaveArg<2>(&actual_size))); | |
348 EXPECT_CALL(*spdy_framer_visitor_, OnStreamFrameData(stream_id, NULL, 0)) | |
349 .Times(1); | |
350 } | |
351 | |
352 std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); | |
353 DataFrame* df = *i++; | |
354 spdy_framer_->ProcessInput(df->data, df->size); | |
355 checkpoint.Call(0); | |
356 df = *i++; | |
357 spdy_framer_->ProcessInput(df->data, df->size); | |
358 | |
359 ASSERT_EQ(2, spdy_framer_->frames_received()); | |
360 ASSERT_EQ(2u, spdy_framer_visitor_->actual_header_block_.size()); | |
361 ASSERT_EQ("404 Not Found", | |
362 spdy_framer_visitor_->actual_header_block_[":status"]); | |
363 ASSERT_EQ("HTTP/1.1", spdy_framer_visitor_->actual_header_block_[":version"]); | |
364 ASSERT_EQ("wtf?", StringPiece(actual_data, actual_size)); | |
365 } | |
366 | |
367 TEST_F(SpdySMProxyTest, SendSynReply) { | |
368 uint32_t stream_id = 82; | |
369 BalsaHeaders headers; | |
370 headers.AppendHeader("key1", "value1"); | |
371 headers.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "OK"); | |
372 | |
373 interface_->SendSynReply(stream_id, headers); | |
374 | |
375 ASSERT_EQ(1u, connection_->output_list()->size()); | |
376 std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); | |
377 DataFrame* df = *i++; | |
378 | |
379 { | |
380 InSequence s; | |
381 EXPECT_CALL(*spdy_framer_visitor_, | |
382 OnHeadersMock(stream_id, /*has_priority=*/false, _, _, _, | |
383 /*fin=*/false, _)); | |
384 } | |
385 | |
386 spdy_framer_->ProcessInput(df->data, df->size); | |
387 ASSERT_EQ(1, spdy_framer_->frames_received()); | |
388 ASSERT_EQ(3u, spdy_framer_visitor_->actual_header_block_.size()); | |
389 ASSERT_EQ("200 OK", spdy_framer_visitor_->actual_header_block_[":status"]); | |
390 ASSERT_EQ("HTTP/1.1", spdy_framer_visitor_->actual_header_block_[":version"]); | |
391 ASSERT_EQ("value1", spdy_framer_visitor_->actual_header_block_["key1"]); | |
392 } | |
393 | |
394 TEST_F(SpdySMProxyTest, SendDataFrame) { | |
395 uint32_t stream_id = 133; | |
396 SpdyDataFlags flags = DATA_FLAG_NONE; | |
397 const char* actual_data; | |
398 size_t actual_size; | |
399 | |
400 interface_->SendDataFrame(stream_id, "hello", 5, flags, true); | |
401 | |
402 ASSERT_EQ(1u, connection_->output_list()->size()); | |
403 std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); | |
404 DataFrame* df = *i++; | |
405 | |
406 { | |
407 InSequence s; | |
408 EXPECT_CALL(*spdy_framer_visitor_, | |
409 OnDataFrameHeader(stream_id, _, false)); | |
410 EXPECT_CALL(*spdy_framer_visitor_, OnStreamFrameData(stream_id, _, _)) | |
411 .WillOnce(DoAll(SaveArg<1>(&actual_data), SaveArg<2>(&actual_size))); | |
412 } | |
413 | |
414 spdy_framer_->ProcessInput(df->data, df->size); | |
415 ASSERT_EQ(1, spdy_framer_->frames_received()); | |
416 ASSERT_EQ("hello", StringPiece(actual_data, actual_size)); | |
417 } | |
418 | |
419 TEST_F(SpdySMProxyTest, SendLongDataFrame) { | |
420 uint32_t stream_id = 133; | |
421 SpdyDataFlags flags = DATA_FLAG_NONE; | |
422 const char* actual_data; | |
423 size_t actual_size; | |
424 | |
425 std::string data = std::string(kSpdySegmentSize, 'a') + | |
426 std::string(kSpdySegmentSize, 'b') + "c"; | |
427 interface_->SendDataFrame(stream_id, data.data(), data.size(), flags, true); | |
428 | |
429 { | |
430 InSequence s; | |
431 for (int i = 0; i < 3; ++i) { | |
432 EXPECT_CALL(*spdy_framer_visitor_, | |
433 OnDataFrameHeader(stream_id, _, false)); | |
434 EXPECT_CALL(*spdy_framer_visitor_, OnStreamFrameData(stream_id, _, _)) | |
435 .WillOnce( | |
436 DoAll(SaveArg<1>(&actual_data), SaveArg<2>(&actual_size))); | |
437 } | |
438 } | |
439 | |
440 ASSERT_EQ(3u, connection_->output_list()->size()); | |
441 std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); | |
442 DataFrame* df = *i++; | |
443 spdy_framer_->ProcessInput(df->data, df->size); | |
444 ASSERT_EQ(std::string(kSpdySegmentSize, 'a'), | |
445 StringPiece(actual_data, actual_size)); | |
446 | |
447 df = *i++; | |
448 spdy_framer_->ProcessInput(df->data, df->size); | |
449 ASSERT_EQ(std::string(kSpdySegmentSize, 'b'), | |
450 StringPiece(actual_data, actual_size)); | |
451 | |
452 df = *i++; | |
453 spdy_framer_->ProcessInput(df->data, df->size); | |
454 ASSERT_EQ("c", StringPiece(actual_data, actual_size)); | |
455 } | |
456 | |
457 TEST_F(SpdySMServerTest, NewStream) { | |
458 uint32_t stream_id = 13; | |
459 std::string filename = "foobar"; | |
460 | |
461 { | |
462 BalsaHeaders headers; | |
463 memory_cache_->InsertFile(&headers, filename, ""); | |
464 } | |
465 | |
466 interface_->NewStream(stream_id, 0, filename); | |
467 ASSERT_TRUE(HasStream(stream_id)); | |
468 } | |
469 | |
470 TEST_F(SpdySMServerTest, NewStreamError) { | |
471 uint32_t stream_id = 82; | |
472 const char* actual_data; | |
473 size_t actual_size; | |
474 testing::MockFunction<void(int)> checkpoint; // NOLINT | |
475 | |
476 interface_->NewStream(stream_id, 0, "nonexistingfile"); | |
477 | |
478 ASSERT_EQ(2u, connection_->output_list()->size()); | |
479 | |
480 { | |
481 InSequence s; | |
482 EXPECT_CALL(*spdy_framer_visitor_, | |
483 OnHeadersMock(stream_id, /*has_priority=*/false, _, _, _, | |
484 /*fin=*/false, _)); | |
485 EXPECT_CALL(checkpoint, Call(0)); | |
486 EXPECT_CALL(*spdy_framer_visitor_, | |
487 OnDataFrameHeader(stream_id, _, true)); | |
488 EXPECT_CALL(*spdy_framer_visitor_, OnStreamFrameData(stream_id, _, _)) | |
489 .Times(1) | |
490 .WillOnce(DoAll(SaveArg<1>(&actual_data), SaveArg<2>(&actual_size))); | |
491 EXPECT_CALL(*spdy_framer_visitor_, OnStreamFrameData(stream_id, NULL, 0)) | |
492 .Times(1); | |
493 } | |
494 | |
495 std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); | |
496 DataFrame* df = *i++; | |
497 spdy_framer_->ProcessInput(df->data, df->size); | |
498 checkpoint.Call(0); | |
499 df = *i++; | |
500 spdy_framer_->ProcessInput(df->data, df->size); | |
501 | |
502 ASSERT_EQ(2, spdy_framer_->frames_received()); | |
503 ASSERT_EQ(2u, spdy_framer_visitor_->actual_header_block_.size()); | |
504 ASSERT_EQ("404 Not Found", | |
505 spdy_framer_visitor_->actual_header_block_["status"]); | |
506 ASSERT_EQ("HTTP/1.1", spdy_framer_visitor_->actual_header_block_["version"]); | |
507 ASSERT_EQ("wtf?", StringPiece(actual_data, actual_size)); | |
508 } | |
509 | |
510 } // namespace | |
511 | |
512 } // namespace net | |
OLD | NEW |