root/net/url_request/url_request_test_job.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. MaybeCreateJob
  2. test_url_1
  3. test_url_2
  4. test_url_3
  5. test_url_4
  6. test_url_error
  7. test_url_redirect_to_url_2
  8. test_data_1
  9. test_data_2
  10. test_data_3
  11. test_data_4
  12. test_headers
  13. test_redirect_headers
  14. test_redirect_to_url_2_headers
  15. test_error_headers
  16. CreateProtocolHandler
  17. weak_factory_
  18. weak_factory_
  19. weak_factory_
  20. GetMimeType
  21. SetPriority
  22. Start
  23. StartAsync
  24. ReadRawData
  25. GetResponseInfo
  26. GetLoadTimingInfo
  27. GetResponseCode
  28. IsRedirectResponse
  29. Kill
  30. ProcessNextOperation
  31. NextReadAsync
  32. AdvanceJob
  33. ProcessOnePendingMessage

// Copyright (c) 2012 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_job.h"

#include <algorithm>
#include <list>

#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/lazy_instance.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/http/http_response_headers.h"

namespace net {

namespace {

typedef std::list<URLRequestTestJob*> URLRequestJobList;
base::LazyInstance<URLRequestJobList>::Leaky
    g_pending_jobs = LAZY_INSTANCE_INITIALIZER;

class TestJobProtocolHandler : public URLRequestJobFactory::ProtocolHandler {
 public:
  // URLRequestJobFactory::ProtocolHandler implementation:
  virtual URLRequestJob* MaybeCreateJob(
      URLRequest* request, NetworkDelegate* network_delegate) const OVERRIDE {
    return new URLRequestTestJob(request, network_delegate);
  }
};

}  // namespace

// static getters for known URLs
GURL URLRequestTestJob::test_url_1() {
  return GURL("test:url1");
}
GURL URLRequestTestJob::test_url_2() {
  return GURL("test:url2");
}
GURL URLRequestTestJob::test_url_3() {
  return GURL("test:url3");
}
GURL URLRequestTestJob::test_url_4() {
  return GURL("test:url4");
}
GURL URLRequestTestJob::test_url_error() {
  return GURL("test:error");
}
GURL URLRequestTestJob::test_url_redirect_to_url_2() {
  return GURL("test:redirect_to_2");
}

// static getters for known URL responses
std::string URLRequestTestJob::test_data_1() {
  return std::string("<html><title>Test One</title></html>");
}
std::string URLRequestTestJob::test_data_2() {
  return std::string("<html><title>Test Two Two</title></html>");
}
std::string URLRequestTestJob::test_data_3() {
  return std::string("<html><title>Test Three Three Three</title></html>");
}
std::string URLRequestTestJob::test_data_4() {
  return std::string("<html><title>Test Four Four Four Four</title></html>");
}

// static getter for simple response headers
std::string URLRequestTestJob::test_headers() {
  static const char kHeaders[] =
      "HTTP/1.1 200 OK\0"
      "Content-type: text/html\0"
      "\0";
  return std::string(kHeaders, arraysize(kHeaders));
}

// static getter for redirect response headers
std::string URLRequestTestJob::test_redirect_headers() {
  static const char kHeaders[] =
      "HTTP/1.1 302 MOVED\0"
      "Location: somewhere\0"
      "\0";
  return std::string(kHeaders, arraysize(kHeaders));
}

// static getter for redirect response headers
std::string URLRequestTestJob::test_redirect_to_url_2_headers() {
  std::string headers = "HTTP/1.1 302 MOVED";
  headers.push_back('\0');
  headers += "Location: ";
  headers += test_url_2().spec();
  headers.push_back('\0');
  headers.push_back('\0');
  return headers;
}

// static getter for error response headers
std::string URLRequestTestJob::test_error_headers() {
  static const char kHeaders[] =
      "HTTP/1.1 500 BOO HOO\0"
      "\0";
  return std::string(kHeaders, arraysize(kHeaders));
}

// static
URLRequestJobFactory::ProtocolHandler*
URLRequestTestJob::CreateProtocolHandler() {
  return new TestJobProtocolHandler();
}

URLRequestTestJob::URLRequestTestJob(URLRequest* request,
                                     NetworkDelegate* network_delegate)
    : URLRequestJob(request, network_delegate),
      auto_advance_(false),
      stage_(WAITING),
      priority_(DEFAULT_PRIORITY),
      offset_(0),
      async_buf_(NULL),
      async_buf_size_(0),
      weak_factory_(this) {
}

URLRequestTestJob::URLRequestTestJob(URLRequest* request,
                                     NetworkDelegate* network_delegate,
                                     bool auto_advance)
    : URLRequestJob(request, network_delegate),
      auto_advance_(auto_advance),
      stage_(WAITING),
      priority_(DEFAULT_PRIORITY),
      offset_(0),
      async_buf_(NULL),
      async_buf_size_(0),
      weak_factory_(this) {
}

URLRequestTestJob::URLRequestTestJob(URLRequest* request,
                                     NetworkDelegate* network_delegate,
                                     const std::string& response_headers,
                                     const std::string& response_data,
                                     bool auto_advance)
    : URLRequestJob(request, network_delegate),
      auto_advance_(auto_advance),
      stage_(WAITING),
      priority_(DEFAULT_PRIORITY),
      response_headers_(new HttpResponseHeaders(response_headers)),
      response_data_(response_data),
      offset_(0),
      async_buf_(NULL),
      async_buf_size_(0),
      weak_factory_(this) {
}

URLRequestTestJob::~URLRequestTestJob() {
  g_pending_jobs.Get().erase(
      std::remove(
          g_pending_jobs.Get().begin(), g_pending_jobs.Get().end(), this),
      g_pending_jobs.Get().end());
}

bool URLRequestTestJob::GetMimeType(std::string* mime_type) const {
  DCHECK(mime_type);
  if (!response_headers_.get())
    return false;
  return response_headers_->GetMimeType(mime_type);
}

void URLRequestTestJob::SetPriority(RequestPriority priority) {
  priority_ = priority;
}

void URLRequestTestJob::Start() {
  // Start reading asynchronously so that all error reporting and data
  // callbacks happen as they would for network requests.
  base::MessageLoop::current()->PostTask(
      FROM_HERE, base::Bind(&URLRequestTestJob::StartAsync,
                            weak_factory_.GetWeakPtr()));
}

void URLRequestTestJob::StartAsync() {
  if (!response_headers_.get()) {
    response_headers_ = new HttpResponseHeaders(test_headers());
    if (request_->url().spec() == test_url_1().spec()) {
      response_data_ = test_data_1();
      stage_ = DATA_AVAILABLE;  // Simulate a synchronous response for this one.
    } else if (request_->url().spec() == test_url_2().spec()) {
      response_data_ = test_data_2();
    } else if (request_->url().spec() == test_url_3().spec()) {
      response_data_ = test_data_3();
    } else if (request_->url().spec() == test_url_4().spec()) {
      response_data_ = test_data_4();
    } else if (request_->url().spec() == test_url_redirect_to_url_2().spec()) {
      response_headers_ =
          new HttpResponseHeaders(test_redirect_to_url_2_headers());
    } else {
      AdvanceJob();

      // unexpected url, return error
      // FIXME(brettw) we may want to use WININET errors or have some more types
      // of errors
      NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
                                  ERR_INVALID_URL));
      // FIXME(brettw): this should emulate a network error, and not just fail
      // initiating a connection
      return;
    }
  }

  AdvanceJob();

  this->NotifyHeadersComplete();
}

bool URLRequestTestJob::ReadRawData(IOBuffer* buf, int buf_size,
                                    int *bytes_read) {
  if (stage_ == WAITING) {
    async_buf_ = buf;
    async_buf_size_ = buf_size;
    SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
    return false;
  }

  DCHECK(bytes_read);
  *bytes_read = 0;

  if (offset_ >= static_cast<int>(response_data_.length())) {
    return true;  // done reading
  }

  int to_read = buf_size;
  if (to_read + offset_ > static_cast<int>(response_data_.length()))
    to_read = static_cast<int>(response_data_.length()) - offset_;

  memcpy(buf->data(), &response_data_.c_str()[offset_], to_read);
  offset_ += to_read;

  *bytes_read = to_read;
  return true;
}

void URLRequestTestJob::GetResponseInfo(HttpResponseInfo* info) {
  if (response_headers_.get())
    info->headers = response_headers_;
}

void URLRequestTestJob::GetLoadTimingInfo(
    LoadTimingInfo* load_timing_info) const {
  // Preserve the times the URLRequest is responsible for, but overwrite all
  // the others.
  base::TimeTicks request_start = load_timing_info->request_start;
  base::Time request_start_time = load_timing_info->request_start_time;
  *load_timing_info = load_timing_info_;
  load_timing_info->request_start = request_start;
  load_timing_info->request_start_time = request_start_time;
}

int URLRequestTestJob::GetResponseCode() const {
  if (response_headers_.get())
    return response_headers_->response_code();
  return -1;
}

bool URLRequestTestJob::IsRedirectResponse(GURL* location,
                                           int* http_status_code) {
  if (!response_headers_.get())
    return false;

  std::string value;
  if (!response_headers_->IsRedirect(&value))
    return false;

  *location = request_->url().Resolve(value);
  *http_status_code = response_headers_->response_code();
  return true;
}

void URLRequestTestJob::Kill() {
  stage_ = DONE;
  URLRequestJob::Kill();
  weak_factory_.InvalidateWeakPtrs();
  g_pending_jobs.Get().erase(
      std::remove(
          g_pending_jobs.Get().begin(), g_pending_jobs.Get().end(), this),
      g_pending_jobs.Get().end());
}

void URLRequestTestJob::ProcessNextOperation() {
  switch (stage_) {
    case WAITING:
      // Must call AdvanceJob() prior to NotifyReadComplete() since that may
      // delete |this|.
      AdvanceJob();
      stage_ = DATA_AVAILABLE;
      // OK if ReadRawData wasn't called yet.
      if (async_buf_) {
        int bytes_read;
        if (!ReadRawData(async_buf_, async_buf_size_, &bytes_read))
          NOTREACHED() << "This should not return false in DATA_AVAILABLE.";
        SetStatus(URLRequestStatus());  // clear the io pending flag
        if (NextReadAsync()) {
          // Make all future reads return io pending until the next
          // ProcessNextOperation().
          stage_ = WAITING;
        }
        NotifyReadComplete(bytes_read);
      }
      break;
    case DATA_AVAILABLE:
      AdvanceJob();
      stage_ = ALL_DATA;  // done sending data
      break;
    case ALL_DATA:
      stage_ = DONE;
      return;
    case DONE:
      return;
    default:
      NOTREACHED() << "Invalid stage";
      return;
  }
}

bool URLRequestTestJob::NextReadAsync() {
  return false;
}

void URLRequestTestJob::AdvanceJob() {
  if (auto_advance_) {
    base::MessageLoop::current()->PostTask(
        FROM_HERE, base::Bind(&URLRequestTestJob::ProcessNextOperation,
                              weak_factory_.GetWeakPtr()));
    return;
  }
  g_pending_jobs.Get().push_back(this);
}

// static
bool URLRequestTestJob::ProcessOnePendingMessage() {
  if (g_pending_jobs.Get().empty())
    return false;

  URLRequestTestJob* next_job(g_pending_jobs.Get().front());
  g_pending_jobs.Get().pop_front();

  DCHECK(!next_job->auto_advance());  // auto_advance jobs should be in this q
  next_job->ProcessNextOperation();
  return true;
}

}  // namespace net

/* [<][>][^][v][top][bottom][index][help] */