OLD | NEW |
| (Empty) |
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 | |
OLD | NEW |