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