// 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_