summaryrefslogtreecommitdiffstats
path: root/net/websockets/websocket_basic_stream.h
blob: 45c183c79625e773f9c4cd1519908487187a839a (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
// Copyright 2013 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.

#ifndef NET_WEBSOCKETS_WEBSOCKET_BASIC_STREAM_H_
#define NET_WEBSOCKETS_WEBSOCKET_BASIC_STREAM_H_

#include <string>

#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "net/websockets/websocket_frame_parser.h"
#include "net/websockets/websocket_stream.h"

namespace net {

class ClientSocketHandle;
class DrainableIOBuffer;
class GrowableIOBuffer;
class HttpRequestHeaders;
class HttpResponseInfo;
class IOBufferWithSize;
struct WebSocketFrame;
struct WebSocketFrameChunk;

// Implementation of WebSocketStream for non-multiplexed ws:// connections (or
// the physical side of a multiplexed ws:// connection).
class NET_EXPORT_PRIVATE WebSocketBasicStream : public WebSocketStream {
 public:
  typedef WebSocketMaskingKey (*WebSocketMaskingKeyGeneratorFunction)();

  // This class should not normally be constructed directly; see
  // WebSocketStream::CreateAndConnectStream.
  explicit WebSocketBasicStream(scoped_ptr<ClientSocketHandle> connection);

  // The destructor has to make sure the connection is closed when we finish so
  // that it does not get returned to the pool.
  virtual ~WebSocketBasicStream();

  // WebSocketStream implementation.
  virtual int ReadFrames(ScopedVector<WebSocketFrame>* frames,
                         const CompletionCallback& callback) OVERRIDE;

  virtual int WriteFrames(ScopedVector<WebSocketFrame>* frames,
                          const CompletionCallback& callback) OVERRIDE;

  virtual void Close() OVERRIDE;

  virtual std::string GetSubProtocol() const OVERRIDE;

  virtual std::string GetExtensions() const OVERRIDE;

  // Writes WebSocket handshake request HTTP-style to the connection. Adds
  // "Sec-WebSocket-Key" header; this should not be included in |headers|.
  virtual int SendHandshakeRequest(const GURL& url,
                                   const HttpRequestHeaders& headers,
                                   HttpResponseInfo* response_info,
                                   const CompletionCallback& callback) OVERRIDE;

  virtual int ReadHandshakeResponse(
      const CompletionCallback& callback) OVERRIDE;

  ////////////////////////////////////////////////////////////////////////////
  // Methods for testing only.

  static scoped_ptr<WebSocketBasicStream> CreateWebSocketBasicStreamForTesting(
      scoped_ptr<ClientSocketHandle> connection,
      const scoped_refptr<GrowableIOBuffer>& http_read_buffer,
      const std::string& sub_protocol,
      const std::string& extensions,
      WebSocketMaskingKeyGeneratorFunction key_generator_function);

 private:
  // Returns OK or calls |callback| when the |buffer| is fully drained or
  // something has failed.
  int WriteEverything(const scoped_refptr<DrainableIOBuffer>& buffer,
                      const CompletionCallback& callback);

  // Wraps the |callback| to continue writing until everything has been written.
  void OnWriteComplete(const scoped_refptr<DrainableIOBuffer>& buffer,
                       const CompletionCallback& callback,
                       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, ScopedVector<WebSocketFrame>* 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(ScopedVector<WebSocketFrameChunk>* frame_chunks,
                            ScopedVector<WebSocketFrame>* 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 non-final
  // 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(scoped_ptr<WebSocketFrameChunk> chunk,
                          scoped_ptr<WebSocketFrame>* 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 with
  // no data and the "final" flag not set 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.
  scoped_ptr<WebSocketFrame> CreateFrame(
      bool is_final_chunk,
      const scoped_refptr<IOBufferWithSize>& data);

  // Adds |data_buffer| to the end of |incomplete_control_frame_body_|, applying
  // bounds checks.
  void AddToIncompleteControlFrameBody(
      const scoped_refptr<IOBufferWithSize>& data_buffer);

  // Called when a read completes. Parses the result and (unless no complete
  // header has been received) calls |callback|.
  void OnReadComplete(ScopedVector<WebSocketFrame>* frames,
                      const CompletionCallback& callback,
                      int result);

  // Storage for pending reads. All active WebSockets spend all the time with a
  // call to ReadFrames() pending, so there is no benefit in trying to share
  // this between sockets.
  scoped_refptr<IOBufferWithSize> read_buffer_;

  // The connection, wrapped in a ClientSocketHandle so that we can prevent it
  // from being returned to the pool.
  scoped_ptr<ClientSocketHandle> 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.
  scoped_ptr<WebSocketFrameHeader> 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 NULL the rest of the time.
  scoped_refptr<GrowableIOBuffer> incomplete_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<GrowableIOBuffer> http_read_buffer_;

  // This keeps the current parse state (including any incomplete headers) and
  // parses frames.
  WebSocketFrameParser parser_;

  // The negotated sub-protocol, or empty for none.
  std::string sub_protocol_;

  // The extensions negotiated with the remote server.
  std::string extensions_;

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

}  // namespace net

#endif  // NET_WEBSOCKETS_WEBSOCKET_BASIC_STREAM_H_