1 // Copyright (c) 2009 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/flip/flip_network_transaction.h" 6 7 #include "base/basictypes.h" 8 #include "base/ref_counted.h" 9 #include "net/base/completion_callback.h" 10 #include "net/base/load_log_unittest.h" 11 #include "net/base/mock_host_resolver.h" 12 #include "net/base/ssl_config_service_defaults.h" 13 #include "net/base/test_completion_callback.h" 14 #include "net/base/upload_data.h" 15 #include "net/flip/flip_protocol.h" 16 #include "net/http/http_network_session.h" 17 #include "net/http/http_transaction_unittest.h" 18 #include "net/proxy/proxy_config_service_fixed.h" 19 #include "net/socket/socket_test_util.h" 20 #include "testing/platform_test.h" 21 22 //----------------------------------------------------------------------------- 23 24 namespace net { 25 26 namespace { 27 28 // Create a proxy service which fails on all requests (falls back to direct). 29 ProxyService* CreateNullProxyService() { 30 return ProxyService::CreateNull(); 31 } 32 33 // Helper to manage the lifetimes of the dependencies for a 34 // FlipNetworkTransaction. 35 class SessionDependencies { 36 public: 37 // Default set of dependencies -- "null" proxy service. 38 SessionDependencies() 39 : host_resolver(new MockHostResolver), 40 proxy_service(CreateNullProxyService()), 41 ssl_config_service(new SSLConfigServiceDefaults), 42 flip_session_pool(new FlipSessionPool) { 43 // Note: The CancelledTransaction test does cleanup by running all tasks 44 // in the message loop (RunAllPending). Unfortunately, that doesn't clean 45 // up tasks on the host resolver thread; and TCPConnectJob is currently 46 // not cancellable. Using synchronous lookups allows the test to shutdown 47 // cleanly. Until we have cancellable TCPConnectJobs, use synchronous 48 // lookups. 49 host_resolver->set_synchronous_mode(true); 50 } 51 52 // Custom proxy service dependency. 53 explicit SessionDependencies(ProxyService* proxy_service) 54 : host_resolver(new MockHostResolver), 55 proxy_service(proxy_service), 56 ssl_config_service(new SSLConfigServiceDefaults), 57 flip_session_pool(new FlipSessionPool) {} 58 59 scoped_refptr<MockHostResolverBase> host_resolver; 60 scoped_refptr<ProxyService> proxy_service; 61 scoped_refptr<SSLConfigService> ssl_config_service; 62 MockClientSocketFactory socket_factory; 63 scoped_refptr<FlipSessionPool> flip_session_pool; 64 }; 65 66 ProxyService* CreateFixedProxyService(const std::string& proxy) { 67 ProxyConfig proxy_config; 68 proxy_config.proxy_rules.ParseFromString(proxy); 69 return ProxyService::CreateFixed(proxy_config); 70 } 71 72 73 HttpNetworkSession* CreateSession(SessionDependencies* session_deps) { 74 return new HttpNetworkSession(NULL, 75 session_deps->host_resolver, 76 session_deps->proxy_service, 77 &session_deps->socket_factory, 78 session_deps->ssl_config_service, 79 session_deps->flip_session_pool); 80 } 81 82 // Chop a frame into an array of MockWrites. 83 // |data| is the frame to chop. 84 // |length| is the length of the frame to chop. 85 // |num_chunks| is the number of chunks to create. 86 MockWrite* ChopFrame(const char* data, int length, int num_chunks) { 87 MockWrite* chunks = new MockWrite[num_chunks + 1]; 88 int chunk_size = length / num_chunks; 89 for (int index = 0; index < num_chunks; index++) { 90 const char* ptr = data + (index * chunk_size); 91 if (index == num_chunks - 1) 92 chunk_size += length % chunk_size; // The last chunk takes the remainder. 93 chunks[index] = MockWrite(true, ptr, chunk_size); 94 } 95 chunks[num_chunks] = MockWrite(true, 0, 0); 96 return chunks; 97 } 98 99 // ---------------------------------------------------------------------------- 100 101 static const unsigned char kGetSyn[] = { 102 0x80, 0x01, 0x00, 0x01, // header 103 0x01, 0x00, 0x00, 0x45, // FIN, len 104 0x00, 0x00, 0x00, 0x01, // stream id 105 0xc0, 0x00, 0x00, 0x03, // 4 headers 106 0x00, 0x06, 'm', 'e', 't', 'h', 'o', 'd', 107 0x00, 0x03, 'G', 'E', 'T', 108 0x00, 0x03, 'u', 'r', 'l', 109 0x00, 0x16, 'h', 't', 't', 'p', ':', '/', '/', 'w', 'w', 'w', 110 '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'c', 'o', 111 'm', '/', 112 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', 113 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', 114 }; 115 116 static const unsigned char kGetSynCompressed[] = { 117 0x80, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x43, 118 0x00, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x78, 0xbb, 119 0xdf, 0xa2, 0x51, 0xb2, 0x62, 0x60, 0x66, 0x60, 120 0xcb, 0x05, 0xe6, 0xc3, 0xfc, 0x14, 0x06, 0x66, 121 0x77, 0xd7, 0x10, 0x06, 0x66, 0x90, 0xa0, 0x58, 122 0x46, 0x49, 0x49, 0x81, 0x95, 0xbe, 0x3e, 0x30, 123 0xe2, 0xf5, 0xd2, 0xf3, 0xf3, 0xd3, 0x73, 0x52, 124 0xf5, 0x92, 0xf3, 0x73, 0xf5, 0x19, 0xd8, 0xa1, 125 0x1a, 0x19, 0x38, 0x60, 0xe6, 0x01, 0x00, 0x00, 126 0x00, 0xff, 0xff 127 }; 128 129 static const unsigned char kGetSynReply[] = { 130 0x80, 0x01, 0x00, 0x02, // header 131 0x00, 0x00, 0x00, 0x45, 132 0x00, 0x00, 0x00, 0x01, 133 0x00, 0x00, 0x00, 0x04, // 4 headers 134 0x00, 0x05, 'h', 'e', 'l', 'l', 'o', // "hello" 135 0x00, 0x03, 'b', 'y', 'e', // "bye" 136 0x00, 0x06, 's', 't', 'a', 't', 'u', 's', // "status" 137 0x00, 0x03, '2', '0', '0', // "200" 138 0x00, 0x03, 'u', 'r', 'l', // "url" 139 0x00, 0x0a, '/', 'i', 'n', 'd', 'e', 'x', '.', 'p', 'h', 'p', // "/index... 140 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', // "version" 141 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', // "HTTP/1.1" 142 }; 143 144 static const unsigned char kGetBodyFrame[] = { 145 0x00, 0x00, 0x00, 0x01, // header 146 0x01, 0x00, 0x00, 0x06, // FIN, length 147 'h', 'e', 'l', 'l', 'o', '!', // "hello" 148 }; 149 150 static const unsigned char kPostSyn[] = { 151 0x80, 0x01, 0x00, 0x01, // header 152 0x00, 0x00, 0x00, 0x46, // flags, len 153 0x00, 0x00, 0x00, 0x01, // stream id 154 0xc0, 0x00, 0x00, 0x03, // 4 headers 155 0x00, 0x06, 'm', 'e', 't', 'h', 'o', 'd', 156 0x00, 0x04, 'P', 'O', 'S', 'T', 157 0x00, 0x03, 'u', 'r', 'l', 158 0x00, 0x16, 'h', 't', 't', 'p', ':', '/', '/', 'w', 'w', 'w', 159 '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'c', 'o', 160 'm', '/', 161 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', 162 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', 163 }; 164 165 static const unsigned char kPostUploadFrame[] = { 166 0x00, 0x00, 0x00, 0x01, // header 167 0x01, 0x00, 0x00, 0x0c, // FIN flag 168 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\0' 169 }; 170 171 // The response 172 static const unsigned char kPostSynReply[] = { 173 0x80, 0x01, 0x00, 0x02, // header 174 0x00, 0x00, 0x00, 0x45, 175 0x00, 0x00, 0x00, 0x01, 176 0x00, 0x00, 0x00, 0x04, // 4 headers 177 0x00, 0x05, 'h', 'e', 'l', 'l', 'o', // "hello" 178 0x00, 0x03, 'b', 'y', 'e', // "bye" 179 0x00, 0x06, 's', 't', 'a', 't', 'u', 's', // "status" 180 0x00, 0x03, '2', '0', '0', // "200" 181 0x00, 0x03, 'u', 'r', 'l', // "url" 182 // "/index.php" 183 0x00, 0x0a, '/', 'i', 'n', 'd', 'e', 'x', '.', 'p', 'h', 'p', 184 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', // "version" 185 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', // "HTTP/1.1" 186 }; 187 188 static const unsigned char kPostBodyFrame[] = { 189 0x00, 0x00, 0x00, 0x01, // header 190 0x01, 0x00, 0x00, 0x06, // FIN, length 191 'h', 'e', 'l', 'l', 'o', '!', // "hello" 192 }; 193 194 } // namespace 195 196 // A DataProvider where the client must write a request before the reads (e.g. 197 // the response) will complete. 198 class DelayedSocketData : public StaticSocketDataProvider, 199 public base::RefCounted<DelayedSocketData> { 200 public: 201 // |reads| the list of MockRead completions. 202 // |write_delay| the number of MockWrites to complete before allowing 203 // a MockRead to complete. 204 // |writes| the list of MockWrite completions. 205 // Note: All MockReads and MockWrites must be async. 206 // Note: The MockRead and MockWrite lists musts end with a EOF 207 // e.g. a MockRead(true, 0, 0); 208 DelayedSocketData(MockRead* reads, int write_delay, MockWrite* writes) 209 : StaticSocketDataProvider(reads, writes), 210 write_delay_(write_delay), 211 ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) { 212 DCHECK_GE(write_delay_, 0); 213 } 214 215 // |connect| the result for the connect phase. 216 // |reads| the list of MockRead completions. 217 // |write_delay| the number of MockWrites to complete before allowing 218 // a MockRead to complete. 219 // |writes| the list of MockWrite completions. 220 // Note: All MockReads and MockWrites must be async. 221 // Note: The MockRead and MockWrite lists musts end with a EOF 222 // e.g. a MockRead(true, 0, 0); 223 DelayedSocketData(const MockConnect& connect, MockRead* reads, 224 int write_delay, MockWrite* writes) 225 : StaticSocketDataProvider(reads, writes), 226 write_delay_(write_delay), 227 ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) { 228 DCHECK_GE(write_delay_, 0); 229 set_connect_data(connect); 230 } 231 232 virtual MockRead GetNextRead() { 233 if (write_delay_) 234 return MockRead(true, ERR_IO_PENDING); 235 return StaticSocketDataProvider::GetNextRead(); 236 } 237 238 virtual MockWriteResult OnWrite(const std::string& data) { 239 MockWriteResult rv = StaticSocketDataProvider::OnWrite(data); 240 // Now that our write has completed, we can allow reads to continue. 241 if (!--write_delay_) 242 MessageLoop::current()->PostDelayedTask(FROM_HERE, 243 factory_.NewRunnableMethod(&DelayedSocketData::CompleteRead), 100); 244 return rv; 245 } 246 247 virtual void Reset() { 248 set_socket(NULL); 249 factory_.RevokeAll(); 250 StaticSocketDataProvider::Reset(); 251 } 252 253 void CompleteRead() { 254 if (socket()) 255 socket()->OnReadComplete(GetNextRead()); 256 } 257 258 private: 259 int write_delay_; 260 ScopedRunnableMethodFactory<DelayedSocketData> factory_; 261 }; 262 263 class FlipNetworkTransactionTest : public PlatformTest { 264 protected: 265 virtual void SetUp() { 266 // By default, all tests turn off compression. 267 EnableCompression(false); 268 } 269 270 virtual void TearDown() { 271 // Empty the current queue. 272 MessageLoop::current()->RunAllPending(); 273 PlatformTest::TearDown(); 274 } 275 276 void KeepAliveConnectionResendRequestTest(const MockRead& read_failure); 277 278 struct TransactionHelperResult { 279 int rv; 280 std::string status_line; 281 std::string response_data; 282 HttpResponseInfo response_info; 283 }; 284 285 void EnableCompression(bool enabled) { 286 flip::FlipFramer::set_enable_compression_default(enabled); 287 } 288 289 TransactionHelperResult TransactionHelper(const HttpRequestInfo& request, 290 DelayedSocketData* data, 291 LoadLog* log) { 292 TransactionHelperResult out; 293 294 // We disable SSL for this test. 295 FlipSession::SetSSLMode(false); 296 297 SessionDependencies session_deps; 298 scoped_ptr<FlipNetworkTransaction> trans( 299 new FlipNetworkTransaction(CreateSession(&session_deps))); 300 301 session_deps.socket_factory.AddSocketDataProvider(data); 302 303 TestCompletionCallback callback; 304 305 int rv = trans->Start(&request, &callback, log); 306 EXPECT_EQ(ERR_IO_PENDING, rv); 307 308 out.rv = callback.WaitForResult(); 309 if (out.rv != OK) 310 return out; 311 312 const HttpResponseInfo* response = trans->GetResponseInfo(); 313 EXPECT_TRUE(response->headers != NULL); 314 EXPECT_TRUE(response->was_fetched_via_spdy); 315 out.status_line = response->headers->GetStatusLine(); 316 out.response_info = *response; // Make a copy so we can verify. 317 318 rv = ReadTransaction(trans.get(), &out.response_data); 319 EXPECT_EQ(OK, rv); 320 321 // Verify that we consumed all test data. 322 EXPECT_TRUE(data->at_read_eof()); 323 EXPECT_TRUE(data->at_write_eof()); 324 325 return out; 326 } 327 328 void ConnectStatusHelperWithExpectedStatus(const MockRead& status, 329 int expected_status); 330 331 void ConnectStatusHelper(const MockRead& status); 332 }; 333 334 //----------------------------------------------------------------------------- 335 336 // Verify FlipNetworkTransaction constructor. 337 TEST_F(FlipNetworkTransactionTest, Constructor) { 338 SessionDependencies session_deps; 339 scoped_refptr<HttpNetworkSession> session = 340 CreateSession(&session_deps); 341 scoped_ptr<HttpTransaction> trans(new FlipNetworkTransaction(session)); 342 } 343 344 TEST_F(FlipNetworkTransactionTest, Get) { 345 MockWrite writes[] = { 346 MockWrite(true, reinterpret_cast<const char*>(kGetSyn), 347 arraysize(kGetSyn)), 348 MockWrite(true, 0, 0) // EOF 349 }; 350 351 MockRead reads[] = { 352 MockRead(true, reinterpret_cast<const char*>(kGetSynReply), 353 arraysize(kGetSynReply)), 354 MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), 355 arraysize(kGetBodyFrame)), 356 MockRead(true, 0, 0) // EOF 357 }; 358 359 HttpRequestInfo request; 360 request.method = "GET"; 361 request.url = GURL("http://www.google.com/"); 362 request.load_flags = 0; 363 scoped_refptr<DelayedSocketData> data( 364 new DelayedSocketData(reads, 1, writes)); 365 TransactionHelperResult out = TransactionHelper(request, data.get(), NULL); 366 EXPECT_EQ(OK, out.rv); 367 EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); 368 EXPECT_EQ("hello!", out.response_data); 369 } 370 371 // Test that a simple POST works. 372 TEST_F(FlipNetworkTransactionTest, Post) { 373 static const char upload[] = { "hello world" }; 374 375 // Setup the request 376 HttpRequestInfo request; 377 request.method = "POST"; 378 request.url = GURL("http://www.google.com/"); 379 request.upload_data = new UploadData(); 380 request.upload_data->AppendBytes(upload, sizeof(upload)); 381 382 MockWrite writes[] = { 383 MockWrite(true, reinterpret_cast<const char*>(kPostSyn), 384 arraysize(kPostSyn)), 385 MockWrite(true, reinterpret_cast<const char*>(kPostUploadFrame), 386 arraysize(kPostUploadFrame)), 387 MockWrite(true, 0, 0) // EOF 388 }; 389 390 MockRead reads[] = { 391 MockRead(true, reinterpret_cast<const char*>(kPostSynReply), 392 arraysize(kPostSynReply)), 393 MockRead(true, reinterpret_cast<const char*>(kPostBodyFrame), 394 arraysize(kPostBodyFrame)), 395 MockRead(true, 0, 0) // EOF 396 }; 397 398 scoped_refptr<DelayedSocketData> data( 399 new DelayedSocketData(reads, 2, writes)); 400 TransactionHelperResult out = TransactionHelper(request, data.get(), NULL); 401 EXPECT_EQ(OK, out.rv); 402 EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); 403 EXPECT_EQ("hello!", out.response_data); 404 } 405 406 // Test that a simple POST works. 407 TEST_F(FlipNetworkTransactionTest, EmptyPost) { 408 static const unsigned char kEmptyPostSyn[] = { 409 0x80, 0x01, 0x00, 0x01, // header 410 0x01, 0x00, 0x00, 0x46, // flags, len 411 0x00, 0x00, 0x00, 0x01, // stream id 412 0xc0, 0x00, 0x00, 0x03, // 4 headers 413 0x00, 0x06, 'm', 'e', 't', 'h', 'o', 'd', 414 0x00, 0x04, 'P', 'O', 'S', 'T', 415 0x00, 0x03, 'u', 'r', 'l', 416 0x00, 0x16, 'h', 't', 't', 'p', ':', '/', '/', 'w', 'w', 'w', 417 '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'c', 'o', 418 'm', '/', 419 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', 420 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', 421 }; 422 423 // Setup the request 424 HttpRequestInfo request; 425 request.method = "POST"; 426 request.url = GURL("http://www.google.com/"); 427 // Create an empty UploadData. 428 request.upload_data = new UploadData(); 429 430 MockWrite writes[] = { 431 MockWrite(true, reinterpret_cast<const char*>(kEmptyPostSyn), 432 arraysize(kEmptyPostSyn)), 433 MockWrite(true, 0, 0) // EOF 434 }; 435 436 MockRead reads[] = { 437 MockRead(true, reinterpret_cast<const char*>(kPostSynReply), 438 arraysize(kPostSynReply)), 439 MockRead(true, reinterpret_cast<const char*>(kPostBodyFrame), 440 arraysize(kGetBodyFrame)), 441 MockRead(true, 0, 0) // EOF 442 }; 443 444 scoped_refptr<DelayedSocketData> data( 445 new DelayedSocketData(reads, 1, writes)); 446 447 TransactionHelperResult out = TransactionHelper(request, data, NULL); 448 EXPECT_EQ(OK, out.rv); 449 EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); 450 EXPECT_EQ("hello!", out.response_data); 451 } 452 453 // Test that the transaction doesn't crash when we don't have a reply. 454 TEST_F(FlipNetworkTransactionTest, ResponseWithoutSynReply) { 455 MockRead reads[] = { 456 MockRead(true, reinterpret_cast<const char*>(kPostBodyFrame), 457 arraysize(kPostBodyFrame)), 458 MockRead(true, 0, 0) // EOF 459 }; 460 461 HttpRequestInfo request; 462 request.method = "GET"; 463 request.url = GURL("http://www.google.com/"); 464 request.load_flags = 0; 465 scoped_refptr<DelayedSocketData> data( 466 new DelayedSocketData(reads, 1, NULL)); 467 TransactionHelperResult out = TransactionHelper(request, data.get(), NULL); 468 EXPECT_EQ(ERR_SYN_REPLY_NOT_RECEIVED, out.rv); 469 } 470 471 TEST_F(FlipNetworkTransactionTest, CancelledTransaction) { 472 MockWrite writes[] = { 473 MockWrite(true, reinterpret_cast<const char*>(kGetSyn), 474 arraysize(kGetSyn)), 475 MockRead(true, 0, 0) // EOF 476 }; 477 478 MockRead reads[] = { 479 MockRead(true, reinterpret_cast<const char*>(kGetSynReply), 480 arraysize(kGetSynReply)), 481 // This following read isn't used by the test, except during the 482 // RunAllPending() call at the end since the FlipSession survives the 483 // FlipNetworkTransaction and still tries to continue Read()'ing. Any 484 // MockRead will do here. 485 MockRead(true, 0, 0) // EOF 486 }; 487 488 HttpRequestInfo request; 489 request.method = "GET"; 490 request.url = GURL("http://www.google.com/"); 491 request.load_flags = 0; 492 493 // We disable SSL for this test. 494 FlipSession::SetSSLMode(false); 495 496 SessionDependencies session_deps; 497 scoped_ptr<FlipNetworkTransaction> trans( 498 new FlipNetworkTransaction(CreateSession(&session_deps))); 499 500 StaticSocketDataProvider data(reads, writes); 501 session_deps.socket_factory.AddSocketDataProvider(&data); 502 503 TestCompletionCallback callback; 504 505 int rv = trans->Start(&request, &callback, NULL); 506 EXPECT_EQ(ERR_IO_PENDING, rv); 507 trans.reset(); // Cancel the transaction. 508 509 // Flush the MessageLoop while the SessionDependencies (in particular, the 510 // MockClientSocketFactory) are still alive. 511 MessageLoop::current()->RunAllPending(); 512 } 513 514 // Verify that various SynReply headers parse correctly through the 515 // HTTP layer. 516 TEST_F(FlipNetworkTransactionTest, SynReplyHeaders) { 517 // This uses a multi-valued cookie header. 518 static const unsigned char syn_reply1[] = { 519 0x80, 0x01, 0x00, 0x02, 520 0x00, 0x00, 0x00, 0x4c, 521 0x00, 0x00, 0x00, 0x01, 522 0x00, 0x00, 0x00, 0x04, 523 0x00, 0x06, 'c', 'o', 'o', 'k', 'i', 'e', 524 0x00, 0x09, 'v', 'a', 'l', '1', '\0', 525 'v', 'a', 'l', '2', 526 0x00, 0x06, 's', 't', 'a', 't', 'u', 's', 527 0x00, 0x03, '2', '0', '0', 528 0x00, 0x03, 'u', 'r', 'l', 529 0x00, 0x0a, '/', 'i', 'n', 'd', 'e', 'x', '.', 'p', 'h', 'p', 530 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', 531 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', 532 }; 533 534 // This is the minimalist set of headers. 535 static const unsigned char syn_reply2[] = { 536 0x80, 0x01, 0x00, 0x02, 537 0x00, 0x00, 0x00, 0x39, 538 0x00, 0x00, 0x00, 0x01, 539 0x00, 0x00, 0x00, 0x04, 540 0x00, 0x06, 's', 't', 'a', 't', 'u', 's', 541 0x00, 0x03, '2', '0', '0', 542 0x00, 0x03, 'u', 'r', 'l', 543 0x00, 0x0a, '/', 'i', 'n', 'd', 'e', 'x', '.', 'p', 'h', 'p', 544 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', 545 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', 546 }; 547 548 // Headers with a comma separated list. 549 static const unsigned char syn_reply3[] = { 550 0x80, 0x01, 0x00, 0x02, 551 0x00, 0x00, 0x00, 0x4c, 552 0x00, 0x00, 0x00, 0x01, 553 0x00, 0x00, 0x00, 0x04, 554 0x00, 0x06, 'c', 'o', 'o', 'k', 'i', 'e', 555 0x00, 0x09, 'v', 'a', 'l', '1', ',', 'v', 'a', 'l', '2', 556 0x00, 0x06, 's', 't', 'a', 't', 'u', 's', 557 0x00, 0x03, '2', '0', '0', 558 0x00, 0x03, 'u', 'r', 'l', 559 0x00, 0x0a, '/', 'i', 'n', 'd', 'e', 'x', '.', 'p', 'h', 'p', 560 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', 561 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', 562 }; 563 564 struct SynReplyTests { 565 const unsigned char* syn_reply; 566 int syn_reply_length; 567 const char* expected_headers; 568 } test_cases[] = { 569 // Test the case of a multi-valued cookie. When the value is delimited 570 // with NUL characters, it needs to be unfolded into multiple headers. 571 { syn_reply1, sizeof(syn_reply1), 572 "cookie: val1\n" 573 "cookie: val2\n" 574 "status: 200\n" 575 "url: /index.php\n" 576 "version: HTTP/1.1\n" 577 }, 578 // This is the simplest set of headers possible. 579 { syn_reply2, sizeof(syn_reply2), 580 "status: 200\n" 581 "url: /index.php\n" 582 "version: HTTP/1.1\n" 583 }, 584 // Test that a comma delimited list is NOT interpreted as a multi-value 585 // name/value pair. The comma-separated list is just a single value. 586 { syn_reply3, sizeof(syn_reply3), 587 "cookie: val1,val2\n" 588 "status: 200\n" 589 "url: /index.php\n" 590 "version: HTTP/1.1\n" 591 } 592 }; 593 594 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) { 595 MockWrite writes[] = { 596 MockWrite(true, reinterpret_cast<const char*>(kGetSyn), 597 arraysize(kGetSyn)), 598 MockWrite(true, 0, 0) // EOF 599 }; 600 601 MockRead reads[] = { 602 MockRead(true, reinterpret_cast<const char*>(test_cases[i].syn_reply), 603 test_cases[i].syn_reply_length), 604 MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), 605 arraysize(kGetBodyFrame)), 606 MockRead(true, 0, 0) // EOF 607 }; 608 609 HttpRequestInfo request; 610 request.method = "GET"; 611 request.url = GURL("http://www.google.com/"); 612 request.load_flags = 0; 613 scoped_refptr<DelayedSocketData> data( 614 new DelayedSocketData(reads, 1, writes)); 615 TransactionHelperResult out = TransactionHelper(request, data.get(), NULL); 616 EXPECT_EQ(OK, out.rv); 617 EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); 618 EXPECT_EQ("hello!", out.response_data); 619 620 scoped_refptr<HttpResponseHeaders> headers = out.response_info.headers; 621 EXPECT_TRUE(headers.get() != NULL); 622 void* iter = NULL; 623 std::string name, value, lines; 624 while (headers->EnumerateHeaderLines(&iter, &name, &value)) { 625 lines.append(name); 626 lines.append(": "); 627 lines.append(value); 628 lines.append("\n"); 629 } 630 EXPECT_EQ(std::string(test_cases[i].expected_headers), lines); 631 } 632 } 633 634 // Verify that we don't crash on invalid SynReply responses. 635 TEST_F(FlipNetworkTransactionTest, InvalidSynReply) { 636 static const unsigned char kSynReplyMissingStatus[] = { 637 0x80, 0x01, 0x00, 0x02, 638 0x00, 0x00, 0x00, 0x3f, 639 0x00, 0x00, 0x00, 0x01, 640 0x00, 0x00, 0x00, 0x04, 641 0x00, 0x06, 'c', 'o', 'o', 'k', 'i', 'e', 642 0x00, 0x09, 'v', 'a', 'l', '1', '\0', 643 'v', 'a', 'l', '2', 644 0x00, 0x03, 'u', 'r', 'l', 645 0x00, 0x0a, '/', 'i', 'n', 'd', 'e', 'x', '.', 'p', 'h', 'p', 646 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', 647 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', 648 }; 649 650 static const unsigned char kSynReplyMissingVersion[] = { 651 0x80, 0x01, 0x00, 0x02, 652 0x00, 0x00, 0x00, 0x26, 653 0x00, 0x00, 0x00, 0x01, 654 0x00, 0x00, 0x00, 0x04, 655 0x00, 0x06, 's', 't', 'a', 't', 'u', 's', 656 0x00, 0x03, '2', '0', '0', 657 0x00, 0x03, 'u', 'r', 'l', 658 0x00, 0x0a, '/', 'i', 'n', 'd', 'e', 'x', '.', 'p', 'h', 'p', 659 }; 660 661 struct SynReplyTests { 662 const unsigned char* syn_reply; 663 int syn_reply_length; 664 } test_cases[] = { 665 { kSynReplyMissingStatus, arraysize(kSynReplyMissingStatus) }, 666 { kSynReplyMissingVersion, arraysize(kSynReplyMissingVersion) } 667 }; 668 669 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) { 670 MockWrite writes[] = { 671 MockWrite(true, reinterpret_cast<const char*>(kGetSyn), 672 arraysize(kGetSyn)), 673 MockWrite(true, 0, 0) // EOF 674 }; 675 676 MockRead reads[] = { 677 MockRead(true, reinterpret_cast<const char*>(test_cases[i].syn_reply), 678 test_cases[i].syn_reply_length), 679 MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), 680 arraysize(kGetBodyFrame)), 681 MockRead(true, 0, 0) // EOF 682 }; 683 684 HttpRequestInfo request; 685 request.method = "GET"; 686 request.url = GURL("http://www.google.com/"); 687 request.load_flags = 0; 688 scoped_refptr<DelayedSocketData> data( 689 new DelayedSocketData(reads, 1, writes)); 690 TransactionHelperResult out = TransactionHelper(request, data.get(), NULL); 691 EXPECT_EQ(ERR_INVALID_RESPONSE, out.rv); 692 } 693 } 694 695 // Verify that we don't crash on some corrupt frames. 696 TEST_F(FlipNetworkTransactionTest, CorruptFrameSessionError) { 697 static const unsigned char kSynReplyMassiveLength[] = { 698 0x80, 0x01, 0x00, 0x02, 699 0x0f, 0x11, 0x11, 0x26, // This is the length field with a big number 700 0x00, 0x00, 0x00, 0x01, 701 0x00, 0x00, 0x00, 0x04, 702 0x00, 0x06, 's', 't', 'a', 't', 'u', 's', 703 0x00, 0x03, '2', '0', '0', 704 0x00, 0x03, 'u', 'r', 'l', 705 0x00, 0x0a, '/', 'i', 'n', 'd', 'e', 'x', '.', 'p', 'h', 'p', 706 }; 707 708 struct SynReplyTests { 709 const unsigned char* syn_reply; 710 int syn_reply_length; 711 } test_cases[] = { 712 { kSynReplyMassiveLength, arraysize(kSynReplyMassiveLength) } 713 }; 714 715 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) { 716 MockWrite writes[] = { 717 MockWrite(true, reinterpret_cast<const char*>(kGetSyn), 718 arraysize(kGetSyn)), 719 MockWrite(true, 0, 0) // EOF 720 }; 721 722 MockRead reads[] = { 723 MockRead(true, reinterpret_cast<const char*>(test_cases[i].syn_reply), 724 test_cases[i].syn_reply_length), 725 MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), 726 arraysize(kGetBodyFrame)), 727 MockRead(true, 0, 0) // EOF 728 }; 729 730 HttpRequestInfo request; 731 request.method = "GET"; 732 request.url = GURL("http://www.google.com/"); 733 request.load_flags = 0; 734 scoped_refptr<DelayedSocketData> data( 735 new DelayedSocketData(reads, 1, writes)); 736 TransactionHelperResult out = TransactionHelper(request, data.get(), NULL); 737 EXPECT_EQ(ERR_FLIP_PROTOCOL_ERROR, out.rv); 738 } 739 } 740 741 TEST_F(FlipNetworkTransactionTest, DISABLED_ServerPush) { 742 // Reply with the X-Associated-Content header. 743 static const unsigned char syn_reply[] = { 744 0x80, 0x01, 0x00, 0x02, 745 0x00, 0x00, 0x00, 0x71, 746 0x00, 0x00, 0x00, 0x01, 747 0x00, 0x00, 0x00, 0x04, 748 0x00, 0x14, 'X', '-', 'A', 's', 's', 'o', 'c', 'i', 'a', 't', 749 'e', 'd', '-', 'C', 'o', 'n', 't', 'e', 'n', 't', 750 0x00, 0x20, '1', '?', '?', 'h', 't', 't', 'p', ':', '/', '/', 'w', 'w', 751 'w', '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'c', 'o', 'm', 752 '/', 'f', 'o', 'o', '.', 'd', 'a', 't', 753 0x00, 0x06, 's', 't', 'a', 't', 'u', 's', 754 0x00, 0x03, '2', '0', '0', 755 0x00, 0x03, 'u', 'r', 'l', 756 0x00, 0x0a, '/', 'i', 'n', 'd', 'e', 'x', '.', 'p', 'h', 'p', 757 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', 758 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', 759 }; 760 761 // Syn for the X-Associated-Content (foo.dat) 762 static const unsigned char syn_push[] = { 763 0x80, 0x01, 0x00, 0x01, 764 0x00, 0x00, 0x00, 0x47, 765 0x00, 0x00, 0x00, 0x02, 766 0x00, 0x00, 0x00, 0x04, 767 0x00, 0x04, 'p', 'a', 't', 'h', 768 0x00, 0x08, '/', 'f', 'o', 'o', '.', 'd', 'a', 't', 769 0x00, 0x06, 's', 't', 'a', 't', 'u', 's', 770 0x00, 0x03, '2', '0', '0', 771 0x00, 0x03, 'u', 'r', 'l', 772 0x00, 0x08, '/', 'f', 'o', 'o', '.', 'd', 'a', 't', 773 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', 774 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', 775 }; 776 777 // Body for stream 2 778 static const unsigned char body_frame_2[] = { 779 0x00, 0x00, 0x00, 0x02, 780 0x01, 0x00, 0x00, 0x07, 781 'g', 'o', 'o', 'd', 'b', 'y', 'e', 782 }; 783 784 MockWrite writes[] = { 785 MockWrite(true, reinterpret_cast<const char*>(kGetSyn), 786 arraysize(kGetSyn)), 787 MockWrite(true, 0, 0) // EOF 788 }; 789 790 MockRead reads[] = { 791 MockRead(true, reinterpret_cast<const char*>(syn_reply), 792 arraysize(syn_reply)), 793 MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), 794 arraysize(kGetBodyFrame)), 795 MockRead(true, ERR_IO_PENDING), // Force a pause 796 MockRead(true, reinterpret_cast<const char*>(syn_push), 797 arraysize(syn_push)), 798 MockRead(true, reinterpret_cast<const char*>(body_frame_2), 799 arraysize(body_frame_2)), 800 MockRead(true, ERR_IO_PENDING), // Force a pause 801 MockRead(true, 0, 0) // EOF 802 }; 803 804 // We disable SSL for this test. 805 FlipSession::SetSSLMode(false); 806 807 enum TestTypes { 808 // Simulate that the server sends the first request, notifying the client 809 // that it *will* push the second stream. But the client issues the 810 // request for the second stream before the push data arrives. 811 PUSH_AFTER_REQUEST, 812 // Simulate that the server is sending the pushed stream data before the 813 // client requests it. The FlipSession will buffer the response and then 814 // deliver the data when the client does make the request. 815 PUSH_BEFORE_REQUEST, 816 DONE 817 }; 818 819 for (int test_type = PUSH_AFTER_REQUEST; test_type != DONE; ++test_type) { 820 // Setup a mock session. 821 SessionDependencies session_deps; 822 scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); 823 scoped_refptr<DelayedSocketData> data( 824 new DelayedSocketData(reads, 1, writes)); 825 session_deps.socket_factory.AddSocketDataProvider(data.get()); 826 827 // Issue the first request 828 { 829 FlipNetworkTransaction trans(session.get()); 830 831 // Issue the first request. 832 HttpRequestInfo request; 833 request.method = "GET"; 834 request.url = GURL("http://www.google.com/"); 835 request.load_flags = 0; 836 TestCompletionCallback callback; 837 int rv = trans.Start(&request, &callback, NULL); 838 EXPECT_EQ(ERR_IO_PENDING, rv); 839 840 rv = callback.WaitForResult(); 841 EXPECT_EQ(rv, OK); 842 843 // Verify the SYN_REPLY. 844 const HttpResponseInfo* response = trans.GetResponseInfo(); 845 EXPECT_TRUE(response->headers != NULL); 846 EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); 847 848 if (test_type == PUSH_BEFORE_REQUEST) 849 data->CompleteRead(); 850 851 // Verify the body. 852 std::string response_data; 853 rv = ReadTransaction(&trans, &response_data); 854 EXPECT_EQ(OK, rv); 855 EXPECT_EQ("hello!", response_data); 856 } 857 858 // Issue a second request for the X-Associated-Content. 859 { 860 FlipNetworkTransaction trans(session.get()); 861 862 HttpRequestInfo request; 863 request.method = "GET"; 864 request.url = GURL("http://www.google.com/foo.dat"); 865 request.load_flags = 0; 866 TestCompletionCallback callback; 867 int rv = trans.Start(&request, &callback, NULL); 868 EXPECT_EQ(ERR_IO_PENDING, rv); 869 870 // In the case where we are Complete the next read now. 871 if (test_type == PUSH_AFTER_REQUEST) 872 data->CompleteRead(); 873 874 rv = callback.WaitForResult(); 875 EXPECT_EQ(rv, OK); 876 877 // Verify the SYN_REPLY. 878 const HttpResponseInfo* response = trans.GetResponseInfo(); 879 EXPECT_TRUE(response->headers != NULL); 880 EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); 881 882 // Verify the body. 883 std::string response_data; 884 rv = ReadTransaction(&trans, &response_data); 885 EXPECT_EQ(OK, rv); 886 EXPECT_EQ("goodbye", response_data); 887 } 888 889 // Complete the next read now and teardown. 890 data->CompleteRead(); 891 892 // Verify that we consumed all test data. 893 EXPECT_TRUE(data->at_read_eof()); 894 EXPECT_TRUE(data->at_write_eof()); 895 } 896 } 897 898 // Test that we shutdown correctly on write errors. 899 TEST_F(FlipNetworkTransactionTest, WriteError) { 900 MockWrite writes[] = { 901 // We'll write 10 bytes successfully 902 MockWrite(true, reinterpret_cast<const char*>(kGetSyn), 10), 903 // Followed by ERROR! 904 MockWrite(true, ERR_FAILED), 905 MockWrite(true, 0, 0) // EOF 906 }; 907 908 MockRead reads[] = { 909 MockRead(true, reinterpret_cast<const char*>(kGetSynReply), 910 arraysize(kGetSynReply)), 911 MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), 912 arraysize(kGetBodyFrame)), 913 MockRead(true, 0, 0) // EOF 914 }; 915 916 HttpRequestInfo request; 917 request.method = "GET"; 918 request.url = GURL("http://www.google.com/"); 919 request.load_flags = 0; 920 scoped_refptr<DelayedSocketData> data( 921 new DelayedSocketData(reads, 2, writes)); 922 TransactionHelperResult out = TransactionHelper(request, data.get(), NULL); 923 EXPECT_EQ(ERR_FAILED, out.rv); 924 data->Reset(); 925 } 926 927 // Test that partial writes work. 928 TEST_F(FlipNetworkTransactionTest, PartialWrite) { 929 // Chop the SYN_STREAM frame into 5 chunks. 930 const int kChunks = 5; 931 scoped_array<MockWrite> writes(ChopFrame( 932 reinterpret_cast<const char*>(kGetSyn), arraysize(kGetSyn), kChunks)); 933 934 MockRead reads[] = { 935 MockRead(true, reinterpret_cast<const char*>(kGetSynReply), 936 arraysize(kGetSynReply)), 937 MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), 938 arraysize(kGetBodyFrame)), 939 MockRead(true, 0, 0) // EOF 940 }; 941 942 HttpRequestInfo request; 943 request.method = "GET"; 944 request.url = GURL("http://www.google.com/"); 945 request.load_flags = 0; 946 scoped_refptr<DelayedSocketData> data( 947 new DelayedSocketData(reads, kChunks, writes.get())); 948 TransactionHelperResult out = TransactionHelper(request, data.get(), NULL); 949 EXPECT_EQ(OK, out.rv); 950 EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); 951 EXPECT_EQ("hello!", out.response_data); 952 } 953 954 TEST_F(FlipNetworkTransactionTest, DISABLED_ConnectFailure) { 955 MockConnect connects[] = { 956 MockConnect(true, ERR_NAME_NOT_RESOLVED), 957 MockConnect(false, ERR_NAME_NOT_RESOLVED), 958 MockConnect(true, ERR_INTERNET_DISCONNECTED), 959 MockConnect(false, ERR_INTERNET_DISCONNECTED) 960 }; 961 962 for (size_t index = 0; index < arraysize(connects); ++index) { 963 MockWrite writes[] = { 964 MockWrite(true, reinterpret_cast<const char*>(kGetSyn), 965 arraysize(kGetSyn)), 966 MockWrite(true, 0, 0) // EOF 967 }; 968 969 MockRead reads[] = { 970 MockRead(true, reinterpret_cast<const char*>(kGetSynReply), 971 arraysize(kGetSynReply)), 972 MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), 973 arraysize(kGetBodyFrame)), 974 MockRead(true, 0, 0) // EOF 975 }; 976 977 HttpRequestInfo request; 978 request.method = "GET"; 979 request.url = GURL("http://www.google.com/"); 980 request.load_flags = 0; 981 scoped_refptr<DelayedSocketData> data( 982 new DelayedSocketData(connects[index], reads, 1, writes)); 983 TransactionHelperResult out = TransactionHelper(request, data.get(), NULL); 984 EXPECT_EQ(connects[index].result, out.rv); 985 } 986 } 987 988 // In this test, we enable compression, but get a uncompressed SynReply from 989 // the server. Verify that teardown is all clean. 990 TEST_F(FlipNetworkTransactionTest, DecompressFailureOnSynReply) { 991 MockWrite writes[] = { 992 MockWrite(true, reinterpret_cast<const char*>(kGetSynCompressed), 993 arraysize(kGetSynCompressed)), 994 MockWrite(true, 0, 0) // EOF 995 }; 996 997 MockRead reads[] = { 998 MockRead(true, reinterpret_cast<const char*>(kGetSynReply), 999 arraysize(kGetSynReply)), 1000 MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), 1001 arraysize(kGetBodyFrame)), 1002 MockRead(true, 0, 0) // EOF 1003 }; 1004 1005 // For this test, we turn on the normal compression. 1006 EnableCompression(true); 1007 1008 HttpRequestInfo request; 1009 request.method = "GET"; 1010 request.url = GURL("http://www.google.com/"); 1011 request.load_flags = 0; 1012 scoped_refptr<DelayedSocketData> data( 1013 new DelayedSocketData(reads, 1, writes)); 1014 TransactionHelperResult out = TransactionHelper(request, data.get(), NULL); 1015 EXPECT_EQ(ERR_SYN_REPLY_NOT_RECEIVED, out.rv); 1016 data->Reset(); 1017 1018 EnableCompression(false); 1019 } 1020 1021 // Test that the LoadLog contains good data for a simple GET request. 1022 TEST_F(FlipNetworkTransactionTest, LoadLog) { 1023 MockWrite writes[] = { 1024 MockWrite(true, reinterpret_cast<const char*>(kGetSyn), 1025 arraysize(kGetSyn)), 1026 MockWrite(true, 0, 0) // EOF 1027 }; 1028 1029 MockRead reads[] = { 1030 MockRead(true, reinterpret_cast<const char*>(kGetSynReply), 1031 arraysize(kGetSynReply)), 1032 MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), 1033 arraysize(kGetBodyFrame)), 1034 MockRead(true, 0, 0) // EOF 1035 }; 1036 1037 scoped_refptr<net::LoadLog> log(new net::LoadLog(net::LoadLog::kUnbounded)); 1038 1039 HttpRequestInfo request; 1040 request.method = "GET"; 1041 request.url = GURL("http://www.google.com/"); 1042 request.load_flags = 0; 1043 scoped_refptr<DelayedSocketData> data( 1044 new DelayedSocketData(reads, 1, writes)); 1045 TransactionHelperResult out = TransactionHelper(request, data.get(), 1046 log); 1047 EXPECT_EQ(OK, out.rv); 1048 EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); 1049 EXPECT_EQ("hello!", out.response_data); 1050 1051 // Check that the LoadLog was filled reasonably. 1052 // This test is intentionally non-specific about the exact ordering of 1053 // the log; instead we just check to make sure that certain events exist. 1054 EXPECT_LT(0u, log->entries().size()); 1055 int pos = 0; 1056 // We know the first event at position 0. 1057 EXPECT_TRUE(net::LogContainsBeginEvent( 1058 *log, 0, net::LoadLog::TYPE_FLIP_TRANSACTION_INIT_CONNECTION)); 1059 // For the rest of the events, allow additional events in the middle, 1060 // but expect these to be logged in order. 1061 pos = net::ExpectLogContainsSomewhere(log, 0, 1062 net::LoadLog::TYPE_FLIP_TRANSACTION_INIT_CONNECTION, 1063 net::LoadLog::PHASE_END); 1064 pos = net::ExpectLogContainsSomewhere(log, pos + 1, 1065 net::LoadLog::TYPE_FLIP_TRANSACTION_SEND_REQUEST, 1066 net::LoadLog::PHASE_BEGIN); 1067 pos = net::ExpectLogContainsSomewhere(log, pos + 1, 1068 net::LoadLog::TYPE_FLIP_TRANSACTION_SEND_REQUEST, 1069 net::LoadLog::PHASE_END); 1070 pos = net::ExpectLogContainsSomewhere(log, pos + 1, 1071 net::LoadLog::TYPE_FLIP_TRANSACTION_READ_HEADERS, 1072 net::LoadLog::PHASE_BEGIN); 1073 pos = net::ExpectLogContainsSomewhere(log, pos + 1, 1074 net::LoadLog::TYPE_FLIP_TRANSACTION_READ_HEADERS, 1075 net::LoadLog::PHASE_END); 1076 pos = net::ExpectLogContainsSomewhere(log, pos + 1, 1077 net::LoadLog::TYPE_FLIP_TRANSACTION_READ_BODY, 1078 net::LoadLog::PHASE_BEGIN); 1079 pos = net::ExpectLogContainsSomewhere(log, pos + 1, 1080 net::LoadLog::TYPE_FLIP_TRANSACTION_READ_BODY, 1081 net::LoadLog::PHASE_END); 1082 } 1083 1084 } // namespace net 1085