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 "base/memory/ref_counted.h" 6 #include "net/spdy/spdy_stream.h" 7 #include "net/spdy/spdy_http_utils.h" 8 #include "net/spdy/spdy_session.h" 9 #include "net/spdy/spdy_test_util.h" 10 #include "testing/gtest/include/gtest/gtest.h" 11 12 namespace net { 13 14 // TODO(ukai): factor out common part with spdy_http_stream_unittest.cc 15 class SpdySessionPoolPeer { 16 public: 17 explicit SpdySessionPoolPeer(SpdySessionPool* pool) 18 : pool_(pool) {} 19 20 void RemoveSpdySession(const scoped_refptr<SpdySession>& session) { 21 pool_->Remove(session); 22 } 23 24 private: 25 SpdySessionPool* const pool_; 26 27 DISALLOW_COPY_AND_ASSIGN(SpdySessionPoolPeer); 28 }; 29 30 namespace { 31 32 class TestSpdyStreamDelegate : public SpdyStream::Delegate { 33 public: 34 TestSpdyStreamDelegate(SpdyStream* stream, 35 IOBufferWithSize* buf, 36 CompletionCallback* callback) 37 : stream_(stream), 38 buf_(buf), 39 callback_(callback), 40 send_headers_completed_(false), 41 response_(new spdy::SpdyHeaderBlock), 42 data_sent_(0), 43 closed_(false) {} 44 virtual ~TestSpdyStreamDelegate() {} 45 46 virtual bool OnSendHeadersComplete(int status) { 47 send_headers_completed_ = true; 48 return true; 49 } 50 virtual int OnSendBody() { 51 ADD_FAILURE() << "OnSendBody should not be called"; 52 return ERR_UNEXPECTED; 53 } 54 virtual int OnSendBodyComplete(int /*status*/, bool* /*eof*/) { 55 ADD_FAILURE() << "OnSendBodyComplete should not be called"; 56 return ERR_UNEXPECTED; 57 } 58 59 virtual int OnResponseReceived(const spdy::SpdyHeaderBlock& response, 60 base::Time response_time, 61 int status) { 62 EXPECT_TRUE(send_headers_completed_); 63 *response_ = response; 64 if (buf_) { 65 EXPECT_EQ(ERR_IO_PENDING, 66 stream_->WriteStreamData(buf_.get(), buf_->size(), 67 spdy::DATA_FLAG_NONE)); 68 } 69 return status; 70 } 71 virtual void OnDataReceived(const char* buffer, int bytes) { 72 received_data_ += std::string(buffer, bytes); 73 } 74 virtual void OnDataSent(int length) { 75 data_sent_ += length; 76 } 77 virtual void OnClose(int status) { 78 closed_ = true; 79 CompletionCallback* callback = callback_; 80 callback_ = NULL; 81 callback->Run(OK); 82 } 83 virtual void set_chunk_callback(net::ChunkCallback *) {} 84 85 bool send_headers_completed() const { return send_headers_completed_; } 86 const linked_ptr<spdy::SpdyHeaderBlock>& response() const { 87 return response_; 88 } 89 const std::string& received_data() const { return received_data_; } 90 int data_sent() const { return data_sent_; } 91 bool closed() const { return closed_; } 92 93 private: 94 SpdyStream* stream_; 95 scoped_refptr<IOBufferWithSize> buf_; 96 CompletionCallback* callback_; 97 bool send_headers_completed_; 98 linked_ptr<spdy::SpdyHeaderBlock> response_; 99 std::string received_data_; 100 int data_sent_; 101 bool closed_; 102 }; 103 104 spdy::SpdyFrame* ConstructSpdyBodyFrame(const char* data, int length) { 105 spdy::SpdyFramer framer; 106 return framer.CreateDataFrame(1, data, length, spdy::DATA_FLAG_NONE); 107 } 108 109 } // anonymous namespace 110 111 class SpdyStreamTest : public testing::Test { 112 protected: 113 SpdyStreamTest() { 114 } 115 116 scoped_refptr<SpdySession> CreateSpdySession() { 117 spdy::SpdyFramer::set_enable_compression_default(false); 118 HostPortPair host_port_pair("www.google.com", 80); 119 HostPortProxyPair pair(host_port_pair, ProxyServer::Direct()); 120 scoped_refptr<SpdySession> session( 121 session_->spdy_session_pool()->Get(pair, BoundNetLog())); 122 return session; 123 } 124 125 virtual void TearDown() { 126 MessageLoop::current()->RunAllPending(); 127 } 128 129 scoped_refptr<HttpNetworkSession> session_; 130 }; 131 132 TEST_F(SpdyStreamTest, SendDataAfterOpen) { 133 SpdySessionDependencies session_deps; 134 135 session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps); 136 SpdySessionPoolPeer pool_peer_(session_->spdy_session_pool()); 137 138 const SpdyHeaderInfo kSynStartHeader = { 139 spdy::SYN_STREAM, 140 1, 141 0, 142 net::ConvertRequestPriorityToSpdyPriority(LOWEST), 143 spdy::CONTROL_FLAG_NONE, 144 false, 145 spdy::INVALID, 146 NULL, 147 0, 148 spdy::DATA_FLAG_NONE 149 }; 150 static const char* const kGetHeaders[] = { 151 "method", 152 "GET", 153 "scheme", 154 "http", 155 "host", 156 "www.google.com", 157 "path", 158 "/", 159 "version", 160 "HTTP/1.1", 161 }; 162 scoped_ptr<spdy::SpdyFrame> req( 163 ConstructSpdyPacket( 164 kSynStartHeader, NULL, 0, kGetHeaders, arraysize(kGetHeaders) / 2)); 165 scoped_ptr<spdy::SpdyFrame> msg( 166 ConstructSpdyBodyFrame("\0hello!\xff", 8)); 167 MockWrite writes[] = { 168 CreateMockWrite(*req), 169 CreateMockWrite(*msg), 170 }; 171 writes[0].sequence_number = 0; 172 writes[1].sequence_number = 2; 173 174 scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); 175 scoped_ptr<spdy::SpdyFrame> echo( 176 ConstructSpdyBodyFrame("\0hello!\xff", 8)); 177 MockRead reads[] = { 178 CreateMockRead(*resp), 179 CreateMockRead(*echo), 180 MockRead(true, 0, 0), // EOF 181 }; 182 reads[0].sequence_number = 1; 183 reads[1].sequence_number = 3; 184 reads[2].sequence_number = 4; 185 186 scoped_refptr<OrderedSocketData> data( 187 new OrderedSocketData(reads, arraysize(reads), 188 writes, arraysize(writes))); 189 MockConnect connect_data(false, OK); 190 data->set_connect_data(connect_data); 191 192 session_deps.socket_factory->AddSocketDataProvider(data.get()); 193 SpdySession::SetSSLMode(false); 194 195 scoped_refptr<SpdySession> session(CreateSpdySession()); 196 const char* kStreamUrl = "http://www.google.com/"; 197 GURL url(kStreamUrl); 198 199 HostPortPair host_port_pair("www.google.com", 80); 200 scoped_refptr<TransportSocketParams> transport_params( 201 new TransportSocketParams(host_port_pair, LOWEST, GURL(), false, false)); 202 203 scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle); 204 EXPECT_EQ(OK, 205 connection->Init(host_port_pair.ToString(), 206 transport_params, 207 LOWEST, 208 NULL, 209 session_->transport_socket_pool(), 210 BoundNetLog())); 211 session->InitializeWithSocket(connection.release(), false, OK); 212 213 scoped_refptr<SpdyStream> stream; 214 ASSERT_EQ( 215 OK, 216 session->CreateStream(url, LOWEST, &stream, BoundNetLog(), NULL)); 217 scoped_refptr<IOBufferWithSize> buf(new IOBufferWithSize(8)); 218 memcpy(buf->data(), "\0hello!\xff", 8); 219 TestCompletionCallback callback; 220 221 scoped_ptr<TestSpdyStreamDelegate> delegate( 222 new TestSpdyStreamDelegate(stream.get(), buf.get(), &callback)); 223 stream->SetDelegate(delegate.get()); 224 225 EXPECT_FALSE(stream->HasUrl()); 226 227 linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock); 228 (*headers)["method"] = "GET"; 229 (*headers)["scheme"] = url.scheme(); 230 (*headers)["host"] = url.host(); 231 (*headers)["path"] = url.path(); 232 (*headers)["version"] = "HTTP/1.1"; 233 stream->set_spdy_headers(headers); 234 EXPECT_TRUE(stream->HasUrl()); 235 EXPECT_EQ(kStreamUrl, stream->GetUrl().spec()); 236 237 EXPECT_EQ(ERR_IO_PENDING, stream->SendRequest(true)); 238 239 EXPECT_EQ(OK, callback.WaitForResult()); 240 241 EXPECT_TRUE(delegate->send_headers_completed()); 242 EXPECT_EQ("200", (*delegate->response())["status"]); 243 EXPECT_EQ("HTTP/1.1", (*delegate->response())["version"]); 244 EXPECT_EQ(std::string("\0hello!\xff", 8), delegate->received_data()); 245 EXPECT_EQ(8, delegate->data_sent()); 246 EXPECT_TRUE(delegate->closed()); 247 } 248 249 TEST_F(SpdyStreamTest, PushedStream) { 250 const char kStreamUrl[] = "http://www.google.com/"; 251 252 SpdySessionDependencies session_deps; 253 session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps); 254 SpdySessionPoolPeer pool_peer_(session_->spdy_session_pool()); 255 scoped_refptr<SpdySession> spdy_session(CreateSpdySession()); 256 BoundNetLog net_log; 257 258 // Conjure up a stream. 259 scoped_refptr<SpdyStream> stream = new SpdyStream(spdy_session, 260 2, 261 true, 262 net_log); 263 EXPECT_FALSE(stream->response_received()); 264 EXPECT_FALSE(stream->HasUrl()); 265 266 // Set a couple of headers. 267 spdy::SpdyHeaderBlock response; 268 response["url"] = kStreamUrl; 269 stream->OnResponseReceived(response); 270 271 // Send some basic headers. 272 spdy::SpdyHeaderBlock headers; 273 response["status"] = "200"; 274 response["version"] = "OK"; 275 stream->OnHeaders(headers); 276 277 stream->set_response_received(); 278 EXPECT_TRUE(stream->response_received()); 279 EXPECT_TRUE(stream->HasUrl()); 280 EXPECT_EQ(kStreamUrl, stream->GetUrl().spec()); 281 } 282 283 284 } // namespace net 285