// Copyright (c) 2009 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/WebCore/websockets/ instead. #ifndef NET_WEBSOCKETS_WEBSOCKET_H_ #define NET_WEBSOCKETS_WEBSOCKET_H_ #include #include #include "base/ref_counted.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 HttpResponseHeaders; class WebSocket; // 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 |socket| is closed. virtual void OnClose(WebSocket* socket) = 0; // Called when an error occured on |socket|. virtual void OnError(const WebSocket* socket, int error) {} }; class WebSocket : public base::RefCountedThreadSafe, public SocketStream::Delegate { public: enum State { INITIALIZED = -1, CONNECTING = 0, OPEN = 1, CLOSED = 2, }; class Request { public: Request(const GURL& url, const std::string protocol, const std::string origin, const std::string location, URLRequestContext* context) : url_(url), protocol_(protocol), origin_(origin), location_(location), context_(context), host_resolver_(NULL), client_socket_factory_(NULL) {} ~Request() {} const GURL& url() const { return url_; } bool is_secure() const; const std::string& protocol() const { return protocol_; } const std::string& origin() const { return origin_; } const std::string& location() const { return location_; } 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_; } // Creates the client handshake message from |this|. std::string CreateClientHandshakeMessage() const; private: GURL url_; std::string protocol_; std::string origin_; std::string location_; scoped_refptr context_; scoped_refptr 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: enum Mode { MODE_INCOMPLETE, MODE_NORMAL, MODE_AUTHENTICATE, }; typedef std::deque< scoped_refptr > PendingDataQueue; friend class WebSocketTest; friend class base::RefCountedThreadSafe; virtual ~WebSocket(); // Checks handshake. // Prerequisite: Server handshake message is received in |current_read_buf_|. // Returns number of bytes for server handshake message, // or negative if server handshake message is not received fully yet. int CheckHandshake(); // Processes server handshake message, parsed as |headers|, and updates // |ws_origin_|, |ws_location_| and |ws_protocol_|. // Returns true if it's ok. // Returns false otherwise (e.g. duplicate WebSocket-Origin: header, etc.) bool ProcessHeaders(const HttpResponseHeaders& headers); // Checks |ws_origin_|, |ws_location_| and |ws_protocol_| are valid // against |request_|. // Returns true if it's ok. // Returns false otherwise (e.g. origin mismatch, etc.) bool CheckResponseHeaders() const; // 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); // Handles closed connection. void DoClose(); // Handles error report. void DoError(int error); State ready_state_; Mode mode_; scoped_ptr request_; WebSocketDelegate* delegate_; MessageLoop* origin_loop_; // Handshake messages that server sent. std::string ws_origin_; std::string ws_location_; std::string ws_protocol_; scoped_refptr 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 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 current_write_buf_; // Deque of IOBuffers in pending. // Front IOBuffer is being sent via |current_write_buf_|. PendingDataQueue pending_write_bufs_; DISALLOW_COPY_AND_ASSIGN(WebSocket); }; } // namespace net #endif // NET_WEBSOCKETS_WEBSOCKET_H_