Home | History | Annotate | Download | only in plugin
      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 "ppapi/native_client/src/trusted/plugin/file_downloader.h"
      6 
      7 #include <stdio.h>
      8 #include <string.h>
      9 #include <string>
     10 
     11 #include "native_client/src/include/portability_io.h"
     12 #include "native_client/src/shared/platform/nacl_check.h"
     13 #include "native_client/src/shared/platform/nacl_time.h"
     14 #include "ppapi/c/pp_errors.h"
     15 #include "ppapi/cpp/url_request_info.h"
     16 #include "ppapi/cpp/url_response_info.h"
     17 #include "ppapi/native_client/src/trusted/plugin/callback_source.h"
     18 #include "ppapi/native_client/src/trusted/plugin/plugin.h"
     19 #include "ppapi/native_client/src/trusted/plugin/utility.h"
     20 
     21 namespace plugin {
     22 
     23 FileDownloader::FileDownloader(Plugin* instance)
     24    : instance_(instance),
     25      file_open_notify_callback_(pp::BlockUntilComplete()),
     26      stream_finish_callback_(pp::BlockUntilComplete()),
     27      mode_(DOWNLOAD_NONE),
     28      data_stream_callback_source_(NULL) {
     29   callback_factory_.Initialize(this);
     30   temp_buffer_.resize(kTempBufferSize);
     31 }
     32 
     33 bool FileDownloader::OpenStream(
     34     const nacl::string& url,
     35     const pp::CompletionCallback& callback,
     36     StreamCallbackSource* stream_callback_source) {
     37   data_stream_callback_source_ = stream_callback_source;
     38   PLUGIN_PRINTF(("FileDownloader::Open (url=%s)\n", url.c_str()));
     39   if (callback.pp_completion_callback().func == NULL || instance_ == NULL)
     40     return false;
     41 
     42   status_code_ = -1;
     43   file_open_notify_callback_ = callback;
     44   mode_ = DOWNLOAD_TO_BUFFER_AND_STREAM;
     45   pp::URLRequestInfo url_request(instance_);
     46 
     47   // Allow CORS.
     48   // Note that "SetAllowCrossOriginRequests" (currently) has the side effect of
     49   // preventing credentials from being sent on same-origin requests.  We
     50   // therefore avoid setting this flag unless we know for sure it is a
     51   // cross-origin request, resulting in behavior similar to XMLHttpRequest.
     52   if (!instance_->DocumentCanRequest(url))
     53     url_request.SetAllowCrossOriginRequests(true);
     54 
     55   if (!extra_request_headers_.empty())
     56     url_request.SetHeaders(extra_request_headers_);
     57 
     58   // Reset the url loader and file reader.
     59   // Note that we have the only reference to the underlying objects, so
     60   // this will implicitly close any pending IO and destroy them.
     61   url_loader_ = pp::URLLoader(instance_);
     62   url_request.SetRecordDownloadProgress(true);
     63 
     64   // Prepare the url request.
     65   url_request.SetURL(url);
     66 
     67   // Request asynchronous download of the url providing an on-load callback.
     68   // As long as this step is guaranteed to be asynchronous, we can call
     69   // synchronously all other internal callbacks that eventually result in the
     70   // invocation of the user callback. The user code will not be reentered.
     71   pp::CompletionCallback onload_callback =
     72       callback_factory_.NewCallback(&FileDownloader::URLLoadStartNotify);
     73   int32_t pp_error = url_loader_.Open(url_request, onload_callback);
     74   PLUGIN_PRINTF(("FileDownloader::Open (pp_error=%" NACL_PRId32 ")\n",
     75                  pp_error));
     76   CHECK(pp_error == PP_OK_COMPLETIONPENDING);
     77   return true;
     78 }
     79 
     80 bool FileDownloader::InitialResponseIsValid() {
     81   // Process the response, validating the headers to confirm successful loading.
     82   url_response_ = url_loader_.GetResponseInfo();
     83   if (url_response_.is_null()) {
     84     PLUGIN_PRINTF((
     85         "FileDownloader::InitialResponseIsValid (url_response_=NULL)\n"));
     86     return false;
     87   }
     88 
     89   pp::Var full_url = url_response_.GetURL();
     90   if (!full_url.is_string()) {
     91     PLUGIN_PRINTF((
     92         "FileDownloader::InitialResponseIsValid (url is not a string)\n"));
     93     return false;
     94   }
     95   full_url_ = full_url.AsString();
     96 
     97   status_code_ = url_response_.GetStatusCode();
     98   PLUGIN_PRINTF(("FileDownloader::InitialResponseIsValid ("
     99                  "response status_code=%" NACL_PRId32 ")\n", status_code_));
    100   return status_code_ == NACL_HTTP_STATUS_OK;
    101 }
    102 
    103 void FileDownloader::URLLoadStartNotify(int32_t pp_error) {
    104   PLUGIN_PRINTF(("FileDownloader::URLLoadStartNotify (pp_error=%"
    105                  NACL_PRId32")\n", pp_error));
    106   if (pp_error != PP_OK) {
    107     file_open_notify_callback_.RunAndClear(pp_error);
    108     return;
    109   }
    110 
    111   if (!InitialResponseIsValid()) {
    112     file_open_notify_callback_.RunAndClear(PP_ERROR_FAILED);
    113     return;
    114   }
    115 
    116   file_open_notify_callback_.RunAndClear(PP_OK);
    117 }
    118 
    119 void FileDownloader::BeginStreaming(
    120     const pp::CompletionCallback& callback) {
    121   stream_finish_callback_ = callback;
    122 
    123   // Finish streaming the body providing an optional callback.
    124   pp::CompletionCallback onread_callback =
    125       callback_factory_.NewOptionalCallback(
    126           &FileDownloader::URLReadBodyNotify);
    127   int32_t temp_size = static_cast<int32_t>(temp_buffer_.size());
    128   int32_t pp_error = url_loader_.ReadResponseBody(&temp_buffer_[0],
    129                                                   temp_size,
    130                                                   onread_callback);
    131   if (pp_error != PP_OK_COMPLETIONPENDING)
    132     onread_callback.RunAndClear(pp_error);
    133 }
    134 
    135 void FileDownloader::URLReadBodyNotify(int32_t pp_error) {
    136   PLUGIN_PRINTF(("FileDownloader::URLReadBodyNotify (pp_error=%"
    137                  NACL_PRId32")\n", pp_error));
    138   if (pp_error < PP_OK) {
    139     stream_finish_callback_.RunAndClear(pp_error);
    140   } else if (pp_error == PP_OK) {
    141     data_stream_callback_source_->GetCallback().RunAndClear(PP_OK);
    142     stream_finish_callback_.RunAndClear(PP_OK);
    143   } else {
    144     PLUGIN_PRINTF(("Running data_stream_callback, temp_buffer_=%p\n",
    145                    &temp_buffer_[0]));
    146     StreamCallback cb = data_stream_callback_source_->GetCallback();
    147     *(cb.output()) = &temp_buffer_;
    148     cb.RunAndClear(pp_error);
    149 
    150     pp::CompletionCallback onread_callback =
    151         callback_factory_.NewOptionalCallback(
    152             &FileDownloader::URLReadBodyNotify);
    153     int32_t temp_size = static_cast<int32_t>(temp_buffer_.size());
    154     pp_error = url_loader_.ReadResponseBody(&temp_buffer_[0],
    155                                             temp_size,
    156                                             onread_callback);
    157     if (pp_error != PP_OK_COMPLETIONPENDING)
    158       onread_callback.RunAndClear(pp_error);
    159   }
    160 }
    161 
    162 bool FileDownloader::GetDownloadProgress(
    163     int64_t* bytes_received,
    164     int64_t* total_bytes_to_be_received) const {
    165   return url_loader_.GetDownloadProgress(bytes_received,
    166                                          total_bytes_to_be_received);
    167 }
    168 
    169 nacl::string FileDownloader::GetResponseHeaders() const {
    170   pp::Var headers = url_response_.GetHeaders();
    171   if (!headers.is_string()) {
    172     PLUGIN_PRINTF((
    173         "FileDownloader::GetResponseHeaders (headers are not a string)\n"));
    174     return nacl::string();
    175   }
    176   return headers.AsString();
    177 }
    178 
    179 }  // namespace plugin
    180