blob: 67b6229978676e2a32e8dc8fbde40361c802b393 [file] [log] [blame]
/*
* 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/span.h"
#include "base/memory/scoped_refptr.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/bindings/core/v8/source_location.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/heap/handle.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/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 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:
// 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;
};
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(const std::string&,
base::OnceClosure completion_callback,
DidCallSendMessage did_call_send_message);
// Initializes message as a blob
explicit Message(scoped_refptr<BlobDataHandle>);
// Initializes message as a ArrayBuffer
Message(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:
struct MessageDataDeleter {
void operator()(char* p) const { WTF::Partitions::FastFree(p); }
};
using MessageData = std::unique_ptr<char[], MessageDataDeleter>;
static MessageData CreateMessageData(std::size_t message_size) {
return MessageData(static_cast<char*>(WTF::Partitions::FastMalloc(
message_size, "blink::WebSockChannelImpl::Message::MessageData")));
}
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_;
};
// 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 base::Optional<WebString>& error);
// Methods for BlobLoader.
void DidFinishLoadingBlob(DOMArrayBuffer*);
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_;
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,
HeapMojoWrapperMode::kWithoutContextObserver>
websocket_;
HeapMojoReceiver<network::mojom::blink::WebSocketHandshakeClient,
WebSocketChannelImpl,
HeapMojoWrapperMode::kWithoutContextObserver>
handshake_client_receiver_;
HeapMojoReceiver<network::mojom::blink::WebSocketClient,
WebSocketChannelImpl,
HeapMojoWrapperMode::kWithoutContextObserver>
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;
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_