• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
FileDownloader(Plugin * instance)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 
OpenStream(const nacl::string & url,const pp::CompletionCallback & callback,StreamCallbackSource * stream_callback_source)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 
InitialResponseIsValid()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 
URLLoadStartNotify(int32_t pp_error)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 
BeginStreaming(const pp::CompletionCallback & callback)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 
URLReadBodyNotify(int32_t pp_error)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 
GetDownloadProgress(int64_t * bytes_received,int64_t * total_bytes_to_be_received) const162 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 
GetResponseHeaders() const169 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