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/http_interface.h" 6 7 #include <list> 8 9 #include "base/memory/scoped_ptr.h" 10 #include "base/stl_util.h" 11 #include "base/strings/string_piece.h" 12 #include "net/tools/balsa/balsa_enums.h" 13 #include "net/tools/balsa/balsa_frame.h" 14 #include "net/tools/balsa/balsa_headers.h" 15 #include "net/tools/flip_server/flip_config.h" 16 #include "net/tools/flip_server/flip_test_utils.h" 17 #include "net/tools/flip_server/mem_cache.h" 18 #include "testing/gmock/include/gmock/gmock.h" 19 #include "testing/gtest/include/gtest/gtest.h" 20 21 namespace net { 22 23 using ::base::StringPiece; 24 using ::testing::_; 25 using ::testing::InSequence; 26 27 namespace { 28 29 class MockSMConnection : public SMConnection { 30 public: 31 MockSMConnection(EpollServer* epoll_server, 32 SSLState* ssl_state, 33 MemoryCache* memory_cache, 34 FlipAcceptor* acceptor, 35 std::string log_prefix) 36 : SMConnection(epoll_server, 37 ssl_state, 38 memory_cache, 39 acceptor, 40 log_prefix) {} 41 42 MOCK_METHOD0(Cleanup, void()); 43 MOCK_METHOD8(InitSMConnection, 44 void(SMConnectionPoolInterface*, 45 SMInterface*, 46 EpollServer*, 47 int, 48 std::string, 49 std::string, 50 std::string, 51 bool)); 52 }; 53 54 class FlipHttpSMTest : public ::testing::Test { 55 public: 56 explicit FlipHttpSMTest(FlipHandlerType type = FLIP_HANDLER_PROXY) { 57 SSLState* ssl_state = NULL; 58 mock_another_interface_.reset(new MockSMInterface); 59 memory_cache_.reset(new MemoryCache); 60 acceptor_.reset(new FlipAcceptor(type, 61 "127.0.0.1", 62 "8941", 63 "ssl_cert_filename", 64 "ssl_key_filename", 65 "127.0.0.1", 66 "8942", 67 "127.0.0.1", 68 "8943", 69 1, 70 0, 71 true, 72 1, 73 false, 74 true, 75 NULL)); 76 epoll_server_.reset(new EpollServer); 77 connection_.reset(new MockSMConnection(epoll_server_.get(), 78 ssl_state, 79 memory_cache_.get(), 80 acceptor_.get(), 81 "log_prefix")); 82 83 interface_.reset(new HttpSM(connection_.get(), 84 mock_another_interface_.get(), 85 memory_cache_.get(), 86 acceptor_.get())); 87 } 88 89 virtual void TearDown() OVERRIDE { 90 if (acceptor_->listen_fd_ >= 0) { 91 epoll_server_->UnregisterFD(acceptor_->listen_fd_); 92 close(acceptor_->listen_fd_); 93 acceptor_->listen_fd_ = -1; 94 } 95 STLDeleteElements(connection_->output_list()); 96 } 97 98 bool HasStream(uint32 stream_id) { 99 return interface_->output_ordering().ExistsInPriorityMaps(stream_id); 100 } 101 102 protected: 103 scoped_ptr<MockSMInterface> mock_another_interface_; 104 scoped_ptr<MemoryCache> memory_cache_; 105 scoped_ptr<FlipAcceptor> acceptor_; 106 scoped_ptr<EpollServer> epoll_server_; 107 scoped_ptr<MockSMConnection> connection_; 108 scoped_ptr<HttpSM> interface_; 109 }; 110 111 class FlipHttpSMProxyTest : public FlipHttpSMTest { 112 public: 113 FlipHttpSMProxyTest() : FlipHttpSMTest(FLIP_HANDLER_PROXY) {} 114 virtual ~FlipHttpSMProxyTest() {} 115 }; 116 117 class FlipHttpSMHttpTest : public FlipHttpSMTest { 118 public: 119 FlipHttpSMHttpTest() : FlipHttpSMTest(FLIP_HANDLER_HTTP_SERVER) {} 120 virtual ~FlipHttpSMHttpTest() {} 121 }; 122 123 class FlipHttpSMSpdyTest : public FlipHttpSMTest { 124 public: 125 FlipHttpSMSpdyTest() : FlipHttpSMTest(FLIP_HANDLER_SPDY_SERVER) {} 126 virtual ~FlipHttpSMSpdyTest() {} 127 }; 128 129 TEST_F(FlipHttpSMTest, Construct) { 130 ASSERT_FALSE(interface_->spdy_framer()->is_request()); 131 } 132 133 TEST_F(FlipHttpSMTest, AddToOutputOrder) { 134 uint32 stream_id = 13; 135 MemCacheIter mci; 136 mci.stream_id = stream_id; 137 138 { 139 BalsaHeaders headers; 140 std::string filename = "foobar"; 141 memory_cache_->InsertFile(&headers, filename, ""); 142 mci.file_data = memory_cache_->GetFileData(filename); 143 } 144 145 interface_->AddToOutputOrder(mci); 146 ASSERT_TRUE(HasStream(stream_id)); 147 } 148 149 TEST_F(FlipHttpSMTest, InitSMInterface) { 150 scoped_ptr<MockSMInterface> mock(new MockSMInterface); 151 { 152 InSequence s; 153 EXPECT_CALL(*mock_another_interface_, SendEOF(_)); 154 EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_)); 155 EXPECT_CALL(*mock, SendEOF(_)); 156 EXPECT_CALL(*mock, ResetForNewInterface(_)); 157 } 158 159 interface_->ResetForNewConnection(); 160 interface_->InitSMInterface(mock.get(), 0); 161 interface_->ResetForNewConnection(); 162 } 163 164 TEST_F(FlipHttpSMTest, InitSMConnection) { 165 EXPECT_CALL(*connection_, InitSMConnection(_, _, _, _, _, _, _, _)); 166 167 interface_->InitSMConnection(NULL, NULL, NULL, 0, "", "", "", false); 168 } 169 170 TEST_F(FlipHttpSMTest, ProcessReadInput) { 171 std::string data = 172 "HTTP/1.1 200 OK\r\n" 173 "Content-Length: 14\r\n\r\n" 174 "hello, world\r\n"; 175 testing::MockFunction<void(int)> checkpoint; // NOLINT 176 { 177 InSequence s; 178 EXPECT_CALL(*mock_another_interface_, SendSynReply(_, _)); 179 EXPECT_CALL(checkpoint, Call(0)); 180 EXPECT_CALL(*mock_another_interface_, SendDataFrame(_, _, _, _, _)); 181 EXPECT_CALL(*mock_another_interface_, SendEOF(_)); 182 } 183 184 ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE, 185 interface_->spdy_framer()->ParseState()); 186 187 size_t read = interface_->ProcessReadInput(data.data(), data.size()); 188 ASSERT_EQ(39u, read); 189 checkpoint.Call(0); 190 read += interface_->ProcessReadInput(&data.data()[read], data.size() - read); 191 ASSERT_EQ(data.size(), read); 192 ASSERT_EQ(BalsaFrameEnums::MESSAGE_FULLY_READ, 193 interface_->spdy_framer()->ParseState()); 194 ASSERT_TRUE(interface_->MessageFullyRead()); 195 } 196 197 TEST_F(FlipHttpSMTest, ProcessWriteInput) { 198 std::string data = "hello, world"; 199 interface_->ProcessWriteInput(data.data(), data.size()); 200 201 ASSERT_EQ(1u, connection_->output_list()->size()); 202 std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); 203 DataFrame* df = *i++; 204 ASSERT_EQ(data, StringPiece(df->data, df->size)); 205 ASSERT_EQ(connection_->output_list()->end(), i); 206 } 207 208 TEST_F(FlipHttpSMTest, Reset) { 209 std::string data = "HTTP/1.1 200 OK\r\n\r\n"; 210 testing::MockFunction<void(int)> checkpoint; // NOLINT 211 { 212 InSequence s; 213 EXPECT_CALL(*mock_another_interface_, SendSynReply(_, _)); 214 EXPECT_CALL(checkpoint, Call(0)); 215 } 216 217 ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE, 218 interface_->spdy_framer()->ParseState()); 219 220 interface_->ProcessReadInput(data.data(), data.size()); 221 checkpoint.Call(0); 222 ASSERT_FALSE(interface_->MessageFullyRead()); 223 ASSERT_EQ(BalsaFrameEnums::READING_UNTIL_CLOSE, 224 interface_->spdy_framer()->ParseState()); 225 226 interface_->Reset(); 227 ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE, 228 interface_->spdy_framer()->ParseState()); 229 } 230 231 TEST_F(FlipHttpSMTest, ResetForNewConnection) { 232 std::string data = "HTTP/1.1 200 OK\r\n\r\n"; 233 testing::MockFunction<void(int)> checkpoint; // NOLINT 234 { 235 InSequence s; 236 EXPECT_CALL(*mock_another_interface_, SendSynReply(_, _)); 237 EXPECT_CALL(checkpoint, Call(0)); 238 EXPECT_CALL(*mock_another_interface_, SendEOF(_)); 239 EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_)); 240 } 241 242 ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE, 243 interface_->spdy_framer()->ParseState()); 244 245 interface_->ProcessReadInput(data.data(), data.size()); 246 checkpoint.Call(0); 247 ASSERT_FALSE(interface_->MessageFullyRead()); 248 ASSERT_EQ(BalsaFrameEnums::READING_UNTIL_CLOSE, 249 interface_->spdy_framer()->ParseState()); 250 251 interface_->ResetForNewConnection(); 252 ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE, 253 interface_->spdy_framer()->ParseState()); 254 } 255 256 TEST_F(FlipHttpSMTest, NewStream) { 257 uint32 stream_id = 4; 258 { 259 BalsaHeaders headers; 260 std::string filename = "foobar"; 261 memory_cache_->InsertFile(&headers, filename, ""); 262 } 263 264 interface_->NewStream(stream_id, 1, "foobar"); 265 ASSERT_TRUE(HasStream(stream_id)); 266 } 267 268 TEST_F(FlipHttpSMTest, NewStreamError) { 269 std::string syn_reply = 270 "HTTP/1.1 404 Not Found\r\n" 271 "transfer-encoding: chunked\r\n\r\n"; 272 std::string body = "e\r\npage not found\r\n"; 273 uint32 stream_id = 4; 274 275 ASSERT_FALSE(HasStream(stream_id)); 276 interface_->NewStream(stream_id, 1, "foobar"); 277 278 ASSERT_EQ(3u, connection_->output_list()->size()); 279 std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); 280 DataFrame* df = *i++; 281 ASSERT_EQ(syn_reply, StringPiece(df->data, df->size)); 282 df = *i++; 283 ASSERT_EQ(body, StringPiece(df->data, df->size)); 284 df = *i++; 285 ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size)); 286 ASSERT_FALSE(HasStream(stream_id)); 287 } 288 289 TEST_F(FlipHttpSMTest, SendErrorNotFound) { 290 std::string syn_reply = 291 "HTTP/1.1 404 Not Found\r\n" 292 "transfer-encoding: chunked\r\n\r\n"; 293 std::string body = "e\r\npage not found\r\n"; 294 uint32 stream_id = 13; 295 MemCacheIter mci; 296 mci.stream_id = stream_id; 297 298 { 299 BalsaHeaders headers; 300 std::string filename = "foobar"; 301 memory_cache_->InsertFile(&headers, filename, ""); 302 mci.file_data = memory_cache_->GetFileData(filename); 303 } 304 305 interface_->AddToOutputOrder(mci); 306 ASSERT_TRUE(HasStream(stream_id)); 307 interface_->SendErrorNotFound(stream_id); 308 309 ASSERT_EQ(3u, connection_->output_list()->size()); 310 std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); 311 DataFrame* df = *i++; 312 ASSERT_EQ(syn_reply, StringPiece(df->data, df->size)); 313 df = *i++; 314 ASSERT_EQ(body, StringPiece(df->data, df->size)); 315 df = *i++; 316 ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size)); 317 ASSERT_FALSE(HasStream(stream_id)); 318 } 319 320 TEST_F(FlipHttpSMTest, SendSynStream) { 321 std::string expected = 322 "GET / HTTP/1.0\r\n" 323 "key1: value1\r\n\r\n"; 324 BalsaHeaders headers; 325 headers.SetResponseFirstlineFromStringPieces("GET", "/path", "HTTP/1.0"); 326 headers.AppendHeader("key1", "value1"); 327 interface_->SendSynStream(18, headers); 328 329 // TODO(yhirano): Is this behavior correct? 330 ASSERT_EQ(0u, connection_->output_list()->size()); 331 } 332 333 TEST_F(FlipHttpSMTest, SendSynReply) { 334 std::string expected = 335 "HTTP/1.1 200 OK\r\n" 336 "key1: value1\r\n\r\n"; 337 BalsaHeaders headers; 338 headers.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "OK"); 339 headers.AppendHeader("key1", "value1"); 340 interface_->SendSynReply(18, headers); 341 342 ASSERT_EQ(1u, connection_->output_list()->size()); 343 DataFrame* df = connection_->output_list()->front(); 344 ASSERT_EQ(expected, StringPiece(df->data, df->size)); 345 } 346 347 TEST_F(FlipHttpSMTest, SendDataFrame) { 348 std::string data = "foo bar baz"; 349 interface_->SendDataFrame(12, data.data(), data.size(), 0, false); 350 351 ASSERT_EQ(1u, connection_->output_list()->size()); 352 DataFrame* df = connection_->output_list()->front(); 353 ASSERT_EQ("b\r\nfoo bar baz\r\n", StringPiece(df->data, df->size)); 354 } 355 356 TEST_F(FlipHttpSMProxyTest, ProcessBodyData) { 357 BalsaVisitorInterface* visitor = interface_.get(); 358 std::string data = "hello, world"; 359 { 360 InSequence s; 361 EXPECT_CALL(*mock_another_interface_, 362 SendDataFrame(0, data.data(), data.size(), 0, false)); 363 } 364 visitor->ProcessBodyData(data.data(), data.size()); 365 } 366 367 // -- 368 // FlipHttpSMProxyTest 369 370 TEST_F(FlipHttpSMProxyTest, ProcessHeaders) { 371 BalsaVisitorInterface* visitor = interface_.get(); 372 { 373 InSequence s; 374 EXPECT_CALL(*mock_another_interface_, SendSynReply(0, _)); 375 } 376 BalsaHeaders headers; 377 visitor->ProcessHeaders(headers); 378 } 379 380 TEST_F(FlipHttpSMProxyTest, MessageDone) { 381 BalsaVisitorInterface* visitor = interface_.get(); 382 { 383 InSequence s; 384 EXPECT_CALL(*mock_another_interface_, SendEOF(0)); 385 } 386 visitor->MessageDone(); 387 } 388 389 TEST_F(FlipHttpSMProxyTest, Cleanup) { 390 EXPECT_CALL(*connection_, Cleanup()).Times(0); 391 interface_->Cleanup(); 392 } 393 394 TEST_F(FlipHttpSMProxyTest, SendEOF) { 395 { 396 InSequence s; 397 EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_)); 398 } 399 interface_->SendEOF(32); 400 ASSERT_EQ(1u, connection_->output_list()->size()); 401 DataFrame* df = connection_->output_list()->front(); 402 ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size)); 403 } 404 405 // -- 406 // FlipHttpSMHttpTest 407 408 TEST_F(FlipHttpSMHttpTest, ProcessHeaders) { 409 BalsaVisitorInterface* visitor = interface_.get(); 410 { 411 BalsaHeaders headers; 412 std::string filename = "GET_/path/file"; 413 memory_cache_->InsertFile(&headers, filename, ""); 414 } 415 416 BalsaHeaders headers; 417 headers.AppendHeader("Host", "example.com"); 418 headers.SetRequestFirstlineFromStringPieces("GET", "/path/file", "HTTP/1.0"); 419 uint32 stream_id = 133; 420 interface_->SetStreamID(stream_id); 421 ASSERT_FALSE(HasStream(stream_id)); 422 visitor->ProcessHeaders(headers); 423 ASSERT_TRUE(HasStream(stream_id)); 424 } 425 426 TEST_F(FlipHttpSMHttpTest, MessageDone) { 427 BalsaVisitorInterface* visitor = interface_.get(); 428 { 429 InSequence s; 430 EXPECT_CALL(*mock_another_interface_, SendEOF(0)).Times(0); 431 } 432 visitor->MessageDone(); 433 } 434 435 TEST_F(FlipHttpSMHttpTest, Cleanup) { 436 EXPECT_CALL(*connection_, Cleanup()).Times(0); 437 interface_->Cleanup(); 438 } 439 440 TEST_F(FlipHttpSMHttpTest, SendEOF) { 441 { 442 InSequence s; 443 EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_)).Times(0); 444 } 445 interface_->SendEOF(32); 446 ASSERT_EQ(1u, connection_->output_list()->size()); 447 DataFrame* df = connection_->output_list()->front(); 448 ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size)); 449 } 450 451 // -- 452 // FlipHttpSMSpdyTest 453 454 TEST_F(FlipHttpSMSpdyTest, ProcessHeaders) { 455 BalsaVisitorInterface* visitor = interface_.get(); 456 { 457 InSequence s; 458 EXPECT_CALL(*mock_another_interface_, SendSynReply(0, _)); 459 } 460 BalsaHeaders headers; 461 visitor->ProcessHeaders(headers); 462 } 463 464 TEST_F(FlipHttpSMSpdyTest, MessageDone) { 465 BalsaVisitorInterface* visitor = interface_.get(); 466 { 467 InSequence s; 468 EXPECT_CALL(*mock_another_interface_, SendEOF(0)).Times(0); 469 } 470 visitor->MessageDone(); 471 } 472 473 TEST_F(FlipHttpSMSpdyTest, Cleanup) { 474 EXPECT_CALL(*connection_, Cleanup()).Times(0); 475 interface_->Cleanup(); 476 } 477 478 TEST_F(FlipHttpSMSpdyTest, SendEOF) { 479 { 480 InSequence s; 481 EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_)).Times(0); 482 } 483 interface_->SendEOF(32); 484 ASSERT_EQ(1u, connection_->output_list()->size()); 485 DataFrame* df = connection_->output_list()->front(); 486 ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size)); 487 } 488 489 } // namespace 490 491 } // namespace net 492