summaryrefslogtreecommitdiffstats
path: root/net/websockets/websocket.h
blob: 566ee6fb777baffff8eec6966ecf3600eb6967c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
// 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 <deque>
#include <string>

#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<WebSocket>,
                  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_;
    }

   private:
    GURL url_;
    std::string protocol_;
    std::string origin_;
    std::string location_;
    scoped_refptr<URLRequestContext> context_;

    scoped_refptr<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:
  enum Mode {
    MODE_INCOMPLETE, MODE_NORMAL, MODE_AUTHENTICATE,
  };
  typedef std::deque< scoped_refptr<IOBufferWithSize> > PendingDataQueue;

  friend class WebSocketTest;

  friend class base::RefCountedThreadSafe<WebSocket>;
  virtual ~WebSocket();

  // Creates client handshake mssage based on |request_|.
  IOBufferWithSize* CreateClientHandshakeMessage() const;

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

  DISALLOW_COPY_AND_ASSIGN(WebSocket);
};

}  // namespace net

#endif  // NET_WEBSOCKETS_WEBSOCKET_H_