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
26 namespace net {
27
28 namespace {
29
30 using ::testing::Return;
31
32 // Inherit from URLRequestHttpJob to expose the priority and some
33 // other hidden functions.
34 class TestURLRequestHttpJob : public URLRequestHttpJob {
35 public:
TestURLRequestHttpJob(URLRequest * request)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:
~TestURLRequestHttpJob()46 virtual ~TestURLRequestHttpJob() {}
47 };
48
49 class URLRequestHttpJobTest : public ::testing::Test {
50 protected:
URLRequestHttpJobTest()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
TransactionAcceptsSdchEncoding()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
EnableSdch()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.
TEST_F(URLRequestHttpJobTest,SetPriorityBasic)98 TEST_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.
TEST_F(URLRequestHttpJobTest,SetTransactionPriorityOnStart)118 TEST_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.
TEST_F(URLRequestHttpJobTest,SetTransactionPriority)133 TEST_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.
TEST_F(URLRequestHttpJobTest,SetSubsequentTransactionPriority)147 TEST_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.
TEST_F(URLRequestHttpJobTest,SdchAdvertisementGet)166 TEST_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.
TEST_F(URLRequestHttpJobTest,SdchAdvertisementPost)176 TEST_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.
187 class URLRequestHttpJobWebSocketTestBase : public ::testing::Test {
188 protected:
URLRequestHttpJobWebSocketTestBase()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
208 class URLRequestHttpJobWebSocketTest
209 : public URLRequestHttpJobWebSocketTestBase {
210 protected:
URLRequestHttpJobWebSocketTest()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
228 class 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.
CreateBasicStream(scoped_ptr<ClientSocketHandle> connection,bool using_proxy)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
247 class FakeWebSocketHandshakeStream : public WebSocketHandshakeStreamBase {
248 public:
FakeWebSocketHandshakeStream()249 FakeWebSocketHandshakeStream() : initialize_stream_was_called_(false) {}
250
initialize_stream_was_called() const251 bool initialize_stream_was_called() const {
252 return initialize_stream_was_called_;
253 }
254
255 // Fake implementation of HttpStreamBase methods.
InitializeStream(const HttpRequestInfo * request_info,RequestPriority priority,const BoundNetLog & net_log,const CompletionCallback & callback)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
SendRequest(const HttpRequestHeaders & request_headers,HttpResponseInfo * response,const CompletionCallback & callback)264 virtual int SendRequest(const HttpRequestHeaders& request_headers,
265 HttpResponseInfo* response,
266 const CompletionCallback& callback) OVERRIDE {
267 return ERR_IO_PENDING;
268 }
269
ReadResponseHeaders(const CompletionCallback & callback)270 virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE {
271 return ERR_IO_PENDING;
272 }
273
ReadResponseBody(IOBuffer * buf,int buf_len,const CompletionCallback & callback)274 virtual int ReadResponseBody(IOBuffer* buf,
275 int buf_len,
276 const CompletionCallback& callback) OVERRIDE {
277 return ERR_IO_PENDING;
278 }
279
Close(bool not_reusable)280 virtual void Close(bool not_reusable) OVERRIDE {}
281
IsResponseBodyComplete() const282 virtual bool IsResponseBodyComplete() const OVERRIDE { return false; }
283
CanFindEndOfResponse() const284 virtual bool CanFindEndOfResponse() const OVERRIDE { return false; }
285
IsConnectionReused() const286 virtual bool IsConnectionReused() const OVERRIDE { return false; }
SetConnectionReused()287 virtual void SetConnectionReused() OVERRIDE {}
288
IsConnectionReusable() const289 virtual bool IsConnectionReusable() const OVERRIDE { return false; }
290
GetTotalReceivedBytes() const291 virtual int64 GetTotalReceivedBytes() const OVERRIDE { return 0; }
292
GetLoadTimingInfo(LoadTimingInfo * load_timing_info) const293 virtual bool GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const
294 OVERRIDE {
295 return false;
296 }
297
GetSSLInfo(SSLInfo * ssl_info)298 virtual void GetSSLInfo(SSLInfo* ssl_info) OVERRIDE {}
299
GetSSLCertRequestInfo(SSLCertRequestInfo * cert_request_info)300 virtual void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info)
301 OVERRIDE {}
302
IsSpdyHttpStream() const303 virtual bool IsSpdyHttpStream() const OVERRIDE { return false; }
304
Drain(HttpNetworkSession * session)305 virtual void Drain(HttpNetworkSession* session) OVERRIDE {}
306
SetPriority(RequestPriority priority)307 virtual void SetPriority(RequestPriority priority) OVERRIDE {}
308
309 // Fake implementation of WebSocketHandshakeStreamBase method(s)
Upgrade()310 virtual scoped_ptr<WebSocketStream> Upgrade() OVERRIDE {
311 return scoped_ptr<WebSocketStream>();
312 }
313
314 private:
315 bool initialize_stream_was_called_;
316 };
317
TEST_F(URLRequestHttpJobWebSocketTest,RejectedWithoutCreateHelper)318 TEST_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
TEST_F(URLRequestHttpJobWebSocketTest,CreateHelperPassedThrough)327 TEST_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