Home | History | Annotate | Download | only in automation
      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 "chrome/browser/automation/url_request_automation_job.h"
      6 
      7 #include "base/compiler_specific.h"
      8 #include "base/message_loop.h"
      9 #include "base/time.h"
     10 #include "chrome/browser/automation/automation_resource_message_filter.h"
     11 #include "chrome/common/automation_messages.h"
     12 #include "content/browser/browser_thread.h"
     13 #include "content/browser/renderer_host/render_view_host.h"
     14 #include "content/browser/renderer_host/resource_dispatcher_host.h"
     15 #include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
     16 #include "net/base/cookie_monster.h"
     17 #include "net/base/host_port_pair.h"
     18 #include "net/base/io_buffer.h"
     19 #include "net/base/net_errors.h"
     20 #include "net/http/http_response_headers.h"
     21 #include "net/http/http_request_headers.h"
     22 #include "net/http/http_util.h"
     23 #include "net/url_request/url_request_context.h"
     24 
     25 using base::Time;
     26 using base::TimeDelta;
     27 
     28 // The list of filtered headers that are removed from requests sent via
     29 // StartAsync(). These must be lower case.
     30 static const char* const kFilteredHeaderStrings[] = {
     31   "connection",
     32   "cookie",
     33   "expect",
     34   "max-forwards",
     35   "proxy-authorization",
     36   "te",
     37   "upgrade",
     38   "via"
     39 };
     40 
     41 int URLRequestAutomationJob::instance_count_ = 0;
     42 bool URLRequestAutomationJob::is_protocol_factory_registered_ = false;
     43 
     44 net::URLRequest::ProtocolFactory* URLRequestAutomationJob::old_http_factory_
     45     = NULL;
     46 net::URLRequest::ProtocolFactory* URLRequestAutomationJob::old_https_factory_
     47     = NULL;
     48 
     49 URLRequestAutomationJob::URLRequestAutomationJob(
     50     net::URLRequest* request,
     51     int tab,
     52     int request_id,
     53     AutomationResourceMessageFilter* filter,
     54     bool is_pending)
     55     : net::URLRequestJob(request),
     56       id_(0),
     57       tab_(tab),
     58       message_filter_(filter),
     59       pending_buf_size_(0),
     60       redirect_status_(0),
     61       request_id_(request_id),
     62       is_pending_(is_pending),
     63       ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
     64   DVLOG(1) << "URLRequestAutomationJob create. Count: " << ++instance_count_;
     65   DCHECK(message_filter_ != NULL);
     66 
     67   if (message_filter_) {
     68     id_ = message_filter_->NewAutomationRequestId();
     69     DCHECK_NE(id_, 0);
     70   }
     71 }
     72 
     73 URLRequestAutomationJob::~URLRequestAutomationJob() {
     74   DVLOG(1) << "URLRequestAutomationJob delete. Count: " << --instance_count_;
     75   Cleanup();
     76 }
     77 
     78 bool URLRequestAutomationJob::EnsureProtocolFactoryRegistered() {
     79   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     80 
     81   if (!is_protocol_factory_registered_) {
     82     old_http_factory_ =
     83         net::URLRequest::RegisterProtocolFactory(
     84             "http", &URLRequestAutomationJob::Factory);
     85     old_https_factory_ =
     86         net::URLRequest::RegisterProtocolFactory(
     87             "https", &URLRequestAutomationJob::Factory);
     88     is_protocol_factory_registered_ = true;
     89   }
     90 
     91   return true;
     92 }
     93 
     94 net::URLRequestJob* URLRequestAutomationJob::Factory(
     95     net::URLRequest* request,
     96     const std::string& scheme) {
     97   bool scheme_is_http = request->url().SchemeIs("http");
     98   bool scheme_is_https = request->url().SchemeIs("https");
     99 
    100   // Returning null here just means that the built-in handler will be used.
    101   if (scheme_is_http || scheme_is_https) {
    102     ResourceDispatcherHostRequestInfo* request_info = NULL;
    103     if (request->GetUserData(NULL))
    104       request_info = ResourceDispatcherHost::InfoForRequest(request);
    105     if (request_info) {
    106       int child_id = request_info->child_id();
    107       int route_id = request_info->route_id();
    108       AutomationResourceMessageFilter::AutomationDetails details;
    109       if (AutomationResourceMessageFilter::LookupRegisteredRenderView(
    110               child_id, route_id, &details)) {
    111         URLRequestAutomationJob* job = new URLRequestAutomationJob(request,
    112             details.tab_handle, request_info->request_id(), details.filter,
    113             details.is_pending_render_view);
    114         return job;
    115       }
    116     }
    117 
    118     if (scheme_is_http && old_http_factory_)
    119       return old_http_factory_(request, scheme);
    120     else if (scheme_is_https && old_https_factory_)
    121       return old_https_factory_(request, scheme);
    122   }
    123   return NULL;
    124 }
    125 
    126 // net::URLRequestJob Implementation.
    127 void URLRequestAutomationJob::Start() {
    128   if (!is_pending()) {
    129     // Start reading asynchronously so that all error reporting and data
    130     // callbacks happen as they would for network requests.
    131     MessageLoop::current()->PostTask(
    132         FROM_HERE,
    133         method_factory_.NewRunnableMethod(
    134             &URLRequestAutomationJob::StartAsync));
    135   } else {
    136     // If this is a pending job, then register it immediately with the message
    137     // filter so it can be serviced later when we receive a request from the
    138     // external host to connect to the corresponding external tab.
    139     message_filter_->RegisterRequest(this);
    140   }
    141 }
    142 
    143 void URLRequestAutomationJob::Kill() {
    144   if (message_filter_.get()) {
    145     if (!is_pending()) {
    146       message_filter_->Send(new AutomationMsg_RequestEnd(tab_, id_,
    147           net::URLRequestStatus(net::URLRequestStatus::CANCELED,
    148                                 net::ERR_ABORTED)));
    149     }
    150   }
    151   DisconnectFromMessageFilter();
    152   net::URLRequestJob::Kill();
    153 }
    154 
    155 bool URLRequestAutomationJob::ReadRawData(
    156     net::IOBuffer* buf, int buf_size, int* bytes_read) {
    157   DVLOG(1) << "URLRequestAutomationJob: " << request_->url().spec()
    158            << " - read pending: " << buf_size;
    159 
    160   // We should not receive a read request for a pending job.
    161   DCHECK(!is_pending());
    162 
    163   pending_buf_ = buf;
    164   pending_buf_size_ = buf_size;
    165 
    166   if (message_filter_) {
    167     message_filter_->Send(new AutomationMsg_RequestRead(tab_, id_, buf_size));
    168     SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
    169   } else {
    170     MessageLoop::current()->PostTask(
    171         FROM_HERE,
    172         method_factory_.NewRunnableMethod(
    173             &URLRequestAutomationJob::NotifyJobCompletionTask));
    174   }
    175   return false;
    176 }
    177 
    178 bool URLRequestAutomationJob::GetMimeType(std::string* mime_type) const {
    179   if (!mime_type_.empty()) {
    180     *mime_type = mime_type_;
    181   } else if (headers_) {
    182     headers_->GetMimeType(mime_type);
    183   }
    184 
    185   return (!mime_type->empty());
    186 }
    187 
    188 bool URLRequestAutomationJob::GetCharset(std::string* charset) {
    189   if (headers_)
    190     return headers_->GetCharset(charset);
    191   return false;
    192 }
    193 
    194 void URLRequestAutomationJob::GetResponseInfo(net::HttpResponseInfo* info) {
    195   if (headers_)
    196     info->headers = headers_;
    197   if (request_->url().SchemeIsSecure()) {
    198     // Make up a fake certificate for this response since we don't have
    199     // access to the real SSL info.
    200     const char* kCertIssuer = "Chrome Internal";
    201     const int kLifetimeDays = 100;
    202 
    203     info->ssl_info.cert =
    204         new net::X509Certificate(request_->url().GetWithEmptyPath().spec(),
    205                                  kCertIssuer,
    206                                  Time::Now(),
    207                                  Time::Now() +
    208                                      TimeDelta::FromDays(kLifetimeDays));
    209     info->ssl_info.cert_status = 0;
    210     info->ssl_info.security_bits = -1;
    211   }
    212 }
    213 
    214 int URLRequestAutomationJob::GetResponseCode() const {
    215   if (headers_)
    216     return headers_->response_code();
    217 
    218   static const int kDefaultResponseCode = 200;
    219   return kDefaultResponseCode;
    220 }
    221 
    222 bool URLRequestAutomationJob::IsRedirectResponse(
    223     GURL* location, int* http_status_code) {
    224   if (!net::HttpResponseHeaders::IsRedirectResponseCode(redirect_status_))
    225     return false;
    226 
    227   *http_status_code = redirect_status_;
    228   *location = GURL(redirect_url_);
    229   return true;
    230 }
    231 
    232 uint64 URLRequestAutomationJob::GetUploadProgress() const {
    233   if (request_ && request_->status().is_success()) {
    234     // We don't support incremental progress notifications in ChromeFrame. When
    235     // we receive a response for the POST request from Chromeframe, it means
    236     // that the upload is fully complete.
    237     ResourceDispatcherHostRequestInfo* request_info =
    238         ResourceDispatcherHost::InfoForRequest(request_);
    239     if (request_info) {
    240       return request_info->upload_size();
    241     }
    242   }
    243   return 0;
    244 }
    245 
    246 net::HostPortPair URLRequestAutomationJob::GetSocketAddress() const {
    247   return socket_address_;
    248 }
    249 
    250 bool URLRequestAutomationJob::MayFilterMessage(const IPC::Message& message,
    251                                                int* request_id) {
    252   switch (message.type()) {
    253     case AutomationMsg_RequestStarted::ID:
    254     case AutomationMsg_RequestData::ID:
    255     case AutomationMsg_RequestEnd::ID: {
    256       void* iter = NULL;
    257       if (message.ReadInt(&iter, request_id))
    258         return true;
    259       break;
    260     }
    261   }
    262 
    263   return false;
    264 }
    265 
    266 void URLRequestAutomationJob::OnMessage(const IPC::Message& message) {
    267   if (!request_) {
    268     NOTREACHED() << __FUNCTION__
    269                  << ": Unexpected request received for job:"
    270                  << id();
    271     return;
    272   }
    273 
    274   IPC_BEGIN_MESSAGE_MAP(URLRequestAutomationJob, message)
    275     IPC_MESSAGE_HANDLER(AutomationMsg_RequestStarted, OnRequestStarted)
    276     IPC_MESSAGE_HANDLER(AutomationMsg_RequestData, OnDataAvailable)
    277     IPC_MESSAGE_HANDLER(AutomationMsg_RequestEnd, OnRequestEnd)
    278   IPC_END_MESSAGE_MAP()
    279 }
    280 
    281 void URLRequestAutomationJob::OnRequestStarted(
    282     int id, const AutomationURLResponse& response) {
    283   DVLOG(1) << "URLRequestAutomationJob: " << request_->url().spec()
    284            << " - response started.";
    285   set_expected_content_size(response.content_length);
    286   mime_type_ = response.mime_type;
    287 
    288   redirect_url_ = response.redirect_url;
    289   redirect_status_ = response.redirect_status;
    290   DCHECK(redirect_status_ == 0 || redirect_status_ == 200 ||
    291          (redirect_status_ >= 300 && redirect_status_ < 400));
    292 
    293   if (!response.headers.empty()) {
    294     headers_ = new net::HttpResponseHeaders(
    295         net::HttpUtil::AssembleRawHeaders(response.headers.data(),
    296                                           response.headers.size()));
    297   }
    298   socket_address_ = response.socket_address;
    299   NotifyHeadersComplete();
    300 }
    301 
    302 void URLRequestAutomationJob::OnDataAvailable(
    303     int id, const std::string& bytes) {
    304   DVLOG(1) << "URLRequestAutomationJob: " << request_->url().spec()
    305            << " - data available, Size: " << bytes.size();
    306   DCHECK(!bytes.empty());
    307 
    308   // The request completed, and we have all the data.
    309   // Clear any IO pending status.
    310   SetStatus(net::URLRequestStatus());
    311 
    312   if (pending_buf_ && pending_buf_->data()) {
    313     DCHECK_GE(pending_buf_size_, bytes.size());
    314     const int bytes_to_copy = std::min(bytes.size(), pending_buf_size_);
    315     memcpy(pending_buf_->data(), &bytes[0], bytes_to_copy);
    316 
    317     pending_buf_ = NULL;
    318     pending_buf_size_ = 0;
    319 
    320     NotifyReadComplete(bytes_to_copy);
    321   } else {
    322     NOTREACHED() << "Received unexpected data of length:" << bytes.size();
    323   }
    324 }
    325 
    326 void URLRequestAutomationJob::OnRequestEnd(
    327     int id, const net::URLRequestStatus& status) {
    328 #ifndef NDEBUG
    329   std::string url;
    330   if (request_)
    331     url = request_->url().spec();
    332   DVLOG(1) << "URLRequestAutomationJob: " << url << " - request end. Status: "
    333            << status.status();
    334 #endif
    335 
    336   // TODO(tommi): When we hit certificate errors, notify the delegate via
    337   // OnSSLCertificateError().  Right now we don't have the certificate
    338   // so we don't.  We could possibly call OnSSLCertificateError with a NULL
    339   // certificate, but I'm not sure if all implementations expect it.
    340   // if (status.status() == net::URLRequestStatus::FAILED &&
    341   //    net::IsCertificateError(status.os_error()) && request_->delegate()) {
    342   //  request_->delegate()->OnSSLCertificateError(request_, status.os_error());
    343   // }
    344 
    345   DisconnectFromMessageFilter();
    346   // NotifyDone may have been called on the job if the original request was
    347   // redirected.
    348   if (!is_done()) {
    349     // We can complete the job if we have a valid response or a pending read.
    350     // An end request can be received in the following cases
    351     // 1. We failed to connect to the server, in which case we did not receive
    352     //    a valid response.
    353     // 2. In response to a read request.
    354     if (!has_response_started() || pending_buf_) {
    355       NotifyDone(status);
    356     } else {
    357       // Wait for the http stack to issue a Read request where we will notify
    358       // that the job has completed.
    359       request_status_ = status;
    360       return;
    361     }
    362   }
    363 
    364   // Reset any pending reads.
    365   if (pending_buf_) {
    366     pending_buf_ = NULL;
    367     pending_buf_size_ = 0;
    368     NotifyReadComplete(0);
    369   }
    370 }
    371 
    372 void URLRequestAutomationJob::Cleanup() {
    373   headers_ = NULL;
    374   mime_type_.erase();
    375 
    376   id_ = 0;
    377   tab_ = 0;
    378 
    379   DCHECK(!message_filter_);
    380   DisconnectFromMessageFilter();
    381 
    382   pending_buf_ = NULL;
    383   pending_buf_size_ = 0;
    384 }
    385 
    386 void URLRequestAutomationJob::StartAsync() {
    387   DVLOG(1) << "URLRequestAutomationJob: start request: "
    388            << (request_ ? request_->url().spec() : "NULL request");
    389 
    390   // If the job is cancelled before we got a chance to start it
    391   // we have nothing much to do here.
    392   if (is_done())
    393     return;
    394 
    395   // We should not receive a Start request for a pending job.
    396   DCHECK(!is_pending());
    397 
    398   if (!request_) {
    399     NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
    400                                            net::ERR_FAILED));
    401     return;
    402   }
    403 
    404   // Register this request with automation message filter.
    405   message_filter_->RegisterRequest(this);
    406 
    407   // Strip unwanted headers.
    408   net::HttpRequestHeaders new_request_headers;
    409   new_request_headers.MergeFrom(request_->extra_request_headers());
    410   for (size_t i = 0; i < arraysize(kFilteredHeaderStrings); ++i)
    411     new_request_headers.RemoveHeader(kFilteredHeaderStrings[i]);
    412 
    413   if (request_->context()) {
    414     // Only add default Accept-Language and Accept-Charset if the request
    415     // didn't have them specified.
    416     if (!new_request_headers.HasHeader(
    417         net::HttpRequestHeaders::kAcceptLanguage) &&
    418         !request_->context()->accept_language().empty()) {
    419       new_request_headers.SetHeader(net::HttpRequestHeaders::kAcceptLanguage,
    420                                     request_->context()->accept_language());
    421     }
    422     if (!new_request_headers.HasHeader(
    423         net::HttpRequestHeaders::kAcceptCharset) &&
    424         !request_->context()->accept_charset().empty()) {
    425       new_request_headers.SetHeader(net::HttpRequestHeaders::kAcceptCharset,
    426                                     request_->context()->accept_charset());
    427     }
    428   }
    429 
    430   // Ensure that we do not send username and password fields in the referrer.
    431   GURL referrer(request_->GetSanitizedReferrer());
    432 
    433   // The referrer header must be suppressed if the preceding URL was
    434   // a secure one and the new one is not.
    435   if (referrer.SchemeIsSecure() && !request_->url().SchemeIsSecure()) {
    436     DVLOG(1) << "Suppressing referrer header since going from secure to "
    437                 "non-secure";
    438     referrer = GURL();
    439   }
    440 
    441   // Get the resource type (main_frame/script/image/stylesheet etc.
    442   ResourceDispatcherHostRequestInfo* request_info =
    443       ResourceDispatcherHost::InfoForRequest(request_);
    444   ResourceType::Type resource_type = ResourceType::MAIN_FRAME;
    445   if (request_info) {
    446     resource_type = request_info->resource_type();
    447   }
    448 
    449   // Ask automation to start this request.
    450   AutomationURLRequest automation_request(
    451       request_->url().spec(),
    452       request_->method(),
    453       referrer.spec(),
    454       new_request_headers.ToString(),
    455       request_->get_upload(),
    456       resource_type,
    457       request_->load_flags());
    458 
    459   DCHECK(message_filter_);
    460   message_filter_->Send(new AutomationMsg_RequestStart(
    461       tab_, id_, automation_request));
    462 }
    463 
    464 void URLRequestAutomationJob::DisconnectFromMessageFilter() {
    465   if (message_filter_) {
    466     message_filter_->UnRegisterRequest(this);
    467     message_filter_ = NULL;
    468   }
    469 }
    470 
    471 void URLRequestAutomationJob::StartPendingJob(
    472     int new_tab_handle,
    473     AutomationResourceMessageFilter* new_filter) {
    474   DCHECK(new_filter != NULL);
    475   tab_ = new_tab_handle;
    476   message_filter_ = new_filter;
    477   is_pending_ = false;
    478   Start();
    479 }
    480 
    481 void URLRequestAutomationJob::NotifyJobCompletionTask() {
    482   if (!is_done()) {
    483     NotifyDone(request_status_);
    484   }
    485   // Reset any pending reads.
    486   if (pending_buf_) {
    487     pending_buf_ = NULL;
    488     pending_buf_size_ = 0;
    489     NotifyReadComplete(0);
    490   }
    491 }
    492