Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/child/npapi/plugin_url_fetcher.h" | 5 #include "content/child/npapi/plugin_url_fetcher.h" |
| 6 | 6 |
| 7 #include "content/child/child_thread.h" | 7 #include "content/child/child_thread.h" |
| 8 #include "content/child/npapi/webplugin.h" | 8 #include "content/child/npapi/webplugin.h" |
| 9 #include "content/child/npapi/plugin_host.h" | 9 #include "content/child/npapi/plugin_host.h" |
| 10 #include "content/child/npapi/plugin_instance.h" | 10 #include "content/child/npapi/plugin_instance.h" |
| 11 #include "content/child/npapi/plugin_stream_url.h" | 11 #include "content/child/npapi/plugin_stream_url.h" |
| 12 #include "content/child/npapi/webplugin_resource_client.h" | 12 #include "content/child/npapi/webplugin_resource_client.h" |
| 13 #include "content/child/plugin_messages.h" | 13 #include "content/child/plugin_messages.h" |
| 14 #include "content/child/resource_dispatcher.h" | 14 #include "content/child/resource_dispatcher.h" |
| 15 #include "net/base/load_flags.h" | 15 #include "net/base/load_flags.h" |
| 16 #include "net/base/net_errors.h" | 16 #include "net/base/net_errors.h" |
| 17 #include "net/http/http_response_headers.h" | 17 #include "net/http/http_response_headers.h" |
| 18 #include "third_party/WebKit/public/platform/WebURLLoaderClient.h" | |
| 19 #include "third_party/WebKit/public/platform/WebURLResponse.h" | |
| 20 #include "webkit/child/multipart_response_delegate.h" | |
| 21 #include "webkit/child/weburlloader_impl.h" | |
| 18 #include "webkit/common/resource_request_body.h" | 22 #include "webkit/common/resource_request_body.h" |
| 19 | 23 |
| 20 namespace content { | 24 namespace content { |
| 25 namespace { | |
| 26 | |
| 27 // This class handles individual multipart responses. It is instantiated when | |
| 28 // we receive HTTP status code 206 in the HTTP response. This indicates | |
| 29 // that the response could have multiple parts each separated by a boundary | |
| 30 // specified in the response header. | |
| 31 // TODO(jam): this is similar to MultiPartResponseClient in webplugin_impl.cc, | |
| 32 // we should remove that other class once we switch to loading from the plugin | |
| 33 // process by default. | |
| 34 class MultiPartResponseClient : public WebKit::WebURLLoaderClient { | |
| 35 public: | |
| 36 explicit MultiPartResponseClient(PluginStreamUrl* plugin_stream) | |
| 37 : byte_range_lower_bound_(0), plugin_stream_(plugin_stream) {} | |
| 38 | |
| 39 // WebKit::WebURLLoaderClient implementation: | |
| 40 virtual void didReceiveResponse( | |
| 41 WebKit::WebURLLoader* loader, | |
| 42 const WebKit::WebURLResponse& response) OVERRIDE { | |
| 43 int64 byte_range_upper_bound, instance_size; | |
| 44 if (!webkit_glue::MultipartResponseDelegate::ReadContentRanges( | |
| 45 response, &byte_range_lower_bound_, &byte_range_upper_bound, | |
| 46 &instance_size)) { | |
| 47 NOTREACHED(); | |
| 48 } | |
| 49 } | |
| 50 virtual void didReceiveData(WebKit::WebURLLoader* loader, | |
| 51 const char* data, | |
| 52 int data_length, | |
| 53 int encoded_data_length) OVERRIDE { | |
| 54 // TODO(ananta) | |
| 55 // We should defer further loads on multipart resources on the same lines | |
| 56 // as regular resources requested by plugins to prevent reentrancy. | |
| 57 plugin_stream_->DidReceiveData(data, data_length, byte_range_lower_bound_); | |
| 58 byte_range_lower_bound_ += data_length; | |
| 59 } | |
| 60 | |
| 61 private: | |
| 62 // The lower bound of the byte range. | |
| 63 int64 byte_range_lower_bound_; | |
| 64 // The handler for the data. | |
| 65 PluginStreamUrl* plugin_stream_; | |
| 66 }; | |
| 67 | |
| 68 } // namespace | |
| 21 | 69 |
| 22 PluginURLFetcher::PluginURLFetcher(PluginStreamUrl* plugin_stream, | 70 PluginURLFetcher::PluginURLFetcher(PluginStreamUrl* plugin_stream, |
| 23 const GURL& url, | 71 const GURL& url, |
| 24 const GURL& first_party_for_cookies, | 72 const GURL& first_party_for_cookies, |
| 25 const std::string& method, | 73 const std::string& method, |
| 26 const std::string& post_data, | 74 const char* buf, |
| 75 unsigned int len, | |
| 27 const GURL& referrer, | 76 const GURL& referrer, |
| 28 bool notify_redirects, | 77 bool notify_redirects, |
| 29 bool is_plugin_src_load, | 78 bool is_plugin_src_load, |
| 30 int origin_pid, | 79 int origin_pid, |
| 31 int render_view_id, | 80 int render_view_id, |
| 32 unsigned long resource_id) | 81 unsigned long resource_id) |
| 33 : plugin_stream_(plugin_stream), | 82 : plugin_stream_(plugin_stream), |
| 34 url_(url), | 83 url_(url), |
| 35 first_party_for_cookies_(first_party_for_cookies), | 84 first_party_for_cookies_(first_party_for_cookies), |
| 36 method_(method), | 85 method_(method), |
| 37 post_data_(post_data), | |
| 38 referrer_(referrer), | 86 referrer_(referrer), |
| 39 notify_redirects_(notify_redirects), | 87 notify_redirects_(notify_redirects), |
| 40 is_plugin_src_load_(is_plugin_src_load), | 88 is_plugin_src_load_(is_plugin_src_load), |
| 41 resource_id_(resource_id), | 89 resource_id_(resource_id), |
| 42 data_offset_(0) { | 90 data_offset_(0) { |
| 43 webkit_glue::ResourceLoaderBridge::RequestInfo request_info; | 91 webkit_glue::ResourceLoaderBridge::RequestInfo request_info; |
| 44 request_info.method = method; | 92 request_info.method = method; |
| 45 request_info.url = url; | 93 request_info.url = url; |
| 46 request_info.first_party_for_cookies = first_party_for_cookies; | 94 request_info.first_party_for_cookies = first_party_for_cookies; |
| 47 request_info.referrer = referrer; | 95 request_info.referrer = referrer; |
| 48 request_info.load_flags = net::LOAD_NORMAL; | 96 request_info.load_flags = net::LOAD_NORMAL; |
| 49 request_info.requestor_pid = origin_pid; | 97 request_info.requestor_pid = origin_pid; |
| 50 request_info.request_type = ResourceType::OBJECT; | 98 request_info.request_type = ResourceType::OBJECT; |
| 51 request_info.routing_id = render_view_id; | 99 request_info.routing_id = render_view_id; |
| 52 | 100 |
| 53 std::vector<char> body; | 101 std::vector<char> body; |
| 54 if (method == "POST") { | 102 if (method == "POST") { |
| 103 bool content_type_found = false; | |
| 55 std::vector<std::string> names; | 104 std::vector<std::string> names; |
| 56 std::vector<std::string> values; | 105 std::vector<std::string> values; |
| 57 PluginHost::SetPostData( | 106 PluginHost::SetPostData(buf, len, &names, &values, &body); |
| 58 post_data.c_str(), post_data.size(), &names, &values, &body); | |
| 59 for (size_t i = 0; i < names.size(); ++i) { | 107 for (size_t i = 0; i < names.size(); ++i) { |
| 60 if (!request_info.headers.empty()) | 108 if (!request_info.headers.empty()) |
| 61 request_info.headers += "\r\n"; | 109 request_info.headers += "\r\n"; |
| 110 request_info.headers += names[i] + ": " + values[i]; | |
| 111 if (LowerCaseEqualsASCII(names[i], "content-type")) | |
| 112 content_type_found = true; | |
| 113 } | |
| 62 | 114 |
| 63 request_info.headers += names[i] + ": " + values[i]; | 115 if (!content_type_found) { |
| 116 if (!request_info.headers.empty()) | |
| 117 request_info.headers += "\r\n"; | |
| 118 request_info.headers += "Content-Type: application/x-www-form-urlencoded"; | |
| 64 } | 119 } |
| 65 } | 120 } |
| 66 | 121 |
| 67 bridge_.reset(ChildThread::current()->resource_dispatcher()->CreateBridge( | 122 bridge_.reset(ChildThread::current()->resource_dispatcher()->CreateBridge( |
| 68 request_info)); | 123 request_info)); |
| 69 if (!body.empty()) { | 124 if (!body.empty()) { |
| 70 scoped_refptr<webkit_glue::ResourceRequestBody> request_body = | 125 scoped_refptr<webkit_glue::ResourceRequestBody> request_body = |
| 71 new webkit_glue::ResourceRequestBody; | 126 new webkit_glue::ResourceRequestBody; |
| 72 request_body->AppendBytes(&body[0], body.size()); | 127 request_body->AppendBytes(&body[0], body.size()); |
| 73 bridge_->SetRequestBody(request_body.get()); | 128 bridge_->SetRequestBody(request_body.get()); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 112 if (is_plugin_src_load_ && | 167 if (is_plugin_src_load_ && |
| 113 !plugin_stream_->instance()->webplugin()->CheckIfRunInsecureContent( | 168 !plugin_stream_->instance()->webplugin()->CheckIfRunInsecureContent( |
| 114 new_url)) { | 169 new_url)) { |
| 115 plugin_stream_->DidFail(resource_id_); // That will delete |this|. | 170 plugin_stream_->DidFail(resource_id_); // That will delete |this|. |
| 116 return false; | 171 return false; |
| 117 } | 172 } |
| 118 | 173 |
| 119 // It's unfortunate that this logic of when a redirect's method changes is | 174 // It's unfortunate that this logic of when a redirect's method changes is |
| 120 // in url_request.cc, but weburlloader_impl.cc and this file have to duplicate | 175 // in url_request.cc, but weburlloader_impl.cc and this file have to duplicate |
| 121 // it instead of passing that information. | 176 // it instead of passing that information. |
| 122 if (info.headers->response_code() != 307) | 177 int response_code = info.headers->response_code(); |
| 178 if (response_code != 307) | |
| 123 method_ = "GET"; | 179 method_ = "GET"; |
| 124 GURL old_url = url_; | 180 GURL old_url = url_; |
| 125 url_ = new_url; | 181 url_ = new_url; |
| 126 *has_new_first_party_for_cookies = true; | 182 *has_new_first_party_for_cookies = true; |
| 127 *new_first_party_for_cookies = first_party_for_cookies_; | 183 *new_first_party_for_cookies = first_party_for_cookies_; |
| 128 | 184 |
| 129 // If the plugin does not participate in url redirect notifications then just | 185 // If the plugin does not participate in url redirect notifications then just |
| 130 // block cross origin 307 POST redirects. | 186 // block cross origin 307 POST redirects. |
| 131 if (!notify_redirects_) { | 187 if (!notify_redirects_) { |
| 132 if (info.headers->response_code() == 307 && method_ == "POST" && | 188 if (response_code == 307 && method_ == "POST" && |
| 133 old_url.GetOrigin() != new_url.GetOrigin()) { | 189 old_url.GetOrigin() != new_url.GetOrigin()) { |
| 134 plugin_stream_->DidFail(resource_id_); // That will delete |this|. | 190 plugin_stream_->DidFail(resource_id_); // That will delete |this|. |
| 135 return false; | 191 return false; |
| 136 } | 192 } |
| 137 } else { | 193 } else { |
| 138 // Pause the request while we ask the plugin what to do about the redirect. | 194 // Pause the request while we ask the plugin what to do about the redirect. |
| 139 bridge_->SetDefersLoading(true); | 195 bridge_->SetDefersLoading(true); |
| 140 plugin_stream_->WillSendRequest(url_, info.headers->response_code()); | 196 plugin_stream_->WillSendRequest(url_, response_code); |
| 141 } | 197 } |
| 142 | 198 |
| 143 return true; | 199 return true; |
| 144 } | 200 } |
| 145 | 201 |
| 146 void PluginURLFetcher::OnReceivedResponse( | 202 void PluginURLFetcher::OnReceivedResponse( |
| 147 const webkit_glue::ResourceResponseInfo& info) { | 203 const webkit_glue::ResourceResponseInfo& info) { |
| 148 // TODO(jam): see WebPluginImpl::didReceiveResponse for request_is_seekable | 204 // TODO(jam): THIS LOGIC IS COPIED FROM WebPluginImpl::didReceiveResponse |
| 149 bool request_is_seekable = false; | 205 // GetAllHeaders, and GetResponseInfo until kDirectNPAPIRequests is the |
| 150 uint32 last_modified = 0; | 206 // default and we can remove the old path there. |
| 207 | |
| 208 bool request_is_seekable = true; | |
| 209 DCHECK(!multipart_delegate_.get()); | |
| 210 if (plugin_stream_->seekable()) { | |
| 211 int response_code = info.headers->response_code(); | |
| 212 if (response_code == 206) { | |
| 213 WebKit::WebURLResponse response; | |
| 214 response.initialize(); | |
| 215 webkit_glue::WebURLLoaderImpl::PopulateURLResponse(url_, info, &response); | |
| 216 | |
| 217 std::string multipart_boundary; | |
| 218 if (webkit_glue::MultipartResponseDelegate::ReadMultipartBoundary( | |
| 219 response, &multipart_boundary)) { | |
| 220 plugin_stream_->instance()->webplugin()->DidStartLoading(); | |
| 221 | |
| 222 MultiPartResponseClient* multi_part_response_client = | |
| 223 new MultiPartResponseClient(plugin_stream_); | |
| 224 | |
| 225 multipart_delegate_.reset(new webkit_glue::MultipartResponseDelegate( | |
| 226 multi_part_response_client, NULL, response, multipart_boundary)); | |
| 227 | |
| 228 // Multiple ranges requested, data will be delivered by | |
| 229 // MultipartResponseDelegate. | |
| 230 data_offset_ = 0; | |
| 231 return; | |
| 232 } | |
| 233 | |
| 234 int64 upper_bound = 0, instance_size = 0; | |
| 235 // Single range requested - go through original processing for | |
| 236 // non-multipart requests, but update data offset. | |
| 237 webkit_glue::MultipartResponseDelegate::ReadContentRanges( | |
| 238 response, &data_offset_, &upper_bound, &instance_size); | |
| 239 } else if (response_code == 200) { | |
| 240 // TODO: should we handle this case? We used to but it's not clear that we | |
|
ananta
2013/09/16 18:35:41
Can we add a CHECK here?
jam
2013/09/16 19:31:34
I'm adding UMA stats in https://codereview.chromiu
| |
| 241 // still need to. This was bug 5403, fixed in r7139. | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 // If the length comes in as -1, then it indicates that it was not | |
| 246 // read off the HTTP headers. We replicate Safari webkit behavior here, | |
| 247 // which is to set it to 0. | |
| 248 int expected_length = std::max(static_cast<int>(info.content_length), 0); | |
| 151 | 249 |
| 152 base::Time temp; | 250 base::Time temp; |
| 153 if (info.headers->GetLastModifiedValue(&temp)) | 251 uint32 last_modified = 0; |
| 154 last_modified = static_cast<uint32>(temp.ToDoubleT()); | 252 std::string headers; |
| 253 if (info.headers) { // NULL for data: urls. | |
| 254 if (info.headers->GetLastModifiedValue(&temp)) | |
| 255 last_modified = static_cast<uint32>(temp.ToDoubleT()); | |
| 155 | 256 |
| 156 std::string headers = info.headers->raw_headers(); | 257 // TODO(darin): Shouldn't we also report HTTP version numbers? |
| 258 headers = base::StringPrintf("HTTP %d ", info.headers->response_code()); | |
| 259 headers += info.headers->GetStatusText(); | |
| 260 headers += "\n"; | |
| 261 | |
| 262 void* iter = NULL; | |
| 263 std::string name, value; | |
| 264 while (info.headers->EnumerateHeaderLines(&iter, &name, &value)) { | |
| 265 // TODO(darin): Should we really exclude headers with an empty value? | |
| 266 if (!name.empty() && !value.empty()) | |
| 267 headers += name + ": " + value + "\n"; | |
| 268 } | |
| 269 } | |
| 157 | 270 |
| 158 plugin_stream_->DidReceiveResponse(info.mime_type, | 271 plugin_stream_->DidReceiveResponse(info.mime_type, |
| 159 headers, | 272 headers, |
| 160 info.content_length, | 273 expected_length, |
| 161 last_modified, | 274 last_modified, |
| 162 request_is_seekable); | 275 request_is_seekable); |
| 163 } | 276 } |
| 164 | 277 |
| 165 void PluginURLFetcher::OnDownloadedData(int len, | 278 void PluginURLFetcher::OnDownloadedData(int len, |
| 166 int encoded_data_length) { | 279 int encoded_data_length) { |
| 167 } | 280 } |
| 168 | 281 |
| 169 void PluginURLFetcher::OnReceivedData(const char* data, | 282 void PluginURLFetcher::OnReceivedData(const char* data, |
| 170 int data_length, | 283 int data_length, |
| 171 int encoded_data_length) { | 284 int encoded_data_length) { |
| 172 plugin_stream_->DidReceiveData(data, data_length, data_offset_); | 285 if (multipart_delegate_) { |
| 173 data_offset_ += data_length; | 286 multipart_delegate_->OnReceivedData(data, data_length, encoded_data_length); |
| 287 } else { | |
| 288 plugin_stream_->DidReceiveData(data, data_length, data_offset_); | |
| 289 data_offset_ += data_length; | |
| 290 } | |
| 174 } | 291 } |
| 175 | 292 |
| 176 void PluginURLFetcher::OnCompletedRequest( | 293 void PluginURLFetcher::OnCompletedRequest( |
| 177 int error_code, | 294 int error_code, |
| 178 bool was_ignored_by_handler, | 295 bool was_ignored_by_handler, |
| 179 const std::string& security_info, | 296 const std::string& security_info, |
| 180 const base::TimeTicks& completion_time) { | 297 const base::TimeTicks& completion_time) { |
| 298 if (multipart_delegate_) { | |
| 299 multipart_delegate_->OnCompletedRequest(); | |
| 300 multipart_delegate_.reset(); | |
| 301 } | |
| 302 | |
| 181 if (error_code == net::OK) { | 303 if (error_code == net::OK) { |
| 182 plugin_stream_->DidFinishLoading(resource_id_); | 304 plugin_stream_->DidFinishLoading(resource_id_); |
| 183 } else { | 305 } else { |
| 184 plugin_stream_->DidFail(resource_id_); | 306 plugin_stream_->DidFail(resource_id_); |
| 185 } | 307 } |
| 186 } | 308 } |
| 187 | 309 |
| 188 } // namespace content | 310 } // namespace content |
| OLD | NEW |