blob: 87ea8378d3ccc64e55ca5354073b539912e09df5 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/websockets/websocket_connector_impl.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "net/storage_access_api/status.h"
#include "services/network/public/cpp/features.h"
#include "url/gurl.h"
namespace content {
namespace {
url::Origin MaybeTreatLocalOriginAsOpaque(const url::Origin& origin) {
if (base::Contains(url::GetLocalSchemes(), origin.scheme()) &&
!base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kAllowFileAccessFromFiles)) {
// For local origins we should use an opaque origin unless
// "--allow-file-access-from-files" is specified. This should have been
// done in content::RenderFrameHost. See https://crbug.com/1206736 for
// details.
return origin.DeriveNewOpaqueOrigin();
}
return origin;
}
constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("websocket_stream", R"(
semantics {
sender: "WebSocket Handshake"
description:
"Renderer process initiated WebSocket handshake. The WebSocket "
"handshake is used to establish a connection between a web page "
"and a consenting server for bi-directional communication."
trigger:
"A handshake is performed every time a new connection is "
"established via the Javascript. Any web page or extension can "
"create a WebSocket connection."
data: "The path and sub-protocols requested when the WebSocket was "
"created, plus the origin of the creating page."
destination: OTHER
}
policy {
cookies_allowed: YES
cookies_store: "user or per-app cookie store"
setting: "These requests cannot be disabled."
policy_exception_justification:
"Not implemented. WebSocket is a core web platform API."
})");
}
WebSocketConnectorImpl::WebSocketConnectorImpl(
int process_id,
int frame_id,
const url::Origin& origin,
const net::IsolationInfo& isolation_info)
: process_id_(process_id),
frame_id_(frame_id),
origin_(MaybeTreatLocalOriginAsOpaque(origin)),
isolation_info_(isolation_info) {}
WebSocketConnectorImpl::~WebSocketConnectorImpl() = default;
void WebSocketConnectorImpl::Connect(
const GURL& url,
const std::vector<std::string>& requested_protocols,
const net::SiteForCookies& site_for_cookies,
const std::optional<std::string>& user_agent,
net::StorageAccessApiStatus storage_access_api_status,
mojo::PendingRemote<network::mojom::WebSocketHandshakeClient>
handshake_client,
const std::optional<base::UnguessableToken>& throttling_profile_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
RenderProcessHost* process = RenderProcessHost::FromID(process_id_);
if (!process) {
return;
}
RenderFrameHost* frame = RenderFrameHost::FromID(process_id_, frame_id_);
const uint32_t options =
GetContentClient()->browser()->GetWebSocketOptions(frame);
if (GetContentClient()->browser()->WillInterceptWebSocket(frame)) {
GetContentClient()->browser()->CreateWebSocket(
frame,
base::BindOnce(ConnectCalledByContentBrowserClient, requested_protocols,
site_for_cookies, storage_access_api_status,
isolation_info_, process_id_, frame_id_, origin_,
options, std::move(throttling_profile_id)),
url, site_for_cookies, user_agent, std::move(handshake_client));
return;
}
std::vector<network::mojom::HttpHeaderPtr> headers;
if (user_agent) {
headers.push_back(network::mojom::HttpHeader::New(
net::HttpRequestHeaders::kUserAgent, *user_agent));
}
process->GetStoragePartition()->GetNetworkContext()->CreateWebSocket(
url, requested_protocols, site_for_cookies, storage_access_api_status,
isolation_info_, std::move(headers), process_id_, origin_, options,
net::MutableNetworkTrafficAnnotationTag(kTrafficAnnotation),
std::move(handshake_client),
process->GetStoragePartition()->CreateURLLoaderNetworkObserverForFrame(
process_id_, frame_id_),
mojo::NullRemote(), mojo::NullRemote(), std::move(throttling_profile_id));
}
void WebSocketConnectorImpl::ConnectCalledByContentBrowserClient(
const std::vector<std::string>& requested_protocols,
const net::SiteForCookies& site_for_cookies,
net::StorageAccessApiStatus storage_access_api_status,
const net::IsolationInfo& isolation_info,
int process_id,
int frame_id,
const url::Origin& origin,
uint32_t options,
std::optional<base::UnguessableToken> throttling_profile_id,
const GURL& url,
std::vector<network::mojom::HttpHeaderPtr> additional_headers,
mojo::PendingRemote<network::mojom::WebSocketHandshakeClient>
handshake_client,
mojo::PendingRemote<network::mojom::WebSocketAuthenticationHandler>
auth_handler,
mojo::PendingRemote<network::mojom::TrustedHeaderClient>
trusted_header_client) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
RenderProcessHost* process = RenderProcessHost::FromID(process_id);
if (!process) {
return;
}
process->GetStoragePartition()->GetNetworkContext()->CreateWebSocket(
url, requested_protocols, site_for_cookies, storage_access_api_status,
isolation_info, std::move(additional_headers), process_id, origin,
options, net::MutableNetworkTrafficAnnotationTag(kTrafficAnnotation),
std::move(handshake_client),
process->GetStoragePartition()->CreateURLLoaderNetworkObserverForFrame(
process_id, frame_id),
std::move(auth_handler), std::move(trusted_header_client),
std::move(throttling_profile_id));
}
} // namespace content