Home | History | Annotate | Download | only in url_request
      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_test_job.h"
      6 
      7 #include <algorithm>
      8 #include <list>
      9 
     10 #include "base/bind.h"
     11 #include "base/compiler_specific.h"
     12 #include "base/lazy_instance.h"
     13 #include "base/message_loop/message_loop.h"
     14 #include "base/strings/string_util.h"
     15 #include "net/base/io_buffer.h"
     16 #include "net/base/net_errors.h"
     17 #include "net/http/http_response_headers.h"
     18 
     19 namespace net {
     20 
     21 namespace {
     22 
     23 typedef std::list<URLRequestTestJob*> URLRequestJobList;
     24 base::LazyInstance<URLRequestJobList>::Leaky
     25     g_pending_jobs = LAZY_INSTANCE_INITIALIZER;
     26 
     27 class TestJobProtocolHandler : public URLRequestJobFactory::ProtocolHandler {
     28  public:
     29   // URLRequestJobFactory::ProtocolHandler implementation:
     30   virtual URLRequestJob* MaybeCreateJob(
     31       URLRequest* request, NetworkDelegate* network_delegate) const OVERRIDE {
     32     return new URLRequestTestJob(request, network_delegate);
     33   }
     34 };
     35 
     36 }  // namespace
     37 
     38 // static getters for known URLs
     39 GURL URLRequestTestJob::test_url_1() {
     40   return GURL("test:url1");
     41 }
     42 GURL URLRequestTestJob::test_url_2() {
     43   return GURL("test:url2");
     44 }
     45 GURL URLRequestTestJob::test_url_3() {
     46   return GURL("test:url3");
     47 }
     48 GURL URLRequestTestJob::test_url_4() {
     49   return GURL("test:url4");
     50 }
     51 GURL URLRequestTestJob::test_url_error() {
     52   return GURL("test:error");
     53 }
     54 GURL URLRequestTestJob::test_url_redirect_to_url_2() {
     55   return GURL("test:redirect_to_2");
     56 }
     57 
     58 // static getters for known URL responses
     59 std::string URLRequestTestJob::test_data_1() {
     60   return std::string("<html><title>Test One</title></html>");
     61 }
     62 std::string URLRequestTestJob::test_data_2() {
     63   return std::string("<html><title>Test Two Two</title></html>");
     64 }
     65 std::string URLRequestTestJob::test_data_3() {
     66   return std::string("<html><title>Test Three Three Three</title></html>");
     67 }
     68 std::string URLRequestTestJob::test_data_4() {
     69   return std::string("<html><title>Test Four Four Four Four</title></html>");
     70 }
     71 
     72 // static getter for simple response headers
     73 std::string URLRequestTestJob::test_headers() {
     74   static const char kHeaders[] =
     75       "HTTP/1.1 200 OK\0"
     76       "Content-type: text/html\0"
     77       "\0";
     78   return std::string(kHeaders, arraysize(kHeaders));
     79 }
     80 
     81 // static getter for redirect response headers
     82 std::string URLRequestTestJob::test_redirect_headers() {
     83   static const char kHeaders[] =
     84       "HTTP/1.1 302 MOVED\0"
     85       "Location: somewhere\0"
     86       "\0";
     87   return std::string(kHeaders, arraysize(kHeaders));
     88 }
     89 
     90 // static getter for redirect response headers
     91 std::string URLRequestTestJob::test_redirect_to_url_2_headers() {
     92   std::string headers = "HTTP/1.1 302 MOVED";
     93   headers.push_back('\0');
     94   headers += "Location: ";
     95   headers += test_url_2().spec();
     96   headers.push_back('\0');
     97   headers.push_back('\0');
     98   return headers;
     99 }
    100 
    101 // static getter for error response headers
    102 std::string URLRequestTestJob::test_error_headers() {
    103   static const char kHeaders[] =
    104       "HTTP/1.1 500 BOO HOO\0"
    105       "\0";
    106   return std::string(kHeaders, arraysize(kHeaders));
    107 }
    108 
    109 // static
    110 URLRequestJobFactory::ProtocolHandler*
    111 URLRequestTestJob::CreateProtocolHandler() {
    112   return new TestJobProtocolHandler();
    113 }
    114 
    115 URLRequestTestJob::URLRequestTestJob(URLRequest* request,
    116                                      NetworkDelegate* network_delegate)
    117     : URLRequestJob(request, network_delegate),
    118       auto_advance_(false),
    119       stage_(WAITING),
    120       priority_(DEFAULT_PRIORITY),
    121       offset_(0),
    122       async_buf_(NULL),
    123       async_buf_size_(0),
    124       weak_factory_(this) {
    125 }
    126 
    127 URLRequestTestJob::URLRequestTestJob(URLRequest* request,
    128                                      NetworkDelegate* network_delegate,
    129                                      bool auto_advance)
    130     : URLRequestJob(request, network_delegate),
    131       auto_advance_(auto_advance),
    132       stage_(WAITING),
    133       priority_(DEFAULT_PRIORITY),
    134       offset_(0),
    135       async_buf_(NULL),
    136       async_buf_size_(0),
    137       weak_factory_(this) {
    138 }
    139 
    140 URLRequestTestJob::URLRequestTestJob(URLRequest* request,
    141                                      NetworkDelegate* network_delegate,
    142                                      const std::string& response_headers,
    143                                      const std::string& response_data,
    144                                      bool auto_advance)
    145     : URLRequestJob(request, network_delegate),
    146       auto_advance_(auto_advance),
    147       stage_(WAITING),
    148       priority_(DEFAULT_PRIORITY),
    149       response_headers_(new HttpResponseHeaders(response_headers)),
    150       response_data_(response_data),
    151       offset_(0),
    152       async_buf_(NULL),
    153       async_buf_size_(0),
    154       weak_factory_(this) {
    155 }
    156 
    157 URLRequestTestJob::~URLRequestTestJob() {
    158   g_pending_jobs.Get().erase(
    159       std::remove(
    160           g_pending_jobs.Get().begin(), g_pending_jobs.Get().end(), this),
    161       g_pending_jobs.Get().end());
    162 }
    163 
    164 bool URLRequestTestJob::GetMimeType(std::string* mime_type) const {
    165   DCHECK(mime_type);
    166   if (!response_headers_.get())
    167     return false;
    168   return response_headers_->GetMimeType(mime_type);
    169 }
    170 
    171 void URLRequestTestJob::SetPriority(RequestPriority priority) {
    172   priority_ = priority;
    173 }
    174 
    175 void URLRequestTestJob::Start() {
    176   // Start reading asynchronously so that all error reporting and data
    177   // callbacks happen as they would for network requests.
    178   base::MessageLoop::current()->PostTask(
    179       FROM_HERE, base::Bind(&URLRequestTestJob::StartAsync,
    180                             weak_factory_.GetWeakPtr()));
    181 }
    182 
    183 void URLRequestTestJob::StartAsync() {
    184   if (!response_headers_.get()) {
    185     response_headers_ = new HttpResponseHeaders(test_headers());
    186     if (request_->url().spec() == test_url_1().spec()) {
    187       response_data_ = test_data_1();
    188       stage_ = DATA_AVAILABLE;  // Simulate a synchronous response for this one.
    189     } else if (request_->url().spec() == test_url_2().spec()) {
    190       response_data_ = test_data_2();
    191     } else if (request_->url().spec() == test_url_3().spec()) {
    192       response_data_ = test_data_3();
    193     } else if (request_->url().spec() == test_url_4().spec()) {
    194       response_data_ = test_data_4();
    195     } else if (request_->url().spec() == test_url_redirect_to_url_2().spec()) {
    196       response_headers_ =
    197           new HttpResponseHeaders(test_redirect_to_url_2_headers());
    198     } else {
    199       AdvanceJob();
    200 
    201       // unexpected url, return error
    202       // FIXME(brettw) we may want to use WININET errors or have some more types
    203       // of errors
    204       NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
    205                                   ERR_INVALID_URL));
    206       // FIXME(brettw): this should emulate a network error, and not just fail
    207       // initiating a connection
    208       return;
    209     }
    210   }
    211 
    212   AdvanceJob();
    213 
    214   this->NotifyHeadersComplete();
    215 }
    216 
    217 bool URLRequestTestJob::ReadRawData(IOBuffer* buf, int buf_size,
    218                                     int *bytes_read) {
    219   if (stage_ == WAITING) {
    220     async_buf_ = buf;
    221     async_buf_size_ = buf_size;
    222     SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
    223     return false;
    224   }
    225 
    226   DCHECK(bytes_read);
    227   *bytes_read = 0;
    228 
    229   if (offset_ >= static_cast<int>(response_data_.length())) {
    230     return true;  // done reading
    231   }
    232 
    233   int to_read = buf_size;
    234   if (to_read + offset_ > static_cast<int>(response_data_.length()))
    235     to_read = static_cast<int>(response_data_.length()) - offset_;
    236 
    237   memcpy(buf->data(), &response_data_.c_str()[offset_], to_read);
    238   offset_ += to_read;
    239 
    240   *bytes_read = to_read;
    241   return true;
    242 }
    243 
    244 void URLRequestTestJob::GetResponseInfo(HttpResponseInfo* info) {
    245   if (response_headers_.get())
    246     info->headers = response_headers_;
    247 }
    248 
    249 void URLRequestTestJob::GetLoadTimingInfo(
    250     LoadTimingInfo* load_timing_info) const {
    251   // Preserve the times the URLRequest is responsible for, but overwrite all
    252   // the others.
    253   base::TimeTicks request_start = load_timing_info->request_start;
    254   base::Time request_start_time = load_timing_info->request_start_time;
    255   *load_timing_info = load_timing_info_;
    256   load_timing_info->request_start = request_start;
    257   load_timing_info->request_start_time = request_start_time;
    258 }
    259 
    260 int URLRequestTestJob::GetResponseCode() const {
    261   if (response_headers_.get())
    262     return response_headers_->response_code();
    263   return -1;
    264 }
    265 
    266 bool URLRequestTestJob::IsRedirectResponse(GURL* location,
    267                                            int* http_status_code) {
    268   if (!response_headers_.get())
    269     return false;
    270 
    271   std::string value;
    272   if (!response_headers_->IsRedirect(&value))
    273     return false;
    274 
    275   *location = request_->url().Resolve(value);
    276   *http_status_code = response_headers_->response_code();
    277   return true;
    278 }
    279 
    280 void URLRequestTestJob::Kill() {
    281   stage_ = DONE;
    282   URLRequestJob::Kill();
    283   weak_factory_.InvalidateWeakPtrs();
    284   g_pending_jobs.Get().erase(
    285       std::remove(
    286           g_pending_jobs.Get().begin(), g_pending_jobs.Get().end(), this),
    287       g_pending_jobs.Get().end());
    288 }
    289 
    290 void URLRequestTestJob::ProcessNextOperation() {
    291   switch (stage_) {
    292     case WAITING:
    293       // Must call AdvanceJob() prior to NotifyReadComplete() since that may
    294       // delete |this|.
    295       AdvanceJob();
    296       stage_ = DATA_AVAILABLE;
    297       // OK if ReadRawData wasn't called yet.
    298       if (async_buf_) {
    299         int bytes_read;
    300         if (!ReadRawData(async_buf_, async_buf_size_, &bytes_read))
    301           NOTREACHED() << "This should not return false in DATA_AVAILABLE.";
    302         SetStatus(URLRequestStatus());  // clear the io pending flag
    303         if (NextReadAsync()) {
    304           // Make all future reads return io pending until the next
    305           // ProcessNextOperation().
    306           stage_ = WAITING;
    307         }
    308         NotifyReadComplete(bytes_read);
    309       }
    310       break;
    311     case DATA_AVAILABLE:
    312       AdvanceJob();
    313       stage_ = ALL_DATA;  // done sending data
    314       break;
    315     case ALL_DATA:
    316       stage_ = DONE;
    317       return;
    318     case DONE:
    319       return;
    320     default:
    321       NOTREACHED() << "Invalid stage";
    322       return;
    323   }
    324 }
    325 
    326 bool URLRequestTestJob::NextReadAsync() {
    327   return false;
    328 }
    329 
    330 void URLRequestTestJob::AdvanceJob() {
    331   if (auto_advance_) {
    332     base::MessageLoop::current()->PostTask(
    333         FROM_HERE, base::Bind(&URLRequestTestJob::ProcessNextOperation,
    334                               weak_factory_.GetWeakPtr()));
    335     return;
    336   }
    337   g_pending_jobs.Get().push_back(this);
    338 }
    339 
    340 // static
    341 bool URLRequestTestJob::ProcessOnePendingMessage() {
    342   if (g_pending_jobs.Get().empty())
    343     return false;
    344 
    345   URLRequestTestJob* next_job(g_pending_jobs.Get().front());
    346   g_pending_jobs.Get().pop_front();
    347 
    348   DCHECK(!next_job->auto_advance());  // auto_advance jobs should be in this q
    349   next_job->ProcessNextOperation();
    350   return true;
    351 }
    352 
    353 }  // namespace net
    354