| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // WebSocket protocol implementation in chromium. |
| // It is intended to be used for live experiment of WebSocket connectivity |
| // metrics. |
| // Note that it is not used for WebKit's WebSocket communication. |
| // See third_party/WebKit/Source/WebCore/websockets/ instead. |
| |
| #ifndef NET_WEBSOCKETS_WEBSOCKET_H_ |
| #define NET_WEBSOCKETS_WEBSOCKET_H_ |
| #pragma once |
| |
| #include <deque> |
| #include <string> |
| |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "googleurl/src/gurl.h" |
| #include "net/base/io_buffer.h" |
| #include "net/socket_stream/socket_stream.h" |
| #include "net/url_request/url_request_context.h" |
| |
| class MessageLoop; |
| |
| namespace net { |
| |
| class ClientSocketFactory; |
| class HostResolver; |
| |
| class WebSocket; |
| class WebSocketHandshake; |
| |
| // Delegate methods will be called on the same message loop as |
| // WebSocket is constructed. |
| class WebSocketDelegate { |
| public: |
| virtual ~WebSocketDelegate() {} |
| |
| // Called when WebSocket connection has been established. |
| virtual void OnOpen(WebSocket* socket) = 0; |
| |
| // Called when |msg| is received at |socket|. |
| // |msg| should be in UTF-8. |
| virtual void OnMessage(WebSocket* socket, const std::string& msg) = 0; |
| |
| // Called when WebSocket error has been detected. |
| virtual void OnError(WebSocket* socket) {} |
| |
| // Called when |socket| is closed. |
| virtual void OnClose(WebSocket* socket, bool was_clean) = 0; |
| |
| // Called when an error occured on |socket|. |
| virtual void OnSocketError(const WebSocket* socket, int error) {} |
| }; |
| |
| class WebSocket : public base::RefCountedThreadSafe<WebSocket>, |
| public SocketStream::Delegate { |
| public: |
| enum State { |
| INITIALIZED = -1, |
| CONNECTING = 0, |
| OPEN = 1, |
| CLOSING = 2, |
| CLOSED = 3, |
| }; |
| enum ProtocolVersion { |
| DEFAULT_VERSION = 0, |
| DRAFT75 = 1, |
| }; |
| class Request { |
| public: |
| Request(const GURL& url, const std::string protocol, |
| const std::string origin, const std::string location, |
| ProtocolVersion version, |
| net::URLRequestContext* context) |
| : url_(url), |
| protocol_(protocol), |
| origin_(origin), |
| location_(location), |
| version_(version), |
| context_(context), |
| host_resolver_(NULL), |
| client_socket_factory_(NULL) {} |
| ~Request() {} |
| |
| const GURL& url() const { return url_; } |
| const std::string& protocol() const { return protocol_; } |
| const std::string& origin() const { return origin_; } |
| const std::string& location() const { return location_; } |
| ProtocolVersion version() const { return version_; } |
| net::URLRequestContext* context() const { return context_; } |
| |
| // Sets an alternative HostResolver. For testing purposes only. |
| void SetHostResolver(HostResolver* host_resolver) { |
| host_resolver_ = host_resolver; |
| } |
| HostResolver* host_resolver() const { return host_resolver_; } |
| |
| // Sets an alternative ClientSocketFactory. Doesn't take ownership of |
| // |factory|. For testing purposes only. |
| void SetClientSocketFactory(ClientSocketFactory* factory) { |
| client_socket_factory_ = factory; |
| } |
| ClientSocketFactory* client_socket_factory() const { |
| return client_socket_factory_; |
| } |
| |
| private: |
| GURL url_; |
| std::string protocol_; |
| std::string origin_; |
| std::string location_; |
| ProtocolVersion version_; |
| scoped_refptr<net::URLRequestContext> context_; |
| |
| HostResolver* host_resolver_; |
| ClientSocketFactory* client_socket_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Request); |
| }; |
| |
| // Constructs new WebSocket. |
| // It takes ownership of |req|. |
| // |delegate| must be alive while this object is alive. |
| WebSocket(Request* req, WebSocketDelegate* delegate); |
| |
| const Request* request() const { return request_.get(); } |
| WebSocketDelegate* delegate() const { return delegate_; } |
| |
| State ready_state() const { return ready_state_; } |
| |
| // Connects new WebSocket. |
| void Connect(); |
| |
| // Sends |msg| on the WebSocket connection. |
| // |msg| should be in UTF-8. |
| void Send(const std::string& msg); |
| |
| // Closes the WebSocket connection. |
| void Close(); |
| |
| // Detach delegate. Call before delegate is deleted. |
| // Once delegate is detached, close the WebSocket connection and never call |
| // delegate back. |
| void DetachDelegate(); |
| |
| // SocketStream::Delegate methods. |
| // Called on IO thread. |
| virtual void OnConnected(SocketStream* socket_stream, |
| int max_pending_send_allowed); |
| virtual void OnSentData(SocketStream* socket_stream, int amount_sent); |
| virtual void OnReceivedData(SocketStream* socket_stream, |
| const char* data, int len); |
| virtual void OnClose(SocketStream* socket); |
| virtual void OnError(const SocketStream* socket, int error); |
| |
| private: |
| typedef std::deque< scoped_refptr<IOBufferWithSize> > PendingDataQueue; |
| |
| friend class WebSocketTest; |
| |
| friend class base::RefCountedThreadSafe<WebSocket>; |
| virtual ~WebSocket(); |
| |
| // Sends pending data in |current_write_buf_| and/or |pending_write_bufs_|. |
| void SendPending(); |
| |
| // Handles received data. |
| void DoReceivedData(); |
| |
| // Processes frame data in |current_read_buf_|. |
| void ProcessFrameData(); |
| |
| // Adds |len| bytes of |data| to |current_read_buf_|. |
| void AddToReadBuffer(const char* data, int len); |
| |
| // Skips |len| bytes in |current_read_buf_|. |
| void SkipReadBuffer(int len); |
| |
| void StartClosingHandshake(); |
| void DoForceCloseConnection(); |
| void FailConnection(); |
| // Handles closed connection. |
| void DoClose(); |
| |
| // Handles socket error report. |
| void DoSocketError(int error); |
| |
| State ready_state_; |
| scoped_ptr<Request> request_; |
| scoped_ptr<WebSocketHandshake> handshake_; |
| WebSocketDelegate* delegate_; |
| MessageLoop* origin_loop_; |
| |
| scoped_refptr<SocketStream> socket_stream_; |
| int max_pending_send_allowed_; |
| |
| // [0..offset) is received data from |socket_stream_|. |
| // [0..read_consumed_len_) is already processed. |
| // [read_consumed_len_..offset) is unprocessed data. |
| // [offset..capacity) is free space. |
| scoped_refptr<GrowableIOBuffer> current_read_buf_; |
| int read_consumed_len_; |
| |
| // Drainable IOBuffer on the front of |pending_write_bufs_|. |
| // [0..offset) is already sent to |socket_stream_|. |
| // [offset..size) is being sent to |socket_stream_|, waiting OnSentData. |
| scoped_refptr<DrainableIOBuffer> current_write_buf_; |
| |
| // Deque of IOBuffers in pending. |
| // Front IOBuffer is being sent via |current_write_buf_|. |
| PendingDataQueue pending_write_bufs_; |
| |
| // True when the 0xFF frame with length 0x00 is received. |
| bool server_closing_handshake_; |
| // True when trying to send 0xFF and 0x00 bytes. |
| bool client_closing_handshake_; |
| // True when send 0xFF and 0x00 bytes. |
| bool closing_handshake_started_; |
| // Task to close the connection after closing handshake has started and |
| // |closing_handshake_timeout_|. |
| CancelableTask* force_close_task_; |
| int64 closing_handshake_timeout_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WebSocket); |
| }; |
| |
| } // namespace net |
| |
| #endif // NET_WEBSOCKETS_WEBSOCKET_H_ |