Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(150)

Side by Side Diff: net/websockets/websocket_basic_stream.cc

Issue 18792002: WebSocketBasicStream framing logic (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Add includes and rename "data" variable Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/websockets/websocket_basic_stream.h"
6
7 #include <algorithm>
8 #include <limits>
9 #include <string>
10 #include <vector>
11
12 #include "base/basictypes.h"
13 #include "base/bind.h"
14 #include "base/logging.h"
15 #include "net/base/io_buffer.h"
16 #include "net/base/net_errors.h"
17 #include "net/socket/client_socket_handle.h"
18 #include "net/websockets/websocket_errors.h"
19 #include "net/websockets/websocket_frame.h"
20 #include "net/websockets/websocket_frame_parser.h"
21
22 namespace net {
23
24 namespace {
25
26 // The number of bytes to attempt to read at a time.
27 // TODO(ricea): See if there is a better number. Should it start small, and get
28 // bigger if needed?
29 const int kReadAtATime = 32 * 1024;
szym 2013/08/27 22:54:04 suggest: kReadBufferSize This is merely the size
Adam Rice 2013/08/28 12:01:11 Some applications of WebSockets may require a larg
30
31 } // namespace
32
33 WebSocketBasicStream::WebSocketBasicStream(
34 scoped_ptr<ClientSocketHandle> connection)
35 : read_buffer_(new IOBufferWithSize(kReadAtATime)),
36 connection_(connection.Pass()),
37 generate_websocket_masking_key_(&GenerateWebSocketMaskingKey) {}
szym 2013/08/27 22:54:04 suggest: DCHECK(connection_->is_initialized())
Adam Rice 2013/08/28 12:01:11 Done.
38
39 WebSocketBasicStream::~WebSocketBasicStream() {
40 connection_->socket()->Disconnect();
szym 2013/08/27 22:54:04 Suggest calling Close() instead.
41 }
42
43 int WebSocketBasicStream::ReadFrames(
44 ScopedVector<WebSocketFrameChunk>* frame_chunks,
45 const CompletionCallback& callback) {
46 DCHECK(frame_chunks->empty());
47 // If there is data left over after parsing the HTTP headers, attempt to parse
48 // it as WebSocket frames.
49 if (http_read_buffer_) {
50 DCHECK_GE(http_read_buffer_->offset(), 0);
51 if (!parser_.Decode(http_read_buffer_->StartOfBuffer(),
52 http_read_buffer_->offset(),
53 frame_chunks)) {
54 http_read_buffer_ = NULL;
55 return WebSocketErrorToNetError(parser_.websocket_error());
56 }
57 http_read_buffer_ = NULL;
58 }
59 // Loop until we either have at least one chunk to return, or we get
60 // ERR_IO_PENDING, or something goes wrong.
61 while (frame_chunks->empty()) {
62 // This use of base::Unretained() is safe because WebSocketChannel will
63 // delete us before deleting frame_chunks.
szym 2013/08/27 22:54:04 Do not refer to WebSocketChannel. This use is safe
Adam Rice 2013/08/28 12:01:11 I plan to rename base::Unretained to base::Critica
64 int result =
65 connection_->socket()->Read(read_buffer_.get(),
66 read_buffer_->size(),
67 base::Bind(&WebSocketBasicStream::ReadDone,
68 base::Unretained(this),
69 base::Unretained(frame_chunks),
70 callback));
71 if (result > 0) {
72 if (!parser_.Decode(read_buffer_->data(), result, frame_chunks)) {
73 return WebSocketErrorToNetError(parser_.websocket_error());
74 }
75 } else if (result == 0 && frame_chunks->empty()) {
76 return ERR_CONNECTION_CLOSED;
77 } else {
78 return result;
79 }
80 }
81 return OK;
82 }
83
84 void WebSocketBasicStream::ReadDone(
szym 2013/08/27 22:54:04 Chromium code style: Function declaration order sh
Adam Rice 2013/08/28 12:01:11 Done.
85 ScopedVector<WebSocketFrameChunk>* frame_chunks,
86 const CompletionCallback& callback,
87 int result) {
88 if (result > 0) {
szym 2013/08/27 22:54:04 The logic here and in ReadFrames is very similar b
Adam Rice 2013/08/28 12:01:11 Done.
89 if (parser_.Decode(read_buffer_->data(), result, frame_chunks)) {
90 if (!frame_chunks->empty()) {
91 result = OK;
92 } else {
93 result = ReadFrames(frame_chunks, callback);
94 if (result == ERR_IO_PENDING) {
95 // This method will be called back again.
96 return;
97 }
98 }
99 } else {
100 result = WebSocketErrorToNetError(parser_.websocket_error());
101 }
102 }
103 if (result == 0 && frame_chunks->empty()) {
104 result = ERR_CONNECTION_CLOSED;
105 }
106 DCHECK_NE(ERR_IO_PENDING, result);
107 callback.Run(result);
108 }
109
110 int WebSocketBasicStream::WriteFrames(
111 ScopedVector<WebSocketFrameChunk>* frame_chunks,
112 const CompletionCallback& callback) {
113 // This function always concatenates all frames into a single buffer.
114 // TODO(ricea): Investigate whether it would be better in some cases to
115 // perform multiple writes with smaller buffers.
116 //
117 // First calculate the size of the buffer we need to allocate.
118 typedef ScopedVector<WebSocketFrameChunk>::const_iterator Iterator;
119 int total_size = 0;
120 for (Iterator it = frame_chunks->begin(); it != frame_chunks->end(); ++it) {
121 WebSocketFrameChunk* chunk = *it;
122 DCHECK(chunk->header && chunk->final_chunk)
szym 2013/08/27 22:54:04 Suggest breaking DCHECK(..&&..) into two DCHECKs
Adam Rice 2013/08/28 12:01:11 Done.
123 << "Only complete frames are supported by WebSocketBasicStream";
124 // Force the masked bit on.
125 chunk->header->masked = true;
126 // We enforce flow control so the renderer should never be able to force us
127 // to cache anywhere near 2GB of frames.
128 int chunk_size =
129 chunk->data->size() + GetWebSocketFrameHeaderSize(*(chunk->header));
130 CHECK_GE(std::numeric_limits<int>::max() - total_size, chunk_size)
szym 2013/08/27 22:54:04 Define a constant const int kMaximumTotalSize = .
Adam Rice 2013/08/28 12:01:11 Done.
131 << "Aborting to prevent overflow";
132 total_size += chunk_size;
133 }
134 scoped_refptr<IOBufferWithSize> combined_buffer(
135 new IOBufferWithSize(total_size));
136 char* dest = combined_buffer->data();
137 int remaining_size = total_size;
138 for (Iterator it = frame_chunks->begin(); it != frame_chunks->end(); ++it) {
139 WebSocketFrameChunk* chunk = *it;
140 WebSocketMaskingKey mask = generate_websocket_masking_key_();
141 int result = WriteWebSocketFrameHeader(
142 *(chunk->header), &mask, dest, remaining_size);
143 DCHECK(result != ERR_INVALID_ARGUMENT)
144 << "WriteWebSocketFrameHeader() says that " << remaining_size
145 << " is not enough to write the header in. This should not happen.";
146 CHECK_GE(result, 0) << "Potentially security-critical check failed";
147 dest += result;
148 remaining_size -= result;
149
150 const char* const frame_data = chunk->data->data();
151 const int frame_size = chunk->data->size();
152 CHECK_GE(remaining_size, frame_size);
153 std::copy(frame_data, frame_data + frame_size, dest);
154 MaskWebSocketFramePayload(mask, 0, dest, frame_size);
155 dest += frame_size;
156 remaining_size -= frame_size;
157 }
158 DCHECK_EQ(0, remaining_size) << "Buffer size calculation was wrong; "
159 << remaining_size << " bytes left over.";
160 scoped_refptr<DrainableIOBuffer> drainable_buffer(
161 new DrainableIOBuffer(combined_buffer, total_size));
162 return WriteEverything(drainable_buffer, callback);
163 }
164
165 int WebSocketBasicStream::WriteEverything(
166 const scoped_refptr<DrainableIOBuffer>& buffer,
167 const CompletionCallback& callback) {
168 while (buffer->BytesRemaining() > 0) {
169 // The use of base::Unretained() here is safe because on destruction we
170 // disconnect the socket, preventing any further callbacks.
171 int result = connection_->socket()->Write(
172 buffer.get(),
173 buffer->BytesRemaining(),
174 base::Bind(&WebSocketBasicStream::WriteDone,
175 base::Unretained(this),
176 buffer,
177 callback));
178 if (result > 0) {
179 buffer->DidConsume(result);
180 } else {
181 return result;
182 }
183 }
184 return OK;
185 }
186
187 void WebSocketBasicStream::WriteDone(
188 const scoped_refptr<DrainableIOBuffer>& buffer,
189 const CompletionCallback& callback,
190 int result) {
191 if (result > 0) {
szym 2013/08/27 22:54:04 Suggest unrolling the if{} folds in this function:
Adam Rice 2013/08/28 12:01:11 Done.
192 buffer->DidConsume(result);
193 if (buffer->BytesRemaining() > 0) {
szym 2013/08/27 22:54:04 You don't need this check. Just call WriteEverythi
Adam Rice 2013/08/28 12:01:11 Done.
194 int result = WriteEverything(buffer, callback);
195 if (result != ERR_IO_PENDING) {
196 callback.Run(result);
197 }
198 } else {
199 callback.Run(OK);
200 }
201 } else {
202 DCHECK(result != ERR_IO_PENDING);
203 callback.Run(result);
szym 2013/08/27 22:54:04 BUG: It's possible to end up in here with result =
Adam Rice 2013/08/28 12:01:11 I've put in a DCHECK for result != 0 for the time
204 }
205 }
206
207 void WebSocketBasicStream::Close() { connection_->socket()->Disconnect(); }
208
209 std::string WebSocketBasicStream::GetSubProtocol() const {
210 return sub_protocol_;
211 }
212
213 std::string WebSocketBasicStream::GetExtensions() const { return extensions_; }
214
215 int WebSocketBasicStream::SendHandshakeRequest(
216 const GURL& url,
217 const HttpRequestHeaders& headers,
218 HttpResponseInfo* response_info,
219 const CompletionCallback& callback) {
220 // TODO(ricea): Implement handshake-related functionality.
221 NOTREACHED();
222 return ERR_NOT_IMPLEMENTED;
223 }
224
225 int WebSocketBasicStream::ReadHandshakeResponse(
226 const CompletionCallback& callback) {
227 NOTREACHED();
228 return ERR_NOT_IMPLEMENTED;
229 }
230
231 /*static*/
232 scoped_ptr<WebSocketBasicStream>
233 WebSocketBasicStream::CreateWebSocketBasicStreamForTesting(
234 scoped_ptr<ClientSocketHandle> connection,
235 const scoped_refptr<GrowableIOBuffer>& http_read_buffer,
236 const std::string& sub_protocol,
237 const std::string& extensions,
238 WebSocketMaskingKeyGeneratorFunction key_generator_function) {
239 scoped_ptr<WebSocketBasicStream> stream(
240 new WebSocketBasicStream(connection.Pass()));
241 if (http_read_buffer) {
242 stream->http_read_buffer_ = http_read_buffer;
243 }
244 stream->sub_protocol_ = sub_protocol;
245 stream->extensions_ = extensions;
246 stream->generate_websocket_masking_key_ = key_generator_function;
247 return stream.Pass();
248 }
249
250 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698