1// Copyright (c) 2009 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#ifndef NET_URL_REQUEST_URL_REQUEST_UNITTEST_H_
6#define NET_URL_REQUEST_URL_REQUEST_UNITTEST_H_
7
8#include <stdlib.h>
9
10#include <sstream>
11#include <string>
12#include <vector>
13
14#include "base/file_path.h"
15#include "base/file_util.h"
16#include "base/logging.h"
17#include "base/message_loop.h"
18#include "base/path_service.h"
19#include "base/process_util.h"
20#include "base/string_util.h"
21#include "base/thread.h"
22#include "base/time.h"
23#include "base/waitable_event.h"
24#include "net/base/cookie_monster.h"
25#include "net/base/cookie_policy.h"
26#include "net/base/host_resolver.h"
27#include "net/base/io_buffer.h"
28#include "net/base/net_errors.h"
29#include "net/base/net_test_constants.h"
30#include "net/base/ssl_config_service_defaults.h"
31#include "net/disk_cache/disk_cache.h"
32#include "net/ftp/ftp_network_layer.h"
33#include "net/http/http_cache.h"
34#include "net/http/http_network_layer.h"
35#include "net/socket/ssl_test_util.h"
36#include "net/url_request/url_request.h"
37#include "net/url_request/url_request_context.h"
38#include "net/proxy/proxy_service.h"
39#include "testing/gtest/include/gtest/gtest.h"
40#include "googleurl/src/url_util.h"
41
42const int kHTTPDefaultPort = 1337;
43const int kFTPDefaultPort = 1338;
44
45const std::string kDefaultHostName("localhost");
46
47using base::TimeDelta;
48
49//-----------------------------------------------------------------------------
50
51class TestCookiePolicy : public net::CookiePolicy {
52 public:
53  enum Options {
54    NO_GET_COOKIES = 1 << 0,
55    NO_SET_COOKIE  = 1 << 1,
56    ASYNC          = 1 << 2
57  };
58
59  explicit TestCookiePolicy(int options_bit_mask)
60      : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
61        options_(options_bit_mask),
62        callback_(NULL) {
63  }
64
65  virtual int CanGetCookies(const GURL& url, const GURL& first_party,
66                            net::CompletionCallback* callback) {
67    if ((options_ & ASYNC) && callback) {
68      callback_ = callback;
69      MessageLoop::current()->PostTask(FROM_HERE,
70          method_factory_.NewRunnableMethod(
71              &TestCookiePolicy::DoGetCookiesPolicy, url, first_party));
72      return net::ERR_IO_PENDING;
73    }
74
75    if (options_ & NO_GET_COOKIES)
76      return net::ERR_ACCESS_DENIED;
77
78    return net::OK;
79  }
80
81  virtual int CanSetCookie(const GURL& url, const GURL& first_party,
82                           const std::string& cookie_line,
83                           net::CompletionCallback* callback) {
84    if ((options_ & ASYNC) && callback) {
85      callback_ = callback;
86      MessageLoop::current()->PostTask(FROM_HERE,
87          method_factory_.NewRunnableMethod(
88              &TestCookiePolicy::DoSetCookiePolicy, url, first_party,
89              cookie_line));
90      return net::ERR_IO_PENDING;
91    }
92
93    if (options_ & NO_SET_COOKIE)
94      return net::ERR_ACCESS_DENIED;
95
96    return net::OK;
97  }
98
99 private:
100  void DoGetCookiesPolicy(const GURL& url, const GURL& first_party) {
101    int policy = CanGetCookies(url, first_party, NULL);
102
103    DCHECK(callback_);
104    net::CompletionCallback* callback = callback_;
105    callback_ = NULL;
106    callback->Run(policy);
107  }
108
109  void DoSetCookiePolicy(const GURL& url, const GURL& first_party,
110                         const std::string& cookie_line) {
111    int policy = CanSetCookie(url, first_party, cookie_line, NULL);
112
113    DCHECK(callback_);
114    net::CompletionCallback* callback = callback_;
115    callback_ = NULL;
116    callback->Run(policy);
117  }
118
119  ScopedRunnableMethodFactory<TestCookiePolicy> method_factory_;
120  int options_;
121  net::CompletionCallback* callback_;
122};
123
124//-----------------------------------------------------------------------------
125
126class TestURLRequestContext : public URLRequestContext {
127 public:
128  TestURLRequestContext() {
129    host_resolver_ = net::CreateSystemHostResolver(NULL);
130    proxy_service_ = net::ProxyService::CreateNull();
131    Init();
132  }
133
134  explicit TestURLRequestContext(const std::string& proxy) {
135    host_resolver_ = net::CreateSystemHostResolver(NULL);
136    net::ProxyConfig proxy_config;
137    proxy_config.proxy_rules.ParseFromString(proxy);
138    proxy_service_ = net::ProxyService::CreateFixed(proxy_config);
139    Init();
140  }
141
142  void set_cookie_policy(net::CookiePolicy* policy) {
143    cookie_policy_ = policy;
144  }
145
146 protected:
147  virtual ~TestURLRequestContext() {
148    delete ftp_transaction_factory_;
149    delete http_transaction_factory_;
150  }
151
152 private:
153  void Init() {
154    ftp_transaction_factory_ = new net::FtpNetworkLayer(host_resolver_);
155    ssl_config_service_ = new net::SSLConfigServiceDefaults;
156    http_transaction_factory_ =
157        new net::HttpCache(
158          net::HttpNetworkLayer::CreateFactory(NULL, host_resolver_,
159                                               proxy_service_,
160                                               ssl_config_service_),
161          disk_cache::CreateInMemoryCacheBackend(0));
162    // In-memory cookie store.
163    cookie_store_ = new net::CookieMonster();
164    accept_language_ = "en-us,fr";
165    accept_charset_ = "iso-8859-1,*,utf-8";
166  }
167};
168
169// TODO(phajdan.jr): Migrate callers to the new name and remove the typedef.
170typedef TestURLRequestContext URLRequestTestContext;
171
172//-----------------------------------------------------------------------------
173
174class TestURLRequest : public URLRequest {
175 public:
176  TestURLRequest(const GURL& url, Delegate* delegate)
177      : URLRequest(url, delegate) {
178    set_context(new TestURLRequestContext());
179  }
180};
181
182//-----------------------------------------------------------------------------
183
184class TestDelegate : public URLRequest::Delegate {
185 public:
186  TestDelegate()
187      : cancel_in_rr_(false),
188        cancel_in_rs_(false),
189        cancel_in_rd_(false),
190        cancel_in_rd_pending_(false),
191        quit_on_complete_(true),
192        quit_on_redirect_(false),
193        allow_certificate_errors_(false),
194        response_started_count_(0),
195        received_bytes_count_(0),
196        received_redirect_count_(0),
197        received_data_before_response_(false),
198        request_failed_(false),
199        have_certificate_errors_(false),
200        buf_(new net::IOBuffer(kBufferSize)) {
201  }
202
203  virtual void OnReceivedRedirect(URLRequest* request, const GURL& new_url,
204                                  bool* defer_redirect) {
205    received_redirect_count_++;
206    if (quit_on_redirect_) {
207      *defer_redirect = true;
208      MessageLoop::current()->Quit();
209    } else if (cancel_in_rr_) {
210      request->Cancel();
211    }
212  }
213
214  virtual void OnResponseStarted(URLRequest* request) {
215    // It doesn't make sense for the request to have IO pending at this point.
216    DCHECK(!request->status().is_io_pending());
217
218    response_started_count_++;
219    if (cancel_in_rs_) {
220      request->Cancel();
221      OnResponseCompleted(request);
222    } else if (!request->status().is_success()) {
223      DCHECK(request->status().status() == URLRequestStatus::FAILED ||
224             request->status().status() == URLRequestStatus::CANCELED);
225      request_failed_ = true;
226      OnResponseCompleted(request);
227    } else {
228      // Initiate the first read.
229      int bytes_read = 0;
230      if (request->Read(buf_, kBufferSize, &bytes_read))
231        OnReadCompleted(request, bytes_read);
232      else if (!request->status().is_io_pending())
233        OnResponseCompleted(request);
234    }
235  }
236
237  virtual void OnReadCompleted(URLRequest* request, int bytes_read) {
238    // It doesn't make sense for the request to have IO pending at this point.
239    DCHECK(!request->status().is_io_pending());
240
241    if (response_started_count_ == 0)
242      received_data_before_response_ = true;
243
244    if (cancel_in_rd_)
245      request->Cancel();
246
247    if (bytes_read >= 0) {
248      // There is data to read.
249      received_bytes_count_ += bytes_read;
250
251      // consume the data
252      data_received_.append(buf_->data(), bytes_read);
253    }
254
255    // If it was not end of stream, request to read more.
256    if (request->status().is_success() && bytes_read > 0) {
257      bytes_read = 0;
258      while (request->Read(buf_, kBufferSize, &bytes_read)) {
259        if (bytes_read > 0) {
260          data_received_.append(buf_->data(), bytes_read);
261          received_bytes_count_ += bytes_read;
262        } else {
263          break;
264        }
265      }
266    }
267    if (!request->status().is_io_pending())
268      OnResponseCompleted(request);
269    else if (cancel_in_rd_pending_)
270      request->Cancel();
271  }
272
273  virtual void OnResponseCompleted(URLRequest* request) {
274    if (quit_on_complete_)
275      MessageLoop::current()->Quit();
276  }
277
278  void OnAuthRequired(URLRequest* request, net::AuthChallengeInfo* auth_info) {
279    if (!username_.empty() || !password_.empty()) {
280      request->SetAuth(username_, password_);
281    } else {
282      request->CancelAuth();
283    }
284  }
285
286  virtual void OnSSLCertificateError(URLRequest* request,
287                                     int cert_error,
288                                     net::X509Certificate* cert) {
289    // The caller can control whether it needs all SSL requests to go through,
290    // independent of any possible errors, or whether it wants SSL errors to
291    // cancel the request.
292    have_certificate_errors_ = true;
293    if (allow_certificate_errors_)
294      request->ContinueDespiteLastError();
295    else
296      request->Cancel();
297  }
298
299  void set_cancel_in_received_redirect(bool val) { cancel_in_rr_ = val; }
300  void set_cancel_in_response_started(bool val) { cancel_in_rs_ = val; }
301  void set_cancel_in_received_data(bool val) { cancel_in_rd_ = val; }
302  void set_cancel_in_received_data_pending(bool val) {
303    cancel_in_rd_pending_ = val;
304  }
305  void set_quit_on_complete(bool val) { quit_on_complete_ = val; }
306  void set_quit_on_redirect(bool val) { quit_on_redirect_ = val; }
307  void set_allow_certificate_errors(bool val) {
308    allow_certificate_errors_ = val;
309  }
310  void set_username(const std::wstring& u) { username_ = u; }
311  void set_password(const std::wstring& p) { password_ = p; }
312
313  // query state
314  const std::string& data_received() const { return data_received_; }
315  int bytes_received() const { return static_cast<int>(data_received_.size()); }
316  int response_started_count() const { return response_started_count_; }
317  int received_redirect_count() const { return received_redirect_count_; }
318  bool received_data_before_response() const {
319    return received_data_before_response_;
320  }
321  bool request_failed() const { return request_failed_; }
322  bool have_certificate_errors() const { return have_certificate_errors_; }
323
324 private:
325  static const int kBufferSize = 4096;
326  // options for controlling behavior
327  bool cancel_in_rr_;
328  bool cancel_in_rs_;
329  bool cancel_in_rd_;
330  bool cancel_in_rd_pending_;
331  bool quit_on_complete_;
332  bool quit_on_redirect_;
333  bool allow_certificate_errors_;
334
335  std::wstring username_;
336  std::wstring password_;
337
338  // tracks status of callbacks
339  int response_started_count_;
340  int received_bytes_count_;
341  int received_redirect_count_;
342  bool received_data_before_response_;
343  bool request_failed_;
344  bool have_certificate_errors_;
345  std::string data_received_;
346
347  // our read buffer
348  scoped_refptr<net::IOBuffer> buf_;
349};
350
351//-----------------------------------------------------------------------------
352
353// This object bounds the lifetime of an external python-based HTTP/FTP server
354// that can provide various responses useful for testing.
355class BaseTestServer : public base::RefCounted<BaseTestServer> {
356 protected:
357  BaseTestServer() {}
358  BaseTestServer(int connection_attempts, int connection_timeout)
359      : launcher_(connection_attempts, connection_timeout) {}
360
361 public:
362  void set_forking(bool forking) {
363    launcher_.set_forking(forking);
364  }
365
366  // Used with e.g. HTTPTestServer::SendQuit()
367  bool WaitToFinish(int milliseconds) {
368    return launcher_.WaitToFinish(milliseconds);
369  }
370
371  bool Stop() {
372    return launcher_.Stop();
373  }
374
375  GURL TestServerPage(const std::string& base_address,
376      const std::string& path) {
377    return GURL(base_address + path);
378  }
379
380  GURL TestServerPage(const std::string& path) {
381    // TODO(phajdan.jr): Check for problems with IPv6.
382    return GURL(scheme_ + "://" + host_name_ + ":" + port_str_ + "/" + path);
383  }
384
385  GURL TestServerPage(const std::string& path,
386                      const std::string& user,
387                      const std::string& password) {
388    // TODO(phajdan.jr): Check for problems with IPv6.
389
390    if (password.empty())
391      return GURL(scheme_ + "://" + user + "@" +
392                  host_name_ + ":" + port_str_ + "/" + path);
393
394    return GURL(scheme_ + "://" + user + ":" + password +
395                "@" + host_name_ + ":" + port_str_ + "/" + path);
396  }
397
398  // Deprecated in favor of TestServerPage.
399  // TODO(phajdan.jr): Remove TestServerPageW.
400  GURL TestServerPageW(const std::wstring& path) {
401    return TestServerPage(WideToUTF8(path));
402  }
403
404  virtual bool MakeGETRequest(const std::string& page_name) = 0;
405
406  FilePath GetDataDirectory() {
407    return launcher_.GetDocumentRootPath();
408  }
409
410 protected:
411  friend class base::RefCounted<BaseTestServer>;
412  virtual ~BaseTestServer() { }
413
414  bool Start(net::TestServerLauncher::Protocol protocol,
415             const std::string& host_name, int port,
416             const FilePath& document_root,
417             const FilePath& cert_path,
418             const std::wstring& file_root_url) {
419    if (!launcher_.Start(protocol,
420        host_name, port, document_root, cert_path, file_root_url))
421      return false;
422
423    if (protocol == net::TestServerLauncher::ProtoFTP)
424      scheme_ = "ftp";
425    else
426      scheme_ = "http";
427    if (!cert_path.empty())
428      scheme_.push_back('s');
429
430    host_name_ = host_name;
431    port_str_ = IntToString(port);
432    return true;
433  }
434
435  // Used by MakeGETRequest to implement sync load behavior.
436  class SyncTestDelegate : public TestDelegate {
437   public:
438    SyncTestDelegate() : event_(false, false), success_(false) {
439    }
440    virtual void OnResponseCompleted(URLRequest* request) {
441      MessageLoop::current()->DeleteSoon(FROM_HERE, request);
442      success_ = request->status().is_success();
443      event_.Signal();
444    }
445    bool Wait(int64 secs) {
446      TimeDelta td = TimeDelta::FromSeconds(secs);
447      if (event_.TimedWait(td))
448        return true;
449      return false;
450    }
451    bool did_succeed() const { return success_; }
452   private:
453    base::WaitableEvent event_;
454    bool success_;
455    DISALLOW_COPY_AND_ASSIGN(SyncTestDelegate);
456  };
457
458  net::TestServerLauncher launcher_;
459  std::string scheme_;
460  std::string host_name_;
461  std::string port_str_;
462};
463
464//-----------------------------------------------------------------------------
465
466// HTTP
467class HTTPTestServer : public BaseTestServer {
468 protected:
469  explicit HTTPTestServer() : loop_(NULL) {
470  }
471
472  explicit HTTPTestServer(int connection_attempts, int connection_timeout)
473      : BaseTestServer(connection_attempts, connection_timeout), loop_(NULL) {
474  }
475
476  virtual ~HTTPTestServer() {}
477
478 public:
479  // Creates and returns a new HTTPTestServer. If |loop| is non-null, requests
480  // are serviced on it, otherwise a new thread and message loop are created.
481  static scoped_refptr<HTTPTestServer> CreateServer(
482      const std::wstring& document_root,
483      MessageLoop* loop) {
484    return CreateServerWithFileRootURL(document_root, std::wstring(), loop);
485  }
486
487  static scoped_refptr<HTTPTestServer> CreateServer(
488      const std::wstring& document_root,
489      MessageLoop* loop,
490      int connection_attempts,
491      int connection_timeout) {
492    return CreateServerWithFileRootURL(document_root, std::wstring(), loop,
493                                       connection_attempts,
494                                       connection_timeout);
495  }
496
497  static scoped_refptr<HTTPTestServer> CreateServerWithFileRootURL(
498      const std::wstring& document_root,
499      const std::wstring& file_root_url,
500      MessageLoop* loop) {
501    return CreateServerWithFileRootURL(document_root, file_root_url, loop,
502                                       net::kDefaultTestConnectionAttempts,
503                                       net::kDefaultTestConnectionTimeout);
504  }
505
506  static scoped_refptr<HTTPTestServer> CreateForkingServer(
507      const std::wstring& document_root) {
508    scoped_refptr<HTTPTestServer> test_server =
509        new HTTPTestServer(net::kDefaultTestConnectionAttempts,
510                           net::kDefaultTestConnectionTimeout);
511    test_server->set_forking(true);
512    FilePath no_cert;
513    FilePath docroot = FilePath::FromWStringHack(document_root);
514    if (!StartTestServer(test_server.get(), docroot, no_cert, std::wstring()))
515      return NULL;
516    return test_server;
517  }
518
519  static scoped_refptr<HTTPTestServer> CreateServerWithFileRootURL(
520      const std::wstring& document_root,
521      const std::wstring& file_root_url,
522      MessageLoop* loop,
523      int connection_attempts,
524      int connection_timeout) {
525    scoped_refptr<HTTPTestServer> test_server =
526        new HTTPTestServer(connection_attempts, connection_timeout);
527    test_server->loop_ = loop;
528    FilePath no_cert;
529    FilePath docroot = FilePath::FromWStringHack(document_root);
530    if (!StartTestServer(test_server.get(), docroot, no_cert, file_root_url))
531      return NULL;
532    return test_server;
533  }
534
535  static bool StartTestServer(HTTPTestServer* server,
536                              const FilePath& document_root,
537                              const FilePath& cert_path,
538                              const std::wstring& file_root_url) {
539    return server->Start(net::TestServerLauncher::ProtoHTTP, kDefaultHostName,
540                         kHTTPDefaultPort, document_root, cert_path,
541                         file_root_url);
542  }
543
544  // A subclass may wish to send the request in a different manner
545  virtual bool MakeGETRequest(const std::string& page_name) {
546    const GURL& url = TestServerPage(page_name);
547
548    // Spin up a background thread for this request so that we have access to
549    // an IO message loop, and in cases where this thread already has an IO
550    // message loop, we also want to avoid spinning a nested message loop.
551    SyncTestDelegate d;
552    {
553      MessageLoop* loop = loop_;
554      scoped_ptr<base::Thread> io_thread;
555
556      if (!loop) {
557        io_thread.reset(new base::Thread("MakeGETRequest"));
558        base::Thread::Options options;
559        options.message_loop_type = MessageLoop::TYPE_IO;
560        io_thread->StartWithOptions(options);
561        loop = io_thread->message_loop();
562      }
563      loop->PostTask(FROM_HERE, NewRunnableFunction(
564            &HTTPTestServer::StartGETRequest, url, &d));
565
566      // Build bot wait for only 300 seconds we should ensure wait do not take
567      // more than 300 seconds
568      if (!d.Wait(250))
569        return false;
570    }
571    return d.did_succeed();
572  }
573
574  static void StartGETRequest(const GURL& url, URLRequest::Delegate* delegate) {
575    URLRequest* request = new URLRequest(url, delegate);
576    request->set_context(new TestURLRequestContext());
577    request->set_method("GET");
578    request->Start();
579    EXPECT_TRUE(request->is_pending());
580  }
581
582  // Some tests use browser javascript to fetch a 'kill' url that causes
583  // the server to exit by itself (rather than letting TestServerLauncher's
584  // destructor kill it).
585  // This method does the same thing so we can unit test that mechanism.
586  // You can then use WaitToFinish() to sleep until the server terminates.
587  void SendQuit() {
588    // Append the time to avoid problems where the kill page
589    // is being cached rather than being executed on the server
590    std::string page_name = StringPrintf("kill?%u",
591        static_cast<int>(base::Time::Now().ToInternalValue()));
592    int retry_count = 5;
593    while (retry_count > 0) {
594      bool r = MakeGETRequest(page_name);
595      // BUG #1048625 causes the kill GET to fail.  For now we just retry.
596      // Once the bug is fixed, we should remove the while loop and put back
597      // the following DCHECK.
598      // DCHECK(r);
599      if (r)
600        break;
601      retry_count--;
602    }
603    // Make sure we were successful in stopping the testserver.
604    DCHECK_GT(retry_count, 0);
605  }
606
607  virtual std::string scheme() { return "http"; }
608
609 private:
610  // If non-null a background thread isn't created and instead this message loop
611  // is used.
612  MessageLoop* loop_;
613};
614
615//-----------------------------------------------------------------------------
616
617class HTTPSTestServer : public HTTPTestServer {
618 protected:
619  explicit HTTPSTestServer() {
620  }
621
622 public:
623  // Create a server with a valid certificate
624  // TODO(dkegel): HTTPSTestServer should not require an instance to specify
625  // stock test certificates
626  static scoped_refptr<HTTPSTestServer> CreateGoodServer(
627      const std::wstring& document_root) {
628    scoped_refptr<HTTPSTestServer> test_server = new HTTPSTestServer();
629    FilePath docroot = FilePath::FromWStringHack(document_root);
630    FilePath certpath = test_server->launcher_.GetOKCertPath();
631    if (!test_server->Start(net::TestServerLauncher::ProtoHTTP,
632        net::TestServerLauncher::kHostName,
633        net::TestServerLauncher::kOKHTTPSPort,
634        docroot, certpath, std::wstring())) {
635      return NULL;
636    }
637    return test_server;
638  }
639
640  // Create a server with an up to date certificate for the wrong hostname
641  // for this host
642  static scoped_refptr<HTTPSTestServer> CreateMismatchedServer(
643      const std::wstring& document_root) {
644    scoped_refptr<HTTPSTestServer> test_server = new HTTPSTestServer();
645    FilePath docroot = FilePath::FromWStringHack(document_root);
646    FilePath certpath = test_server->launcher_.GetOKCertPath();
647    if (!test_server->Start(net::TestServerLauncher::ProtoHTTP,
648        net::TestServerLauncher::kMismatchedHostName,
649        net::TestServerLauncher::kOKHTTPSPort,
650        docroot, certpath, std::wstring())) {
651      return NULL;
652    }
653    return test_server;
654  }
655
656  // Create a server with an expired certificate
657  static scoped_refptr<HTTPSTestServer> CreateExpiredServer(
658      const std::wstring& document_root) {
659    scoped_refptr<HTTPSTestServer> test_server = new HTTPSTestServer();
660    FilePath docroot = FilePath::FromWStringHack(document_root);
661    FilePath certpath = test_server->launcher_.GetExpiredCertPath();
662    if (!test_server->Start(net::TestServerLauncher::ProtoHTTP,
663        net::TestServerLauncher::kHostName,
664        net::TestServerLauncher::kBadHTTPSPort,
665        docroot, certpath, std::wstring())) {
666      return NULL;
667    }
668    return test_server;
669  }
670
671  // Create a server with an arbitrary certificate
672  static scoped_refptr<HTTPSTestServer> CreateServer(
673      const std::string& host_name, int port,
674      const std::wstring& document_root,
675      const std::wstring& cert_path) {
676    scoped_refptr<HTTPSTestServer> test_server = new HTTPSTestServer();
677    FilePath docroot = FilePath::FromWStringHack(document_root);
678    FilePath certpath = FilePath::FromWStringHack(cert_path);
679    if (!test_server->Start(net::TestServerLauncher::ProtoHTTP,
680        host_name, port, docroot, certpath, std::wstring())) {
681      return NULL;
682    }
683    return test_server;
684  }
685
686 protected:
687  std::wstring cert_path_;
688
689 private:
690  virtual ~HTTPSTestServer() {}
691};
692
693//-----------------------------------------------------------------------------
694
695class FTPTestServer : public BaseTestServer {
696 public:
697  FTPTestServer() {
698  }
699
700  static scoped_refptr<FTPTestServer> CreateServer(
701      const std::wstring& document_root) {
702    scoped_refptr<FTPTestServer> test_server = new FTPTestServer();
703    FilePath docroot = FilePath::FromWStringHack(document_root);
704    FilePath no_cert;
705    if (!test_server->Start(net::TestServerLauncher::ProtoFTP,
706        kDefaultHostName, kFTPDefaultPort, docroot, no_cert, std::wstring())) {
707      return NULL;
708    }
709    return test_server;
710  }
711
712  virtual bool MakeGETRequest(const std::string& page_name) {
713    const GURL& url = TestServerPage(page_name);
714    TestDelegate d;
715    URLRequest request(url, &d);
716    request.set_context(new TestURLRequestContext());
717    request.set_method("GET");
718    request.Start();
719    EXPECT_TRUE(request.is_pending());
720
721    MessageLoop::current()->Run();
722    if (request.is_pending())
723      return false;
724
725    return true;
726  }
727
728 private:
729  ~FTPTestServer() {}
730};
731
732#endif  // NET_URL_REQUEST_URL_REQUEST_UNITTEST_H_
733