| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "extensions/browser/api/web_request/web_request_event_details.h" |
| |
| #include <utility> |
| |
| #include "base/callback.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/resource_request_info.h" |
| #include "content/public/browser/websocket_handshake_request_info.h" |
| #include "content/public/common/child_process_host.h" |
| #include "extensions/browser/api/web_request/upload_data_presenter.h" |
| #include "extensions/browser/api/web_request/web_request_api_constants.h" |
| #include "extensions/browser/api/web_request/web_request_api_helpers.h" |
| #include "extensions/browser/api/web_request/web_request_resource_type.h" |
| #include "ipc/ipc_message.h" |
| #include "net/base/auth.h" |
| #include "net/base/upload_data_stream.h" |
| #include "net/http/http_request_headers.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/url_request/url_request.h" |
| |
| using extension_web_request_api_helpers::ExtraInfoSpec; |
| |
| namespace helpers = extension_web_request_api_helpers; |
| namespace keys = extension_web_request_api_constants; |
| |
| namespace extensions { |
| |
| WebRequestEventDetails::WebRequestEventDetails(const net::URLRequest* request, |
| int extra_info_spec) |
| : extra_info_spec_(extra_info_spec), |
| render_process_id_(content::ChildProcessHost::kInvalidUniqueID), |
| render_frame_id_(MSG_ROUTING_NONE) { |
| auto resource_type = GetWebRequestResourceType(request); |
| const content::ResourceRequestInfo* info = |
| content::ResourceRequestInfo::ForRequest(request); |
| if (info) { |
| render_process_id_ = info->GetChildID(); |
| render_frame_id_ = info->GetRenderFrameID(); |
| } else if (resource_type == WebRequestResourceType::WEB_SOCKET) { |
| // TODO(pkalinnikov): Consider embedding WebSocketHandshakeRequestInfo into |
| // UrlRequestUserData. |
| const content::WebSocketHandshakeRequestInfo* ws_info = |
| content::WebSocketHandshakeRequestInfo::ForRequest(request); |
| if (ws_info) { |
| render_process_id_ = ws_info->GetChildId(); |
| render_frame_id_ = ws_info->GetRenderFrameId(); |
| } |
| } else { |
| // Fallback for requests that are not allocated by a |
| // ResourceDispatcherHost, such as the TemplateURLFetcher. |
| content::ResourceRequestInfo::GetRenderFrameForRequest( |
| request, &render_process_id_, &render_frame_id_); |
| } |
| |
| dict_.SetString(keys::kMethodKey, request->method()); |
| dict_.SetString(keys::kRequestIdKey, |
| base::Uint64ToString(request->identifier())); |
| dict_.SetDouble(keys::kTimeStampKey, base::Time::Now().ToDoubleT() * 1000); |
| dict_.SetString(keys::kTypeKey, |
| WebRequestResourceTypeToString(resource_type)); |
| dict_.SetString(keys::kUrlKey, request->url().spec()); |
| } |
| |
| WebRequestEventDetails::~WebRequestEventDetails() {} |
| |
| void WebRequestEventDetails::SetRequestBody(const net::URLRequest* request) { |
| if (!(extra_info_spec_ & ExtraInfoSpec::REQUEST_BODY)) |
| return; |
| |
| const net::UploadDataStream* upload_data = request->get_upload(); |
| if (!upload_data || |
| (request->method() != "POST" && request->method() != "PUT")) |
| return; |
| |
| base::DictionaryValue* request_body = new base::DictionaryValue(); |
| request_body_.reset(request_body); |
| |
| // Get the data presenters, ordered by how specific they are. |
| ParsedDataPresenter parsed_data_presenter(*request); |
| RawDataPresenter raw_data_presenter; |
| UploadDataPresenter* const presenters[] = { |
| &parsed_data_presenter, // 1: any parseable forms? (Specific to forms.) |
| &raw_data_presenter // 2: any data at all? (Non-specific.) |
| }; |
| // Keys for the results of the corresponding presenters. |
| static const char* const kKeys[] = {keys::kRequestBodyFormDataKey, |
| keys::kRequestBodyRawKey}; |
| |
| const std::vector<std::unique_ptr<net::UploadElementReader>>* readers = |
| upload_data->GetElementReaders(); |
| bool some_succeeded = false; |
| if (readers) { |
| for (size_t i = 0; i < arraysize(presenters); ++i) { |
| for (const auto& reader : *readers) |
| presenters[i]->FeedNext(*reader); |
| if (presenters[i]->Succeeded()) { |
| request_body->Set(kKeys[i], presenters[i]->Result()); |
| some_succeeded = true; |
| break; |
| } |
| } |
| } |
| if (!some_succeeded) |
| request_body->SetString(keys::kRequestBodyErrorKey, "Unknown error."); |
| } |
| |
| void WebRequestEventDetails::SetRequestHeaders( |
| const net::HttpRequestHeaders& request_headers) { |
| if (!(extra_info_spec_ & ExtraInfoSpec::REQUEST_HEADERS)) |
| return; |
| |
| base::ListValue* headers = new base::ListValue(); |
| for (net::HttpRequestHeaders::Iterator it(request_headers); it.GetNext();) |
| headers->Append(helpers::CreateHeaderDictionary(it.name(), it.value())); |
| request_headers_.reset(headers); |
| } |
| |
| void WebRequestEventDetails::SetAuthInfo( |
| const net::AuthChallengeInfo& auth_info) { |
| dict_.SetBoolean(keys::kIsProxyKey, auth_info.is_proxy); |
| if (!auth_info.scheme.empty()) |
| dict_.SetString(keys::kSchemeKey, auth_info.scheme); |
| if (!auth_info.realm.empty()) |
| dict_.SetString(keys::kRealmKey, auth_info.realm); |
| auto challenger = base::MakeUnique<base::DictionaryValue>(); |
| challenger->SetString(keys::kHostKey, auth_info.challenger.host()); |
| challenger->SetInteger(keys::kPortKey, auth_info.challenger.port()); |
| dict_.Set(keys::kChallengerKey, std::move(challenger)); |
| } |
| |
| void WebRequestEventDetails::SetResponseHeaders( |
| const net::URLRequest* request, |
| const net::HttpResponseHeaders* response_headers) { |
| if (!response_headers) { |
| // Not all URLRequestJobs specify response headers. E.g. URLRequestFTPJob, |
| // URLRequestFileJob and some redirects. |
| dict_.SetInteger(keys::kStatusCodeKey, request->GetResponseCode()); |
| dict_.SetString(keys::kStatusLineKey, ""); |
| } else { |
| dict_.SetInteger(keys::kStatusCodeKey, response_headers->response_code()); |
| dict_.SetString(keys::kStatusLineKey, response_headers->GetStatusLine()); |
| } |
| |
| if (extra_info_spec_ & ExtraInfoSpec::RESPONSE_HEADERS) { |
| base::ListValue* headers = new base::ListValue(); |
| if (response_headers) { |
| size_t iter = 0; |
| std::string name; |
| std::string value; |
| while (response_headers->EnumerateHeaderLines(&iter, &name, &value)) |
| headers->Append(helpers::CreateHeaderDictionary(name, value)); |
| } |
| response_headers_.reset(headers); |
| } |
| } |
| |
| void WebRequestEventDetails::SetResponseSource(const net::URLRequest* request) { |
| dict_.SetBoolean(keys::kFromCache, request->was_cached()); |
| const std::string response_ip = request->GetSocketAddress().host(); |
| if (!response_ip.empty()) |
| dict_.SetString(keys::kIpKey, response_ip); |
| } |
| |
| void WebRequestEventDetails::SetFrameData( |
| const ExtensionApiFrameIdMap::FrameData& frame_data) { |
| dict_.SetInteger(keys::kTabIdKey, frame_data.tab_id); |
| dict_.SetInteger(keys::kFrameIdKey, frame_data.frame_id); |
| dict_.SetInteger(keys::kParentFrameIdKey, frame_data.parent_frame_id); |
| } |
| |
| void WebRequestEventDetails::DetermineFrameDataOnUI() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| content::RenderFrameHost* rfh = |
| content::RenderFrameHost::FromID(render_process_id_, render_frame_id_); |
| ExtensionApiFrameIdMap::FrameData frame_data = |
| ExtensionApiFrameIdMap::Get()->GetFrameData(rfh); |
| SetFrameData(frame_data); |
| } |
| |
| void WebRequestEventDetails::DetermineFrameDataOnIO( |
| const DeterminedFrameDataCallback& callback) { |
| std::unique_ptr<WebRequestEventDetails> self(this); |
| ExtensionApiFrameIdMap::Get()->GetFrameDataOnIO( |
| render_process_id_, render_frame_id_, |
| base::Bind(&WebRequestEventDetails::OnDeterminedFrameData, |
| base::Unretained(this), base::Passed(&self), callback)); |
| } |
| |
| std::unique_ptr<base::DictionaryValue> WebRequestEventDetails::GetFilteredDict( |
| int extra_info_spec) const { |
| std::unique_ptr<base::DictionaryValue> result = dict_.CreateDeepCopy(); |
| if ((extra_info_spec & ExtraInfoSpec::REQUEST_BODY) && request_body_) { |
| result->Set(keys::kRequestBodyKey, |
| base::MakeUnique<base::Value>(*request_body_)); |
| } |
| if ((extra_info_spec & ExtraInfoSpec::REQUEST_HEADERS) && request_headers_) { |
| result->Set(keys::kRequestHeadersKey, |
| base::MakeUnique<base::Value>(*request_headers_)); |
| } |
| if ((extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS) && |
| response_headers_) { |
| result->Set(keys::kResponseHeadersKey, |
| base::MakeUnique<base::Value>(*response_headers_)); |
| } |
| return result; |
| } |
| |
| std::unique_ptr<base::DictionaryValue> |
| WebRequestEventDetails::GetAndClearDict() { |
| std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue); |
| dict_.Swap(result.get()); |
| return result; |
| } |
| |
| void WebRequestEventDetails::FilterForPublicSession() { |
| request_body_ = nullptr; |
| request_headers_ = nullptr; |
| response_headers_ = nullptr; |
| |
| extra_info_spec_ = 0; |
| |
| static const char* const kSafeAttributes[] = { |
| "method", "requestId", "timeStamp", "type", "tabId", "frameId", |
| "parentFrameId", "fromCache", "error", "ip", "statusLine", "statusCode" |
| }; |
| |
| auto copy = GetAndClearDict(); |
| |
| for (const char* safe_attr : kSafeAttributes) { |
| std::unique_ptr<base::Value> val; |
| if (copy->Remove(safe_attr, &val)) |
| dict_.Set(safe_attr, std::move(val)); |
| } |
| |
| // URL is stripped down to the origin. |
| std::string url; |
| copy->GetString(keys::kUrlKey, &url); |
| GURL gurl(url); |
| dict_.SetString(keys::kUrlKey, gurl.GetOrigin().spec()); |
| } |
| |
| WebRequestEventDetails::WebRequestEventDetails() |
| : extra_info_spec_(0), render_process_id_(0), render_frame_id_(0) {} |
| |
| void WebRequestEventDetails::OnDeterminedFrameData( |
| std::unique_ptr<WebRequestEventDetails> self, |
| const DeterminedFrameDataCallback& callback, |
| const ExtensionApiFrameIdMap::FrameData& frame_data) { |
| SetFrameData(frame_data); |
| callback.Run(std::move(self)); |
| } |
| |
| } // namespace extensions |