// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/url_request/url_request_test_util.h"

#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/threading/thread.h"
#include "net/base/host_port_pair.h"
#include "net/http/http_network_session.h"
#include "net/http/http_server_properties_impl.h"
#include "net/url_request/url_request_job_factory.h"

namespace {

// These constants put the net::NetworkDelegate events of TestNetworkDelegate
// into an order. They are used in conjunction with
// |TestNetworkDelegate::next_states_| to check that we do not send
// events in the wrong order.
const int kStageBeforeURLRequest = 1 << 0;
const int kStageBeforeSendHeaders = 1 << 1;
const int kStageSendHeaders = 1 << 2;
const int kStageHeadersReceived = 1 << 3;
const int kStageAuthRequired = 1 << 4;
const int kStageBeforeRedirect = 1 << 5;
const int kStageResponseStarted = 1 << 6;
const int kStageCompletedSuccess = 1 << 7;
const int kStageCompletedError = 1 << 8;
const int kStageURLRequestDestroyed = 1 << 9;
const int kStageDestruction = 1 << 10;

}  // namespace

TestURLRequestContext::TestURLRequestContext()
    : initialized_(false),
      ALLOW_THIS_IN_INITIALIZER_LIST(context_storage_(this)) {
  context_storage_.set_host_resolver(
      net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism,
                                    net::HostResolver::kDefaultRetryAttempts,
                                    NULL));
  SetProxyDirect();
  Init();
}

TestURLRequestContext::TestURLRequestContext(bool delay_initialization)
    : initialized_(false),
      ALLOW_THIS_IN_INITIALIZER_LIST(context_storage_(this)) {
  context_storage_.set_host_resolver(
      net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism,
                                    net::HostResolver::kDefaultRetryAttempts,
                                    NULL));
  SetProxyDirect();
  if (!delay_initialization)
    Init();
}

TestURLRequestContext::TestURLRequestContext(const std::string& proxy)
    : initialized_(false),
      ALLOW_THIS_IN_INITIALIZER_LIST(context_storage_(this)) {
  context_storage_.set_host_resolver(
      net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism,
                                    net::HostResolver::kDefaultRetryAttempts,
                                    NULL));
  SetProxyFromString(proxy);
  Init();
}

TestURLRequestContext::TestURLRequestContext(const char* proxy)
    : initialized_(false),
      ALLOW_THIS_IN_INITIALIZER_LIST(context_storage_(this)) {
  context_storage_.set_host_resolver(
      net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism,
                                    net::HostResolver::kDefaultRetryAttempts,
                                    NULL));
  SetProxyFromString(proxy);
  Init();
}

TestURLRequestContext::TestURLRequestContext(const std::string& proxy,
                                             net::HostResolver* host_resolver)
    : initialized_(false),
      ALLOW_THIS_IN_INITIALIZER_LIST(context_storage_(this)) {
  context_storage_.set_host_resolver(host_resolver);
  SetProxyFromString(proxy);
  Init();
}

void TestURLRequestContext::SetProxyFromString(const std::string& proxy) {
  DCHECK(!initialized_);
  net::ProxyConfig proxy_config;
  proxy_config.proxy_rules().ParseFromString(proxy);
  context_storage_.set_proxy_service(
      net::ProxyService::CreateFixed(proxy_config));
}

void TestURLRequestContext::SetProxyDirect() {
  DCHECK(!initialized_);
  context_storage_.set_proxy_service(net::ProxyService::CreateDirect());
}

TestURLRequestContext::~TestURLRequestContext() {
  DCHECK(initialized_);
}

void TestURLRequestContext::Init() {
  DCHECK(!initialized_);
  initialized_ = true;
  if (!cert_verifier())
    context_storage_.set_cert_verifier(new net::CertVerifier);
  if (!ftp_transaction_factory()) {
    context_storage_.set_ftp_transaction_factory(
        new net::FtpNetworkLayer(host_resolver()));
  }
  if (!ssl_config_service())
    context_storage_.set_ssl_config_service(new net::SSLConfigServiceDefaults);
  if (!http_auth_handler_factory()) {
    context_storage_.set_http_auth_handler_factory(
        net::HttpAuthHandlerFactory::CreateDefault(host_resolver()));
  }
  if (!http_server_properties()) {
    context_storage_.set_http_server_properties(
        new net::HttpServerPropertiesImpl);
  }
  net::HttpNetworkSession::Params params;
  params.host_resolver = host_resolver();
  params.cert_verifier = cert_verifier();
  params.proxy_service = proxy_service();
  params.ssl_config_service = ssl_config_service();
  params.http_auth_handler_factory = http_auth_handler_factory();
  params.network_delegate = network_delegate();
  params.http_server_properties = http_server_properties();

  if (!http_transaction_factory()) {
    context_storage_.set_http_transaction_factory(new net::HttpCache(
        new net::HttpNetworkSession(params),
        net::HttpCache::DefaultBackend::InMemory(0)));
  }
  // In-memory cookie store.
  if (!cookie_store())
    context_storage_.set_cookie_store(new net::CookieMonster(NULL, NULL));
  if (accept_language().empty())
    set_accept_language("en-us,fr");
  if (accept_charset().empty())
    set_accept_charset("iso-8859-1,*,utf-8");
  if (!job_factory())
    context_storage_.set_job_factory(new net::URLRequestJobFactory);
}


TestURLRequest::TestURLRequest(const GURL& url, Delegate* delegate)
    : net::URLRequest(url, delegate) {
  set_context(new TestURLRequestContext());
}

TestURLRequest::~TestURLRequest() {}

TestDelegate::TestDelegate()
    : cancel_in_rr_(false),
      cancel_in_rs_(false),
      cancel_in_rd_(false),
      cancel_in_rd_pending_(false),
      quit_on_complete_(true),
      quit_on_redirect_(false),
      allow_certificate_errors_(false),
      cookie_options_bit_mask_(0),
      response_started_count_(0),
      received_bytes_count_(0),
      received_redirect_count_(0),
      blocked_get_cookies_count_(0),
      blocked_set_cookie_count_(0),
      set_cookie_count_(0),
      received_data_before_response_(false),
      request_failed_(false),
      have_certificate_errors_(false),
      auth_required_(false),
      buf_(new net::IOBuffer(kBufferSize)) {
}

TestDelegate::~TestDelegate() {}

void TestDelegate::OnReceivedRedirect(net::URLRequest* request,
                                      const GURL& new_url,
                                      bool* defer_redirect) {
  received_redirect_count_++;
  if (quit_on_redirect_) {
    *defer_redirect = true;
    MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
  } else if (cancel_in_rr_) {
    request->Cancel();
  }
}

void TestDelegate::OnAuthRequired(net::URLRequest* request,
                                  net::AuthChallengeInfo* auth_info) {
  auth_required_ = true;
  if (!credentials_.Empty()) {
    request->SetAuth(credentials_);
  } else {
    request->CancelAuth();
  }
}

void TestDelegate::OnSSLCertificateError(net::URLRequest* request,
                                         const net::SSLInfo& ssl_info,
                                         bool is_hsts_host) {
  // The caller can control whether it needs all SSL requests to go through,
  // independent of any possible errors, or whether it wants SSL errors to
  // cancel the request.
  have_certificate_errors_ = true;
  if (allow_certificate_errors_)
    request->ContinueDespiteLastError();
  else
    request->Cancel();
}

bool TestDelegate::CanGetCookies(const net::URLRequest* request,
                                 const net::CookieList& cookie_list) const {
  bool allow = true;
  if (cookie_options_bit_mask_ & NO_GET_COOKIES)
    allow = false;

  if (!allow) {
    blocked_get_cookies_count_++;
  }

  return allow;
}

bool TestDelegate::CanSetCookie(const net::URLRequest* request,
                                const std::string& cookie_line,
                                net::CookieOptions* options) const {
  bool allow = true;
  if (cookie_options_bit_mask_ & NO_SET_COOKIE)
    allow = false;

  if (cookie_options_bit_mask_ & FORCE_SESSION)
    options->set_force_session();


  if (!allow) {
    blocked_set_cookie_count_++;
  } else {
    set_cookie_count_++;
  }

  return allow;
}

void TestDelegate::OnResponseStarted(net::URLRequest* request) {
  // It doesn't make sense for the request to have IO pending at this point.
  DCHECK(!request->status().is_io_pending());

  response_started_count_++;
  if (cancel_in_rs_) {
    request->Cancel();
    OnResponseCompleted(request);
  } else if (!request->status().is_success()) {
    DCHECK(request->status().status() == net::URLRequestStatus::FAILED ||
           request->status().status() == net::URLRequestStatus::CANCELED);
    request_failed_ = true;
    OnResponseCompleted(request);
  } else {
    // Initiate the first read.
    int bytes_read = 0;
    if (request->Read(buf_, kBufferSize, &bytes_read))
      OnReadCompleted(request, bytes_read);
    else if (!request->status().is_io_pending())
      OnResponseCompleted(request);
  }
}

void TestDelegate::OnReadCompleted(net::URLRequest* request, int bytes_read) {
  // It doesn't make sense for the request to have IO pending at this point.
  DCHECK(!request->status().is_io_pending());

  if (response_started_count_ == 0)
    received_data_before_response_ = true;

  if (cancel_in_rd_)
    request->Cancel();

  if (bytes_read >= 0) {
    // There is data to read.
    received_bytes_count_ += bytes_read;

    // consume the data
    data_received_.append(buf_->data(), bytes_read);
  }

  // If it was not end of stream, request to read more.
  if (request->status().is_success() && bytes_read > 0) {
    bytes_read = 0;
    while (request->Read(buf_, kBufferSize, &bytes_read)) {
      if (bytes_read > 0) {
        data_received_.append(buf_->data(), bytes_read);
        received_bytes_count_ += bytes_read;
      } else {
        break;
      }
    }
  }
  if (!request->status().is_io_pending())
    OnResponseCompleted(request);
  else if (cancel_in_rd_pending_)
    request->Cancel();
}

void TestDelegate::OnResponseCompleted(net::URLRequest* request) {
  if (quit_on_complete_)
    MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
}

TestNetworkDelegate::TestNetworkDelegate()
  : last_error_(0),
    error_count_(0),
    created_requests_(0),
    destroyed_requests_(0),
    completed_requests_(0) {
}

TestNetworkDelegate::~TestNetworkDelegate() {
  for (std::map<int, int>::iterator i = next_states_.begin();
       i != next_states_.end(); ++i) {
    event_order_[i->first] += "~TestNetworkDelegate\n";
    EXPECT_TRUE(i->second & kStageDestruction) << event_order_[i->first];
  }
}

void TestNetworkDelegate::InitRequestStatesIfNew(int request_id) {
  if (next_states_.find(request_id) == next_states_.end()) {
    next_states_[request_id] = kStageBeforeURLRequest;
    event_order_[request_id] = "";
  }
}

int TestNetworkDelegate::OnBeforeURLRequest(
    net::URLRequest* request,
    net::OldCompletionCallback* callback,
    GURL* new_url ) {
  int req_id = request->identifier();
  InitRequestStatesIfNew(req_id);
  event_order_[req_id] += "OnBeforeURLRequest\n";
  EXPECT_TRUE(next_states_[req_id] & kStageBeforeURLRequest) <<
      event_order_[req_id];
  next_states_[req_id] =
      kStageBeforeSendHeaders |
      kStageResponseStarted |  // data: URLs do not trigger sending headers
      kStageBeforeRedirect |  // a delegate can trigger a redirection
      kStageCompletedError |  // request canceled by delegate
      kStageAuthRequired;  // Auth can come next for FTP requests
  created_requests_++;
  return net::OK;
}

int TestNetworkDelegate::OnBeforeSendHeaders(
    net::URLRequest* request,
    net::OldCompletionCallback* callback,
    net::HttpRequestHeaders* headers) {
  int req_id = request->identifier();
  InitRequestStatesIfNew(req_id);
  event_order_[req_id] += "OnBeforeSendHeaders\n";
  EXPECT_TRUE(next_states_[req_id] & kStageBeforeSendHeaders) <<
      event_order_[req_id];
  next_states_[req_id] =
      kStageSendHeaders |
      kStageCompletedError;  // request canceled by delegate

  return net::OK;
}

void TestNetworkDelegate::OnSendHeaders(
    net::URLRequest* request,
    const net::HttpRequestHeaders& headers) {
  int req_id = request->identifier();
  InitRequestStatesIfNew(req_id);
  event_order_[req_id] += "OnSendHeaders\n";
  EXPECT_TRUE(next_states_[req_id] & kStageSendHeaders) <<
      event_order_[req_id];
  next_states_[req_id] =
      kStageHeadersReceived |
      kStageCompletedError;
}

int TestNetworkDelegate::OnHeadersReceived(
    net::URLRequest* request,
    net::OldCompletionCallback* callback,
    net::HttpResponseHeaders* original_response_headers,
    scoped_refptr<net::HttpResponseHeaders>* override_response_headers) {
  int req_id = request->identifier();
  event_order_[req_id] += "OnHeadersReceived\n";
  InitRequestStatesIfNew(req_id);
  EXPECT_TRUE(next_states_[req_id] & kStageHeadersReceived) <<
      event_order_[req_id];
  next_states_[req_id] =
      kStageBeforeRedirect |
      kStageResponseStarted |
      kStageAuthRequired |
      kStageCompletedError;  // e.g. proxy resolution problem

  // Basic authentication sends a second request from the URLRequestHttpJob
  // layer before the URLRequest reports that a response has started.
  next_states_[req_id] |= kStageBeforeSendHeaders;

  return net::OK;
}

void TestNetworkDelegate::OnBeforeRedirect(net::URLRequest* request,
                                           const GURL& new_location) {
  int req_id = request->identifier();
  InitRequestStatesIfNew(req_id);
  event_order_[req_id] += "OnBeforeRedirect\n";
  EXPECT_TRUE(next_states_[req_id] & kStageBeforeRedirect) <<
      event_order_[req_id];
  next_states_[req_id] =
      kStageBeforeURLRequest |  // HTTP redirects trigger this.
      kStageBeforeSendHeaders |  // Redirects from the network delegate do not
                                 // trigger onBeforeURLRequest.
      kStageCompletedError;

  // A redirect can lead to a file or a data URL. In this case, we do not send
  // headers.
  next_states_[req_id] |= kStageResponseStarted;
}

void TestNetworkDelegate::OnResponseStarted(net::URLRequest* request) {
  int req_id = request->identifier();
  InitRequestStatesIfNew(req_id);
  event_order_[req_id] += "OnResponseStarted\n";
  EXPECT_TRUE(next_states_[req_id] & kStageResponseStarted) <<
      event_order_[req_id];
  next_states_[req_id] = kStageCompletedSuccess | kStageCompletedError;
  if (request->status().status() == net::URLRequestStatus::FAILED) {
    error_count_++;
    last_error_ = request->status().error();
  }
}

void TestNetworkDelegate::OnRawBytesRead(const net::URLRequest& request,
                                         int bytes_read) {
}

void TestNetworkDelegate::OnCompleted(net::URLRequest* request) {
  int req_id = request->identifier();
  InitRequestStatesIfNew(req_id);
  event_order_[req_id] += "OnCompleted\n";
  // Expect "Success -> (next_states_ & kStageCompletedSuccess)"
  // is logically identical to
  // Expect "!(Success) || (next_states_ & kStageCompletedSuccess)"
  EXPECT_TRUE(!request->status().is_success() ||
              (next_states_[req_id] & kStageCompletedSuccess)) <<
      event_order_[req_id];
  EXPECT_TRUE(request->status().is_success() ||
              (next_states_[req_id] & kStageCompletedError)) <<
      event_order_[req_id];
  next_states_[req_id] = kStageURLRequestDestroyed;
  completed_requests_++;
  if (request->status().status() == net::URLRequestStatus::FAILED) {
    error_count_++;
    last_error_ = request->status().error();
  }
}

void TestNetworkDelegate::OnURLRequestDestroyed(
    net::URLRequest* request) {
  int req_id = request->identifier();
  InitRequestStatesIfNew(req_id);
  event_order_[req_id] += "OnURLRequestDestroyed\n";
  EXPECT_TRUE(next_states_[req_id] & kStageURLRequestDestroyed) <<
      event_order_[req_id];
  next_states_[req_id] = kStageDestruction;
  destroyed_requests_++;
}

void TestNetworkDelegate::OnPACScriptError(int line_number,
                                           const string16& error) {
}

net::NetworkDelegate::AuthRequiredResponse TestNetworkDelegate::OnAuthRequired(
    net::URLRequest* request,
    const net::AuthChallengeInfo& auth_info,
    const AuthCallback& callback,
    net::AuthCredentials* credentials) {
  int req_id = request->identifier();
  InitRequestStatesIfNew(req_id);
  event_order_[req_id] += "OnAuthRequired\n";
  EXPECT_TRUE(next_states_[req_id] & kStageAuthRequired) <<
      event_order_[req_id];
  next_states_[req_id] = kStageBeforeSendHeaders |
      kStageHeadersReceived |  // Request canceled by delegate simulates empty
                               // response.
      kStageResponseStarted |  // data: URLs do not trigger sending headers
      kStageBeforeRedirect;  // a delegate can trigger a redirection
  return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION;
}