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