| // Copyright 2013 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef NET_WEBSOCKETS_WEBSOCKET_BASIC_STREAM_H_ |
| #define NET_WEBSOCKETS_WEBSOCKET_BASIC_STREAM_H_ |
| |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "base/containers/queue.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/time/time.h" |
| #include "net/base/completion_once_callback.h" |
| #include "net/base/net_export.h" |
| #include "net/log/net_log_with_source.h" |
| #include "net/traffic_annotation/network_traffic_annotation.h" |
| #include "net/websockets/websocket_frame_parser.h" |
| #include "net/websockets/websocket_stream.h" |
| |
| namespace net { |
| |
| class ClientSocketHandle; |
| class DrainableIOBuffer; |
| class GrowableIOBuffer; |
| class IOBuffer; |
| class IOBufferWithSize; |
| struct WebSocketFrame; |
| struct WebSocketFrameChunk; |
| |
| // Implementation of WebSocketStream for non-multiplexed ws:// connections (or |
| // the physical side of a multiplexed ws:// connection). |
| // |
| // Please update the traffic annotations in the websocket_basic_stream.cc and |
| // websocket_stream.cc if the class is used for any communication with Google. |
| // In such a case, annotation should be passed from the callers to this class |
| // and a local annotation can not be used anymore. |
| class NET_EXPORT_PRIVATE WebSocketBasicStream final : public WebSocketStream { |
| public: |
| typedef WebSocketMaskingKey (*WebSocketMaskingKeyGeneratorFunction)(); |
| |
| enum class BufferSize : uint8_t { |
| kSmall, |
| kLarge, |
| }; |
| |
| // A class that calculates whether the associated WebSocketBasicStream |
| // should use a small buffer or large buffer, given the timing information |
| // or Read calls. This class is public for testing. |
| class NET_EXPORT_PRIVATE BufferSizeManager final { |
| public: |
| BufferSizeManager(); |
| BufferSizeManager(const BufferSizeManager&) = delete; |
| BufferSizeManager& operator=(const BufferSizeManager&) = delete; |
| ~BufferSizeManager(); |
| |
| // Called when the associated WebSocketBasicStream starts reading data |
| // into a buffer. |
| void OnRead(base::TimeTicks now); |
| |
| // Called when the Read operation completes. `size` must be positive. |
| void OnReadComplete(base::TimeTicks now, int size); |
| |
| // Returns the appropriate buffer size the associated WebSocketBasicStream |
| // should use. |
| BufferSize buffer_size() const { return buffer_size_; } |
| |
| // Set the rolling average window for tests. |
| void set_window_for_test(size_t size) { rolling_average_window_ = size; } |
| |
| private: |
| // This keeps the best read buffer size. |
| BufferSize buffer_size_ = BufferSize::kSmall; |
| |
| // The number of results to calculate the throughput. This is a variable so |
| // that unittests can set other values. |
| size_t rolling_average_window_ = 100; |
| |
| // This keeps the timestamps to calculate the throughput. |
| base::queue<base::TimeTicks> read_start_timestamps_; |
| |
| // The sum of the last few read size. |
| int rolling_byte_total_ = 0; |
| |
| // This keeps the read size. |
| base::queue<int> recent_read_sizes_; |
| }; |
| |
| // Adapter that allows WebSocketBasicStream to use |
| // either a TCP/IP or TLS socket, or an HTTP/2 stream. |
| class Adapter { |
| public: |
| virtual ~Adapter() = default; |
| virtual int Read(IOBuffer* buf, |
| int buf_len, |
| CompletionOnceCallback callback) = 0; |
| virtual int Write( |
| IOBuffer* buf, |
| int buf_len, |
| CompletionOnceCallback callback, |
| const NetworkTrafficAnnotationTag& traffic_annotation) = 0; |
| virtual void Disconnect() = 0; |
| virtual bool is_initialized() const = 0; |
| }; |
| |
| // This class should not normally be constructed directly; see |
| // WebSocketStream::CreateAndConnectStream() and |
| // WebSocketBasicHandshakeStream::Upgrade(). |
| WebSocketBasicStream(std::unique_ptr<Adapter> connection, |
| const scoped_refptr<GrowableIOBuffer>& http_read_buffer, |
| const std::string& sub_protocol, |
| const std::string& extensions, |
| const NetLogWithSource& net_log); |
| |
| // The destructor has to make sure the connection is closed when we finish so |
| // that it does not get returned to the pool. |
| ~WebSocketBasicStream() override; |
| |
| // WebSocketStream implementation. |
| int ReadFrames(std::vector<std::unique_ptr<WebSocketFrame>>* frames, |
| CompletionOnceCallback callback) override; |
| |
| int WriteFrames(std::vector<std::unique_ptr<WebSocketFrame>>* frames, |
| CompletionOnceCallback callback) override; |
| |
| void Close() override; |
| |
| std::string GetSubProtocol() const override; |
| |
| std::string GetExtensions() const override; |
| |
| const NetLogWithSource& GetNetLogWithSource() const override; |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // Methods for testing only. |
| |
| static std::unique_ptr<WebSocketBasicStream> |
| CreateWebSocketBasicStreamForTesting( |
| std::unique_ptr<ClientSocketHandle> connection, |
| const scoped_refptr<GrowableIOBuffer>& http_read_buffer, |
| const std::string& sub_protocol, |
| const std::string& extensions, |
| const NetLogWithSource& net_log, |
| WebSocketMaskingKeyGeneratorFunction key_generator_function); |
| |
| private: |
| // Reads until socket read returns asynchronously or returns error. |
| // If returns ERR_IO_PENDING, then |read_callback_| will be called with result |
| // later. |
| int ReadEverything(std::vector<std::unique_ptr<WebSocketFrame>>* frames); |
| |
| // Called when a read completes. Parses the result, tries to read more. |
| // Might call |read_callback_|. |
| void OnReadComplete(std::vector<std::unique_ptr<WebSocketFrame>>* frames, |
| int result); |
| |
| // Writes until |buffer| is fully drained (in which case returns OK) or a |
| // socket write returns asynchronously or returns an error. If returns |
| // ERR_IO_PENDING, then |write_callback_| will be called with result later. |
| int WriteEverything(const scoped_refptr<DrainableIOBuffer>& buffer); |
| |
| // Called when a write completes. Tries to write more. |
| // Might call |write_callback_|. |
| void OnWriteComplete(const scoped_refptr<DrainableIOBuffer>& buffer, |
| int result); |
| |
| // Attempts to parse the output of a read as WebSocket frames. On success, |
| // returns OK and places the frame(s) in |frames|. |
| int HandleReadResult(int result, |
| std::vector<std::unique_ptr<WebSocketFrame>>* frames); |
| |
| // Converts the chunks in |frame_chunks| into frames and writes them to |
| // |frames|. |frame_chunks| is destroyed in the process. Returns |
| // ERR_WS_PROTOCOL_ERROR if an invalid chunk was found. If one or more frames |
| // was added to |frames|, then returns OK, otherwise returns ERR_IO_PENDING. |
| int ConvertChunksToFrames( |
| std::vector<std::unique_ptr<WebSocketFrameChunk>>* frame_chunks, |
| std::vector<std::unique_ptr<WebSocketFrame>>* frames); |
| |
| // Converts a |chunk| to a |frame|. |*frame| should be NULL on entry to this |
| // method. If |chunk| is an incomplete control frame, or an empty middle |
| // frame, then |*frame| may still be NULL on exit. If an invalid control frame |
| // is found, returns ERR_WS_PROTOCOL_ERROR and the stream is no longer |
| // usable. Otherwise returns OK (even if frame is still NULL). |
| int ConvertChunkToFrame(std::unique_ptr<WebSocketFrameChunk> chunk, |
| std::unique_ptr<WebSocketFrame>* frame); |
| |
| // Creates a frame based on the value of |is_final_chunk|, |data| and |
| // |current_frame_header_|. Clears |current_frame_header_| if |is_final_chunk| |
| // is true. |data| may be NULL if the frame has an empty payload. A frame in |
| // the middle of a message with no data is not useful; in this case the |
| // returned frame will be NULL. Otherwise, |current_frame_header_->opcode| is |
| // set to Continuation after use if it was Text or Binary, in accordance with |
| // WebSocket RFC6455 section 5.4. |
| std::unique_ptr<WebSocketFrame> CreateFrame(bool is_final_chunk, |
| base::span<const char> data); |
| |
| // Adds |data_buffer| to the end of |incomplete_control_frame_body_|, applying |
| // bounds checks. |
| void AddToIncompleteControlFrameBody(base::span<const char> data); |
| |
| // Storage for pending reads. |
| scoped_refptr<IOBufferWithSize> read_buffer_; |
| |
| // The best read buffer size for the current throughput. |
| size_t target_read_buffer_size_; |
| |
| // The connection, wrapped in a ClientSocketHandle so that we can prevent it |
| // from being returned to the pool. |
| std::unique_ptr<Adapter> connection_; |
| |
| // Frame header for the frame currently being received. Only non-NULL while we |
| // are processing the frame. If the frame arrives in multiple chunks, it can |
| // remain non-NULL until additional chunks arrive. If the header of the frame |
| // was invalid, this is set to NULL, the channel is failed, and subsequent |
| // chunks of the same frame will be ignored. |
| std::unique_ptr<WebSocketFrameHeader> current_frame_header_; |
| |
| // Although it should rarely happen in practice, a control frame can arrive |
| // broken into chunks. This variable provides storage for a partial control |
| // frame until the rest arrives. It will be empty the rest of the time. |
| std::vector<char> incomplete_control_frame_body_; |
| // Storage for payload of combined (see |incomplete_control_frame_body_|) |
| // control frame. |
| std::vector<char> complete_control_frame_body_; |
| |
| // Only used during handshake. Some data may be left in this buffer after the |
| // handshake, in which case it will be picked up during the first call to |
| // ReadFrames(). The type is GrowableIOBuffer for compatibility with |
| // net::HttpStreamParser, which is used to parse the handshake. |
| scoped_refptr<GrowableIOBuffer> http_read_buffer_; |
| // Flag to keep above buffer until next ReadFrames() after decoding. |
| bool is_http_read_buffer_decoded_ = false; |
| |
| // This keeps the current parse state (including any incomplete headers) and |
| // parses frames. |
| WebSocketFrameParser parser_; |
| |
| // The negotated sub-protocol, or empty for none. |
| const std::string sub_protocol_; |
| |
| // The extensions negotiated with the remote server. |
| const std::string extensions_; |
| |
| NetLogWithSource net_log_; |
| |
| // This is used for adaptive read buffer size. |
| BufferSizeManager buffer_size_manager_; |
| |
| // This keeps the current read buffer size. |
| BufferSize buffer_size_ = buffer_size_manager_.buffer_size(); |
| |
| // This can be overridden in tests to make the output deterministic. We don't |
| // use a Callback here because a function pointer is faster and good enough |
| // for our purposes. |
| WebSocketMaskingKeyGeneratorFunction generate_websocket_masking_key_; |
| |
| // User callback saved for asynchronous writes and reads. |
| CompletionOnceCallback write_callback_; |
| CompletionOnceCallback read_callback_; |
| }; |
| |
| } // namespace net |
| |
| #endif // NET_WEBSOCKETS_WEBSOCKET_BASIC_STREAM_H_ |