blob: 00b9cd67c5d171fb4084a24e69d9e33f0b0c89d5 [file] [log] [blame]
// Copyright 2017 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_info.h"
#include <memory>
#include <string>
#include "base/values.h"
#include "content/public/browser/resource_request_info.h"
#include "content/public/browser/websocket_handshake_request_info.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/extension_api_frame_id_map.h"
#include "extensions/browser/extension_navigation_ui_data.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
#include "net/base/upload_data_stream.h"
#include "net/log/net_log_with_source.h"
#include "net/url_request/url_request.h"
namespace keys = extension_web_request_api_constants;
namespace extensions {
namespace {
std::unique_ptr<base::Value> NetLogExtensionIdCallback(
const std::string& extension_id,
net::NetLogCaptureMode capture_mode) {
auto params = std::make_unique<base::DictionaryValue>();
params->SetString("extension_id", extension_id);
return params;
}
// Implements Logger using NetLog, mirroring the logging facilities used prior
// to the introduction of WebRequestInfo.
// TODO(crbug.com/721414): Transition away from using NetLog.
class NetLogLogger : public WebRequestInfo::Logger {
public:
explicit NetLogLogger(net::URLRequest* request) : request_(request) {}
~NetLogLogger() override = default;
// WebRequestInfo::Logger:
void LogEvent(net::NetLogEventType event_type,
const std::string& extension_id) override {
request_->net_log().AddEvent(
event_type,
base::BindRepeating(&NetLogExtensionIdCallback, extension_id));
}
void LogBlockedBy(const std::string& blocker_info) override {
// LogAndReport allows extensions that block requests to be displayed in the
// load status bar.
request_->LogAndReportBlockedBy(blocker_info.c_str());
}
void LogUnblocked() override { request_->LogUnblocked(); }
private:
net::URLRequest* const request_;
DISALLOW_COPY_AND_ASSIGN(NetLogLogger);
};
std::unique_ptr<base::DictionaryValue> ExtractRequestBodyData(
net::URLRequest* url_request) {
const net::UploadDataStream* upload_data = url_request->get_upload();
if (!upload_data ||
(url_request->method() != "POST" && url_request->method() != "PUT")) {
return nullptr;
}
auto request_body_data = std::make_unique<base::DictionaryValue>();
// Get the data presenters, ordered by how specific they are.
ParsedDataPresenter parsed_data_presenter(*url_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_data->Set(kKeys[i], presenters[i]->Result());
some_succeeded = true;
break;
}
}
}
if (!some_succeeded) {
request_body_data->SetString(keys::kRequestBodyErrorKey, "Unknown error.");
}
return request_body_data;
}
} // namespace
WebRequestInfo::WebRequestInfo() = default;
WebRequestInfo::WebRequestInfo(net::URLRequest* url_request)
: id(url_request->identifier()),
url(url_request->url()),
site_for_cookies(url_request->site_for_cookies()),
method(url_request->method()),
initiator(url_request->initiator()),
extra_request_headers(url_request->extra_request_headers()),
is_pac_request(url_request->is_pac_request()),
request_body_data(ExtractRequestBodyData(url_request)),
logger(std::make_unique<NetLogLogger>(url_request)) {
if (url.SchemeIsWSOrWSS()) {
web_request_type = WebRequestResourceType::WEB_SOCKET;
// TODO(pkalinnikov): Consider embedding WebSocketHandshakeRequestInfo into
// UrlRequestUserData.
const content::WebSocketHandshakeRequestInfo* ws_info =
content::WebSocketHandshakeRequestInfo::ForRequest(url_request);
if (ws_info) {
render_process_id = ws_info->GetChildId();
frame_id = ws_info->GetRenderFrameId();
}
} else if (auto* info =
content::ResourceRequestInfo::ForRequest(url_request)) {
render_process_id = info->GetChildID();
routing_id = info->GetRouteID();
frame_id = info->GetRenderFrameID();
type = info->GetResourceType();
web_request_type = ToWebRequestResourceType(type.value());
is_async = info->IsAsync();
} else {
// There may be basic process and frame info associated with the request
// even when |info| is null. Attempt to grab it as a last ditch effort. If
// this fails, we have no frame info.
content::ResourceRequestInfo::GetRenderFrameForRequest(
url_request, &render_process_id, &frame_id);
}
ExtensionsBrowserClient* browser_client = ExtensionsBrowserClient::Get();
ExtensionNavigationUIData* navigation_ui_data =
browser_client ? browser_client->GetExtensionNavigationUIData(url_request)
: nullptr;
if (navigation_ui_data) {
is_browser_side_navigation = true;
is_web_view = navigation_ui_data->is_web_view();
web_view_instance_id = navigation_ui_data->web_view_instance_id();
web_view_rules_registry_id =
navigation_ui_data->web_view_rules_registry_id();
// PlzNavigate: if this request corresponds to a navigation, we always have
// FrameData available from the ExtensionNavigationUIData. Use that.
frame_data = navigation_ui_data->frame_data();
} else if (frame_id >= 0) {
// Grab any WebView-related information if relevant.
WebViewRendererState::WebViewInfo web_view_info;
if (WebViewRendererState::GetInstance()->GetInfo(
render_process_id, routing_id, &web_view_info)) {
is_web_view = true;
web_view_instance_id = web_view_info.instance_id;
web_view_rules_registry_id = web_view_info.rules_registry_id;
web_view_embedder_process_id = web_view_info.embedder_process_id;
}
// For subresource loads or non-browser-side navigation requests, attempt to
// resolve the FrameData immediately anyway using cached information.
ExtensionApiFrameIdMap::FrameData data;
bool was_cached = ExtensionApiFrameIdMap::Get()->GetCachedFrameDataOnIO(
render_process_id, frame_id, &data);
if (was_cached)
frame_data = data;
}
}
WebRequestInfo::~WebRequestInfo() = default;
void WebRequestInfo::AddResponseInfoFromURLRequest(
net::URLRequest* url_request) {
response_code = url_request->GetResponseCode();
response_headers = url_request->response_headers();
response_ip = url_request->GetSocketAddress().host();
response_from_cache = url_request->was_cached();
}
} // namespace extensions