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_ptr.h"
9#include "base/memory/scoped_vector.h"
10#include "base/run_loop.h"
11#include "net/base/host_port_pair.h"
12#include "net/base/request_priority.h"
13#include "net/ftp/ftp_auth_cache.h"
14#include "net/http/http_transaction_test_util.h"
15#include "net/proxy/mock_proxy_resolver.h"
16#include "net/proxy/proxy_config_service.h"
17#include "net/proxy/proxy_config_service_fixed.h"
18#include "net/socket/socket_test_util.h"
19#include "net/url_request/ftp_protocol_handler.h"
20#include "net/url_request/url_request.h"
21#include "net/url_request/url_request_context.h"
22#include "net/url_request/url_request_job_factory_impl.h"
23#include "net/url_request/url_request_status.h"
24#include "net/url_request/url_request_test_util.h"
25#include "testing/gtest/include/gtest/gtest.h"
26#include "url/gurl.h"
27
28using base::ASCIIToUTF16;
29
30namespace net {
31
32class FtpTestURLRequestContext : public TestURLRequestContext {
33 public:
34  FtpTestURLRequestContext(ClientSocketFactory* socket_factory,
35                           ProxyService* proxy_service,
36                           NetworkDelegate* network_delegate,
37                           FtpTransactionFactory* ftp_transaction_factory)
38      : TestURLRequestContext(true),
39        ftp_protocol_handler_(new FtpProtocolHandler(ftp_transaction_factory)) {
40    set_client_socket_factory(socket_factory);
41    context_storage_.set_proxy_service(proxy_service);
42    set_network_delegate(network_delegate);
43    URLRequestJobFactoryImpl* job_factory = new URLRequestJobFactoryImpl;
44    job_factory->SetProtocolHandler("ftp", ftp_protocol_handler_);
45    context_storage_.set_job_factory(job_factory);
46    Init();
47  }
48
49  FtpAuthCache* GetFtpAuthCache() {
50    return ftp_protocol_handler_->ftp_auth_cache_.get();
51  }
52
53  void set_proxy_service(ProxyService* proxy_service) {
54    context_storage_.set_proxy_service(proxy_service);
55  }
56
57 private:
58  FtpProtocolHandler* ftp_protocol_handler_;
59};
60
61namespace {
62
63class SimpleProxyConfigService : public ProxyConfigService {
64 public:
65  SimpleProxyConfigService() {
66    // Any FTP requests that ever go through HTTP paths are proxied requests.
67    config_.proxy_rules().ParseFromString("ftp=localhost");
68  }
69
70  virtual void AddObserver(Observer* observer) OVERRIDE {
71    observer_ = observer;
72  }
73
74  virtual void RemoveObserver(Observer* observer) OVERRIDE {
75    if (observer_ == observer) {
76      observer_ = NULL;
77    }
78  }
79
80  virtual ConfigAvailability GetLatestProxyConfig(
81      ProxyConfig* config) OVERRIDE {
82    *config = config_;
83    return CONFIG_VALID;
84  }
85
86  void IncrementConfigId() {
87    config_.set_id(config_.id() + 1);
88    observer_->OnProxyConfigChanged(config_, ProxyConfigService::CONFIG_VALID);
89  }
90
91 private:
92  ProxyConfig config_;
93  Observer* observer_;
94};
95
96// Inherit from URLRequestFtpJob to expose the priority and some
97// other hidden functions.
98class TestURLRequestFtpJob : public URLRequestFtpJob {
99 public:
100  TestURLRequestFtpJob(URLRequest* request,
101                       FtpTransactionFactory* ftp_factory,
102                       FtpAuthCache* ftp_auth_cache)
103      : URLRequestFtpJob(request, NULL, ftp_factory, ftp_auth_cache) {}
104
105  using URLRequestFtpJob::SetPriority;
106  using URLRequestFtpJob::Start;
107  using URLRequestFtpJob::Kill;
108  using URLRequestFtpJob::priority;
109
110 protected:
111  virtual ~TestURLRequestFtpJob() {}
112};
113
114class MockFtpTransactionFactory : public FtpTransactionFactory {
115 public:
116  virtual FtpTransaction* CreateTransaction() OVERRIDE {
117    return NULL;
118  }
119
120  virtual void Suspend(bool suspend) OVERRIDE {}
121};
122
123// Fixture for priority-related tests. Priority matters when there is
124// an HTTP proxy.
125class URLRequestFtpJobPriorityTest : public testing::Test {
126 protected:
127  URLRequestFtpJobPriorityTest()
128      : proxy_service_(new SimpleProxyConfigService, NULL, NULL),
129        req_(context_.CreateRequest(GURL("ftp://ftp.example.com"),
130                                    DEFAULT_PRIORITY,
131                                    &delegate_,
132                                    NULL)) {
133    context_.set_proxy_service(&proxy_service_);
134    context_.set_http_transaction_factory(&network_layer_);
135  }
136
137  ProxyService proxy_service_;
138  MockNetworkLayer network_layer_;
139  MockFtpTransactionFactory ftp_factory_;
140  FtpAuthCache ftp_auth_cache_;
141  TestURLRequestContext context_;
142  TestDelegate delegate_;
143  scoped_ptr<URLRequest> req_;
144};
145
146// Make sure that SetPriority actually sets the URLRequestFtpJob's
147// priority, both before and after start.
148TEST_F(URLRequestFtpJobPriorityTest, SetPriorityBasic) {
149  scoped_refptr<TestURLRequestFtpJob> job(new TestURLRequestFtpJob(
150      req_.get(), &ftp_factory_, &ftp_auth_cache_));
151  EXPECT_EQ(DEFAULT_PRIORITY, job->priority());
152
153  job->SetPriority(LOWEST);
154  EXPECT_EQ(LOWEST, job->priority());
155
156  job->SetPriority(LOW);
157  EXPECT_EQ(LOW, job->priority());
158
159  job->Start();
160  EXPECT_EQ(LOW, job->priority());
161
162  job->SetPriority(MEDIUM);
163  EXPECT_EQ(MEDIUM, job->priority());
164}
165
166// Make sure that URLRequestFtpJob passes on its priority to its
167// transaction on start.
168TEST_F(URLRequestFtpJobPriorityTest, SetTransactionPriorityOnStart) {
169  scoped_refptr<TestURLRequestFtpJob> job(new TestURLRequestFtpJob(
170      req_.get(), &ftp_factory_, &ftp_auth_cache_));
171  job->SetPriority(LOW);
172
173  EXPECT_FALSE(network_layer_.last_transaction());
174
175  job->Start();
176
177  ASSERT_TRUE(network_layer_.last_transaction());
178  EXPECT_EQ(LOW, network_layer_.last_transaction()->priority());
179}
180
181// Make sure that URLRequestFtpJob passes on its priority updates to
182// its transaction.
183TEST_F(URLRequestFtpJobPriorityTest, SetTransactionPriority) {
184  scoped_refptr<TestURLRequestFtpJob> job(new TestURLRequestFtpJob(
185      req_.get(), &ftp_factory_, &ftp_auth_cache_));
186  job->SetPriority(LOW);
187  job->Start();
188  ASSERT_TRUE(network_layer_.last_transaction());
189  EXPECT_EQ(LOW, network_layer_.last_transaction()->priority());
190
191  job->SetPriority(HIGHEST);
192  EXPECT_EQ(HIGHEST, network_layer_.last_transaction()->priority());
193}
194
195// Make sure that URLRequestFtpJob passes on its priority updates to
196// newly-created transactions after the first one.
197TEST_F(URLRequestFtpJobPriorityTest, SetSubsequentTransactionPriority) {
198  scoped_refptr<TestURLRequestFtpJob> job(new TestURLRequestFtpJob(
199      req_.get(), &ftp_factory_, &ftp_auth_cache_));
200  job->Start();
201
202  job->SetPriority(LOW);
203  ASSERT_TRUE(network_layer_.last_transaction());
204  EXPECT_EQ(LOW, network_layer_.last_transaction()->priority());
205
206  job->Kill();
207  network_layer_.ClearLastTransaction();
208
209  // Creates a second transaction.
210  job->Start();
211  ASSERT_TRUE(network_layer_.last_transaction());
212  EXPECT_EQ(LOW, network_layer_.last_transaction()->priority());
213}
214
215class URLRequestFtpJobTest : public testing::Test {
216 public:
217  URLRequestFtpJobTest()
218      : request_context_(&socket_factory_,
219                         new ProxyService(
220                             new SimpleProxyConfigService, NULL, NULL),
221                         &network_delegate_,
222                         &ftp_transaction_factory_) {
223  }
224
225  virtual ~URLRequestFtpJobTest() {
226    // Clean up any remaining tasks that mess up unrelated tests.
227    base::RunLoop run_loop;
228    run_loop.RunUntilIdle();
229  }
230
231  void AddSocket(MockRead* reads, size_t reads_size,
232                 MockWrite* writes, size_t writes_size) {
233    DeterministicSocketData* socket_data = new DeterministicSocketData(
234        reads, reads_size, writes, writes_size);
235    socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
236    socket_data->StopAfter(reads_size + writes_size - 1);
237    socket_factory_.AddSocketDataProvider(socket_data);
238
239    socket_data_.push_back(socket_data);
240  }
241
242  FtpTestURLRequestContext* request_context() { return &request_context_; }
243  TestNetworkDelegate* network_delegate() { return &network_delegate_; }
244  DeterministicSocketData* socket_data(size_t index) {
245    return socket_data_[index];
246  }
247
248 private:
249  ScopedVector<DeterministicSocketData> socket_data_;
250  DeterministicMockClientSocketFactory socket_factory_;
251  TestNetworkDelegate network_delegate_;
252  MockFtpTransactionFactory ftp_transaction_factory_;
253
254  FtpTestURLRequestContext request_context_;
255};
256
257TEST_F(URLRequestFtpJobTest, FtpProxyRequest) {
258  MockWrite writes[] = {
259    MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/ HTTP/1.1\r\n"
260              "Host: ftp.example.com\r\n"
261              "Proxy-Connection: keep-alive\r\n\r\n"),
262  };
263  MockRead reads[] = {
264    MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"),
265    MockRead(ASYNC, 2, "Content-Length: 9\r\n\r\n"),
266    MockRead(ASYNC, 3, "test.html"),
267  };
268
269  AddSocket(reads, arraysize(reads), writes, arraysize(writes));
270
271  TestDelegate request_delegate;
272  scoped_ptr<URLRequest> url_request(request_context()->CreateRequest(
273      GURL("ftp://ftp.example.com/"), DEFAULT_PRIORITY,
274      &request_delegate, NULL));
275  url_request->Start();
276  ASSERT_TRUE(url_request->is_pending());
277  socket_data(0)->RunFor(4);
278
279  EXPECT_TRUE(url_request->status().is_success());
280  EXPECT_TRUE(url_request->proxy_server().Equals(
281      net::HostPortPair::FromString("localhost:80")));
282  EXPECT_EQ(1, network_delegate()->completed_requests());
283  EXPECT_EQ(0, network_delegate()->error_count());
284  EXPECT_FALSE(request_delegate.auth_required_called());
285  EXPECT_EQ("test.html", request_delegate.data_received());
286}
287
288// Regression test for http://crbug.com/237526 .
289TEST_F(URLRequestFtpJobTest, FtpProxyRequestOrphanJob) {
290  // Use a PAC URL so that URLRequestFtpJob's |pac_request_| field is non-NULL.
291  request_context()->set_proxy_service(
292      new ProxyService(
293          new ProxyConfigServiceFixed(
294              ProxyConfig::CreateFromCustomPacURL(GURL("http://foo"))),
295          new MockAsyncProxyResolver, NULL));
296
297  TestDelegate request_delegate;
298  scoped_ptr<URLRequest> url_request(request_context()->CreateRequest(
299      GURL("ftp://ftp.example.com/"), DEFAULT_PRIORITY, &request_delegate,
300      NULL));
301  url_request->Start();
302
303  // Now |url_request| will be deleted before its completion,
304  // resulting in it being orphaned. It should not crash.
305}
306
307TEST_F(URLRequestFtpJobTest, FtpProxyRequestNeedProxyAuthNoCredentials) {
308  MockWrite writes[] = {
309    MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/ HTTP/1.1\r\n"
310              "Host: ftp.example.com\r\n"
311              "Proxy-Connection: keep-alive\r\n\r\n"),
312  };
313  MockRead reads[] = {
314    // No credentials.
315    MockRead(ASYNC, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"),
316    MockRead(ASYNC, 2, "Proxy-Authenticate: Basic "
317             "realm=\"MyRealm1\"\r\n"),
318    MockRead(ASYNC, 3, "Content-Length: 9\r\n\r\n"),
319    MockRead(ASYNC, 4, "test.html"),
320  };
321
322  AddSocket(reads, arraysize(reads), writes, arraysize(writes));
323
324  TestDelegate request_delegate;
325  scoped_ptr<URLRequest> url_request(request_context()->CreateRequest(
326      GURL("ftp://ftp.example.com/"), DEFAULT_PRIORITY, &request_delegate,
327      NULL));
328  url_request->Start();
329  ASSERT_TRUE(url_request->is_pending());
330  socket_data(0)->RunFor(5);
331
332  EXPECT_TRUE(url_request->status().is_success());
333  EXPECT_TRUE(url_request->proxy_server().Equals(
334      net::HostPortPair::FromString("localhost:80")));
335  EXPECT_EQ(1, network_delegate()->completed_requests());
336  EXPECT_EQ(0, network_delegate()->error_count());
337  EXPECT_TRUE(request_delegate.auth_required_called());
338  EXPECT_EQ("test.html", request_delegate.data_received());
339}
340
341TEST_F(URLRequestFtpJobTest, FtpProxyRequestNeedProxyAuthWithCredentials) {
342  MockWrite writes[] = {
343    MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/ HTTP/1.1\r\n"
344              "Host: ftp.example.com\r\n"
345              "Proxy-Connection: keep-alive\r\n\r\n"),
346    MockWrite(ASYNC, 5, "GET ftp://ftp.example.com/ HTTP/1.1\r\n"
347              "Host: ftp.example.com\r\n"
348              "Proxy-Connection: keep-alive\r\n"
349              "Proxy-Authorization: Basic bXl1c2VyOm15cGFzcw==\r\n\r\n"),
350  };
351  MockRead reads[] = {
352    // No credentials.
353    MockRead(ASYNC, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"),
354    MockRead(ASYNC, 2, "Proxy-Authenticate: Basic "
355             "realm=\"MyRealm1\"\r\n"),
356    MockRead(ASYNC, 3, "Content-Length: 9\r\n\r\n"),
357    MockRead(ASYNC, 4, "test.html"),
358
359    // Second response.
360    MockRead(ASYNC, 6, "HTTP/1.1 200 OK\r\n"),
361    MockRead(ASYNC, 7, "Content-Length: 10\r\n\r\n"),
362    MockRead(ASYNC, 8, "test2.html"),
363  };
364
365  AddSocket(reads, arraysize(reads), writes, arraysize(writes));
366
367  TestDelegate request_delegate;
368  request_delegate.set_credentials(
369      AuthCredentials(ASCIIToUTF16("myuser"), ASCIIToUTF16("mypass")));
370  scoped_ptr<URLRequest> url_request(request_context()->CreateRequest(
371      GURL("ftp://ftp.example.com/"), DEFAULT_PRIORITY, &request_delegate,
372      NULL));
373  url_request->Start();
374  ASSERT_TRUE(url_request->is_pending());
375  socket_data(0)->RunFor(9);
376
377  EXPECT_TRUE(url_request->status().is_success());
378  EXPECT_EQ(1, network_delegate()->completed_requests());
379  EXPECT_EQ(0, network_delegate()->error_count());
380  EXPECT_TRUE(request_delegate.auth_required_called());
381  EXPECT_EQ("test2.html", request_delegate.data_received());
382}
383
384TEST_F(URLRequestFtpJobTest, FtpProxyRequestNeedServerAuthNoCredentials) {
385  MockWrite writes[] = {
386    MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/ HTTP/1.1\r\n"
387              "Host: ftp.example.com\r\n"
388              "Proxy-Connection: keep-alive\r\n\r\n"),
389  };
390  MockRead reads[] = {
391    // No credentials.
392    MockRead(ASYNC, 1, "HTTP/1.1 401 Unauthorized\r\n"),
393    MockRead(ASYNC, 2, "WWW-Authenticate: Basic "
394             "realm=\"MyRealm1\"\r\n"),
395    MockRead(ASYNC, 3, "Content-Length: 9\r\n\r\n"),
396    MockRead(ASYNC, 4, "test.html"),
397  };
398
399  AddSocket(reads, arraysize(reads), writes, arraysize(writes));
400
401  TestDelegate request_delegate;
402  scoped_ptr<URLRequest> url_request(request_context()->CreateRequest(
403      GURL("ftp://ftp.example.com/"), DEFAULT_PRIORITY, &request_delegate,
404      NULL));
405  url_request->Start();
406  ASSERT_TRUE(url_request->is_pending());
407  socket_data(0)->RunFor(5);
408
409  EXPECT_TRUE(url_request->status().is_success());
410  EXPECT_EQ(1, network_delegate()->completed_requests());
411  EXPECT_EQ(0, network_delegate()->error_count());
412  EXPECT_TRUE(request_delegate.auth_required_called());
413  EXPECT_EQ("test.html", request_delegate.data_received());
414}
415
416TEST_F(URLRequestFtpJobTest, FtpProxyRequestNeedServerAuthWithCredentials) {
417  MockWrite writes[] = {
418    MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/ HTTP/1.1\r\n"
419              "Host: ftp.example.com\r\n"
420              "Proxy-Connection: keep-alive\r\n\r\n"),
421    MockWrite(ASYNC, 5, "GET ftp://ftp.example.com/ HTTP/1.1\r\n"
422              "Host: ftp.example.com\r\n"
423              "Proxy-Connection: keep-alive\r\n"
424              "Authorization: Basic bXl1c2VyOm15cGFzcw==\r\n\r\n"),
425  };
426  MockRead reads[] = {
427    // No credentials.
428    MockRead(ASYNC, 1, "HTTP/1.1 401 Unauthorized\r\n"),
429    MockRead(ASYNC, 2, "WWW-Authenticate: Basic "
430             "realm=\"MyRealm1\"\r\n"),
431    MockRead(ASYNC, 3, "Content-Length: 9\r\n\r\n"),
432    MockRead(ASYNC, 4, "test.html"),
433
434    // Second response.
435    MockRead(ASYNC, 6, "HTTP/1.1 200 OK\r\n"),
436    MockRead(ASYNC, 7, "Content-Length: 10\r\n\r\n"),
437    MockRead(ASYNC, 8, "test2.html"),
438  };
439
440  AddSocket(reads, arraysize(reads), writes, arraysize(writes));
441
442  TestDelegate request_delegate;
443  request_delegate.set_credentials(
444      AuthCredentials(ASCIIToUTF16("myuser"), ASCIIToUTF16("mypass")));
445  scoped_ptr<URLRequest> url_request(request_context()->CreateRequest(
446      GURL("ftp://ftp.example.com/"), DEFAULT_PRIORITY, &request_delegate,
447      NULL));
448  url_request->Start();
449  ASSERT_TRUE(url_request->is_pending());
450  socket_data(0)->RunFor(9);
451
452  EXPECT_TRUE(url_request->status().is_success());
453  EXPECT_EQ(1, network_delegate()->completed_requests());
454  EXPECT_EQ(0, network_delegate()->error_count());
455  EXPECT_TRUE(request_delegate.auth_required_called());
456  EXPECT_EQ("test2.html", request_delegate.data_received());
457}
458
459TEST_F(URLRequestFtpJobTest, FtpProxyRequestNeedProxyAndServerAuth) {
460  MockWrite writes[] = {
461    MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/ HTTP/1.1\r\n"
462              "Host: ftp.example.com\r\n"
463              "Proxy-Connection: keep-alive\r\n\r\n"),
464    MockWrite(ASYNC, 5, "GET ftp://ftp.example.com/ HTTP/1.1\r\n"
465              "Host: ftp.example.com\r\n"
466              "Proxy-Connection: keep-alive\r\n"
467              "Proxy-Authorization: Basic "
468              "cHJveHl1c2VyOnByb3h5cGFzcw==\r\n\r\n"),
469    MockWrite(ASYNC, 10, "GET ftp://ftp.example.com/ HTTP/1.1\r\n"
470              "Host: ftp.example.com\r\n"
471              "Proxy-Connection: keep-alive\r\n"
472              "Proxy-Authorization: Basic "
473              "cHJveHl1c2VyOnByb3h5cGFzcw==\r\n"
474              "Authorization: Basic bXl1c2VyOm15cGFzcw==\r\n\r\n"),
475  };
476  MockRead reads[] = {
477    // No credentials.
478    MockRead(ASYNC, 1, "HTTP/1.1 407 Proxy Authentication Required\r\n"),
479    MockRead(ASYNC, 2, "Proxy-Authenticate: Basic "
480             "realm=\"MyRealm1\"\r\n"),
481    MockRead(ASYNC, 3, "Content-Length: 9\r\n\r\n"),
482    MockRead(ASYNC, 4, "test.html"),
483
484    // Second response.
485    MockRead(ASYNC, 6, "HTTP/1.1 401 Unauthorized\r\n"),
486    MockRead(ASYNC, 7, "WWW-Authenticate: Basic "
487             "realm=\"MyRealm1\"\r\n"),
488    MockRead(ASYNC, 8, "Content-Length: 9\r\n\r\n"),
489    MockRead(ASYNC, 9, "test.html"),
490
491    // Third response.
492    MockRead(ASYNC, 11, "HTTP/1.1 200 OK\r\n"),
493    MockRead(ASYNC, 12, "Content-Length: 10\r\n\r\n"),
494    MockRead(ASYNC, 13, "test2.html"),
495  };
496
497  AddSocket(reads, arraysize(reads), writes, arraysize(writes));
498
499  GURL url("ftp://ftp.example.com");
500
501  // Make sure cached FTP credentials are not used for proxy authentication.
502  request_context()->GetFtpAuthCache()->Add(
503      url.GetOrigin(),
504      AuthCredentials(ASCIIToUTF16("userdonotuse"),
505                      ASCIIToUTF16("passworddonotuse")));
506
507  TestDelegate request_delegate;
508  request_delegate.set_credentials(
509      AuthCredentials(ASCIIToUTF16("proxyuser"), ASCIIToUTF16("proxypass")));
510  scoped_ptr<URLRequest> url_request(request_context()->CreateRequest(
511      url, DEFAULT_PRIORITY, &request_delegate, NULL));
512  url_request->Start();
513  ASSERT_TRUE(url_request->is_pending());
514  socket_data(0)->RunFor(5);
515
516  request_delegate.set_credentials(
517      AuthCredentials(ASCIIToUTF16("myuser"), ASCIIToUTF16("mypass")));
518  socket_data(0)->RunFor(9);
519
520  EXPECT_TRUE(url_request->status().is_success());
521  EXPECT_EQ(1, network_delegate()->completed_requests());
522  EXPECT_EQ(0, network_delegate()->error_count());
523  EXPECT_TRUE(request_delegate.auth_required_called());
524  EXPECT_EQ("test2.html", request_delegate.data_received());
525}
526
527TEST_F(URLRequestFtpJobTest, FtpProxyRequestDoNotSaveCookies) {
528  MockWrite writes[] = {
529    MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/ HTTP/1.1\r\n"
530              "Host: ftp.example.com\r\n"
531              "Proxy-Connection: keep-alive\r\n\r\n"),
532  };
533  MockRead reads[] = {
534    MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"),
535    MockRead(ASYNC, 2, "Content-Length: 9\r\n"),
536    MockRead(ASYNC, 3, "Set-Cookie: name=value\r\n\r\n"),
537    MockRead(ASYNC, 4, "test.html"),
538  };
539
540  AddSocket(reads, arraysize(reads), writes, arraysize(writes));
541
542  TestDelegate request_delegate;
543  scoped_ptr<URLRequest> url_request(request_context()->CreateRequest(
544      GURL("ftp://ftp.example.com/"), DEFAULT_PRIORITY, &request_delegate,
545      NULL));
546  url_request->Start();
547  ASSERT_TRUE(url_request->is_pending());
548
549  socket_data(0)->RunFor(5);
550
551  EXPECT_TRUE(url_request->status().is_success());
552  EXPECT_EQ(1, network_delegate()->completed_requests());
553  EXPECT_EQ(0, network_delegate()->error_count());
554
555  // Make sure we do not accept cookies.
556  EXPECT_EQ(0, network_delegate()->set_cookie_count());
557
558  EXPECT_FALSE(request_delegate.auth_required_called());
559  EXPECT_EQ("test.html", request_delegate.data_received());
560}
561
562TEST_F(URLRequestFtpJobTest, FtpProxyRequestDoNotFollowRedirects) {
563  MockWrite writes[] = {
564    MockWrite(SYNCHRONOUS, 0, "GET ftp://ftp.example.com/ HTTP/1.1\r\n"
565              "Host: ftp.example.com\r\n"
566              "Proxy-Connection: keep-alive\r\n\r\n"),
567  };
568  MockRead reads[] = {
569    MockRead(SYNCHRONOUS, 1, "HTTP/1.1 302 Found\r\n"),
570    MockRead(ASYNC, 2, "Location: http://other.example.com/\r\n\r\n"),
571  };
572
573  AddSocket(reads, arraysize(reads), writes, arraysize(writes));
574
575  TestDelegate request_delegate;
576  scoped_ptr<URLRequest> url_request(request_context()->CreateRequest(
577      GURL("ftp://ftp.example.com/"), DEFAULT_PRIORITY, &request_delegate,
578      NULL));
579  url_request->Start();
580  EXPECT_TRUE(url_request->is_pending());
581
582  base::MessageLoop::current()->RunUntilIdle();
583
584  EXPECT_TRUE(url_request->is_pending());
585  EXPECT_EQ(0, request_delegate.response_started_count());
586  EXPECT_EQ(0, network_delegate()->error_count());
587  ASSERT_TRUE(url_request->status().is_success());
588
589  socket_data(0)->RunFor(1);
590
591  EXPECT_EQ(1, network_delegate()->completed_requests());
592  EXPECT_EQ(1, network_delegate()->error_count());
593  EXPECT_FALSE(url_request->status().is_success());
594  EXPECT_EQ(ERR_UNSAFE_REDIRECT, url_request->status().error());
595}
596
597// We should re-use socket for requests using the same scheme, host, and port.
598TEST_F(URLRequestFtpJobTest, FtpProxyRequestReuseSocket) {
599  MockWrite writes[] = {
600    MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/first HTTP/1.1\r\n"
601              "Host: ftp.example.com\r\n"
602              "Proxy-Connection: keep-alive\r\n\r\n"),
603    MockWrite(ASYNC, 4, "GET ftp://ftp.example.com/second HTTP/1.1\r\n"
604              "Host: ftp.example.com\r\n"
605              "Proxy-Connection: keep-alive\r\n\r\n"),
606  };
607  MockRead reads[] = {
608    MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"),
609    MockRead(ASYNC, 2, "Content-Length: 10\r\n\r\n"),
610    MockRead(ASYNC, 3, "test1.html"),
611    MockRead(ASYNC, 5, "HTTP/1.1 200 OK\r\n"),
612    MockRead(ASYNC, 6, "Content-Length: 10\r\n\r\n"),
613    MockRead(ASYNC, 7, "test2.html"),
614  };
615
616  AddSocket(reads, arraysize(reads), writes, arraysize(writes));
617
618  TestDelegate request_delegate1;
619
620  scoped_ptr<URLRequest> url_request1(request_context()->CreateRequest(
621      GURL("ftp://ftp.example.com/first"), DEFAULT_PRIORITY, &request_delegate1,
622      NULL));
623  url_request1->Start();
624  ASSERT_TRUE(url_request1->is_pending());
625  socket_data(0)->RunFor(4);
626
627  EXPECT_TRUE(url_request1->status().is_success());
628  EXPECT_TRUE(url_request1->proxy_server().Equals(
629      net::HostPortPair::FromString("localhost:80")));
630  EXPECT_EQ(1, network_delegate()->completed_requests());
631  EXPECT_EQ(0, network_delegate()->error_count());
632  EXPECT_FALSE(request_delegate1.auth_required_called());
633  EXPECT_EQ("test1.html", request_delegate1.data_received());
634
635  TestDelegate request_delegate2;
636  scoped_ptr<URLRequest> url_request2(request_context()->CreateRequest(
637      GURL("ftp://ftp.example.com/second"), DEFAULT_PRIORITY,
638      &request_delegate2, NULL));
639  url_request2->Start();
640  ASSERT_TRUE(url_request2->is_pending());
641  socket_data(0)->RunFor(4);
642
643  EXPECT_TRUE(url_request2->status().is_success());
644  EXPECT_EQ(2, network_delegate()->completed_requests());
645  EXPECT_EQ(0, network_delegate()->error_count());
646  EXPECT_FALSE(request_delegate2.auth_required_called());
647  EXPECT_EQ("test2.html", request_delegate2.data_received());
648}
649
650// We should not re-use socket when there are two requests to the same host,
651// but one is FTP and the other is HTTP.
652TEST_F(URLRequestFtpJobTest, FtpProxyRequestDoNotReuseSocket) {
653  MockWrite writes1[] = {
654    MockWrite(ASYNC, 0, "GET ftp://ftp.example.com/first HTTP/1.1\r\n"
655              "Host: ftp.example.com\r\n"
656              "Proxy-Connection: keep-alive\r\n\r\n"),
657  };
658  MockWrite writes2[] = {
659    MockWrite(ASYNC, 0, "GET /second HTTP/1.1\r\n"
660              "Host: ftp.example.com\r\n"
661              "Connection: keep-alive\r\n"
662              "User-Agent:\r\n"
663              "Accept-Encoding: gzip, deflate\r\n"
664              "Accept-Language: en-us,fr\r\n\r\n"),
665  };
666  MockRead reads1[] = {
667    MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"),
668    MockRead(ASYNC, 2, "Content-Length: 10\r\n\r\n"),
669    MockRead(ASYNC, 3, "test1.html"),
670  };
671  MockRead reads2[] = {
672    MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"),
673    MockRead(ASYNC, 2, "Content-Length: 10\r\n\r\n"),
674    MockRead(ASYNC, 3, "test2.html"),
675  };
676
677  AddSocket(reads1, arraysize(reads1), writes1, arraysize(writes1));
678  AddSocket(reads2, arraysize(reads2), writes2, arraysize(writes2));
679
680  TestDelegate request_delegate1;
681  scoped_ptr<URLRequest> url_request1(request_context()->CreateRequest(
682      GURL("ftp://ftp.example.com/first"), DEFAULT_PRIORITY,
683      &request_delegate1, NULL));
684  url_request1->Start();
685  ASSERT_TRUE(url_request1->is_pending());
686  socket_data(0)->RunFor(4);
687
688  EXPECT_TRUE(url_request1->status().is_success());
689  EXPECT_EQ(1, network_delegate()->completed_requests());
690  EXPECT_EQ(0, network_delegate()->error_count());
691  EXPECT_FALSE(request_delegate1.auth_required_called());
692  EXPECT_EQ("test1.html", request_delegate1.data_received());
693
694  TestDelegate request_delegate2;
695  scoped_ptr<URLRequest> url_request2(request_context()->CreateRequest(
696      GURL("http://ftp.example.com/second"), DEFAULT_PRIORITY,
697      &request_delegate2, NULL));
698  url_request2->Start();
699  ASSERT_TRUE(url_request2->is_pending());
700  socket_data(1)->RunFor(4);
701
702  EXPECT_TRUE(url_request2->status().is_success());
703  EXPECT_EQ(2, network_delegate()->completed_requests());
704  EXPECT_EQ(0, network_delegate()->error_count());
705  EXPECT_FALSE(request_delegate2.auth_required_called());
706  EXPECT_EQ("test2.html", request_delegate2.data_received());
707}
708
709}  // namespace
710
711}  // namespace net
712