Home | History | Annotate | Download | only in npapi
      1 // Copyright (c) 2013 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 "content/child/npapi/plugin_url_fetcher.h"
      6 
      7 #include "base/memory/scoped_ptr.h"
      8 #include "content/child/child_thread.h"
      9 #include "content/child/multipart_response_delegate.h"
     10 #include "content/child/npapi/plugin_host.h"
     11 #include "content/child/npapi/plugin_instance.h"
     12 #include "content/child/npapi/plugin_stream_url.h"
     13 #include "content/child/npapi/webplugin.h"
     14 #include "content/child/npapi/webplugin_resource_client.h"
     15 #include "content/child/plugin_messages.h"
     16 #include "content/child/request_extra_data.h"
     17 #include "content/child/request_info.h"
     18 #include "content/child/resource_dispatcher.h"
     19 #include "content/child/resource_loader_bridge.h"
     20 #include "content/child/web_url_loader_impl.h"
     21 #include "content/common/resource_request_body.h"
     22 #include "content/common/service_worker/service_worker_types.h"
     23 #include "content/public/common/resource_response_info.h"
     24 #include "net/base/load_flags.h"
     25 #include "net/base/net_errors.h"
     26 #include "net/http/http_response_headers.h"
     27 #include "net/url_request/redirect_info.h"
     28 #include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
     29 #include "third_party/WebKit/public/platform/WebURLResponse.h"
     30 
     31 namespace content {
     32 namespace {
     33 
     34 // This class handles individual multipart responses. It is instantiated when
     35 // we receive HTTP status code 206 in the HTTP response. This indicates
     36 // that the response could have multiple parts each separated by a boundary
     37 // specified in the response header.
     38 // TODO(jam): this is similar to MultiPartResponseClient in webplugin_impl.cc,
     39 // we should remove that other class once we switch to loading from the plugin
     40 // process by default.
     41 class MultiPartResponseClient : public blink::WebURLLoaderClient {
     42  public:
     43   explicit MultiPartResponseClient(PluginStreamUrl* plugin_stream)
     44       : byte_range_lower_bound_(0), plugin_stream_(plugin_stream) {}
     45 
     46   // blink::WebURLLoaderClient implementation:
     47   virtual void didReceiveResponse(
     48       blink::WebURLLoader* loader,
     49       const blink::WebURLResponse& response) OVERRIDE {
     50     int64 byte_range_upper_bound, instance_size;
     51     if (!MultipartResponseDelegate::ReadContentRanges(response,
     52                                                       &byte_range_lower_bound_,
     53                                                       &byte_range_upper_bound,
     54                                                       &instance_size)) {
     55       NOTREACHED();
     56     }
     57   }
     58   virtual void didReceiveData(blink::WebURLLoader* loader,
     59                               const char* data,
     60                               int data_length,
     61                               int encoded_data_length) OVERRIDE {
     62     // TODO(ananta)
     63     // We should defer further loads on multipart resources on the same lines
     64     // as regular resources requested by plugins to prevent reentrancy.
     65     int64 data_offset = byte_range_lower_bound_;
     66     byte_range_lower_bound_ += data_length;
     67     plugin_stream_->DidReceiveData(data, data_length, data_offset);
     68     // DANGER: this instance may be deleted at this point.
     69   }
     70 
     71  private:
     72   // The lower bound of the byte range.
     73   int64 byte_range_lower_bound_;
     74   // The handler for the data.
     75   PluginStreamUrl* plugin_stream_;
     76 };
     77 
     78 }  // namespace
     79 
     80 PluginURLFetcher::PluginURLFetcher(PluginStreamUrl* plugin_stream,
     81                                    const GURL& url,
     82                                    const GURL& first_party_for_cookies,
     83                                    const std::string& method,
     84                                    const char* buf,
     85                                    unsigned int len,
     86                                    const GURL& referrer,
     87                                    const std::string& range,
     88                                    bool notify_redirects,
     89                                    bool is_plugin_src_load,
     90                                    int origin_pid,
     91                                    int render_frame_id,
     92                                    int render_view_id,
     93                                    unsigned long resource_id,
     94                                    bool copy_stream_data)
     95     : plugin_stream_(plugin_stream),
     96       url_(url),
     97       first_party_for_cookies_(first_party_for_cookies),
     98       referrer_(referrer),
     99       notify_redirects_(notify_redirects),
    100       is_plugin_src_load_(is_plugin_src_load),
    101       origin_pid_(origin_pid),
    102       render_frame_id_(render_frame_id),
    103       render_view_id_(render_view_id),
    104       resource_id_(resource_id),
    105       copy_stream_data_(copy_stream_data),
    106       data_offset_(0),
    107       pending_failure_notification_(false) {
    108   RequestInfo request_info;
    109   request_info.method = method;
    110   request_info.url = url;
    111   request_info.first_party_for_cookies = first_party_for_cookies;
    112   request_info.referrer = referrer;
    113   request_info.load_flags = net::LOAD_NORMAL;
    114   request_info.requestor_pid = origin_pid;
    115   request_info.request_type = RESOURCE_TYPE_OBJECT;
    116   request_info.routing_id = render_view_id;
    117 
    118   RequestExtraData extra_data;
    119   extra_data.set_render_frame_id(render_frame_id);
    120   extra_data.set_is_main_frame(false);
    121   request_info.extra_data = &extra_data;
    122 
    123   std::vector<char> body;
    124   if (method == "POST") {
    125     bool content_type_found = false;
    126     std::vector<std::string> names;
    127     std::vector<std::string> values;
    128     PluginHost::SetPostData(buf, len, &names, &values, &body);
    129     for (size_t i = 0; i < names.size(); ++i) {
    130       if (!request_info.headers.empty())
    131         request_info.headers += "\r\n";
    132       request_info.headers += names[i] + ": " + values[i];
    133       if (LowerCaseEqualsASCII(names[i], "content-type"))
    134         content_type_found = true;
    135     }
    136 
    137     if (!content_type_found) {
    138       if (!request_info.headers.empty())
    139         request_info.headers += "\r\n";
    140       request_info.headers += "Content-Type: application/x-www-form-urlencoded";
    141     }
    142   } else {
    143     if (!range.empty())
    144       request_info.headers = std::string("Range: ") + range;
    145   }
    146 
    147   bridge_.reset(ChildThread::current()->resource_dispatcher()->CreateBridge(
    148       request_info));
    149   if (!body.empty()) {
    150     scoped_refptr<ResourceRequestBody> request_body =
    151         new ResourceRequestBody;
    152     request_body->AppendBytes(&body[0], body.size());
    153     bridge_->SetRequestBody(request_body.get());
    154   }
    155 
    156   bridge_->Start(this);
    157 
    158   // TODO(jam): range requests
    159 }
    160 
    161 PluginURLFetcher::~PluginURLFetcher() {
    162 }
    163 
    164 void PluginURLFetcher::Cancel() {
    165   bridge_->Cancel();
    166 
    167   // Due to races and nested event loops, PluginURLFetcher may still receive
    168   // events from the bridge before being destroyed. Do not forward additional
    169   // events back to the plugin, via either |plugin_stream_| or
    170   // |multipart_delegate_| which has its own pointer via
    171   // MultiPartResponseClient.
    172   if (multipart_delegate_)
    173     multipart_delegate_->Cancel();
    174   plugin_stream_ = NULL;
    175 }
    176 
    177 void PluginURLFetcher::URLRedirectResponse(bool allow) {
    178   if (!plugin_stream_)
    179     return;
    180 
    181   if (allow) {
    182     bridge_->SetDefersLoading(false);
    183   } else {
    184     bridge_->Cancel();
    185     plugin_stream_->DidFail(resource_id_);  // That will delete |this|.
    186   }
    187 }
    188 
    189 void PluginURLFetcher::OnUploadProgress(uint64 position, uint64 size) {
    190 }
    191 
    192 bool PluginURLFetcher::OnReceivedRedirect(
    193     const net::RedirectInfo& redirect_info,
    194     const ResourceResponseInfo& info) {
    195   if (!plugin_stream_)
    196     return false;
    197 
    198   // TODO(jam): THIS LOGIC IS COPIED FROM WebPluginImpl::willSendRequest until
    199   // kDirectNPAPIRequests is the default and we can remove the old path there.
    200 
    201   // Currently this check is just to catch an https -> http redirect when
    202   // loading the main plugin src URL. Longer term, we could investigate
    203   // firing mixed diplay or scripting issues for subresource loads
    204   // initiated by plug-ins.
    205   if (is_plugin_src_load_ &&
    206       !plugin_stream_->instance()->webplugin()->CheckIfRunInsecureContent(
    207           redirect_info.new_url)) {
    208     plugin_stream_->DidFail(resource_id_);  // That will delete |this|.
    209     return false;
    210   }
    211 
    212   GURL old_url = url_;
    213   url_ = redirect_info.new_url;
    214   first_party_for_cookies_ = redirect_info.new_first_party_for_cookies;
    215 
    216   // If the plugin does not participate in url redirect notifications then just
    217   // block cross origin 307 POST redirects.
    218   if (!notify_redirects_) {
    219     if (redirect_info.status_code == 307 &&
    220         redirect_info.new_method == "POST" &&
    221         old_url.GetOrigin() != url_.GetOrigin()) {
    222       plugin_stream_->DidFail(resource_id_);  // That will delete |this|.
    223       return false;
    224     }
    225   } else {
    226     // Pause the request while we ask the plugin what to do about the redirect.
    227     bridge_->SetDefersLoading(true);
    228     plugin_stream_->WillSendRequest(url_, redirect_info.status_code);
    229   }
    230 
    231   return true;
    232 }
    233 
    234 void PluginURLFetcher::OnReceivedResponse(const ResourceResponseInfo& info) {
    235   if (!plugin_stream_)
    236     return;
    237 
    238   // TODO(jam): THIS LOGIC IS COPIED FROM WebPluginImpl::didReceiveResponse
    239   // GetAllHeaders, and GetResponseInfo until kDirectNPAPIRequests is the
    240   // default and we can remove the old path there.
    241 
    242   bool request_is_seekable = true;
    243   DCHECK(!multipart_delegate_.get());
    244   if (plugin_stream_->seekable()) {
    245     int response_code = info.headers->response_code();
    246     if (response_code == 206) {
    247       blink::WebURLResponse response;
    248       response.initialize();
    249       WebURLLoaderImpl::PopulateURLResponse(url_, info, &response);
    250 
    251       std::string multipart_boundary;
    252       if (MultipartResponseDelegate::ReadMultipartBoundary(
    253               response, &multipart_boundary)) {
    254         plugin_stream_->instance()->webplugin()->DidStartLoading();
    255 
    256         MultiPartResponseClient* multi_part_response_client =
    257             new MultiPartResponseClient(plugin_stream_);
    258 
    259         multipart_delegate_.reset(new MultipartResponseDelegate(
    260             multi_part_response_client, NULL, response, multipart_boundary));
    261 
    262         // Multiple ranges requested, data will be delivered by
    263         // MultipartResponseDelegate.
    264         data_offset_ = 0;
    265         return;
    266       }
    267 
    268       int64 upper_bound = 0, instance_size = 0;
    269       // Single range requested - go through original processing for
    270       // non-multipart requests, but update data offset.
    271       MultipartResponseDelegate::ReadContentRanges(
    272           response, &data_offset_, &upper_bound, &instance_size);
    273     } else if (response_code == 200) {
    274       // TODO: should we handle this case? We used to but it's not clear that we
    275       // still need to. This was bug 5403, fixed in r7139.
    276     }
    277   }
    278 
    279   // If the length comes in as -1, then it indicates that it was not
    280   // read off the HTTP headers. We replicate Safari webkit behavior here,
    281   // which is to set it to 0.
    282   int expected_length = std::max(static_cast<int>(info.content_length), 0);
    283 
    284   base::Time temp;
    285   uint32 last_modified = 0;
    286   std::string headers;
    287   if (info.headers.get()) {  // NULL for data: urls.
    288     if (info.headers->GetLastModifiedValue(&temp))
    289       last_modified = static_cast<uint32>(temp.ToDoubleT());
    290 
    291      // TODO(darin): Shouldn't we also report HTTP version numbers?
    292     int response_code = info.headers->response_code();
    293     headers = base::StringPrintf("HTTP %d ", response_code);
    294     headers += info.headers->GetStatusText();
    295     headers += "\n";
    296 
    297     void* iter = NULL;
    298     std::string name, value;
    299     while (info.headers->EnumerateHeaderLines(&iter, &name, &value)) {
    300       // TODO(darin): Should we really exclude headers with an empty value?
    301       if (!name.empty() && !value.empty())
    302         headers += name + ": " + value + "\n";
    303     }
    304 
    305     // Bug http://b/issue?id=925559. The flash plugin would not handle the HTTP
    306     // error codes in the stream header and as a result, was unaware of the fate
    307     // of the HTTP requests issued via NPN_GetURLNotify. Webkit and FF destroy
    308     // the stream and invoke the NPP_DestroyStream function on the plugin if the
    309     // HTTPrequest fails.
    310     if ((url_.SchemeIs(url::kHttpScheme) || url_.SchemeIs(url::kHttpsScheme)) &&
    311         (response_code < 100 || response_code >= 400)) {
    312       pending_failure_notification_ = true;
    313     }
    314   }
    315 
    316   plugin_stream_->DidReceiveResponse(info.mime_type,
    317                                      headers,
    318                                      expected_length,
    319                                      last_modified,
    320                                      request_is_seekable);
    321 }
    322 
    323 void PluginURLFetcher::OnDownloadedData(int len,
    324                                         int encoded_data_length) {
    325 }
    326 
    327 void PluginURLFetcher::OnReceivedData(const char* data,
    328                                       int data_length,
    329                                       int encoded_data_length) {
    330   if (!plugin_stream_)
    331     return;
    332 
    333   if (multipart_delegate_) {
    334     multipart_delegate_->OnReceivedData(data, data_length, encoded_data_length);
    335   } else {
    336     int64 offset = data_offset_;
    337     data_offset_ += data_length;
    338 
    339     if (copy_stream_data_) {
    340       // QuickTime writes to this memory, and since we got it from
    341       // ResourceDispatcher it's not mapped for write access in this process.
    342       // http://crbug.com/308466.
    343       scoped_ptr<char[]> data_copy(new char[data_length]);
    344       memcpy(data_copy.get(), data, data_length);
    345       plugin_stream_->DidReceiveData(data_copy.get(), data_length, offset);
    346     } else {
    347       plugin_stream_->DidReceiveData(data, data_length, offset);
    348     }
    349     // DANGER: this instance may be deleted at this point.
    350   }
    351 }
    352 
    353 void PluginURLFetcher::OnCompletedRequest(
    354     int error_code,
    355     bool was_ignored_by_handler,
    356     bool stale_copy_in_cache,
    357     const std::string& security_info,
    358     const base::TimeTicks& completion_time,
    359     int64 total_transfer_size) {
    360   if (!plugin_stream_)
    361     return;
    362 
    363   if (multipart_delegate_) {
    364     multipart_delegate_->OnCompletedRequest();
    365     multipart_delegate_.reset();
    366   }
    367 
    368   if (error_code == net::OK) {
    369     plugin_stream_->DidFinishLoading(resource_id_);
    370   } else {
    371     plugin_stream_->DidFail(resource_id_);
    372   }
    373 }
    374 
    375 }  // namespace content
    376