1// Copyright (c) 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/url_request/url_request_http_job.h" 6 7#include <cstddef> 8 9#include "base/compiler_specific.h" 10#include "base/memory/ref_counted.h" 11#include "base/memory/scoped_ptr.h" 12#include "base/run_loop.h" 13#include "net/base/auth.h" 14#include "net/base/request_priority.h" 15#include "net/http/http_transaction_factory.h" 16#include "net/http/http_transaction_test_util.h" 17#include "net/socket/socket_test_util.h" 18#include "net/url_request/url_request.h" 19#include "net/url_request/url_request_status.h" 20#include "net/url_request/url_request_test_util.h" 21#include "net/websockets/websocket_handshake_stream_base.h" 22#include "testing/gmock/include/gmock/gmock.h" 23#include "testing/gtest/include/gtest/gtest.h" 24#include "url/gurl.h" 25 26namespace net { 27 28namespace { 29 30using ::testing::Return; 31 32// Inherit from URLRequestHttpJob to expose the priority and some 33// other hidden functions. 34class TestURLRequestHttpJob : public URLRequestHttpJob { 35 public: 36 explicit TestURLRequestHttpJob(URLRequest* request) 37 : URLRequestHttpJob(request, request->context()->network_delegate(), 38 request->context()->http_user_agent_settings()) {} 39 40 using URLRequestHttpJob::SetPriority; 41 using URLRequestHttpJob::Start; 42 using URLRequestHttpJob::Kill; 43 using URLRequestHttpJob::priority; 44 45 protected: 46 virtual ~TestURLRequestHttpJob() {} 47}; 48 49class URLRequestHttpJobTest : public ::testing::Test { 50 protected: 51 URLRequestHttpJobTest() 52 : req_(context_.CreateRequest(GURL("http://www.example.com"), 53 DEFAULT_PRIORITY, 54 &delegate_, 55 NULL)) { 56 context_.set_http_transaction_factory(&network_layer_); 57 } 58 59 bool TransactionAcceptsSdchEncoding() { 60 base::WeakPtr<MockNetworkTransaction> transaction( 61 network_layer_.last_transaction()); 62 EXPECT_TRUE(transaction); 63 if (!transaction) return false; 64 65 const HttpRequestInfo* request_info = transaction->request(); 66 EXPECT_TRUE(request_info); 67 if (!request_info) return false; 68 69 std::string encoding_headers; 70 bool get_success = request_info->extra_headers.GetHeader( 71 "Accept-Encoding", &encoding_headers); 72 EXPECT_TRUE(get_success); 73 if (!get_success) return false; 74 75 // This check isn't wrapped with EXPECT* macros because different 76 // results from this function may be expected in different tests. 77 std::vector<std::string> tokens; 78 size_t num_tokens = Tokenize(encoding_headers, ", ", &tokens); 79 for (size_t i = 0; i < num_tokens; i++) { 80 if (!base::strncasecmp(tokens[i].data(), "sdch", tokens[i].length())) 81 return true; 82 } 83 return false; 84 } 85 86 void EnableSdch() { 87 context_.SetSdchManager(scoped_ptr<SdchManager>(new SdchManager).Pass()); 88 } 89 90 MockNetworkLayer network_layer_; 91 TestURLRequestContext context_; 92 TestDelegate delegate_; 93 scoped_ptr<URLRequest> req_; 94}; 95 96// Make sure that SetPriority actually sets the URLRequestHttpJob's 97// priority, both before and after start. 98TEST_F(URLRequestHttpJobTest, SetPriorityBasic) { 99 scoped_refptr<TestURLRequestHttpJob> job( 100 new TestURLRequestHttpJob(req_.get())); 101 EXPECT_EQ(DEFAULT_PRIORITY, job->priority()); 102 103 job->SetPriority(LOWEST); 104 EXPECT_EQ(LOWEST, job->priority()); 105 106 job->SetPriority(LOW); 107 EXPECT_EQ(LOW, job->priority()); 108 109 job->Start(); 110 EXPECT_EQ(LOW, job->priority()); 111 112 job->SetPriority(MEDIUM); 113 EXPECT_EQ(MEDIUM, job->priority()); 114} 115 116// Make sure that URLRequestHttpJob passes on its priority to its 117// transaction on start. 118TEST_F(URLRequestHttpJobTest, SetTransactionPriorityOnStart) { 119 scoped_refptr<TestURLRequestHttpJob> job( 120 new TestURLRequestHttpJob(req_.get())); 121 job->SetPriority(LOW); 122 123 EXPECT_FALSE(network_layer_.last_transaction()); 124 125 job->Start(); 126 127 ASSERT_TRUE(network_layer_.last_transaction()); 128 EXPECT_EQ(LOW, network_layer_.last_transaction()->priority()); 129} 130 131// Make sure that URLRequestHttpJob passes on its priority updates to 132// its transaction. 133TEST_F(URLRequestHttpJobTest, SetTransactionPriority) { 134 scoped_refptr<TestURLRequestHttpJob> job( 135 new TestURLRequestHttpJob(req_.get())); 136 job->SetPriority(LOW); 137 job->Start(); 138 ASSERT_TRUE(network_layer_.last_transaction()); 139 EXPECT_EQ(LOW, network_layer_.last_transaction()->priority()); 140 141 job->SetPriority(HIGHEST); 142 EXPECT_EQ(HIGHEST, network_layer_.last_transaction()->priority()); 143} 144 145// Make sure that URLRequestHttpJob passes on its priority updates to 146// newly-created transactions after the first one. 147TEST_F(URLRequestHttpJobTest, SetSubsequentTransactionPriority) { 148 scoped_refptr<TestURLRequestHttpJob> job( 149 new TestURLRequestHttpJob(req_.get())); 150 job->Start(); 151 152 job->SetPriority(LOW); 153 ASSERT_TRUE(network_layer_.last_transaction()); 154 EXPECT_EQ(LOW, network_layer_.last_transaction()->priority()); 155 156 job->Kill(); 157 network_layer_.ClearLastTransaction(); 158 159 // Creates a second transaction. 160 job->Start(); 161 ASSERT_TRUE(network_layer_.last_transaction()); 162 EXPECT_EQ(LOW, network_layer_.last_transaction()->priority()); 163} 164 165// Confirm we do advertise SDCH encoding in the case of a GET. 166TEST_F(URLRequestHttpJobTest, SdchAdvertisementGet) { 167 EnableSdch(); 168 req_->set_method("GET"); // Redundant with default. 169 scoped_refptr<TestURLRequestHttpJob> job( 170 new TestURLRequestHttpJob(req_.get())); 171 job->Start(); 172 EXPECT_TRUE(TransactionAcceptsSdchEncoding()); 173} 174 175// Confirm we don't advertise SDCH encoding in the case of a POST. 176TEST_F(URLRequestHttpJobTest, SdchAdvertisementPost) { 177 EnableSdch(); 178 req_->set_method("POST"); 179 scoped_refptr<TestURLRequestHttpJob> job( 180 new TestURLRequestHttpJob(req_.get())); 181 job->Start(); 182 EXPECT_FALSE(TransactionAcceptsSdchEncoding()); 183} 184 185// This base class just serves to set up some things before the TestURLRequest 186// constructor is called. 187class URLRequestHttpJobWebSocketTestBase : public ::testing::Test { 188 protected: 189 URLRequestHttpJobWebSocketTestBase() : socket_data_(NULL, 0, NULL, 0), 190 context_(true) { 191 // A Network Delegate is required for the WebSocketHandshakeStreamBase 192 // object to be passed on to the HttpNetworkTransaction. 193 context_.set_network_delegate(&network_delegate_); 194 195 // Attempting to create real ClientSocketHandles is not going to work out so 196 // well. Set up a fake socket factory. 197 socket_factory_.AddSocketDataProvider(&socket_data_); 198 context_.set_client_socket_factory(&socket_factory_); 199 context_.Init(); 200 } 201 202 StaticSocketDataProvider socket_data_; 203 TestNetworkDelegate network_delegate_; 204 MockClientSocketFactory socket_factory_; 205 TestURLRequestContext context_; 206}; 207 208class URLRequestHttpJobWebSocketTest 209 : public URLRequestHttpJobWebSocketTestBase { 210 protected: 211 URLRequestHttpJobWebSocketTest() 212 : req_(context_.CreateRequest(GURL("ws://www.example.com"), 213 DEFAULT_PRIORITY, 214 &delegate_, 215 NULL)) { 216 // The TestNetworkDelegate expects a call to NotifyBeforeURLRequest before 217 // anything else happens. 218 GURL url("ws://localhost/"); 219 TestCompletionCallback dummy; 220 network_delegate_.NotifyBeforeURLRequest( 221 req_.get(), dummy.callback(), &url); 222 } 223 224 TestDelegate delegate_; 225 scoped_ptr<URLRequest> req_; 226}; 227 228class MockCreateHelper : public WebSocketHandshakeStreamBase::CreateHelper { 229 public: 230 // GoogleMock does not appear to play nicely with move-only types like 231 // scoped_ptr, so this forwarding method acts as a workaround. 232 virtual WebSocketHandshakeStreamBase* CreateBasicStream( 233 scoped_ptr<ClientSocketHandle> connection, 234 bool using_proxy) OVERRIDE { 235 // Discard the arguments since we don't need them anyway. 236 return CreateBasicStreamMock(); 237 } 238 239 MOCK_METHOD0(CreateBasicStreamMock, 240 WebSocketHandshakeStreamBase*()); 241 242 MOCK_METHOD2(CreateSpdyStream, 243 WebSocketHandshakeStreamBase*(const base::WeakPtr<SpdySession>&, 244 bool)); 245}; 246 247class FakeWebSocketHandshakeStream : public WebSocketHandshakeStreamBase { 248 public: 249 FakeWebSocketHandshakeStream() : initialize_stream_was_called_(false) {} 250 251 bool initialize_stream_was_called() const { 252 return initialize_stream_was_called_; 253 } 254 255 // Fake implementation of HttpStreamBase methods. 256 virtual int InitializeStream(const HttpRequestInfo* request_info, 257 RequestPriority priority, 258 const BoundNetLog& net_log, 259 const CompletionCallback& callback) OVERRIDE { 260 initialize_stream_was_called_ = true; 261 return ERR_IO_PENDING; 262 } 263 264 virtual int SendRequest(const HttpRequestHeaders& request_headers, 265 HttpResponseInfo* response, 266 const CompletionCallback& callback) OVERRIDE { 267 return ERR_IO_PENDING; 268 } 269 270 virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE { 271 return ERR_IO_PENDING; 272 } 273 274 virtual int ReadResponseBody(IOBuffer* buf, 275 int buf_len, 276 const CompletionCallback& callback) OVERRIDE { 277 return ERR_IO_PENDING; 278 } 279 280 virtual void Close(bool not_reusable) OVERRIDE {} 281 282 virtual bool IsResponseBodyComplete() const OVERRIDE { return false; } 283 284 virtual bool CanFindEndOfResponse() const OVERRIDE { return false; } 285 286 virtual bool IsConnectionReused() const OVERRIDE { return false; } 287 virtual void SetConnectionReused() OVERRIDE {} 288 289 virtual bool IsConnectionReusable() const OVERRIDE { return false; } 290 291 virtual int64 GetTotalReceivedBytes() const OVERRIDE { return 0; } 292 293 virtual bool GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const 294 OVERRIDE { 295 return false; 296 } 297 298 virtual void GetSSLInfo(SSLInfo* ssl_info) OVERRIDE {} 299 300 virtual void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info) 301 OVERRIDE {} 302 303 virtual bool IsSpdyHttpStream() const OVERRIDE { return false; } 304 305 virtual void Drain(HttpNetworkSession* session) OVERRIDE {} 306 307 virtual void SetPriority(RequestPriority priority) OVERRIDE {} 308 309 // Fake implementation of WebSocketHandshakeStreamBase method(s) 310 virtual scoped_ptr<WebSocketStream> Upgrade() OVERRIDE { 311 return scoped_ptr<WebSocketStream>(); 312 } 313 314 private: 315 bool initialize_stream_was_called_; 316}; 317 318TEST_F(URLRequestHttpJobWebSocketTest, RejectedWithoutCreateHelper) { 319 scoped_refptr<TestURLRequestHttpJob> job( 320 new TestURLRequestHttpJob(req_.get())); 321 job->Start(); 322 base::RunLoop().RunUntilIdle(); 323 EXPECT_EQ(URLRequestStatus::FAILED, req_->status().status()); 324 EXPECT_EQ(ERR_DISALLOWED_URL_SCHEME, req_->status().error()); 325} 326 327TEST_F(URLRequestHttpJobWebSocketTest, CreateHelperPassedThrough) { 328 scoped_refptr<TestURLRequestHttpJob> job( 329 new TestURLRequestHttpJob(req_.get())); 330 scoped_ptr<MockCreateHelper> create_helper( 331 new ::testing::StrictMock<MockCreateHelper>()); 332 FakeWebSocketHandshakeStream* fake_handshake_stream( 333 new FakeWebSocketHandshakeStream); 334 // Ownership of fake_handshake_stream is transferred when CreateBasicStream() 335 // is called. 336 EXPECT_CALL(*create_helper, CreateBasicStreamMock()) 337 .WillOnce(Return(fake_handshake_stream)); 338 req_->SetUserData(WebSocketHandshakeStreamBase::CreateHelper::DataKey(), 339 create_helper.release()); 340 req_->SetLoadFlags(LOAD_DISABLE_CACHE); 341 job->Start(); 342 base::RunLoop().RunUntilIdle(); 343 EXPECT_EQ(URLRequestStatus::IO_PENDING, req_->status().status()); 344 EXPECT_TRUE(fake_handshake_stream->initialize_stream_was_called()); 345} 346 347} // namespace 348 349} // namespace net 350