// 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 #include #include #include #include #include "base/containers/queue.h" #include "base/containers/span.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.h" #include "net/websockets/websocket_frame_parser.h" #include "net/websockets/websocket_stream.h" namespace base { class TimeTicks; } // namespace base namespace net { class ClientSocketHandle; class DrainableIOBuffer; class GrowableIOBuffer; class IOBuffer; class IOBufferWithSize; struct WebSocketFrame; struct WebSocketFrameChunk; struct NetworkTrafficAnnotationTag; // 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 read_start_timestamps_; // The sum of the last few read size. int rolling_byte_total_ = 0; // This keeps the read size. base::queue 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 connection, const scoped_refptr& 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>* frames, CompletionOnceCallback callback) override; int WriteFrames(std::vector>* 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 CreateWebSocketBasicStreamForTesting( std::unique_ptr connection, const scoped_refptr& 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>* frames); // Called when a read completes. Parses the result, tries to read more. // Might call |read_callback_|. void OnReadComplete(std::vector>* 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& buffer); // Called when a write completes. Tries to write more. // Might call |write_callback_|. void OnWriteComplete(const scoped_refptr& 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>* 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>* frame_chunks, std::vector>* 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 chunk, std::unique_ptr* 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 CreateFrame(bool is_final_chunk, base::span data); // Adds |data_buffer| to the end of |incomplete_control_frame_body_|, applying // bounds checks. void AddToIncompleteControlFrameBody(base::span data); // Storage for pending reads. scoped_refptr 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 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 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 incomplete_control_frame_body_; // Storage for payload of combined (see |incomplete_control_frame_body_|) // control frame. std::vector 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 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_