diff options
Diffstat (limited to 'net/spdy/spdy_network_transaction_unittest.cc')
-rw-r--r-- | net/spdy/spdy_network_transaction_unittest.cc | 736 |
1 files changed, 681 insertions, 55 deletions
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc index 7400756..730d53f 100644 --- a/net/spdy/spdy_network_transaction_unittest.cc +++ b/net/spdy/spdy_network_transaction_unittest.cc @@ -177,7 +177,6 @@ class SpdyNetworkTransactionTest output_.status_line = response->headers->GetStatusLine(); output_.response_info = *response; // Make a copy so we can verify. output_.rv = ReadTransaction(trans_.get(), &output_.response_data); - EXPECT_EQ(OK, output_.rv); return; } @@ -352,7 +351,7 @@ class SpdyNetworkTransactionTest // to skip over data destined for other transactions while we consume // the data for |trans|. int ReadResult(HttpNetworkTransaction* trans, - OrderedSocketData* data, + StaticSocketDataProvider* data, std::string* result) { const int kSize = 3000; @@ -397,19 +396,14 @@ class SpdyNetworkTransactionTest EXPECT_EQ(0u, spdy_session->num_unclaimed_pushed_streams()); } - void RunServerPushTest(MockWrite writes[], int writes_length, - MockRead reads[], int reads_length, + void RunServerPushTest(OrderedSocketData* data, HttpResponseInfo* response, HttpResponseInfo* response2, std::string& expected) { - scoped_refptr<OrderedSocketData> data( - new OrderedSocketData(reads, reads_length, - writes, writes_length)); NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); - helper.RunPreTestSetup(); - helper.AddData(data.get()); + helper.AddData(data); HttpNetworkTransaction* trans = helper.trans(); @@ -1198,7 +1192,7 @@ TEST_P(SpdyNetworkTransactionTest, Put) { "content-length", "0" }; scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyPacket(kSynStartHeader, NULL, 0, - kPutHeaders, arraysize(kPutHeaders)/2)); + kPutHeaders, arraysize(kPutHeaders) / 2)); MockWrite writes[] = { CreateMockWrite(*req) }; @@ -1222,7 +1216,7 @@ TEST_P(SpdyNetworkTransactionTest, Put) { "content-length", "1234" }; scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyPacket(kSynReplyHeader, - NULL, 0, kStandardGetHeaders, arraysize(kStandardGetHeaders)/2)); + NULL, 0, kStandardGetHeaders, arraysize(kStandardGetHeaders) / 2)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*body), @@ -1269,7 +1263,7 @@ TEST_P(SpdyNetworkTransactionTest, Head) { "content-length", "0" }; scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyPacket(kSynStartHeader, NULL, 0, - kHeadHeaders, arraysize(kHeadHeaders)/2)); + kHeadHeaders, arraysize(kHeadHeaders) / 2)); MockWrite writes[] = { CreateMockWrite(*req) }; @@ -1293,7 +1287,7 @@ TEST_P(SpdyNetworkTransactionTest, Head) { "content-length", "1234" }; scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyPacket(kSynReplyHeader, - NULL, 0, kStandardGetHeaders, arraysize(kStandardGetHeaders)/2)); + NULL, 0, kStandardGetHeaders, arraysize(kStandardGetHeaders) / 2)); MockRead reads[] = { CreateMockRead(*resp), CreateMockRead(*body), @@ -1650,7 +1644,7 @@ TEST_P(SpdyNetworkTransactionTest, WindowUpdateReceived) { SpdyHttpStream* stream = static_cast<SpdyHttpStream*>(trans->stream_.get()); ASSERT_TRUE(stream != NULL); ASSERT_TRUE(stream->stream() != NULL); - EXPECT_EQ(spdy::kInitialWindowSize + + EXPECT_EQ(static_cast<int>(spdy::kSpdyStreamInitialWindowSize) + kDeltaWindowSize * kDeltaCount - kMaxSpdyFrameChunkSize * kFrameCount, stream->stream()->send_window_size()); @@ -1709,8 +1703,9 @@ TEST_P(SpdyNetworkTransactionTest, WindowUpdateSent) { ASSERT_TRUE(stream != NULL); ASSERT_TRUE(stream->stream() != NULL); - EXPECT_EQ(spdy::kInitialWindowSize - kUploadDataSize, - stream->stream()->recv_window_size()); + EXPECT_EQ( + static_cast<int>(spdy::kSpdyStreamInitialWindowSize) - kUploadDataSize, + stream->stream()->recv_window_size()); const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); @@ -1841,17 +1836,19 @@ TEST_P(SpdyNetworkTransactionTest, FlowControlStallResume) { // frames plus SYN_STREAM plus the last data frame; also we need another // data frame that we will send once the WINDOW_UPDATE is received, // therefore +3. - size_t nwrites = spdy::kInitialWindowSize / kMaxSpdyFrameChunkSize + 3; + size_t nwrites = + spdy::kSpdyStreamInitialWindowSize / kMaxSpdyFrameChunkSize + 3; // Calculate last frame's size; 0 size data frame is legal. - size_t last_frame_size = spdy::kInitialWindowSize % kMaxSpdyFrameChunkSize; + size_t last_frame_size = + spdy::kSpdyStreamInitialWindowSize % kMaxSpdyFrameChunkSize; // Construct content for a data frame of maximum size. scoped_ptr<std::string> content( new std::string(kMaxSpdyFrameChunkSize, 'a')); scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyPost( - spdy::kInitialWindowSize + kUploadDataSize, NULL, 0)); + spdy::kSpdyStreamInitialWindowSize + kUploadDataSize, NULL, 0)); // Full frames. scoped_ptr<spdy::SpdyFrame> body1( @@ -1898,7 +1895,7 @@ TEST_P(SpdyNetworkTransactionTest, FlowControlStallResume) { request.url = GURL("http://www.google.com/"); request.upload_data = new UploadData(); scoped_ptr<std::string> upload_data( - new std::string(spdy::kInitialWindowSize, 'a')); + new std::string(spdy::kSpdyStreamInitialWindowSize, 'a')); upload_data->append(kUploadData, kUploadDataSize); request.upload_data->AppendBytes(upload_data->c_str(), upload_data->size()); NormalSpdyTransactionHelper helper(request, @@ -2199,11 +2196,11 @@ TEST_P(SpdyNetworkTransactionTest, RedirectGetRequest) { // Setup writes/reads to www.google.com scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyPacket( - kSynStartHeader, kExtraHeaders, arraysize(kExtraHeaders)/2, - kStandardGetHeaders, arraysize(kStandardGetHeaders)/2)); + kSynStartHeader, kExtraHeaders, arraysize(kExtraHeaders) / 2, + kStandardGetHeaders, arraysize(kStandardGetHeaders) / 2)); scoped_ptr<spdy::SpdyFrame> req2(ConstructSpdyPacket( - kSynStartHeader, kExtraHeaders, arraysize(kExtraHeaders)/2, - kStandardGetHeaders2, arraysize(kStandardGetHeaders2)/2)); + kSynStartHeader, kExtraHeaders, arraysize(kExtraHeaders) / 2, + kStandardGetHeaders2, arraysize(kStandardGetHeaders2) / 2)); scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReplyRedirect(1)); MockWrite writes[] = { CreateMockWrite(*req, 1), @@ -2308,16 +2305,27 @@ TEST_P(SpdyNetworkTransactionTest, RedirectServerPush) { }; // Setup writes/reads to www.google.com - scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyPacket( - kSynStartHeader, kExtraHeaders, arraysize(kExtraHeaders)/2, - kStandardGetHeaders, arraysize(kStandardGetHeaders)/2)); - scoped_ptr<spdy::SpdyFrame> req2(ConstructSpdyPacket( - kSynStartHeader, kExtraHeaders, arraysize(kExtraHeaders)/2, - kStandardGetHeaders2, arraysize(kStandardGetHeaders2)/2)); + scoped_ptr<spdy::SpdyFrame> req( + ConstructSpdyPacket(kSynStartHeader, + kExtraHeaders, + arraysize(kExtraHeaders) / 2, + kStandardGetHeaders, + arraysize(kStandardGetHeaders) / 2)); + scoped_ptr<spdy::SpdyFrame> req2( + ConstructSpdyPacket(kSynStartHeader, + kExtraHeaders, + arraysize(kExtraHeaders) / 2, + kStandardGetHeaders2, + arraysize(kStandardGetHeaders2) / 2)); scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); - scoped_ptr<spdy::SpdyFrame> rep(ConstructSpdyPush(NULL, 0, 2, 1, "/foo.dat", - "301 Moved Permanently", "http://www.foo.com/index.php", - "http://www.foo.com/index.php")); + scoped_ptr<spdy::SpdyFrame> rep( + ConstructSpdyPush(NULL, + 0, + 2, + 1, + "http://www.google.com/foo.dat", + "301 Moved Permanently", + "http://www.foo.com/index.php")); scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); MockWrite writes[] = { CreateMockWrite(*req, 1), @@ -2411,7 +2419,11 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushSingleDataFrame) { scoped_ptr<spdy::SpdyFrame> stream1_reply(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr<spdy::SpdyFrame> - stream2_syn(ConstructSpdyPush(NULL, 0, 2, 1, "/foo.dat")); + stream2_syn(ConstructSpdyPush(NULL, + 0, + 2, + 1, + "http://www.google.com/foo.dat")); MockRead reads[] = { CreateMockRead(*stream1_reply, 2), CreateMockRead(*stream2_syn, 3), @@ -2424,8 +2436,15 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushSingleDataFrame) { HttpResponseInfo response; HttpResponseInfo response2; std::string expected_push_result("pushed"); - RunServerPushTest(writes, arraysize(writes), reads, arraysize(reads), - &response, &response2, expected_push_result); + scoped_refptr<OrderedSocketData> data(new OrderedSocketData( + reads, + arraysize(reads), + writes, + arraysize(writes))); + RunServerPushTest(data.get(), + &response, + &response2, + expected_push_result); // Verify the SYN_REPLY. EXPECT_TRUE(response.headers != NULL); @@ -2451,7 +2470,11 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushSingleDataFrame2) { scoped_ptr<spdy::SpdyFrame> stream1_reply(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr<spdy::SpdyFrame> - stream2_syn(ConstructSpdyPush(NULL, 0, 2, 1, "/foo.dat")); + stream2_syn(ConstructSpdyPush(NULL, + 0, + 2, + 1, + "http://www.google.com/foo.dat")); scoped_ptr<spdy::SpdyFrame> stream1_body(ConstructSpdyBodyFrame(1, true)); MockRead reads[] = { @@ -2466,8 +2489,15 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushSingleDataFrame2) { HttpResponseInfo response; HttpResponseInfo response2; std::string expected_push_result("pushed"); - RunServerPushTest(writes, arraysize(writes), reads, arraysize(reads), - &response, &response2, expected_push_result); + scoped_refptr<OrderedSocketData> data(new OrderedSocketData( + reads, + arraysize(reads), + writes, + arraysize(writes))); + RunServerPushTest(data.get(), + &response, + &response2, + expected_push_result); // Verify the SYN_REPLY. EXPECT_TRUE(response.headers != NULL); @@ -2490,7 +2520,11 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushServerAborted) { scoped_ptr<spdy::SpdyFrame> stream1_reply(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr<spdy::SpdyFrame> - stream2_syn(ConstructSpdyPush(NULL, 0, 2, 1, "/foo.dat")); + stream2_syn(ConstructSpdyPush(NULL, + 0, + 2, + 1, + "http://www.google.com/foo.dat")); scoped_ptr<spdy::SpdyFrame> stream2_rst(ConstructSpdyRstStream(2, spdy::PROTOCOL_ERROR)); MockRead reads[] = { @@ -2558,9 +2592,17 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushDuplicate) { scoped_ptr<spdy::SpdyFrame> stream1_reply(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr<spdy::SpdyFrame> - stream2_syn(ConstructSpdyPush(NULL, 0, 2, 1, "/foo.dat")); + stream2_syn(ConstructSpdyPush(NULL, + 0, + 2, + 1, + "http://www.google.com/foo.dat")); scoped_ptr<spdy::SpdyFrame> - stream3_syn(ConstructSpdyPush(NULL, 0, 4, 1, "/foo.dat")); + stream3_syn(ConstructSpdyPush(NULL, + 0, + 4, + 1, + "http://www.google.com/foo.dat")); MockRead reads[] = { CreateMockRead(*stream1_reply, 2), CreateMockRead(*stream2_syn, 3), @@ -2574,8 +2616,15 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushDuplicate) { HttpResponseInfo response; HttpResponseInfo response2; std::string expected_push_result("pushed"); - RunServerPushTest(writes, arraysize(writes), reads, arraysize(reads), - &response, &response2, expected_push_result); + scoped_refptr<OrderedSocketData> data(new OrderedSocketData( + reads, + arraysize(reads), + writes, + arraysize(writes))); + RunServerPushTest(data.get(), + &response, + &response2, + expected_push_result); // Verify the SYN_REPLY. EXPECT_TRUE(response.headers != NULL); @@ -2607,7 +2656,11 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushMultipleDataFrame) { scoped_ptr<spdy::SpdyFrame> stream1_reply(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr<spdy::SpdyFrame> - stream2_syn(ConstructSpdyPush(NULL, 0, 2, 1, "/foo.dat")); + stream2_syn(ConstructSpdyPush(NULL, + 0, + 2, + 1, + "http://www.google.com/foo.dat")); MockRead reads[] = { CreateMockRead(*stream1_reply, 2), CreateMockRead(*stream2_syn, 3), @@ -2626,8 +2679,15 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushMultipleDataFrame) { HttpResponseInfo response; HttpResponseInfo response2; std::string expected_push_result("pushed my darling hello my baby"); - RunServerPushTest(writes, arraysize(writes), reads, arraysize(reads), - &response, &response2, expected_push_result); + scoped_refptr<OrderedSocketData> data(new OrderedSocketData( + reads, + arraysize(reads), + writes, + arraysize(writes))); + RunServerPushTest(data.get(), + &response, + &response2, + expected_push_result); // Verify the SYN_REPLY. EXPECT_TRUE(response.headers != NULL); @@ -2659,7 +2719,11 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushMultipleDataFrameInterrupted) { scoped_ptr<spdy::SpdyFrame> stream1_reply(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr<spdy::SpdyFrame> - stream2_syn(ConstructSpdyPush(NULL, 0, 2, 1, "/foo.dat")); + stream2_syn(ConstructSpdyPush(NULL, + 0, + 2, + 1, + "http://www.google.com/foo.dat")); MockRead reads[] = { CreateMockRead(*stream1_reply, 2), CreateMockRead(*stream2_syn, 3), @@ -2679,8 +2743,15 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushMultipleDataFrameInterrupted) { HttpResponseInfo response; HttpResponseInfo response2; std::string expected_push_result("pushed my darling hello my baby"); - RunServerPushTest(writes, arraysize(writes), reads, arraysize(reads), - &response, &response2, expected_push_result); + scoped_refptr<OrderedSocketData> data(new OrderedSocketData( + reads, + arraysize(reads), + writes, + arraysize(writes))); + RunServerPushTest(data.get(), + &response, + &response2, + expected_push_result); // Verify the SYN_REPLY. EXPECT_TRUE(response.headers != NULL); @@ -2706,7 +2777,11 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushInvalidAssociatedStreamID0) { scoped_ptr<spdy::SpdyFrame> stream1_reply(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr<spdy::SpdyFrame> - stream2_syn(ConstructSpdyPush(NULL, 0, 2, 0, "/foo.dat")); + stream2_syn(ConstructSpdyPush(NULL, + 0, + 2, + 0, + "http://www.google.com/foo.dat")); MockRead reads[] = { CreateMockRead(*stream1_reply, 2), CreateMockRead(*stream2_syn, 3), @@ -2763,7 +2838,11 @@ TEST_P(SpdyNetworkTransactionTest, ServerPushInvalidAssociatedStreamID9) { scoped_ptr<spdy::SpdyFrame> stream1_reply(ConstructSpdyGetSynReply(NULL, 0, 1)); scoped_ptr<spdy::SpdyFrame> - stream2_syn(ConstructSpdyPush(NULL, 0, 2, 9, "/foo.dat")); + stream2_syn(ConstructSpdyPush(NULL, + 0, + 2, + 9, + "http://www.google.com/foo.dat")); MockRead reads[] = { CreateMockRead(*stream1_reply, 2), CreateMockRead(*stream2_syn, 3), @@ -3181,7 +3260,7 @@ TEST_P(SpdyNetworkTransactionTest, InvalidSynReply) { BoundNetLog(), GetParam()); helper.RunToCompletion(data.get()); TransactionHelperResult out = helper.output(); - EXPECT_EQ(ERR_INVALID_RESPONSE, out.rv); + EXPECT_EQ(ERR_INCOMPLETE_SPDY_HEADERS, out.rv); } } @@ -4602,7 +4681,8 @@ TEST_P(SpdyNetworkTransactionTest, SpdyBasicAuth) { }; scoped_ptr<spdy::SpdyFrame> req_get_authorization( ConstructSpdyGet( - kExtraAuthorizationHeaders, arraysize(kExtraAuthorizationHeaders)/2, + kExtraAuthorizationHeaders, + arraysize(kExtraAuthorizationHeaders) / 2, false, 3, LOWEST)); MockWrite spdy_writes[] = { CreateMockWrite(*req_get, 1), @@ -4619,7 +4699,8 @@ TEST_P(SpdyNetworkTransactionTest, SpdyBasicAuth) { scoped_ptr<spdy::SpdyFrame> resp_authentication( ConstructSpdySynReplyError( "401 Authentication Required", - kExtraAuthenticationHeaders, arraysize(kExtraAuthenticationHeaders)/2, + kExtraAuthenticationHeaders, + arraysize(kExtraAuthenticationHeaders) / 2, 1)); scoped_ptr<spdy::SpdyFrame> body_authentication( ConstructSpdyBodyFrame(1, true)); @@ -4677,4 +4758,549 @@ TEST_P(SpdyNetworkTransactionTest, SpdyBasicAuth) { EXPECT_TRUE(response_restart->auth_challenge.get() == NULL); } +TEST_P(SpdyNetworkTransactionTest, ServerPushWithHeaders) { + static const unsigned char kPushBodyFrame[] = { + 0x00, 0x00, 0x00, 0x02, // header, ID + 0x01, 0x00, 0x00, 0x06, // FIN, length + 'p', 'u', 's', 'h', 'e', 'd' // "pushed" + }; + scoped_ptr<spdy::SpdyFrame> + stream1_syn(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); + scoped_ptr<spdy::SpdyFrame> + stream1_body(ConstructSpdyBodyFrame(1, true)); + MockWrite writes[] = { + CreateMockWrite(*stream1_syn, 1), + }; + + static const char* const kInitialHeaders[] = { + "url", + "http://www.google.com/foo.dat", + }; + static const char* const kLateHeaders[] = { + "hello", + "bye", + "status", + "200", + "version", + "HTTP/1.1" + }; + scoped_ptr<spdy::SpdyFrame> + stream2_syn(ConstructSpdyControlFrame(kInitialHeaders, + arraysize(kInitialHeaders) / 2, + false, + 2, + LOWEST, + spdy::SYN_STREAM, + spdy::CONTROL_FLAG_NONE, + NULL, + 0, + 1)); + scoped_ptr<spdy::SpdyFrame> + stream2_headers(ConstructSpdyControlFrame(kLateHeaders, + arraysize(kLateHeaders) / 2, + false, + 2, + LOWEST, + spdy::HEADERS, + spdy::CONTROL_FLAG_NONE, + NULL, + 0, + 0)); + + scoped_ptr<spdy::SpdyFrame> + stream1_reply(ConstructSpdyGetSynReply(NULL, 0, 1)); + MockRead reads[] = { + CreateMockRead(*stream1_reply, 2), + CreateMockRead(*stream2_syn, 3), + CreateMockRead(*stream2_headers, 4), + CreateMockRead(*stream1_body, 5, false), + MockRead(true, reinterpret_cast<const char*>(kPushBodyFrame), + arraysize(kPushBodyFrame), 6), + MockRead(true, ERR_IO_PENDING, 7), // Force a pause + }; + + HttpResponseInfo response; + HttpResponseInfo response2; + std::string expected_push_result("pushed"); + scoped_refptr<OrderedSocketData> data(new OrderedSocketData( + reads, + arraysize(reads), + writes, + arraysize(writes))); + RunServerPushTest(data.get(), + &response, + &response2, + expected_push_result); + + // Verify the SYN_REPLY. + EXPECT_TRUE(response.headers != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response.headers->GetStatusLine()); + + // Verify the pushed stream. + EXPECT_TRUE(response2.headers != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response2.headers->GetStatusLine()); +} + +TEST_P(SpdyNetworkTransactionTest, ServerPushClaimBeforeHeaders) { + // We push a stream and attempt to claim it before the headers come down. + static const unsigned char kPushBodyFrame[] = { + 0x00, 0x00, 0x00, 0x02, // header, ID + 0x01, 0x00, 0x00, 0x06, // FIN, length + 'p', 'u', 's', 'h', 'e', 'd' // "pushed" + }; + scoped_ptr<spdy::SpdyFrame> + stream1_syn(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); + scoped_ptr<spdy::SpdyFrame> + stream1_body(ConstructSpdyBodyFrame(1, true)); + MockWrite writes[] = { + CreateMockWrite(*stream1_syn, 0, false), + }; + + static const char* const kInitialHeaders[] = { + "url", + "http://www.google.com/foo.dat", + }; + static const char* const kLateHeaders[] = { + "hello", + "bye", + "status", + "200", + "version", + "HTTP/1.1" + }; + scoped_ptr<spdy::SpdyFrame> + stream2_syn(ConstructSpdyControlFrame(kInitialHeaders, + arraysize(kInitialHeaders) / 2, + false, + 2, + LOWEST, + spdy::SYN_STREAM, + spdy::CONTROL_FLAG_NONE, + NULL, + 0, + 1)); + scoped_ptr<spdy::SpdyFrame> + stream2_headers(ConstructSpdyControlFrame(kLateHeaders, + arraysize(kLateHeaders) / 2, + false, + 2, + LOWEST, + spdy::HEADERS, + spdy::CONTROL_FLAG_NONE, + NULL, + 0, + 0)); + + scoped_ptr<spdy::SpdyFrame> + stream1_reply(ConstructSpdyGetSynReply(NULL, 0, 1)); + MockRead reads[] = { + CreateMockRead(*stream1_reply, 1), + CreateMockRead(*stream2_syn, 2), + CreateMockRead(*stream1_body, 3), + CreateMockRead(*stream2_headers, 4), + MockRead(true, reinterpret_cast<const char*>(kPushBodyFrame), + arraysize(kPushBodyFrame), 5), + MockRead(true, 0, 5), // EOF + }; + + HttpResponseInfo response; + HttpResponseInfo response2; + std::string expected_push_result("pushed"); + scoped_refptr<DeterministicSocketData> data(new DeterministicSocketData( + reads, + arraysize(reads), + writes, + arraysize(writes))); + + NormalSpdyTransactionHelper helper(CreateGetRequest(), + BoundNetLog(), GetParam()); + helper.SetDeterministic(); + helper.AddDeterministicData(static_cast<DeterministicSocketData*>(data)); + helper.RunPreTestSetup(); + + HttpNetworkTransaction* trans = helper.trans(); + + // Run until we've received the primary SYN_STREAM, the pushed SYN_STREAM, + // and the body of the primary stream, but before we've received the HEADERS + // for the pushed stream. + data->SetStop(3); + + // Start the transaction. + TestCompletionCallback callback; + int rv = trans->Start(&CreateGetRequest(), &callback, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + data->Run(); + rv = callback.WaitForResult(); + EXPECT_EQ(0, rv); + + // Request the pushed path. At this point, we've received the push, but the + // headers are not yet complete. + scoped_ptr<HttpNetworkTransaction> trans2( + new HttpNetworkTransaction(helper.session())); + rv = trans2->Start(&CreateGetPushRequest(), &callback, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + data->RunFor(3); + MessageLoop::current()->RunAllPending(); + + // Read the server push body. + std::string result2; + ReadResult(trans2.get(), data.get(), &result2); + // Read the response body. + std::string result; + ReadResult(trans, data, &result); + + // Verify that we consumed all test data. + EXPECT_TRUE(data->at_read_eof()); + EXPECT_TRUE(data->at_write_eof()); + + // Verify that the received push data is same as the expected push data. + EXPECT_EQ(result2.compare(expected_push_result), 0) + << "Received data: " + << result2 + << "||||| Expected data: " + << expected_push_result; + + // Verify the SYN_REPLY. + // Copy the response info, because trans goes away. + response = *trans->GetResponseInfo(); + response2 = *trans2->GetResponseInfo(); + + VerifyStreamsClosed(helper); + + // Verify the SYN_REPLY. + EXPECT_TRUE(response.headers != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response.headers->GetStatusLine()); + + // Verify the pushed stream. + EXPECT_TRUE(response2.headers != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response2.headers->GetStatusLine()); +} + +TEST_P(SpdyNetworkTransactionTest, ServerPushWithTwoHeaderFrames) { + // We push a stream and attempt to claim it before the headers come down. + static const unsigned char kPushBodyFrame[] = { + 0x00, 0x00, 0x00, 0x02, // header, ID + 0x01, 0x00, 0x00, 0x06, // FIN, length + 'p', 'u', 's', 'h', 'e', 'd' // "pushed" + }; + scoped_ptr<spdy::SpdyFrame> + stream1_syn(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); + scoped_ptr<spdy::SpdyFrame> + stream1_body(ConstructSpdyBodyFrame(1, true)); + MockWrite writes[] = { + CreateMockWrite(*stream1_syn, 0, false), + }; + + static const char* const kInitialHeaders[] = { + "url", + "http://www.google.com/foo.dat", + }; + static const char* const kMiddleHeaders[] = { + "hello", + "bye", + }; + static const char* const kLateHeaders[] = { + "status", + "200", + "version", + "HTTP/1.1" + }; + scoped_ptr<spdy::SpdyFrame> + stream2_syn(ConstructSpdyControlFrame(kInitialHeaders, + arraysize(kInitialHeaders) / 2, + false, + 2, + LOWEST, + spdy::SYN_STREAM, + spdy::CONTROL_FLAG_NONE, + NULL, + 0, + 1)); + scoped_ptr<spdy::SpdyFrame> + stream2_headers1(ConstructSpdyControlFrame(kMiddleHeaders, + arraysize(kMiddleHeaders) / 2, + false, + 2, + LOWEST, + spdy::HEADERS, + spdy::CONTROL_FLAG_NONE, + NULL, + 0, + 0)); + scoped_ptr<spdy::SpdyFrame> + stream2_headers2(ConstructSpdyControlFrame(kLateHeaders, + arraysize(kLateHeaders) / 2, + false, + 2, + LOWEST, + spdy::HEADERS, + spdy::CONTROL_FLAG_NONE, + NULL, + 0, + 0)); + + scoped_ptr<spdy::SpdyFrame> + stream1_reply(ConstructSpdyGetSynReply(NULL, 0, 1)); + MockRead reads[] = { + CreateMockRead(*stream1_reply, 1), + CreateMockRead(*stream2_syn, 2), + CreateMockRead(*stream1_body, 3), + CreateMockRead(*stream2_headers1, 4), + CreateMockRead(*stream2_headers2, 5), + MockRead(true, reinterpret_cast<const char*>(kPushBodyFrame), + arraysize(kPushBodyFrame), 6), + MockRead(true, 0, 6), // EOF + }; + + HttpResponseInfo response; + HttpResponseInfo response2; + std::string expected_push_result("pushed"); + scoped_refptr<DeterministicSocketData> data(new DeterministicSocketData( + reads, + arraysize(reads), + writes, + arraysize(writes))); + + NormalSpdyTransactionHelper helper(CreateGetRequest(), + BoundNetLog(), GetParam()); + helper.SetDeterministic(); + helper.AddDeterministicData(static_cast<DeterministicSocketData*>(data)); + helper.RunPreTestSetup(); + + HttpNetworkTransaction* trans = helper.trans(); + + // Run until we've received the primary SYN_STREAM, the pushed SYN_STREAM, + // the first HEADERS frame, and the body of the primary stream, but before + // we've received the final HEADERS for the pushed stream. + data->SetStop(4); + + // Start the transaction. + TestCompletionCallback callback; + int rv = trans->Start(&CreateGetRequest(), &callback, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + data->Run(); + rv = callback.WaitForResult(); + EXPECT_EQ(0, rv); + + // Request the pushed path. At this point, we've received the push, but the + // headers are not yet complete. + scoped_ptr<HttpNetworkTransaction> trans2( + new HttpNetworkTransaction(helper.session())); + rv = trans2->Start(&CreateGetPushRequest(), &callback, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + data->RunFor(3); + MessageLoop::current()->RunAllPending(); + + // Read the server push body. + std::string result2; + ReadResult(trans2.get(), data, &result2); + // Read the response body. + std::string result; + ReadResult(trans, data, &result); + + // Verify that we consumed all test data. + EXPECT_TRUE(data->at_read_eof()); + EXPECT_TRUE(data->at_write_eof()); + + // Verify that the received push data is same as the expected push data. + EXPECT_EQ(result2.compare(expected_push_result), 0) + << "Received data: " + << result2 + << "||||| Expected data: " + << expected_push_result; + + // Verify the SYN_REPLY. + // Copy the response info, because trans goes away. + response = *trans->GetResponseInfo(); + response2 = *trans2->GetResponseInfo(); + + VerifyStreamsClosed(helper); + + // Verify the SYN_REPLY. + EXPECT_TRUE(response.headers != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response.headers->GetStatusLine()); + + // Verify the pushed stream. + EXPECT_TRUE(response2.headers != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response2.headers->GetStatusLine()); + + // Verify we got all the headers + EXPECT_TRUE(response2.headers->HasHeaderValue( + "url", + "http://www.google.com/foo.dat")); + EXPECT_TRUE(response2.headers->HasHeaderValue("hello", "bye")); + EXPECT_TRUE(response2.headers->HasHeaderValue("status", "200")); + EXPECT_TRUE(response2.headers->HasHeaderValue("version", "HTTP/1.1")); +} + +TEST_P(SpdyNetworkTransactionTest, SynReplyWithHeaders) { + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); + MockWrite writes[] = { CreateMockWrite(*req) }; + + static const char* const kInitialHeaders[] = { + "status", + "200 OK", + "version", + "HTTP/1.1" + }; + static const char* const kLateHeaders[] = { + "hello", + "bye", + }; + scoped_ptr<spdy::SpdyFrame> + stream1_reply(ConstructSpdyControlFrame(kInitialHeaders, + arraysize(kInitialHeaders) / 2, + false, + 1, + LOWEST, + spdy::SYN_REPLY, + spdy::CONTROL_FLAG_NONE, + NULL, + 0, + 0)); + scoped_ptr<spdy::SpdyFrame> + stream1_headers(ConstructSpdyControlFrame(kLateHeaders, + arraysize(kLateHeaders) / 2, + false, + 1, + LOWEST, + spdy::HEADERS, + spdy::CONTROL_FLAG_NONE, + NULL, + 0, + 0)); + scoped_ptr<spdy::SpdyFrame> stream1_body(ConstructSpdyBodyFrame(1, true)); + MockRead reads[] = { + CreateMockRead(*stream1_reply), + CreateMockRead(*stream1_headers), + CreateMockRead(*stream1_body), + MockRead(true, 0, 0) // EOF + }; + + scoped_refptr<DelayedSocketData> data( + new DelayedSocketData(1, reads, arraysize(reads), + writes, arraysize(writes))); + NormalSpdyTransactionHelper helper(CreateGetRequest(), + BoundNetLog(), GetParam()); + helper.RunToCompletion(data.get()); + TransactionHelperResult out = helper.output(); + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); + EXPECT_EQ("hello!", out.response_data); +} + +TEST_P(SpdyNetworkTransactionTest, SynReplyWithLateHeaders) { + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); + MockWrite writes[] = { CreateMockWrite(*req) }; + + static const char* const kInitialHeaders[] = { + "status", + "200 OK", + "version", + "HTTP/1.1" + }; + static const char* const kLateHeaders[] = { + "hello", + "bye", + }; + scoped_ptr<spdy::SpdyFrame> + stream1_reply(ConstructSpdyControlFrame(kInitialHeaders, + arraysize(kInitialHeaders) / 2, + false, + 1, + LOWEST, + spdy::SYN_REPLY, + spdy::CONTROL_FLAG_NONE, + NULL, + 0, + 0)); + scoped_ptr<spdy::SpdyFrame> + stream1_headers(ConstructSpdyControlFrame(kLateHeaders, + arraysize(kLateHeaders) / 2, + false, + 1, + LOWEST, + spdy::HEADERS, + spdy::CONTROL_FLAG_NONE, + NULL, + 0, + 0)); + scoped_ptr<spdy::SpdyFrame> stream1_body(ConstructSpdyBodyFrame(1, false)); + scoped_ptr<spdy::SpdyFrame> stream1_body2(ConstructSpdyBodyFrame(1, true)); + MockRead reads[] = { + CreateMockRead(*stream1_reply), + CreateMockRead(*stream1_body), + CreateMockRead(*stream1_headers), + CreateMockRead(*stream1_body2), + MockRead(true, 0, 0) // EOF + }; + + scoped_refptr<DelayedSocketData> data( + new DelayedSocketData(1, reads, arraysize(reads), + writes, arraysize(writes))); + NormalSpdyTransactionHelper helper(CreateGetRequest(), + BoundNetLog(), GetParam()); + helper.RunToCompletion(data.get()); + TransactionHelperResult out = helper.output(); + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); + EXPECT_EQ("hello!hello!", out.response_data); +} + +TEST_P(SpdyNetworkTransactionTest, SynReplyWithDuplicateLateHeaders) { + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); + MockWrite writes[] = { CreateMockWrite(*req) }; + + static const char* const kInitialHeaders[] = { + "status", + "200 OK", + "version", + "HTTP/1.1" + }; + static const char* const kLateHeaders[] = { + "status", + "500 Server Error", + }; + scoped_ptr<spdy::SpdyFrame> + stream1_reply(ConstructSpdyControlFrame(kInitialHeaders, + arraysize(kInitialHeaders) / 2, + false, + 1, + LOWEST, + spdy::SYN_REPLY, + spdy::CONTROL_FLAG_NONE, + NULL, + 0, + 0)); + scoped_ptr<spdy::SpdyFrame> + stream1_headers(ConstructSpdyControlFrame(kLateHeaders, + arraysize(kLateHeaders) / 2, + false, + 1, + LOWEST, + spdy::HEADERS, + spdy::CONTROL_FLAG_NONE, + NULL, + 0, + 0)); + scoped_ptr<spdy::SpdyFrame> stream1_body(ConstructSpdyBodyFrame(1, false)); + scoped_ptr<spdy::SpdyFrame> stream1_body2(ConstructSpdyBodyFrame(1, true)); + MockRead reads[] = { + CreateMockRead(*stream1_reply), + CreateMockRead(*stream1_body), + CreateMockRead(*stream1_headers), + CreateMockRead(*stream1_body2), + MockRead(true, 0, 0) // EOF + }; + + scoped_refptr<DelayedSocketData> data( + new DelayedSocketData(1, reads, arraysize(reads), + writes, arraysize(writes))); + NormalSpdyTransactionHelper helper(CreateGetRequest(), + BoundNetLog(), GetParam()); + helper.RunToCompletion(data.get()); + TransactionHelperResult out = helper.output(); + EXPECT_EQ(ERR_SPDY_PROTOCOL_ERROR, out.rv); +} + } // namespace net |