url_request_ftp_job_unittest.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
1// Copyright (c) 2012 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_ftp_job.h" 6 7#include "base/memory/ref_counted.h" 8#include "base/memory/scoped_vector.h" 9#include "base/run_loop.h" 10#include "googleurl/src/gurl.h" 11#include "net/http/http_transaction_unittest.h" 12#include "net/proxy/proxy_config_service.h" 13#include "net/socket/socket_test_util.h" 14#include "net/url_request/url_request.h" 15#include "net/url_request/url_request_context.h" 16#include "net/url_request/url_request_status.h" 17#include "net/url_request/url_request_test_util.h" 18#include "testing/gtest/include/gtest/gtest.h" 19 20namespace net { 21 22namespace { 23 24class SimpleProxyConfigService : public ProxyConfigService { 25 public: 26 SimpleProxyConfigService() { 27 // Any FTP requests that ever go through HTTP paths are proxied requests. 28 config_.proxy_rules().ParseFromString("ftp=localhost"); 29 } 30 31 virtual void AddObserver(Observer* observer) OVERRIDE { 32 observer_ = observer; 33 } 34 35 virtual void RemoveObserver(Observer* observer) OVERRIDE { 36 if (observer_ == observer) { 37 observer_ = NULL; 38 } 39 } 40 41 virtual ConfigAvailability GetLatestProxyConfig( 42 ProxyConfig* config) OVERRIDE { 43 *config = config_; 44 return CONFIG_VALID; 45 } 46 47 void IncrementConfigId() { 48 config_.set_id(config_.id() + 1); 49 observer_->OnProxyConfigChanged(config_, ProxyConfigService::CONFIG_VALID); 50 } 51 52 private: 53 ProxyConfig config_; 54 Observer* observer_; 55}; 56 57// Inherit from URLRequestFtpJob to expose the priority and some 58// other hidden functions. 59class TestURLRequestFtpJob : public URLRequestFtpJob { 60 public: 61 explicit TestURLRequestFtpJob(URLRequest* request) 62 : URLRequestFtpJob(request, NULL, 63 request->context()->ftp_transaction_factory(), 64 request->context()->ftp_auth_cache()) {} 65 66 using URLRequestFtpJob::SetPriority; 67 using URLRequestFtpJob::Start; 68 using URLRequestFtpJob::Kill; 69 using URLRequestFtpJob::priority; 70 71 protected: 72 virtual ~TestURLRequestFtpJob() {} 73}; 74 75// Fixture for priority-related tests. Priority matters when there is 76// an HTTP proxy. 77class URLRequestFtpJobPriorityTest : public testing::Test { 78 protected: 79 URLRequestFtpJobPriorityTest() 80 : proxy_service_(new SimpleProxyConfigService, NULL, NULL), 81 req_(GURL("ftp://ftp.example.com"), &delegate_, &context_, NULL) { 82 context_.set_proxy_service(&proxy_service_); 83 context_.set_http_transaction_factory(&network_layer_); 84 } 85 86 ProxyService proxy_service_; 87 MockNetworkLayer network_layer_; 88 TestURLRequestContext context_; 89 TestDelegate delegate_; 90 TestURLRequest req_; 91}; 92 93// Make sure that SetPriority actually sets the URLRequestFtpJob's 94// priority, both before and after start. 95TEST_F(URLRequestFtpJobPriorityTest, SetPriorityBasic) { 96 scoped_refptr<TestURLRequestFtpJob> job(new TestURLRequestFtpJob(&req_)); 97 EXPECT_EQ(DEFAULT_PRIORITY, job->priority()); 98 99 job->SetPriority(LOWEST); 100 EXPECT_EQ(LOWEST, job->priority()); 101 102 job->SetPriority(LOW); 103 EXPECT_EQ(LOW, job->priority()); 104 105 job->Start(); 106 EXPECT_EQ(LOW, job->priority()); 107 108 job->SetPriority(MEDIUM); 109 EXPECT_EQ(MEDIUM, job->priority()); 110} 111 112// Make sure that URLRequestFtpJob passes on its priority to its 113// transaction on start. 114TEST_F(URLRequestFtpJobPriorityTest, SetTransactionPriorityOnStart) { 115 scoped_refptr<TestURLRequestFtpJob> job(new TestURLRequestFtpJob(&req_)); 116 job->SetPriority(LOW); 117 118 EXPECT_FALSE(network_layer_.last_transaction()); 119 120 job->Start(); 121 122 ASSERT_TRUE(network_layer_.last_transaction()); 123 EXPECT_EQ(LOW, network_layer_.last_transaction()->priority()); 124} 125 126// Make sure that URLRequestFtpJob passes on its priority updates to 127// its transaction. 128TEST_F(URLRequestFtpJobPriorityTest, SetTransactionPriority) { 129 scoped_refptr<TestURLRequestFtpJob> job(new TestURLRequestFtpJob(&req_)); 130 job->SetPriority(LOW); 131 job->Start(); 132 ASSERT_TRUE(network_layer_.last_transaction()); 133 EXPECT_EQ(LOW, network_layer_.last_transaction()->priority()); 134 135 job->SetPriority(HIGHEST); 136 EXPECT_EQ(HIGHEST, network_layer_.last_transaction()->priority()); 137} 138 139// Make sure that URLRequestFtpJob passes on its priority updates to 140// newly-created transactions after the first one. 141TEST_F(URLRequestFtpJobPriorityTest, SetSubsequentTransactionPriority) { 142 scoped_refptr<TestURLRequestFtpJob> job(new TestURLRequestFtpJob(&req_)); 143 job->Start(); 144 145 job->SetPriority(LOW); 146 ASSERT_TRUE(network_layer_.last_transaction()); 147 EXPECT_EQ(LOW, network_layer_.last_transaction()->priority()); 148 149 job->Kill(); 150 network_layer_.ClearLastTransaction(); 151 152 // Creates a second transaction. 153 job->Start(); 154 ASSERT_TRUE(network_layer_.last_transaction()); 155 EXPECT_EQ(LOW, network_layer_.last_transaction()->priority()); 156} 157 158class FtpTestURLRequestContext : public TestURLRequestContext { 159 public: 160 FtpTestURLRequestContext(ClientSocketFactory* socket_factory, 161 ProxyService* proxy_service, 162 NetworkDelegate* network_delegate) 163 : TestURLRequestContext(true) { 164 set_client_socket_factory(socket_factory); 165 context_storage_.set_proxy_service(proxy_service); 166 set_network_delegate(network_delegate); 167 Init(); 168 } 169}; 170 171class URLRequestFtpJobTest : public testing::Test { 172 public: 173 URLRequestFtpJobTest() 174 : proxy_service_(new ProxyService( 175 new SimpleProxyConfigService, NULL, NULL)), 176 request_context_(&socket_factory_, 177 proxy_service_, 178 &network_delegate_) { 179 } 180 181 virtual ~URLRequestFtpJobTest() { 182 // Clean up any remaining tasks that mess up unrelated tests. 183 base::RunLoop run_loop; 184 run_loop.RunUntilIdle(); 185 } 186 187 void AddSocket(MockRead* reads, size_t reads_size, 188 MockWrite* writes, size_t writes_size) { 189 DeterministicSocketData* socket_data = new DeterministicSocketData( 190 reads, reads_size, writes, writes_size); 191 socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK)); 192 socket_data->StopAfter(reads_size + writes_size - 1); 193 socket_factory_.AddSocketDataProvider(socket_data); 194 195 socket_data_.push_back(socket_data); 196 } 197 198 URLRequestContext* request_context() { return &request_context_; } 199 TestNetworkDelegate* network_delegate() { return &network_delegate_; } 200 DeterministicSocketData* socket_data(size_t index) { 201 return socket_data_[index]; 202 } 203 204 private: 205 ScopedVector<DeterministicSocketData> socket_data_; 206 DeterministicMockClientSocketFactory socket_factory_; 207 TestNetworkDelegate network_delegate_; 208 209 // Owned by |request_context_|: 210 ProxyService* proxy_service_; 211 212 FtpTestURLRequestContext request_context_; 213}; 214 215TEST_F(URLRequestFtpJobTest, FtpProxyRequest) { 216 MockWrite writes[] = { 217 MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/ HTTP/1.1\r\n" 218 "Host: ftp.example.com\r\n" 219 "Proxy-Connection: keep-alive\r\n\r\n"), 220 }; 221 MockRead reads[] = { 222 MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"), 223 MockRead(ASYNC, 2, "Content-Length: 9\r\n\r\n"), 224 MockRead(ASYNC, 3, "test.html"), 225 }; 226 227 AddSocket(reads, arraysize(reads), writes, arraysize(writes)); 228 229 TestDelegate request_delegate; 230 URLRequest url_request(GURL("ftp://ftp.example.com/"), 231 &request_delegate, 232 request_context(), 233 network_delegate()); 234 url_request.Start(); 235 ASSERT_TRUE(url_request.is_pending()); 236 socket_data(0)->RunFor(4); 237 238 EXPECT_TRUE(url_request.status().is_success()); 239 EXPECT_EQ(1, network_delegate()->completed_requests()); 240 EXPECT_EQ(0, network_delegate()->error_count()); 241 EXPECT_FALSE(request_delegate.auth_required_called()); 242 EXPECT_EQ("test.html", request_delegate.data_received()); 243} 244 245TEST_F(URLRequestFtpJobTest, FtpProxyRequestNeedAuth) { 246 MockWrite writes[] = { 247 MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/ HTTP/1.1\r\n" 248 "Host: ftp.example.com\r\n" 249 "Proxy-Connection: keep-alive\r\n\r\n"), 250 }; 251 MockRead reads[] = { 252 // No credentials. 253 MockRead(ASYNC, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"), 254 MockRead(ASYNC, 2, "Proxy-Authenticate: Basic " 255 "realm=\"MyRealm1\"\r\n"), 256 MockRead(ASYNC, 3, "Content-Length: 9\r\n\r\n"), 257 MockRead(ASYNC, 4, "test.html"), 258 }; 259 260 AddSocket(reads, arraysize(reads), writes, arraysize(writes)); 261 262 TestDelegate request_delegate; 263 URLRequest url_request(GURL("ftp://ftp.example.com/"), 264 &request_delegate, 265 request_context(), 266 network_delegate()); 267 url_request.Start(); 268 ASSERT_TRUE(url_request.is_pending()); 269 socket_data(0)->RunFor(5); 270 271 EXPECT_TRUE(url_request.status().is_success()); 272 EXPECT_EQ(1, network_delegate()->completed_requests()); 273 EXPECT_EQ(0, network_delegate()->error_count()); 274 EXPECT_FALSE(request_delegate.auth_required_called()); 275 EXPECT_EQ("test.html", request_delegate.data_received()); 276} 277 278TEST_F(URLRequestFtpJobTest, FtpProxyRequestDoNotSaveCookies) { 279 MockWrite writes[] = { 280 MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/ HTTP/1.1\r\n" 281 "Host: ftp.example.com\r\n" 282 "Proxy-Connection: keep-alive\r\n\r\n"), 283 }; 284 MockRead reads[] = { 285 MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"), 286 MockRead(ASYNC, 2, "Content-Length: 9\r\n"), 287 MockRead(ASYNC, 3, "Set-Cookie: name=value\r\n\r\n"), 288 MockRead(ASYNC, 4, "test.html"), 289 }; 290 291 AddSocket(reads, arraysize(reads), writes, arraysize(writes)); 292 293 TestDelegate request_delegate; 294 URLRequest url_request(GURL("ftp://ftp.example.com/"), 295 &request_delegate, 296 request_context(), 297 network_delegate()); 298 url_request.Start(); 299 ASSERT_TRUE(url_request.is_pending()); 300 301 socket_data(0)->RunFor(5); 302 303 EXPECT_TRUE(url_request.status().is_success()); 304 EXPECT_EQ(1, network_delegate()->completed_requests()); 305 EXPECT_EQ(0, network_delegate()->error_count()); 306 307 // Make sure we do not accept cookies. 308 EXPECT_EQ(0, network_delegate()->set_cookie_count()); 309 310 EXPECT_FALSE(request_delegate.auth_required_called()); 311 EXPECT_EQ("test.html", request_delegate.data_received()); 312} 313 314TEST_F(URLRequestFtpJobTest, FtpProxyRequestDoNotFollowRedirects) { 315 MockWrite writes[] = { 316 MockWrite(SYNCHRONOUS, 0, "GET ftp://ftp.example.com/ HTTP/1.1\r\n" 317 "Host: ftp.example.com\r\n" 318 "Proxy-Connection: keep-alive\r\n\r\n"), 319 }; 320 MockRead reads[] = { 321 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 302 Found\r\n"), 322 MockRead(ASYNC, 2, "Location: http://other.example.com/\r\n\r\n"), 323 }; 324 325 AddSocket(reads, arraysize(reads), writes, arraysize(writes)); 326 327 TestDelegate request_delegate; 328 URLRequest url_request(GURL("ftp://ftp.example.com/"), 329 &request_delegate, 330 request_context(), 331 network_delegate()); 332 url_request.Start(); 333 EXPECT_TRUE(url_request.is_pending()); 334 335 MessageLoop::current()->RunUntilIdle(); 336 337 EXPECT_TRUE(url_request.is_pending()); 338 EXPECT_EQ(0, request_delegate.response_started_count()); 339 EXPECT_EQ(0, network_delegate()->error_count()); 340 ASSERT_TRUE(url_request.status().is_success()); 341 342 socket_data(0)->RunFor(1); 343 344 EXPECT_EQ(1, network_delegate()->completed_requests()); 345 EXPECT_EQ(1, network_delegate()->error_count()); 346 EXPECT_FALSE(url_request.status().is_success()); 347 EXPECT_EQ(ERR_UNSAFE_REDIRECT, url_request.status().error()); 348} 349 350// We should re-use socket for requests using the same scheme, host, and port. 351TEST_F(URLRequestFtpJobTest, FtpProxyRequestReuseSocket) { 352 MockWrite writes[] = { 353 MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/first HTTP/1.1\r\n" 354 "Host: ftp.example.com\r\n" 355 "Proxy-Connection: keep-alive\r\n\r\n"), 356 MockWrite(ASYNC, 4, "GET ftp://ftp.example.com/second HTTP/1.1\r\n" 357 "Host: ftp.example.com\r\n" 358 "Proxy-Connection: keep-alive\r\n\r\n"), 359 }; 360 MockRead reads[] = { 361 MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"), 362 MockRead(ASYNC, 2, "Content-Length: 10\r\n\r\n"), 363 MockRead(ASYNC, 3, "test1.html"), 364 MockRead(ASYNC, 5, "HTTP/1.1 200 OK\r\n"), 365 MockRead(ASYNC, 6, "Content-Length: 10\r\n\r\n"), 366 MockRead(ASYNC, 7, "test2.html"), 367 }; 368 369 AddSocket(reads, arraysize(reads), writes, arraysize(writes)); 370 371 TestDelegate request_delegate1; 372 URLRequest url_request1(GURL("ftp://ftp.example.com/first"), 373 &request_delegate1, 374 request_context(), 375 network_delegate()); 376 url_request1.Start(); 377 ASSERT_TRUE(url_request1.is_pending()); 378 socket_data(0)->RunFor(4); 379 380 EXPECT_TRUE(url_request1.status().is_success()); 381 EXPECT_EQ(1, network_delegate()->completed_requests()); 382 EXPECT_EQ(0, network_delegate()->error_count()); 383 EXPECT_FALSE(request_delegate1.auth_required_called()); 384 EXPECT_EQ("test1.html", request_delegate1.data_received()); 385 386 TestDelegate request_delegate2; 387 URLRequest url_request2(GURL("ftp://ftp.example.com/second"), 388 &request_delegate2, 389 request_context(), 390 network_delegate()); 391 url_request2.Start(); 392 ASSERT_TRUE(url_request2.is_pending()); 393 socket_data(0)->RunFor(4); 394 395 EXPECT_TRUE(url_request2.status().is_success()); 396 EXPECT_EQ(2, network_delegate()->completed_requests()); 397 EXPECT_EQ(0, network_delegate()->error_count()); 398 EXPECT_FALSE(request_delegate2.auth_required_called()); 399 EXPECT_EQ("test2.html", request_delegate2.data_received()); 400} 401 402// We should not re-use socket when there are two requests to the same host, 403// but one is FTP and the other is HTTP. 404TEST_F(URLRequestFtpJobTest, FtpProxyRequestDoNotReuseSocket) { 405 MockWrite writes1[] = { 406 MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/first HTTP/1.1\r\n" 407 "Host: ftp.example.com\r\n" 408 "Proxy-Connection: keep-alive\r\n\r\n"), 409 }; 410 MockWrite writes2[] = { 411 MockWrite(ASYNC, 0, "GET /second HTTP/1.1\r\n" 412 "Host: ftp.example.com\r\n" 413 "Connection: keep-alive\r\n" 414 "User-Agent:\r\n" 415 "Accept-Encoding: gzip,deflate\r\n" 416 "Accept-Language: en-us,fr\r\n\r\n"), 417 }; 418 MockRead reads1[] = { 419 MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"), 420 MockRead(ASYNC, 2, "Content-Length: 10\r\n\r\n"), 421 MockRead(ASYNC, 3, "test1.html"), 422 }; 423 MockRead reads2[] = { 424 MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"), 425 MockRead(ASYNC, 2, "Content-Length: 10\r\n\r\n"), 426 MockRead(ASYNC, 3, "test2.html"), 427 }; 428 429 AddSocket(reads1, arraysize(reads1), writes1, arraysize(writes1)); 430 AddSocket(reads2, arraysize(reads2), writes2, arraysize(writes2)); 431 432 TestDelegate request_delegate1; 433 URLRequest url_request1(GURL("ftp://ftp.example.com/first"), 434 &request_delegate1, 435 request_context(), 436 network_delegate()); 437 url_request1.Start(); 438 ASSERT_TRUE(url_request1.is_pending()); 439 socket_data(0)->RunFor(4); 440 441 EXPECT_TRUE(url_request1.status().is_success()); 442 EXPECT_EQ(1, network_delegate()->completed_requests()); 443 EXPECT_EQ(0, network_delegate()->error_count()); 444 EXPECT_FALSE(request_delegate1.auth_required_called()); 445 EXPECT_EQ("test1.html", request_delegate1.data_received()); 446 447 TestDelegate request_delegate2; 448 URLRequest url_request2(GURL("http://ftp.example.com/second"), 449 &request_delegate2, 450 request_context(), 451 network_delegate()); 452 url_request2.Start(); 453 ASSERT_TRUE(url_request2.is_pending()); 454 socket_data(1)->RunFor(4); 455 456 EXPECT_TRUE(url_request2.status().is_success()); 457 EXPECT_EQ(2, network_delegate()->completed_requests()); 458 EXPECT_EQ(0, network_delegate()->error_count()); 459 EXPECT_FALSE(request_delegate2.auth_required_called()); 460 EXPECT_EQ("test2.html", request_delegate2.data_received()); 461} 462 463} // namespace 464 465} // namespace net 466