Home | History | Annotate | Download | only in url_request
      1 // Copyright (c) 2011 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 <vector>
      6 
      7 #include "net/url_request/url_request_test_job.h"
      8 
      9 #include "base/message_loop.h"
     10 #include "base/string_util.h"
     11 #include "net/base/io_buffer.h"
     12 #include "net/base/net_errors.h"
     13 #include "net/http/http_response_headers.h"
     14 #include "net/url_request/url_request.h"
     15 
     16 namespace net {
     17 
     18 // This emulates the global message loop for the test URL request class, since
     19 // this is only test code, it's probably not too dangerous to have this static
     20 // object.
     21 static std::vector< scoped_refptr<URLRequestTestJob> > g_pending_jobs;
     22 
     23 // static getters for known URLs
     24 GURL URLRequestTestJob::test_url_1() {
     25   return GURL("test:url1");
     26 }
     27 GURL URLRequestTestJob::test_url_2() {
     28   return GURL("test:url2");
     29 }
     30 GURL URLRequestTestJob::test_url_3() {
     31   return GURL("test:url3");
     32 }
     33 GURL URLRequestTestJob::test_url_error() {
     34   return GURL("test:error");
     35 }
     36 
     37 // static getters for known URL responses
     38 std::string URLRequestTestJob::test_data_1() {
     39   return std::string("<html><title>Test One</title></html>");
     40 }
     41 std::string URLRequestTestJob::test_data_2() {
     42   return std::string("<html><title>Test Two Two</title></html>");
     43 }
     44 std::string URLRequestTestJob::test_data_3() {
     45   return std::string("<html><title>Test Three Three Three</title></html>");
     46 }
     47 
     48 // static getter for simple response headers
     49 std::string URLRequestTestJob::test_headers() {
     50   const char headers[] =
     51       "HTTP/1.1 200 OK\0"
     52       "Content-type: text/html\0"
     53       "\0";
     54   return std::string(headers, arraysize(headers));
     55 }
     56 
     57 // static getter for redirect response headers
     58 std::string URLRequestTestJob::test_redirect_headers() {
     59   const char headers[] =
     60       "HTTP/1.1 302 MOVED\0"
     61       "Location: somewhere\0"
     62       "\0";
     63   return std::string(headers, arraysize(headers));
     64 }
     65 
     66 // static getter for error response headers
     67 std::string URLRequestTestJob::test_error_headers() {
     68   const char headers[] =
     69       "HTTP/1.1 500 BOO HOO\0"
     70       "\0";
     71   return std::string(headers, arraysize(headers));
     72 }
     73 
     74 // static
     75 URLRequestJob* URLRequestTestJob::Factory(URLRequest* request,
     76                                           const std::string& scheme) {
     77   return new URLRequestTestJob(request);
     78 }
     79 
     80 URLRequestTestJob::URLRequestTestJob(URLRequest* request)
     81     : URLRequestJob(request),
     82       auto_advance_(false),
     83       stage_(WAITING),
     84       offset_(0),
     85       async_buf_(NULL),
     86       async_buf_size_(0) {
     87 }
     88 
     89 URLRequestTestJob::URLRequestTestJob(URLRequest* request,
     90                                      bool auto_advance)
     91     : URLRequestJob(request),
     92       auto_advance_(auto_advance),
     93       stage_(WAITING),
     94       offset_(0),
     95       async_buf_(NULL),
     96       async_buf_size_(0) {
     97 }
     98 
     99 URLRequestTestJob::URLRequestTestJob(URLRequest* request,
    100                                      const std::string& response_headers,
    101                                      const std::string& response_data,
    102                                      bool auto_advance)
    103     : URLRequestJob(request),
    104       auto_advance_(auto_advance),
    105       stage_(WAITING),
    106       response_headers_(new HttpResponseHeaders(response_headers)),
    107       response_data_(response_data),
    108       offset_(0),
    109       async_buf_(NULL),
    110       async_buf_size_(0) {
    111 }
    112 
    113 URLRequestTestJob::~URLRequestTestJob() {
    114 }
    115 
    116 bool URLRequestTestJob::GetMimeType(std::string* mime_type) const {
    117   DCHECK(mime_type);
    118   if (!response_headers_)
    119     return false;
    120   return response_headers_->GetMimeType(mime_type);
    121 }
    122 
    123 void URLRequestTestJob::Start() {
    124   // Start reading asynchronously so that all error reporting and data
    125   // callbacks happen as they would for network requests.
    126   MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
    127        this, &URLRequestTestJob::StartAsync));
    128 }
    129 
    130 void URLRequestTestJob::StartAsync() {
    131   if (!response_headers_) {
    132     response_headers_ = new HttpResponseHeaders(test_headers());
    133     if (request_->url().spec() == test_url_1().spec()) {
    134       response_data_ = test_data_1();
    135       stage_ = DATA_AVAILABLE;  // Simulate a synchronous response for this one.
    136     } else if (request_->url().spec() == test_url_2().spec()) {
    137       response_data_ = test_data_2();
    138     } else if (request_->url().spec() == test_url_3().spec()) {
    139       response_data_ = test_data_3();
    140     } else {
    141       // unexpected url, return error
    142       // FIXME(brettw) we may want to use WININET errors or have some more types
    143       // of errors
    144       NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
    145                                   ERR_INVALID_URL));
    146       // FIXME(brettw): this should emulate a network error, and not just fail
    147       // initiating a connection
    148       return;
    149     }
    150   }
    151 
    152   AdvanceJob();
    153 
    154   this->NotifyHeadersComplete();
    155 }
    156 
    157 bool URLRequestTestJob::ReadRawData(IOBuffer* buf, int buf_size,
    158                                     int *bytes_read) {
    159   if (stage_ == WAITING) {
    160     async_buf_ = buf;
    161     async_buf_size_ = buf_size;
    162     SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
    163     return false;
    164   }
    165 
    166   DCHECK(bytes_read);
    167   *bytes_read = 0;
    168 
    169   if (offset_ >= static_cast<int>(response_data_.length())) {
    170     return true;  // done reading
    171   }
    172 
    173   int to_read = buf_size;
    174   if (to_read + offset_ > static_cast<int>(response_data_.length()))
    175     to_read = static_cast<int>(response_data_.length()) - offset_;
    176 
    177   memcpy(buf->data(), &response_data_.c_str()[offset_], to_read);
    178   offset_ += to_read;
    179 
    180   *bytes_read = to_read;
    181   return true;
    182 }
    183 
    184 void URLRequestTestJob::GetResponseInfo(HttpResponseInfo* info) {
    185   if (response_headers_)
    186     info->headers = response_headers_;
    187 }
    188 
    189 int URLRequestTestJob::GetResponseCode() const {
    190   if (response_headers_)
    191     return response_headers_->response_code();
    192   return -1;
    193 }
    194 
    195 bool URLRequestTestJob::IsRedirectResponse(GURL* location,
    196                                            int* http_status_code) {
    197   if (!response_headers_)
    198     return false;
    199 
    200   std::string value;
    201   if (!response_headers_->IsRedirect(&value))
    202     return false;
    203 
    204   *location = request_->url().Resolve(value);
    205   *http_status_code = response_headers_->response_code();
    206   return true;
    207 }
    208 
    209 
    210 void URLRequestTestJob::Kill() {
    211   stage_ = DONE;
    212   URLRequestJob::Kill();
    213 }
    214 
    215 void URLRequestTestJob::ProcessNextOperation() {
    216   switch (stage_) {
    217     case WAITING:
    218       stage_ = DATA_AVAILABLE;
    219       // OK if ReadRawData wasn't called yet.
    220       if (async_buf_) {
    221         int bytes_read;
    222         if (!ReadRawData(async_buf_, async_buf_size_, &bytes_read))
    223           NOTREACHED() << "This should not return false in DATA_AVAILABLE.";
    224         SetStatus(URLRequestStatus());  // clear the io pending flag
    225         NotifyReadComplete(bytes_read);
    226       }
    227       break;
    228     case DATA_AVAILABLE:
    229       stage_ = ALL_DATA;  // done sending data
    230       break;
    231     case ALL_DATA:
    232       stage_ = DONE;
    233       return;
    234     case DONE:
    235       return;
    236     default:
    237       NOTREACHED() << "Invalid stage";
    238       return;
    239   }
    240   AdvanceJob();
    241 }
    242 
    243 void URLRequestTestJob::AdvanceJob() {
    244   if (auto_advance_) {
    245     MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
    246          this, &URLRequestTestJob::ProcessNextOperation));
    247     return;
    248   }
    249   g_pending_jobs.push_back(scoped_refptr<URLRequestTestJob>(this));
    250 }
    251 
    252 // static
    253 bool URLRequestTestJob::ProcessOnePendingMessage() {
    254   if (g_pending_jobs.empty())
    255     return false;
    256 
    257   scoped_refptr<URLRequestTestJob> next_job(g_pending_jobs[0]);
    258   g_pending_jobs.erase(g_pending_jobs.begin());
    259 
    260   DCHECK(!next_job->auto_advance());  // auto_advance jobs should be in this q
    261   next_job->ProcessNextOperation();
    262   return true;
    263 }
    264 
    265 }  // namespace net
    266