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