一、WebSocket前端接口定义:
WebSocket
对象提供了用于创建和管理 WebSocket 连接,以及可以通过该连接发送和接收数据的 API。
使用 WebSocket() 构造函数来构造一个 WebSocket
。
构造函数
返回一个 WebSocket
对象。
常量
Constant | Value |
---|---|
WebSocket.CONNECTING |
0 |
WebSocket.OPEN |
1 |
WebSocket.CLOSING |
2 |
WebSocket.CLOSED |
3 |
属性
使用二进制的数据类型连接。
未发送至服务器的字节数。
服务器选择的扩展。
用于指定连接关闭后的回调函数。
用于指定连接失败后的回调函数。
用于指定当从服务器接受到信息时的回调函数。
用于指定连接成功后的回调函数。
服务器选择的下属协议。
当前的链接状态。
WebSocket 的绝对路径。
方法
WebSocket.close([code[, reason]])
关闭当前链接。
对要传输的数据进行排队。
事件
使用 addEventListener()
或将一个事件监听器赋值给本接口的 oneventname
属性,来监听下面的事件。
当一个 WebSocket
连接被关闭时触发。 也可以通过 onclose 属性来设置。
当一个 WebSocket
连接因错误而关闭时触发,例如无法发送数据时。 也可以通过 onerror 属性来设置。
当通过 WebSocket
收到数据时触发。 也可以通过 onmessage 属性来设置。
当一个 WebSocket
连接成功时触发。 也可以通过 onopen 属性来设置。
更多参考:WebSocket - Web API | MDN (mozilla.org)
二、websocket c++接口实现和定义
1、websocket.idl接口定义:
third_party\blink\renderer\modules\websockets\websocket.idl
cpp
// https://html.spec.whatwg.org/C/#the-websocket-interface
enum BinaryType { "blob", "arraybuffer" };
[
ActiveScriptWrappable,
Exposed=(Window,Worker),
ImplementedAs=DOMWebSocket
] interface WebSocket : EventTarget {
[CallWith=ExecutionContext, RaisesException] constructor(USVString url, optional (DOMString or sequence<DOMString>) protocols);
readonly attribute USVString url;
// ready state
const unsigned short CONNECTING = 0;
const unsigned short OPEN = 1;
const unsigned short CLOSING = 2;
const unsigned short CLOSED = 3;
readonly attribute unsigned short readyState;
readonly attribute unsigned long long bufferedAmount;
// networking
attribute EventHandler onopen;
attribute EventHandler onerror;
attribute EventHandler onclose;
readonly attribute DOMString extensions;
readonly attribute DOMString protocol;
[RaisesException] void close(optional [Clamp] unsigned short code, optional USVString reason);
// messaging
attribute EventHandler onmessage;
attribute BinaryType binaryType;
[RaisesException] void send(USVString data);
[RaisesException] void send(Blob data);
[RaisesException] void send(ArrayBuffer data);
[RaisesException] void send(ArrayBufferView data);
};
2、websocket.idl接口实现blink:
third_party\blink\renderer\modules\websockets\dom_websocket.h
third_party\blink\renderer\modules\websockets\dom_websocket.cc
cpp
namespace blink {
class Blob;
class DOMArrayBuffer;
class DOMArrayBufferView;
class ExceptionState;
class ExecutionContext;
class V8UnionStringOrStringSequence;
class MODULES_EXPORT DOMWebSocket
: public EventTarget,
public ActiveScriptWrappable<DOMWebSocket>,
public ExecutionContextLifecycleStateObserver,
public WebSocketChannelClient {
DEFINE_WRAPPERTYPEINFO();
public:
// These definitions are required by V8DOMWebSocket.
static constexpr auto kConnecting = WebSocketCommon::kConnecting;
static constexpr auto kOpen = WebSocketCommon::kOpen;
static constexpr auto kClosing = WebSocketCommon::kClosing;
static constexpr auto kClosed = WebSocketCommon::kClosed;
// DOMWebSocket instances must be used with a wrapper since this class's
// lifetime management is designed assuming the V8 holds a ref on it while
// hasPendingActivity() returns true.
static DOMWebSocket* Create(ExecutionContext*,
const String& url,
ExceptionState&);
static DOMWebSocket* Create(ExecutionContext* execution_context,
const String& url,
const V8UnionStringOrStringSequence* protocols,
ExceptionState& exception_state);
explicit DOMWebSocket(ExecutionContext*);
~DOMWebSocket() override;
void Connect(const String& url,
const Vector<String>& protocols,
ExceptionState&);
void send(const String& message, ExceptionState&);
void send(DOMArrayBuffer*, ExceptionState&);
void send(NotShared<DOMArrayBufferView>, ExceptionState&);
void send(Blob*, ExceptionState&);
// To distinguish close method call with the code parameter from one
// without, we have these three signatures. Use of
// Optional=DefaultIsUndefined in the IDL file doesn't help for now since
// it's bound to a value of 0 which is indistinguishable from the case 0
// is passed as code parameter.
void close(uint16_t code, const String& reason, ExceptionState&);
void close(ExceptionState&);
void close(uint16_t code, ExceptionState&);
const KURL& url() const;
WebSocketCommon::State readyState() const;
uint64_t bufferedAmount() const;
String protocol() const;
String extensions() const;
String binaryType() const;
void setBinaryType(const String&);
DEFINE_ATTRIBUTE_EVENT_LISTENER(open, kOpen)
DEFINE_ATTRIBUTE_EVENT_LISTENER(message, kMessage)
DEFINE_ATTRIBUTE_EVENT_LISTENER(error, kError)
DEFINE_ATTRIBUTE_EVENT_LISTENER(close, kClose)
// EventTarget functions.
const AtomicString& InterfaceName() const override;
ExecutionContext* GetExecutionContext() const override;
// ExecutionContextLifecycleStateObserver functions.
void ContextDestroyed() override;
void ContextLifecycleStateChanged(mojom::FrameLifecycleState) override;
// ScriptWrappable functions.
// Prevent this instance from being collected while it's not in CLOSED
// state.
bool HasPendingActivity() const final;
// WebSocketChannelClient functions.
void DidConnect(const String& subprotocol, const String& extensions) override;
void DidReceiveTextMessage(const String& message) override;
void DidReceiveBinaryMessage(
const Vector<base::span<const char>>& data) override;
void DidError() override;
void DidConsumeBufferedAmount(uint64_t) override;
void DidStartClosingHandshake() override;
void DidClose(ClosingHandshakeCompletionStatus,
uint16_t code,
const String& reason) override;
void Trace(Visitor*) const override;
private:
// FIXME: This should inherit blink::EventQueue.
class EventQueue final : public GarbageCollected<EventQueue> {
public:
static EventQueue* Create(EventTarget* target) {
return MakeGarbageCollected<EventQueue>(target);
}
explicit EventQueue(EventTarget*);
~EventQueue();
// Dispatches the event if this queue is active.
// Queues the event if this queue is suspended.
// Does nothing otherwise.
void Dispatch(Event* /* event */);
bool IsEmpty() const;
void Pause();
void Unpause();
void ContextDestroyed();
bool IsPaused();
void Trace(Visitor*) const;
private:
enum State {
kActive,
kPaused,
kUnpausePosted,
kStopped,
};
// Dispatches queued events if this queue is active.
// Does nothing otherwise.
void DispatchQueuedEvents();
void UnpauseTask();
State state_;
Member<EventTarget> target_;
HeapDeque<Member<Event>> events_;
};
enum class WebSocketSendType {
kString,
kArrayBuffer,
kArrayBufferView,
kBlob,
kMaxValue = kBlob,
};
enum BinaryType { kBinaryTypeBlob, kBinaryTypeArrayBuffer };
// This function is virtual for unittests.
virtual WebSocketChannel* CreateChannel(ExecutionContext* context,
WebSocketChannelClient* client) {
return WebSocketChannelImpl::Create(context, client,
CaptureSourceLocation(context));
}
// Adds a console message with JSMessageSource and ErrorMessageLevel.
void LogError(const String& message);
// Handle the JavaScript close method call. close() methods on this class
// are just for determining if the optional code argument is supplied or
// not.
void CloseInternal(int, const String&, ExceptionState&);
// Updates |buffered_amount_after_close_| given the amount of data passed to
// send() method after the state changed to CLOSING or CLOSED.
void UpdateBufferedAmountAfterClose(uint64_t);
// Causes |buffered_amount_| to be updated asynchronously after returning to
// the event loop. Uses |buffered_amount_update_task_pending_| to avoid
// posting multiple tasks simultaneously.
void PostBufferedAmountUpdateTask();
// Updates |buffered_amount_| and resets
// |buffered_amount_update_task_pending_|.
void BufferedAmountUpdateTask();
// Updates |buffered_amount_| provided the object is not currently paused.
void ReflectBufferedAmountConsumption();
void ReleaseChannel();
// Called on web socket message activity (sending or receiving a message) that
// the execution context may want to handle, such as to extend its own
// lifetime.
void NotifyWebSocketActivity();
Member<WebSocketChannel> channel_;
WebSocketCommon common_;
String origin_string_;
uint64_t buffered_amount_;
// The consumed buffered amount that will be reflected to |buffered_amount_|
// later. It will be cleared once reflected.
uint64_t consumed_buffered_amount_;
uint64_t buffered_amount_after_close_;
BinaryType binary_type_;
// The subprotocol the server selected.
String subprotocol_;
String extensions_;
Member<EventQueue> event_queue_;
bool buffered_amount_update_task_pending_;
};
} // namespace blink
3、websocket.idl接口实现blink/v8:
out\Debug\gen\third_party\blink\renderer\bindings\modules\v8\v8_websocket.h
out\Debug\gen\third_party\blink\renderer\bindings\modules\v8\v8_websocket.cc
截取部分 websocket.send,更多参考v8_websocket.cc
cpp
void SendOperationOverload1(const v8::FunctionCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
const ExceptionContextType exception_context_type = ExceptionContextType::kOperationInvoke;
const char* const class_like_name = "WebSocket";
const char* const property_name = "send";
ExceptionState exception_state(isolate, exception_context_type, class_like_name, property_name);
if (UNLIKELY(info.Length() < 1)) {
exception_state.ThrowTypeError(ExceptionMessages::NotEnoughArguments(1, info.Length()));
return;
}
v8::Local<v8::Object> v8_receiver = info.This();
DOMWebSocket* blink_receiver = V8WebSocket::ToWrappableUnsafe(isolate, v8_receiver);
auto&& arg1_data = NativeValueTraits<IDLUSVString>::ArgumentValue(isolate, 0, info[0], exception_state);
if (UNLIKELY(exception_state.HadException())) {
return;
}
blink_receiver->send(arg1_data, exception_state);
if (UNLIKELY(exception_state.HadException())) {
return;
}
}
三、websocket网络进程实现接口:
1、websocket.mojom接口定义:
services\network\public\mojom\websocket.mojom
作用:用来与render进程window.socket前端接口通信收发数据等。
cpp
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
module network.mojom;
import "url/mojom/url.mojom";
import "services/network/public/mojom/network_param.mojom";
import "services/network/public/mojom/ip_endpoint.mojom";
enum WebSocketMessageType {
CONTINUATION,
TEXT,
BINARY,
LAST = BINARY
};
// TODO(darin): Move to a more general location.
struct HttpHeader {
string name;
string value;
};
// TODO(darin): Remove redundancy b/w |headers| and |headers_text|.
struct WebSocketHandshakeRequest {
url.mojom.Url url;
array<HttpHeader> headers;
string headers_text;
};
struct WebSocketHandshakeResponse {
url.mojom.Url url;
HttpVersion http_version;
int32 status_code;
string status_text;
IPEndPoint remote_endpoint;
array<HttpHeader> headers;
string headers_text;
// Sub-protocol the server selected, or empty if no sub-protocol was selected.
string selected_protocol;
// The list of extensions negotiated for the connection.
string extensions;
};
// This interface is for HTTP Authentication.
interface WebSocketAuthenticationHandler {
// Returns null credentials when it wants to cancel authentication, and
// returns a non-null credentials when it wants to use the credentials for
// authentication.
OnAuthRequired(AuthChallengeInfo info,
HttpResponseHeaders headers,
IPEndPoint remote_endpoint) => (AuthCredentials? credentials);
};
// This interface is for client-side WebSocket handshake. Used to initialize
// the WebSocket Connection.
interface WebSocketHandshakeClient {
// Notify the renderer that the browser has started an opening handshake.
OnOpeningHandshakeStarted(WebSocketHandshakeRequest request);
// Called when the WebSocket connection has failed.
// |message| may contain a human-readable explanation of the error, but may be
// empty.
// |net_error| contains a network error code, which will be |ERR_FAILED| for
// WebSocket-level protocol errors that do not have their own error code.
// |response_code| contains the HTTP status code that caused the failure, if
// it was caused by an unexpected status code, or else is -1.
OnFailure(string message, int32 net_error, int32 response_code);
// Called when the connection is established.
// |response| may contain cookie-related headers when the client has
// an access to raw cookie information.
// |readable| is readable datapipe to receive data from network service.
// |writable| is writable datapipe used to transfer the actual content of the
// message(data) to the network service. The network services later sends out
// the actual message by framing each message from the meta-info given from
// the renderer side with |SendMessage()|.
OnConnectionEstablished(pending_remote<WebSocket> socket,
pending_receiver<WebSocketClient> client_receiver,
WebSocketHandshakeResponse response,
handle<data_pipe_consumer> readable,
handle<data_pipe_producer> writable);
};
// The interface for the client side of WebSocket. Implemented by renderer
// processes to receive messages from the network service.
interface WebSocketClient {
// Receive a non-control frame from the remote server.
// - |fin| indicates that this frame is the last in the current message.
// - |type| is the type of the message. On the first frame of a message, it
// must be set to either WebSocketMessageType.TEXT or
// WebSocketMessageType.BINARY. On subsequent frames, it must be set to
// WebSocketMessageType.CONTINUATION, and the type is the same as that of
// the first message.
// - |data_length| is the length of data and actual data is read via
// |readable| on WebSocketHandshakeClient.OnConnectionEstablished.
// If |type| is WebSocketMessageType.TEXT, then the concatenation of all
// the frames in the message must be valid UTF-8.
// If |fin| is not set, |data_length| must be non-zero.
OnDataFrame(bool fin,
WebSocketMessageType type,
uint64 data_length);
// Drop the channel.
//
// When sent by the renderer, this will cause a Close message will be sent and
// the TCP/IP connection will be closed.
//
// When sent by the browser, this indicates that a Close has been received,
// the connection was closed, or a network or protocol error occurred.
//
// - |code| is one of the reason codes specified in RFC6455.
// - |reason|, if non-empty, is a UTF-8 encoded string which may be useful
// for debugging but is not necessarily human-readable, as supplied by the
// server in the Close message.
// - If |was_clean| is false, then the WebSocket connection was not closed
// cleanly.
OnDropChannel(bool was_clean, uint16 code, string reason);
// Notify the renderer that a closing handshake has been initiated by the
// server, so that it can set the Javascript readyState to CLOSING.
OnClosingHandshake();
};
// The interface for the server side of WebSocket. Implemented by the network
// service. Used to send out data to the network service.
interface WebSocket {
// Sends a message via mojo datapipe to the remote server.
// - |type| is the type of the message. It must be set to either
// WebSocketMessageType.TEXT or WebSocketMessageType.BINARY.
// - |data_length| is the actual length of message. The message is written to
// the datapipe named |writable| in the
// WebSocketHandshakeClient.OnConnectionEstablished message.
//
// If |type| is WebSocketMessageType.TEXT, then the message must be
// valid UTF-8.
SendMessage(WebSocketMessageType type, uint64 data_length);
// Let browser to start receiving WebSocket data frames from network stream.
// TODO(yoichio): Remove this by move Connect() after checking throttle at
// WebSocketChannelImpl::Connect so that OnAddChannelResponse is
// actual signal to start receive data frame.
StartReceiving();
// Close the channel gracefully.
//
// When sent by the renderer, this will cause a Close message will be sent and
// the TCP/IP connection will be closed.
//
// - |code| is one of the reason codes specified in RFC6455.
// - |reason|, if non-empty, is a UTF-8 encoded string which may be useful for
// debugging but is not necessarily human-readable, as supplied by the
// server in the Close message.
StartClosingHandshake(uint16 code, string reason);
};
2、websocket.mojom接口实现:
out\Debug\gen\services\network\public\mojom\websocket.mojom.cc
out\Debug\gen\services\network\public\mojom\websocket.mojom.h
2.1)、数据发送接口SendMessage:
2.1.1)、数据发送接口发送端WebSocketProxy::SendMessage
//message.set_method_name("SendMessage");c
cpp
void WebSocketProxy::SendMessage(
WebSocketMessageType in_type, uint64_t in_data_length) {
#if BUILDFLAG(MOJO_TRACE_ENABLED)
TRACE_EVENT1(
"mojom", "Send network::mojom::WebSocket::SendMessage", "input_parameters",
[&](perfetto::TracedValue context){
auto dict = std::move(context).WriteDictionary();
perfetto::WriteIntoTracedValueWithFallback(
dict.AddItem("type"), in_type,
"<value of type WebSocketMessageType>");
perfetto::WriteIntoTracedValueWithFallback(
dict.AddItem("data_length"), in_data_length,
"<value of type uint64_t>");
});
#endif
const bool kExpectsResponse = false;
const bool kIsSync = false;
const bool kAllowInterrupt = true;
const bool is_urgent = false;
const uint32_t kFlags =
((kExpectsResponse) ? mojo::Message::kFlagExpectsResponse : 0) |
((kIsSync) ? mojo::Message::kFlagIsSync : 0) |
((kAllowInterrupt) ? 0 : mojo::Message::kFlagNoInterrupt) |
((is_urgent) ? mojo::Message::kFlagIsUrgent : 0);
mojo::Message message(
internal::kWebSocket_SendMessage_Name, kFlags, 0, 0, nullptr);
mojo::internal::MessageFragment<
::network::mojom::internal::WebSocket_SendMessage_Params_Data> params(
message);
params.Allocate();
mojo::internal::Serialize<::network::mojom::WebSocketMessageType>(
in_type, ¶ms->type);
params->data_length = in_data_length;
#if defined(ENABLE_IPC_FUZZER)
message.set_interface_name(WebSocket::Name_);
message.set_method_name("SendMessage");
#endif
// This return value may be ignored as false implies the Connector has
// encountered an error, which will be visible through other means.
::mojo::internal::SendMojoMessage(*receiver_, message);
}
2.1.2)、数据发送接口接收端实现:
impl->SendMessage(std::move(p_type),std::move(p_data_length));
cpp
// static
bool WebSocketStubDispatch::Accept(
WebSocket* impl,
mojo::Message* message) {
switch (message->header()->name) {
case internal::kWebSocket_SendMessage_Name: {
DCHECK(message->is_serialized());
internal::WebSocket_SendMessage_Params_Data* params =
reinterpret_cast<internal::WebSocket_SendMessage_Params_Data*>(
message->mutable_payload());
bool success = true;
WebSocketMessageType p_type{};
uint64_t p_data_length{};
WebSocket_SendMessage_ParamsDataView input_data_view(params, message);
if (success && !input_data_view.ReadType(&p_type))
success = false;
if (success)
p_data_length = input_data_view.data_length();
if (!success) {
ReportValidationErrorForMessage(
message,
mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED,
WebSocket::Name_, 0, false);
return false;
}
// A null |impl| means no implementation was bound.
DCHECK(impl);
impl->SendMessage(
std::move(p_type),
std::move(p_data_length));
return true;
}
.........................................
}
2.2)、数据接收接口OnDataFrame
2.2.1)、数据接收接口 发送端OnDataFrame
WebSocketClientProxy::OnDataFrame
message.set_method_name("OnDataFrame");
cpp
void WebSocketClientProxy::OnDataFrame(
bool in_fin, WebSocketMessageType in_type, uint64_t in_data_length) {
#if BUILDFLAG(MOJO_TRACE_ENABLED)
TRACE_EVENT1(
"mojom", "Send network::mojom::WebSocketClient::OnDataFrame", "input_parameters",
[&](perfetto::TracedValue context){
auto dict = std::move(context).WriteDictionary();
perfetto::WriteIntoTracedValueWithFallback(
dict.AddItem("fin"), in_fin,
"<value of type bool>");
perfetto::WriteIntoTracedValueWithFallback(
dict.AddItem("type"), in_type,
"<value of type WebSocketMessageType>");
perfetto::WriteIntoTracedValueWithFallback(
dict.AddItem("data_length"), in_data_length,
"<value of type uint64_t>");
});
#endif
const bool kExpectsResponse = false;
const bool kIsSync = false;
const bool kAllowInterrupt = true;
const bool is_urgent = false;
const uint32_t kFlags =
((kExpectsResponse) ? mojo::Message::kFlagExpectsResponse : 0) |
((kIsSync) ? mojo::Message::kFlagIsSync : 0) |
((kAllowInterrupt) ? 0 : mojo::Message::kFlagNoInterrupt) |
((is_urgent) ? mojo::Message::kFlagIsUrgent : 0);
mojo::Message message(
internal::kWebSocketClient_OnDataFrame_Name, kFlags, 0, 0, nullptr);
mojo::internal::MessageFragment<
::network::mojom::internal::WebSocketClient_OnDataFrame_Params_Data> params(
message);
params.Allocate();
params->fin = in_fin;
mojo::internal::Serialize<::network::mojom::WebSocketMessageType>(
in_type, ¶ms->type);
params->data_length = in_data_length;
#if defined(ENABLE_IPC_FUZZER)
message.set_interface_name(WebSocketClient::Name_);
message.set_method_name("OnDataFrame");
#endif
// This return value may be ignored as false implies the Connector has
// encountered an error, which will be visible through other means.
::mojo::internal::SendMojoMessage(*receiver_, message);
}
2.2.2)、数据接收接口 接收端OnDataFrame
impl->OnDataFrame(std::move(p_fin), std::move(p_type), std::move(p_data_length));
cpp
// static
bool WebSocketClientStubDispatch::Accept(
WebSocketClient* impl,
mojo::Message* message) {
switch (message->header()->name) {
case internal::kWebSocketClient_OnDataFrame_Name: {
DCHECK(message->is_serialized());
internal::WebSocketClient_OnDataFrame_Params_Data* params =
reinterpret_cast<internal::WebSocketClient_OnDataFrame_Params_Data*>(
message->mutable_payload());
bool success = true;
bool p_fin{};
WebSocketMessageType p_type{};
uint64_t p_data_length{};
WebSocketClient_OnDataFrame_ParamsDataView input_data_view(params, message);
if (success)
p_fin = input_data_view.fin();
if (success && !input_data_view.ReadType(&p_type))
success = false;
if (success)
p_data_length = input_data_view.data_length();
if (!success) {
ReportValidationErrorForMessage(
message,
mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED,
WebSocketClient::Name_, 0, false);
return false;
}
// A null |impl| means no implementation was bound.
DCHECK(impl);
impl->OnDataFrame(
std::move(p_fin),
std::move(p_type),
std::move(p_data_length));
return true;
}
。。。。。。。。。。。。。。。。。。。。。。。。。。。
}
3、websocket.mojom接口 network实现**[net进程]**:
services\network\websocket.h
services\network\websocket.cc
WebSocket::SendMessage和WebSocket::WebSocketEventHandler::OnDataFrame
与render进程的WebSocketProxy::SendMessage()和WebSocketClientStubDispatch::OnDataFrame相互对应。
cpp
class GURL;
namespace base {
class Location;
} // namespace base
namespace net {
class IOBuffer;
class IsolationInfo;
class SSLInfo;
class SiteForCookies;
class WebSocketChannel;
} // namespace net
namespace network {
class WebSocketFactory;
// Host of net::WebSocketChannel.
class COMPONENT_EXPORT(NETWORK_SERVICE) WebSocket : public mojom::WebSocket {
public:
using HasRawHeadersAccess =
base::StrongAlias<class HasRawHeadersAccessTag, bool>;
WebSocket(
WebSocketFactory* factory,
const GURL& url,
const std::vector<std::string>& requested_protocols,
const net::SiteForCookies& site_for_cookies,
bool has_storage_access,
const net::IsolationInfo& isolation_info,
std::vector<mojom::HttpHeaderPtr> additional_headers,
const url::Origin& origin,
uint32_t options,
net::NetworkTrafficAnnotationTag traffic_annotation,
HasRawHeadersAccess has_raw_cookie_access,
mojo::PendingRemote<mojom::WebSocketHandshakeClient> handshake_client,
mojo::PendingRemote<mojom::URLLoaderNetworkServiceObserver>
url_loader_network_observer,
mojo::PendingRemote<mojom::WebSocketAuthenticationHandler> auth_handler,
mojo::PendingRemote<mojom::TrustedHeaderClient> header_client,
absl::optional<WebSocketThrottler::PendingConnection>
pending_connection_tracker,
base::TimeDelta delay,
const absl::optional<base::UnguessableToken>& throttling_profile_id);
WebSocket(const WebSocket&) = delete;
WebSocket& operator=(const WebSocket&) = delete;
~WebSocket() override;
// mojom::WebSocket methods:
void SendMessage(mojom::WebSocketMessageType type,
uint64_t data_length) override;
void StartReceiving() override;
void StartClosingHandshake(uint16_t code, const std::string& reason) override;
// Whether to allow sending/setting cookies during WebSocket handshakes for
// |url|. This decision is based on the |options_| and |origin_| this
// WebSocket was created with.
bool AllowCookies(const GURL& url) const;
// These methods are called by the network delegate to forward these events to
// the |header_client_|.
int OnBeforeStartTransaction(
const net::HttpRequestHeaders& headers,
net::NetworkDelegate::OnBeforeStartTransactionCallback callback);
int OnHeadersReceived(
net::CompletionOnceCallback callback,
const net::HttpResponseHeaders* original_response_headers,
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
absl::optional<GURL>* preserve_fragment_on_redirect_url);
// Gets the WebSocket associated with this request.
static WebSocket* ForRequest(const net::URLRequest& request);
static const void* const kUserDataKey;
private:
class WebSocketEventHandler;
struct CloseInfo;
// This class is used to set the WebSocket as user data on a URLRequest. This
// is used instead of WebSocket directly because SetUserData requires a
// std::unique_ptr. This is safe because WebSocket owns the URLRequest, so is
// guaranteed to outlive it.
class UnownedPointer : public base::SupportsUserData::Data {
public:
explicit UnownedPointer(WebSocket* pointer) : pointer_(pointer) {}
UnownedPointer(const UnownedPointer&) = delete;
UnownedPointer& operator=(const UnownedPointer&) = delete;
WebSocket* get() const { return pointer_; }
private:
const raw_ptr<WebSocket> pointer_;
};
struct DataFrame final {
DataFrame(mojom::WebSocketMessageType type,
uint64_t data_length,
bool do_not_fragment)
: type(type),
data_length(data_length),
do_not_fragment(do_not_fragment) {}
mojom::WebSocketMessageType type;
uint64_t data_length;
const bool do_not_fragment;
};
void OnConnectionError(const base::Location& set_from);
void AddChannel(const GURL& socket_url,
const std::vector<std::string>& requested_protocols,
const net::SiteForCookies& site_for_cookies,
bool has_storage_access,
const net::IsolationInfo& isolation_info,
std::vector<mojom::HttpHeaderPtr> additional_headers);
void OnSSLCertificateErrorResponse(
std::unique_ptr<net::WebSocketEventInterface::SSLErrorCallbacks>
callbacks,
const net::SSLInfo& ssl_info,
int net_error);
void OnAuthRequiredComplete(
base::OnceCallback<void(const net::AuthCredentials*)> callback,
const absl::optional<net::AuthCredentials>& credential);
void OnBeforeSendHeadersComplete(
net::NetworkDelegate::OnBeforeStartTransactionCallback callback,
int result,
const absl::optional<net::HttpRequestHeaders>& headers);
void OnHeadersReceivedComplete(
net::CompletionOnceCallback callback,
scoped_refptr<net::HttpResponseHeaders>* out_headers,
absl::optional<GURL>* out_preserve_fragment_on_redirect_url,
int result,
const absl::optional<std::string>& headers,
const absl::optional<GURL>& preserve_fragment_on_redirect_url);
void Reset();
enum class InterruptionReason {
// Not interrupted or not resuming after interruptions (but processing a
// brand new frame)
kNone,
// Interrupted by empty Mojo pipe or resuming afterwards
kMojoPipe,
// Interrupted by the interceptor or resuming afterwards
kInterceptor,
};
// Datapipe functions to receive.
void OnWritable(MojoResult result, const mojo::HandleSignalsState& state);
void SendPendingDataFrames(InterruptionReason resume_reason);
void SendDataFrame(base::span<const char>* data_span);
// Datapipe functions to send.
void OnReadable(MojoResult result, const mojo::HandleSignalsState& state);
void ReadAndSendFromDataPipe(InterruptionReason resume_reason);
// This helper method only called from ReadAndSendFromDataPipe.
// Note that it may indirectly delete |this|.
// Returns true if the frame has been sent completely.
bool ReadAndSendFrameFromDataPipe(DataFrame* data_frame);
void ResumeDataPipeReading();
// |factory_| owns |this|.
const raw_ptr<WebSocketFactory> factory_;
mojo::Receiver<mojom::WebSocket> receiver_{this};
mojo::Remote<mojom::URLLoaderNetworkServiceObserver>
url_loader_network_observer_;
mojo::Remote<mojom::WebSocketHandshakeClient> handshake_client_;
mojo::Remote<mojom::WebSocketClient> client_;
mojo::Remote<mojom::WebSocketAuthenticationHandler> auth_handler_;
mojo::Remote<mojom::TrustedHeaderClient> header_client_;
absl::optional<WebSocketThrottler::PendingConnection>
pending_connection_tracker_;
// The channel we use to send events to the network.
std::unique_ptr<net::WebSocketChannel> channel_;
// Delay used for per-renderer WebSocket throttling.
const base::TimeDelta delay_;
const uint32_t options_;
const net::NetworkTrafficAnnotationTag traffic_annotation_;
// The web origin to use for the WebSocket.
const url::Origin origin_;
// For 3rd-party cookie permission checking.
net::SiteForCookies site_for_cookies_;
bool handshake_succeeded_ = false;
const HasRawHeadersAccess has_raw_headers_access_;
InterruptionReason incoming_frames_interrupted_ = InterruptionReason::kNone;
InterruptionReason outgoing_frames_interrupted_ = InterruptionReason::kNone;
// Datapipe fields to receive.
mojo::ScopedDataPipeProducerHandle writable_;
mojo::SimpleWatcher writable_watcher_;
base::queue<base::span<const char>> pending_data_frames_;
// Datapipe fields to send.
mojo::ScopedDataPipeConsumerHandle readable_;
mojo::SimpleWatcher readable_watcher_;
base::queue<DataFrame> pending_send_data_frames_;
bool blocked_on_websocket_channel_ = false;
// True if we should preserve the old behaviour where <=64KB messages were
// never fragmented.
// TODO(ricea): Remove the flag once we know whether we really need this or
// not. See https://crbug.com/1086273.
const bool reassemble_short_messages_;
// Temporary buffer for storage of short messages that have been fragmented by
// the data pipe. Only messages that are actually fragmented are copied into
// here.
scoped_refptr<net::IOBuffer> message_under_reassembly_;
// Number of bytes that have been written to |message_under_reassembly_| so
// far.
size_t bytes_reassembled_ = 0;
// Set when StartClosingHandshake() is called while
// |pending_send_data_frames_| is non-empty. This can happen due to a race
// condition between the readable signal on the data pipe and the channel on
// which StartClosingHandshake() is called.
std::unique_ptr<CloseInfo> pending_start_closing_handshake_;
const absl::optional<base::UnguessableToken> throttling_profile_id_;
uint32_t net_log_source_id_ = net::NetLogSource::kInvalidId;
std::unique_ptr<WebSocketInterceptor> frame_interceptor_;
base::WeakPtrFactory<WebSocket> weak_ptr_factory_{this};
};
} // namespace network
4、websocket.mojom接口 network实现**[render进程]**:
third_party\blink\renderer\modules\websockets\websocket_channel_impl.h
third_party\blink\renderer\modules\websockets\websocket_channel_impl.cc
HeapMojoRemote<network::mojom::blink::WebSocket> websocket_;//WebSocketProxy对象定义:
WebSocketProxy::SendMessage()和WebSocketClientStubDispatch::OnDataFrame
是与net进程 发送和接收数据的具体实现。
cpp
namespace v8 {
class Isolate;
} // namespace v8
namespace blink {
class BaseFetchContext;
enum class FileErrorCode;
class WebSocketChannelClient;
class WebSocketHandshakeThrottle;
// This is an implementation of WebSocketChannel. This is created on the main
// thread for Document, or on the worker thread for WorkerGlobalScope. All
// functions must be called on the execution context's thread.
class MODULES_EXPORT WebSocketChannelImpl final
: public WebSocketChannel,
public network::mojom::blink::WebSocketHandshakeClient,
public network::mojom::blink::WebSocketClient {
USING_PRE_FINALIZER(WebSocketChannelImpl, Dispose);
public:
// Public for use in tests.
static constexpr size_t kMaxWebSocketsPerRenderProcess = 255u;
// You can specify the source file and the line number information
// explicitly by passing the last parameter.
// In the usual case, they are set automatically and you don't have to
// pass it.
static WebSocketChannelImpl* Create(ExecutionContext* context,
WebSocketChannelClient* client,
std::unique_ptr<SourceLocation> location);
static WebSocketChannelImpl* CreateForTesting(
ExecutionContext*,
WebSocketChannelClient*,
std::unique_ptr<SourceLocation>,
std::unique_ptr<WebSocketHandshakeThrottle>);
WebSocketChannelImpl(ExecutionContext*,
WebSocketChannelClient*,
std::unique_ptr<SourceLocation>);
~WebSocketChannelImpl() override;
// WebSocketChannel functions.
bool Connect(const KURL&, const String& protocol) override;
SendResult Send(const std::string& message,
base::OnceClosure completion_callback) override;
SendResult Send(const DOMArrayBuffer&,
size_t byte_offset,
size_t byte_length,
base::OnceClosure completion_callback) override;
void Send(scoped_refptr<BlobDataHandle>) override;
// Start closing handshake. Use the CloseEventCodeNotSpecified for the code
// argument to omit payload.
void Close(int code, const String& reason) override;
void Fail(const String& reason,
mojom::ConsoleMessageLevel,
std::unique_ptr<SourceLocation>) override;
void Disconnect() override;
void CancelHandshake() override;
void ApplyBackpressure() override;
void RemoveBackpressure() override;
// network::mojom::blink::WebSocketHandshakeClient methods:
void OnOpeningHandshakeStarted(
network::mojom::blink::WebSocketHandshakeRequestPtr) override;
void OnFailure(const WTF::String& message,
int net_error,
int response_code) override;
void OnConnectionEstablished(
mojo::PendingRemote<network::mojom::blink::WebSocket> websocket,
mojo::PendingReceiver<network::mojom::blink::WebSocketClient>
client_receiver,
network::mojom::blink::WebSocketHandshakeResponsePtr,
mojo::ScopedDataPipeConsumerHandle readable,
mojo::ScopedDataPipeProducerHandle writable) override;
// network::mojom::blink::WebSocketClient methods:
void OnDataFrame(bool fin,
network::mojom::blink::WebSocketMessageType,
uint64_t data_length) override;
void OnDropChannel(bool was_clean,
uint16_t code,
const String& reason) override;
void OnClosingHandshake() override;
void Trace(Visitor*) const override;
private:
struct DataFrame final {
DataFrame(bool fin,
network::mojom::blink::WebSocketMessageType type,
uint32_t data_length)
: fin(fin), type(type), data_length(data_length) {}
bool fin;
network::mojom::blink::WebSocketMessageType type;
uint32_t data_length;
};
// Used by BlobLoader and Message, so defined here so that it can be shared.
class MessageDataDeleter {
public:
// This constructor exists to permit default construction of the MessageData
// type, but the deleter cannot be called when it was used.
MessageDataDeleter() : isolate_(nullptr), size_(0) {}
MessageDataDeleter(v8::Isolate* isolate, size_t size)
: isolate_(isolate), size_(size) {}
MessageDataDeleter(const MessageDataDeleter&) = default;
MessageDataDeleter& operator=(const MessageDataDeleter&) = default;
void operator()(char* p) const;
private:
v8::Isolate* isolate_;
size_t size_;
};
using MessageData = std::unique_ptr<char[], MessageDataDeleter>;
static MessageData CreateMessageData(v8::Isolate*, size_t);
friend class WebSocketChannelImplHandshakeThrottleTest;
FRIEND_TEST_ALL_PREFIXES(WebSocketChannelImplHandshakeThrottleTest,
ThrottleSucceedsFirst);
FRIEND_TEST_ALL_PREFIXES(WebSocketChannelImplHandshakeThrottleTest,
HandshakeSucceedsFirst);
FRIEND_TEST_ALL_PREFIXES(WebSocketChannelImplHandshakeThrottleTest,
ThrottleReportsErrorBeforeConnect);
FRIEND_TEST_ALL_PREFIXES(WebSocketChannelImplHandshakeThrottleTest,
ThrottleReportsErrorAfterConnect);
class BlobLoader;
class Message;
struct ConnectInfo;
enum MessageType {
kMessageTypeText,
kMessageTypeBlob,
kMessageTypeArrayBuffer,
kMessageTypeClose,
};
struct ReceivedMessage {
bool is_message_text;
Vector<char> data;
};
class Message final {
DISALLOW_NEW();
public:
using DidCallSendMessage =
base::StrongAlias<class DidCallSendMessageTag, bool>;
// Initializes message as a string
Message(v8::Isolate*,
const std::string&,
base::OnceClosure completion_callback,
DidCallSendMessage did_call_send_message);
// Initializes message as a blob
explicit Message(scoped_refptr<BlobDataHandle>);
// Initializes message from the contents of a blob
Message(MessageData, size_t);
// Initializes message as a ArrayBuffer
Message(v8::Isolate*,
base::span<const char> message,
base::OnceClosure completion_callback,
DidCallSendMessage did_call_send_message);
// Initializes a Blank message
Message(MessageType type,
base::span<const char> message,
base::OnceClosure completion_callback);
// Close message
Message(uint16_t code, const String& reason);
Message(const Message&) = delete;
Message& operator=(const Message&) = delete;
Message(Message&&);
Message& operator=(Message&&);
MessageType Type() const;
scoped_refptr<BlobDataHandle> GetBlobDataHandle();
DidCallSendMessage GetDidCallSendMessage() const;
uint16_t Code() const;
String Reason() const;
base::OnceClosure CompletionCallback();
// Returns a mutable |pending_payload_|. Since calling code always mutates
// the value, |pending_payload_| only has a mutable getter.
base::span<const char>& MutablePendingPayload();
void SetDidCallSendMessage(DidCallSendMessage did_call_send_message);
private:
MessageData message_data_;
MessageType type_;
scoped_refptr<BlobDataHandle> blob_data_handle_;
base::span<const char> pending_payload_;
DidCallSendMessage did_call_send_message_ = DidCallSendMessage(false);
uint16_t code_ = 0;
String reason_;
base::OnceClosure completion_callback_;
};
// A handle to a global count of the number of WebSockets that have been
// created. Can be used to limit the total number of WebSockets that have been
// created in this render process.
class ConnectionCountTrackerHandle {
DISALLOW_NEW();
public:
enum class CountStatus {
kOkayToConnect,
kShouldNotConnect,
};
ConnectionCountTrackerHandle() = default;
~ConnectionCountTrackerHandle() = default;
ConnectionCountTrackerHandle(const ConnectionCountTrackerHandle&) = delete;
ConnectionCountTrackerHandle& operator=(
const ConnectionCountTrackerHandle&) = delete;
// Increments the count and returns SHOULD_NOT_CONNECT if it exceeds
// kMaxWebSocketsPerRenderProcess. Should only be called once.
CountStatus IncrementAndCheckStatus();
// Decrements the count. Should be called at least once. If there is no
// matching call to IncrementAndCheckStatus() it does nothing, so it is safe
// to call multiple times.
void Decrement();
private:
bool incremented_ = false;
};
// The state is defined to see the conceptual state more clearly than checking
// various members (for DCHECKs for example). This is only used internally.
enum class State {
// The channel is running an opening handshake. This is the initial state.
// It becomes |kOpen| when the connection is established. It becomes
// |kDisconnected| when detecting an error.
kConnecting,
// The channel is ready to send / receive messages. It becomes
// |kDisconnected| when the connection is closed or when an error happens.
kOpen,
// The channel is not ready for communication. The channel stays in this
// state forever.
kDisconnected,
};
State GetState() const;
bool MaybeSendSynchronously(network::mojom::blink::WebSocketMessageType,
base::span<const char>* data);
void ProcessSendQueue();
bool SendMessageData(base::span<const char>* data);
void FailAsError(const String& reason) {
Fail(reason, mojom::ConsoleMessageLevel::kError,
location_at_construction_->Clone());
}
void AbortAsyncOperations();
void HandleDidClose(bool was_clean, uint16_t code, const String& reason);
// Completion callback. It is called with the results of throttling.
void OnCompletion(const absl::optional<WebString>& error);
// Methods for BlobLoader.
void DidFinishLoadingBlob(MessageData, size_t);
void BlobTooLarge();
void DidFailLoadingBlob(FileErrorCode);
void TearDownFailedConnection();
bool ShouldDisallowConnection(const KURL&);
BaseFetchContext* GetBaseFetchContext() const;
// Called when |readable_| becomes readable.
void OnReadable(MojoResult result, const mojo::HandleSignalsState& state);
void ConsumePendingDataFrames();
void ConsumeDataFrame(bool fin,
network::mojom::blink::WebSocketMessageType type,
const char* data,
size_t data_size);
// Called when |writable_| becomes writable.
void OnWritable(MojoResult result, const mojo::HandleSignalsState& state);
MojoResult ProduceData(base::span<const char>* data,
uint64_t* consumed_buffered_amount);
String GetTextMessage(const Vector<base::span<const char>>& chunks,
wtf_size_t size);
void OnConnectionError(const base::Location& set_from,
uint32_t custom_reason,
const std::string& description);
void Dispose();
const Member<WebSocketChannelClient> client_;
KURL url_;
uint64_t identifier_;
Member<BlobLoader> blob_loader_;
WTF::Deque<Message> messages_;
Member<WebSocketMessageChunkAccumulator> message_chunks_;
const Member<ExecutionContext> execution_context_;
bool backpressure_ = false;
bool receiving_message_type_is_text_ = false;
bool received_text_is_all_ascii_ = true;
bool throttle_passed_ = false;
bool has_initiated_opening_handshake_ = false;
size_t sent_size_of_top_message_ = 0;
FrameScheduler::SchedulingAffectingFeatureHandle
feature_handle_for_scheduler_;
WTF::String failure_message_;
const std::unique_ptr<const SourceLocation> location_at_construction_;
network::mojom::blink::WebSocketHandshakeRequestPtr handshake_request_;
std::unique_ptr<WebSocketHandshakeThrottle> handshake_throttle_;
// This field is only initialised if the object is still waiting for a
// throttle response when DidConnect is called.
std::unique_ptr<ConnectInfo> connect_info_;
HeapMojoRemote<network::mojom::blink::WebSocket> websocket_;
HeapMojoReceiver<network::mojom::blink::WebSocketHandshakeClient,
WebSocketChannelImpl>
handshake_client_receiver_;
HeapMojoReceiver<network::mojom::blink::WebSocketClient, WebSocketChannelImpl>
client_receiver_;
mojo::ScopedDataPipeConsumerHandle readable_;
mojo::SimpleWatcher readable_watcher_;
WTF::Deque<DataFrame> pending_data_frames_;
mojo::ScopedDataPipeProducerHandle writable_;
mojo::SimpleWatcher writable_watcher_;
bool wait_for_writable_ = false;
ConnectionCountTrackerHandle connection_count_tracker_handle_;
const scoped_refptr<base::SingleThreadTaskRunner> file_reading_task_runner_;
};
MODULES_EXPORT std::ostream& operator<<(std::ostream&,
const WebSocketChannelImpl*);
} // namespace blink