Chromium Code Reviews| Index: content/child/npapi/plugin_url_fetcher.cc |
| =================================================================== |
| --- content/child/npapi/plugin_url_fetcher.cc (revision 223347) |
| +++ content/child/npapi/plugin_url_fetcher.cc (working copy) |
| @@ -15,15 +15,64 @@ |
| #include "net/base/load_flags.h" |
| #include "net/base/net_errors.h" |
| #include "net/http/http_response_headers.h" |
| +#include "third_party/WebKit/public/platform/WebURLLoaderClient.h" |
| +#include "third_party/WebKit/public/platform/WebURLResponse.h" |
| +#include "webkit/child/multipart_response_delegate.h" |
| +#include "webkit/child/weburlloader_impl.h" |
| #include "webkit/common/resource_request_body.h" |
| namespace content { |
| +namespace { |
| +// This class handles individual multipart responses. It is instantiated when |
| +// we receive HTTP status code 206 in the HTTP response. This indicates |
| +// that the response could have multiple parts each separated by a boundary |
| +// specified in the response header. |
| +// TODO(jam): this is similar to MultiPartResponseClient in webplugin_impl.cc, |
| +// we should remove that other class once we switch to loading from the plugin |
| +// process by default. |
| +class MultiPartResponseClient : public WebKit::WebURLLoaderClient { |
| + public: |
| + explicit MultiPartResponseClient(PluginStreamUrl* plugin_stream) |
| + : byte_range_lower_bound_(0), plugin_stream_(plugin_stream) {} |
| + |
| + // WebKit::WebURLLoaderClient implementation: |
| + virtual void didReceiveResponse( |
| + WebKit::WebURLLoader* loader, |
| + const WebKit::WebURLResponse& response) OVERRIDE { |
| + int64 byte_range_upper_bound, instance_size; |
| + if (!webkit_glue::MultipartResponseDelegate::ReadContentRanges( |
| + response, &byte_range_lower_bound_, &byte_range_upper_bound, |
| + &instance_size)) { |
| + NOTREACHED(); |
| + } |
| + } |
| + virtual void didReceiveData(WebKit::WebURLLoader* loader, |
| + const char* data, |
| + int data_length, |
| + int encoded_data_length) OVERRIDE { |
| + // TODO(ananta) |
| + // We should defer further loads on multipart resources on the same lines |
| + // as regular resources requested by plugins to prevent reentrancy. |
| + plugin_stream_->DidReceiveData(data, data_length, byte_range_lower_bound_); |
| + byte_range_lower_bound_ += data_length; |
| + } |
| + |
| + private: |
| + // The lower bound of the byte range. |
| + int64 byte_range_lower_bound_; |
| + // The handler for the data. |
| + PluginStreamUrl* plugin_stream_; |
| +}; |
| + |
| +} // namespace |
| + |
| PluginURLFetcher::PluginURLFetcher(PluginStreamUrl* plugin_stream, |
| const GURL& url, |
| const GURL& first_party_for_cookies, |
| const std::string& method, |
| - const std::string& post_data, |
| + const char* buf, |
| + unsigned int len, |
| const GURL& referrer, |
| bool notify_redirects, |
| bool is_plugin_src_load, |
| @@ -34,7 +83,6 @@ |
| url_(url), |
| first_party_for_cookies_(first_party_for_cookies), |
| method_(method), |
| - post_data_(post_data), |
| referrer_(referrer), |
| notify_redirects_(notify_redirects), |
| is_plugin_src_load_(is_plugin_src_load), |
| @@ -52,16 +100,23 @@ |
| std::vector<char> body; |
| if (method == "POST") { |
| + bool content_type_found = false; |
| std::vector<std::string> names; |
| std::vector<std::string> values; |
| - PluginHost::SetPostData( |
| - post_data.c_str(), post_data.size(), &names, &values, &body); |
| + PluginHost::SetPostData(buf, len, &names, &values, &body); |
| for (size_t i = 0; i < names.size(); ++i) { |
| if (!request_info.headers.empty()) |
| request_info.headers += "\r\n"; |
| - |
| request_info.headers += names[i] + ": " + values[i]; |
| + if (LowerCaseEqualsASCII(names[i], "content-type")) |
| + content_type_found = true; |
| } |
| + |
| + if (!content_type_found) { |
| + if (!request_info.headers.empty()) |
| + request_info.headers += "\r\n"; |
| + request_info.headers += "Content-Type: application/x-www-form-urlencoded"; |
| + } |
| } |
| bridge_.reset(ChildThread::current()->resource_dispatcher()->CreateBridge( |
| @@ -119,7 +174,8 @@ |
| // It's unfortunate that this logic of when a redirect's method changes is |
| // in url_request.cc, but weburlloader_impl.cc and this file have to duplicate |
| // it instead of passing that information. |
| - if (info.headers->response_code() != 307) |
| + int response_code = info.headers->response_code(); |
| + if (response_code != 307) |
| method_ = "GET"; |
| GURL old_url = url_; |
| url_ = new_url; |
| @@ -129,7 +185,7 @@ |
| // If the plugin does not participate in url redirect notifications then just |
| // block cross origin 307 POST redirects. |
| if (!notify_redirects_) { |
| - if (info.headers->response_code() == 307 && method_ == "POST" && |
| + if (response_code == 307 && method_ == "POST" && |
| old_url.GetOrigin() != new_url.GetOrigin()) { |
| plugin_stream_->DidFail(resource_id_); // That will delete |this|. |
| return false; |
| @@ -137,7 +193,7 @@ |
| } else { |
| // Pause the request while we ask the plugin what to do about the redirect. |
| bridge_->SetDefersLoading(true); |
| - plugin_stream_->WillSendRequest(url_, info.headers->response_code()); |
| + plugin_stream_->WillSendRequest(url_, response_code); |
| } |
| return true; |
| @@ -145,21 +201,78 @@ |
| void PluginURLFetcher::OnReceivedResponse( |
| const webkit_glue::ResourceResponseInfo& info) { |
| - // TODO(jam): see WebPluginImpl::didReceiveResponse for request_is_seekable |
| - bool request_is_seekable = false; |
| - uint32 last_modified = 0; |
| + // TODO(jam): THIS LOGIC IS COPIED FROM WebPluginImpl::didReceiveResponse |
| + // GetAllHeaders, and GetResponseInfo until kDirectNPAPIRequests is the |
| + // default and we can remove the old path there. |
| + bool request_is_seekable = true; |
| + DCHECK(!multipart_delegate_.get()); |
| + if (plugin_stream_->seekable()) { |
| + int response_code = info.headers->response_code(); |
| + if (response_code == 206) { |
| + WebKit::WebURLResponse response; |
| + response.initialize(); |
| + webkit_glue::WebURLLoaderImpl::PopulateURLResponse(url_, info, &response); |
| + |
| + std::string multipart_boundary; |
| + if (webkit_glue::MultipartResponseDelegate::ReadMultipartBoundary( |
| + response, &multipart_boundary)) { |
| + plugin_stream_->instance()->webplugin()->DidStartLoading(); |
| + |
| + MultiPartResponseClient* multi_part_response_client = |
| + new MultiPartResponseClient(plugin_stream_); |
| + |
| + multipart_delegate_.reset(new webkit_glue::MultipartResponseDelegate( |
| + multi_part_response_client, NULL, response, multipart_boundary)); |
| + |
| + // Multiple ranges requested, data will be delivered by |
| + // MultipartResponseDelegate. |
| + data_offset_ = 0; |
| + return; |
| + } |
| + |
| + int64 upper_bound = 0, instance_size = 0; |
| + // Single range requested - go through original processing for |
| + // non-multipart requests, but update data offset. |
| + webkit_glue::MultipartResponseDelegate::ReadContentRanges( |
| + response, &data_offset_, &upper_bound, &instance_size); |
| + } else if (response_code == 200) { |
| + // 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
|
| + // still need to. This was bug 5403, fixed in r7139. |
| + } |
| + } |
| + |
| + // If the length comes in as -1, then it indicates that it was not |
| + // read off the HTTP headers. We replicate Safari webkit behavior here, |
| + // which is to set it to 0. |
| + int expected_length = std::max(static_cast<int>(info.content_length), 0); |
| + |
| base::Time temp; |
| - if (info.headers->GetLastModifiedValue(&temp)) |
| - last_modified = static_cast<uint32>(temp.ToDoubleT()); |
| + uint32 last_modified = 0; |
| + std::string headers; |
| + if (info.headers) { // NULL for data: urls. |
| + if (info.headers->GetLastModifiedValue(&temp)) |
| + last_modified = static_cast<uint32>(temp.ToDoubleT()); |
| - std::string headers = info.headers->raw_headers(); |
| + // TODO(darin): Shouldn't we also report HTTP version numbers? |
| + headers = base::StringPrintf("HTTP %d ", info.headers->response_code()); |
| + headers += info.headers->GetStatusText(); |
| + headers += "\n"; |
| + void* iter = NULL; |
| + std::string name, value; |
| + while (info.headers->EnumerateHeaderLines(&iter, &name, &value)) { |
| + // TODO(darin): Should we really exclude headers with an empty value? |
| + if (!name.empty() && !value.empty()) |
| + headers += name + ": " + value + "\n"; |
| + } |
| + } |
| + |
| plugin_stream_->DidReceiveResponse(info.mime_type, |
| - headers, |
| - info.content_length, |
| - last_modified, |
| - request_is_seekable); |
| + headers, |
| + expected_length, |
| + last_modified, |
| + request_is_seekable); |
| } |
| void PluginURLFetcher::OnDownloadedData(int len, |
| @@ -169,8 +282,12 @@ |
| void PluginURLFetcher::OnReceivedData(const char* data, |
| int data_length, |
| int encoded_data_length) { |
| - plugin_stream_->DidReceiveData(data, data_length, data_offset_); |
| - data_offset_ += data_length; |
| + if (multipart_delegate_) { |
| + multipart_delegate_->OnReceivedData(data, data_length, encoded_data_length); |
| + } else { |
| + plugin_stream_->DidReceiveData(data, data_length, data_offset_); |
| + data_offset_ += data_length; |
| + } |
| } |
| void PluginURLFetcher::OnCompletedRequest( |
| @@ -178,6 +295,11 @@ |
| bool was_ignored_by_handler, |
| const std::string& security_info, |
| const base::TimeTicks& completion_time) { |
| + if (multipart_delegate_) { |
| + multipart_delegate_->OnCompletedRequest(); |
| + multipart_delegate_.reset(); |
| + } |
| + |
| if (error_code == net::OK) { |
| plugin_stream_->DidFinishLoading(resource_id_); |
| } else { |