1 // Copyright (c) 2011 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/spdy/spdy_http_stream.h" 6 #include "net/spdy/spdy_session.h" 7 #include "net/spdy/spdy_test_util.h" 8 #include "testing/gtest/include/gtest/gtest.h" 9 10 namespace net { 11 12 class SpdyHttpStreamTest : public testing::Test { 13 public: 14 OrderedSocketData* data() { return data_; } 15 protected: 16 SpdyHttpStreamTest() {} 17 18 void EnableCompression(bool enabled) { 19 spdy::SpdyFramer::set_enable_compression_default(enabled); 20 } 21 22 virtual void TearDown() { 23 MessageLoop::current()->RunAllPending(); 24 } 25 int InitSession(MockRead* reads, size_t reads_count, 26 MockWrite* writes, size_t writes_count, 27 HostPortPair& host_port_pair) { 28 HostPortProxyPair pair(host_port_pair, ProxyServer::Direct()); 29 data_ = new OrderedSocketData(reads, reads_count, writes, writes_count); 30 session_deps_.socket_factory->AddSocketDataProvider(data_.get()); 31 http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_); 32 session_ = http_session_->spdy_session_pool()->Get(pair, BoundNetLog()); 33 transport_params_ = new TransportSocketParams(host_port_pair, 34 MEDIUM, GURL(), false, false); 35 TestCompletionCallback callback; 36 scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle); 37 EXPECT_EQ(ERR_IO_PENDING, 38 connection->Init(host_port_pair.ToString(), 39 transport_params_, 40 MEDIUM, 41 &callback, 42 http_session_->transport_socket_pool(), 43 BoundNetLog())); 44 EXPECT_EQ(OK, callback.WaitForResult()); 45 return session_->InitializeWithSocket(connection.release(), false, OK); 46 } 47 SpdySessionDependencies session_deps_; 48 scoped_refptr<OrderedSocketData> data_; 49 scoped_refptr<HttpNetworkSession> http_session_; 50 scoped_refptr<SpdySession> session_; 51 scoped_refptr<TransportSocketParams> transport_params_; 52 }; 53 54 TEST_F(SpdyHttpStreamTest, SendRequest) { 55 EnableCompression(false); 56 SpdySession::SetSSLMode(false); 57 58 scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); 59 MockWrite writes[] = { 60 CreateMockWrite(*req.get(), 1), 61 }; 62 scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); 63 MockRead reads[] = { 64 CreateMockRead(*resp, 2), 65 MockRead(false, 0, 3) // EOF 66 }; 67 68 HostPortPair host_port_pair("www.google.com", 80); 69 HostPortProxyPair pair(host_port_pair, ProxyServer::Direct()); 70 EXPECT_EQ(OK, InitSession(reads, arraysize(reads), writes, arraysize(writes), 71 host_port_pair)); 72 73 HttpRequestInfo request; 74 request.method = "GET"; 75 request.url = GURL("http://www.google.com/"); 76 TestCompletionCallback callback; 77 HttpResponseInfo response; 78 HttpRequestHeaders headers; 79 BoundNetLog net_log; 80 scoped_ptr<SpdyHttpStream> http_stream( 81 new SpdyHttpStream(session_.get(), true)); 82 ASSERT_EQ( 83 OK, 84 http_stream->InitializeStream(&request, net_log, NULL)); 85 86 EXPECT_EQ(ERR_IO_PENDING, 87 http_stream->SendRequest(headers, NULL, &response, &callback)); 88 EXPECT_TRUE(http_session_->spdy_session_pool()->HasSession(pair)); 89 90 // This triggers the MockWrite and read 2 91 callback.WaitForResult(); 92 93 // This triggers read 3. The empty read causes the session to shut down. 94 data()->CompleteRead(); 95 96 // Because we abandoned the stream, we don't expect to find a session in the 97 // pool anymore. 98 EXPECT_FALSE(http_session_->spdy_session_pool()->HasSession(pair)); 99 EXPECT_TRUE(data()->at_read_eof()); 100 EXPECT_TRUE(data()->at_write_eof()); 101 } 102 103 TEST_F(SpdyHttpStreamTest, SendChunkedPost) { 104 EnableCompression(false); 105 SpdySession::SetSSLMode(false); 106 UploadDataStream::set_merge_chunks(false); 107 108 scoped_ptr<spdy::SpdyFrame> req(ConstructChunkedSpdyPost(NULL, 0)); 109 scoped_ptr<spdy::SpdyFrame> chunk1(ConstructSpdyBodyFrame(1, false)); 110 scoped_ptr<spdy::SpdyFrame> chunk2(ConstructSpdyBodyFrame(1, true)); 111 MockWrite writes[] = { 112 CreateMockWrite(*req.get(), 1), 113 CreateMockWrite(*chunk1, 2), // POST upload frames 114 CreateMockWrite(*chunk2, 3), 115 }; 116 scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyPostSynReply(NULL, 0)); 117 MockRead reads[] = { 118 CreateMockRead(*resp, 4), 119 CreateMockRead(*chunk1, 5), 120 CreateMockRead(*chunk2, 5), 121 MockRead(false, 0, 6) // EOF 122 }; 123 124 HostPortPair host_port_pair("www.google.com", 80); 125 HostPortProxyPair pair(host_port_pair, ProxyServer::Direct()); 126 EXPECT_EQ(OK, InitSession(reads, arraysize(reads), writes, arraysize(writes), 127 host_port_pair)); 128 129 HttpRequestInfo request; 130 request.method = "POST"; 131 request.url = GURL("http://www.google.com/"); 132 request.upload_data = new UploadData(); 133 request.upload_data->set_is_chunked(true); 134 request.upload_data->AppendChunk(kUploadData, kUploadDataSize, false); 135 request.upload_data->AppendChunk(kUploadData, kUploadDataSize, true); 136 TestCompletionCallback callback; 137 HttpResponseInfo response; 138 HttpRequestHeaders headers; 139 BoundNetLog net_log; 140 SpdyHttpStream http_stream(session_.get(), true); 141 ASSERT_EQ( 142 OK, 143 http_stream.InitializeStream(&request, net_log, NULL)); 144 145 UploadDataStream* upload_stream = 146 UploadDataStream::Create(request.upload_data, NULL); 147 EXPECT_EQ(ERR_IO_PENDING, http_stream.SendRequest( 148 headers, upload_stream, &response, &callback)); 149 EXPECT_TRUE(http_session_->spdy_session_pool()->HasSession(pair)); 150 151 // This triggers the MockWrite and read 2 152 callback.WaitForResult(); 153 154 // This triggers read 3. The empty read causes the session to shut down. 155 data()->CompleteRead(); 156 MessageLoop::current()->RunAllPending(); 157 158 // Because we abandoned the stream, we don't expect to find a session in the 159 // pool anymore. 160 EXPECT_FALSE(http_session_->spdy_session_pool()->HasSession(pair)); 161 EXPECT_TRUE(data()->at_read_eof()); 162 EXPECT_TRUE(data()->at_write_eof()); 163 } 164 165 // Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058 166 TEST_F(SpdyHttpStreamTest, SpdyURLTest) { 167 EnableCompression(false); 168 SpdySession::SetSSLMode(false); 169 170 const char * const full_url = "http://www.google.com/foo?query=what#anchor"; 171 const char * const base_url = "http://www.google.com/foo?query=what"; 172 scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(base_url, false, 1, LOWEST)); 173 MockWrite writes[] = { 174 CreateMockWrite(*req.get(), 1), 175 }; 176 scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); 177 MockRead reads[] = { 178 CreateMockRead(*resp, 2), 179 MockRead(false, 0, 3) // EOF 180 }; 181 182 HostPortPair host_port_pair("www.google.com", 80); 183 HostPortProxyPair pair(host_port_pair, ProxyServer::Direct()); 184 EXPECT_EQ(OK, InitSession(reads, arraysize(reads), writes, arraysize(writes), 185 host_port_pair)); 186 187 HttpRequestInfo request; 188 request.method = "GET"; 189 request.url = GURL(full_url); 190 TestCompletionCallback callback; 191 HttpResponseInfo response; 192 HttpRequestHeaders headers; 193 BoundNetLog net_log; 194 scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true)); 195 ASSERT_EQ( 196 OK, 197 http_stream->InitializeStream(&request, net_log, NULL)); 198 199 EXPECT_EQ(ERR_IO_PENDING, 200 http_stream->SendRequest(headers, NULL, &response, &callback)); 201 202 spdy::SpdyHeaderBlock* spdy_header = 203 http_stream->stream()->spdy_headers().get(); 204 EXPECT_TRUE(spdy_header != NULL); 205 if (spdy_header->find("url") != spdy_header->end()) 206 EXPECT_EQ("/foo?query=what", spdy_header->find("url")->second); 207 else 208 FAIL() << "No url is set in spdy_header!"; 209 210 // This triggers the MockWrite and read 2 211 callback.WaitForResult(); 212 213 // This triggers read 3. The empty read causes the session to shut down. 214 data()->CompleteRead(); 215 216 // Because we abandoned the stream, we don't expect to find a session in the 217 // pool anymore. 218 EXPECT_FALSE(http_session_->spdy_session_pool()->HasSession(pair)); 219 EXPECT_TRUE(data()->at_read_eof()); 220 EXPECT_TRUE(data()->at_write_eof()); 221 } 222 223 // TODO(willchan): Write a longer test for SpdyStream that exercises all 224 // methods. 225 226 } // namespace net 227