| /* |
| * Copyright (C) 2013 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBSOCKETS_WEBSOCKET_CHANNEL_IMPL_H_ |
| #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBSOCKETS_WEBSOCKET_CHANNEL_IMPL_H_ |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/containers/heap_array.h" |
| #include "base/containers/span.h" |
| #include "base/gtest_prod_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/raw_ptr_exclusion.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "mojo/public/cpp/bindings/pending_receiver.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "mojo/public/cpp/system/simple_watcher.h" |
| #include "services/network/public/mojom/websocket.mojom-blink.h" |
| #include "third_party/blink/public/mojom/websockets/websocket_connector.mojom-blink-forward.h" |
| #include "third_party/blink/renderer/core/fileapi/blob.h" |
| #include "third_party/blink/renderer/modules/modules_export.h" |
| #include "third_party/blink/renderer/modules/websockets/websocket_channel.h" |
| #include "third_party/blink/renderer/modules/websockets/websocket_message_chunk_accumulator.h" |
| #include "third_party/blink/renderer/platform/bindings/source_location.h" |
| #include "third_party/blink/renderer/platform/bindings/v8_external_memory_accounter.h" |
| #include "third_party/blink/renderer/platform/heap/garbage_collected.h" |
| #include "third_party/blink/renderer/platform/heap/prefinalizer.h" |
| #include "third_party/blink/renderer/platform/mojo/heap_mojo_receiver.h" |
| #include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h" |
| #include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h" |
| #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h" |
| #include "third_party/blink/renderer/platform/weborigin/kurl.h" |
| #include "third_party/blink/renderer/platform/wtf/deque.h" |
| #include "third_party/blink/renderer/platform/wtf/gc_plugin.h" |
| #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" |
| #include "third_party/blink/renderer/platform/wtf/vector.h" |
| #include "third_party/blink/renderer/platform/wtf/wtf_size_t.h" |
| |
| 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: |
| using MessageTypeForMojo = network::mojom::blink::WebSocketMessageType; |
| |
| // 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, |
| SourceLocation* location); |
| static WebSocketChannelImpl* CreateForTesting( |
| ExecutionContext*, |
| WebSocketChannelClient*, |
| SourceLocation*, |
| std::unique_ptr<WebSocketHandshakeThrottle>); |
| |
| WebSocketChannelImpl(ExecutionContext*, |
| WebSocketChannelClient*, |
| SourceLocation*); |
| ~WebSocketChannelImpl() override; |
| |
| // WebSocketChannel functions. |
| bool Connect(const KURL&, const String& protocol) override; |
| void Send(const std::string& message, |
| std::unique_ptr<SendCompletionWatcher>) override; |
| void Send(const DOMArrayBuffer&, |
| size_t byte_offset, |
| size_t byte_length, |
| std::unique_ptr<SendCompletionWatcher>) 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, |
| 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 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, MessageTypeForMojo, 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, MessageTypeForMojo type, size_t data_length) |
| : fin(fin), type(type), data_length(data_length) {} |
| |
| bool fin; |
| MessageTypeForMojo type; |
| size_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); |
| |
| MessageDataDeleter(MessageDataDeleter&&) = default; |
| MessageDataDeleter& operator=(MessageDataDeleter&&) = default; |
| |
| void operator()(uint8_t* p) const; |
| |
| private: |
| raw_ptr<v8::Isolate> isolate_; |
| size_t size_; |
| NO_UNIQUE_ADDRESS mutable V8ExternalMemoryAccounterBase |
| external_memory_accounter_; |
| }; |
| |
| using MessageData = base::HeapArray<uint8_t, 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 blob |
| explicit Message(scoped_refptr<BlobDataHandle>); |
| |
| // Initializes message from the contents of a blob |
| explicit Message(MessageData); |
| |
| // Initializes message as a string or ArrayBuffer |
| Message(MessageType, |
| v8::Isolate*, |
| base::span<const uint8_t> message, |
| std::unique_ptr<SendCompletionWatcher>, |
| DidCallSendMessage did_call_send_message); |
| |
| // 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; |
| std::unique_ptr<SendCompletionWatcher> TakeSendCompletionWatcher(); |
| |
| // Returns a mutable `pending_payload_`. Since calling code always mutates |
| // the value, `pending_payload_` only has a mutable getter. |
| base::span<const uint8_t>& MutablePendingPayload(); |
| |
| void SetDidCallSendMessage(DidCallSendMessage did_call_send_message); |
| |
| private: |
| MessageData message_data_; |
| MessageType type_; |
| |
| scoped_refptr<BlobDataHandle> blob_data_handle_; |
| // TODO(crbug.com/367764863) Rewrite to base::raw_span. |
| RAW_PTR_EXCLUSION base::span<const uint8_t> pending_payload_; |
| DidCallSendMessage did_call_send_message_ = DidCallSendMessage(false); |
| uint16_t code_ = 0; |
| String reason_; |
| std::unique_ptr<SendCompletionWatcher> watcher_; |
| }; |
| |
| // 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; |
| |
| // Send a message for which the underlying data is available immediately. |
| // Anything which cannot be sent immediately is queued. |
| void SendFromMemory(MessageType, |
| base::span<const uint8_t> data, |
| std::unique_ptr<SendCompletionWatcher>); |
| |
| // Send as much of `data` as can be sent immediately. `data` will be modified |
| // to point to the remaining unsent data. Returns `true` if all of `data` was |
| // sent. |
| bool MaybeSendSynchronously(MessageTypeForMojo, |
| base::span<const uint8_t>* data); |
| void ProcessSendQueue(); |
| bool SendMessageData(base::span<const uint8_t>* 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 std::optional<WebString>& error); |
| |
| // Methods for BlobLoader. |
| void DidFinishLoadingBlob(MessageData); |
| 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, |
| MessageTypeForMojo type, |
| base::span<const uint8_t> data); |
| // Called when |writable_| becomes writable. |
| void OnWritable(MojoResult result, const mojo::HandleSignalsState& state); |
| MojoResult ProduceData(base::span<const uint8_t>* data, |
| uint64_t* consumed_buffered_amount); |
| String GetTextMessage(const Vector<base::span<const uint8_t>>& 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_; |
| 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_; |
| String failure_message_; |
| |
| const Member<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_; |
| 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 |
| |
| #endif // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBSOCKETS_WEBSOCKET_CHANNEL_IMPL_H_ |