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

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: Fixes from tyoshino code review. 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"
tyoshino (SeeGerritForStatus) 2013/08/22 21:08:43 add base/logging.h
tyoshino (SeeGerritForStatus) 2013/08/22 21:08:43 add net/base/net_errors.h
Adam Rice 2013/08/23 06:05:18 Done.
Adam Rice 2013/08/23 06:05:18 Done.
14 #include "net/base/io_buffer.h"
15 #include "net/socket/client_socket_handle.h"
16 #include "net/websockets/websocket_errors.h"
17 #include "net/websockets/websocket_frame.h"
18 #include "net/websockets/websocket_frame_parser.h"
19
20 namespace net {
21
22 namespace {
23
24 // The number of bytes to attempt to read at a time.
25 // TODO(ricea): See if there is a better number. Should it start small, and get
26 // bigger if needed?
27 const int kReadAtATime = 32 * 1024;
28
29 } // namespace
30
31 WebSocketBasicStream::WebSocketBasicStream(
32 scoped_ptr<ClientSocketHandle> connection)
33 : read_buffer_(new IOBufferWithSize(kReadAtATime)),
34 connection_(connection.Pass()),
35 generate_websocket_masking_key_(&GenerateWebSocketMaskingKey) {}
36
37 WebSocketBasicStream::~WebSocketBasicStream() {
38 connection_->socket()->Disconnect();
39 }
40
41 int WebSocketBasicStream::ReadFrames(
42 ScopedVector<WebSocketFrameChunk>* frame_chunks,
43 const CompletionCallback& callback) {
44 DCHECK(frame_chunks->empty());
45 // If there is data left over after parsing the HTTP headers, attempt to parse
46 // it as WebSocket frames.
47 if (http_read_buffer_) {
48 DCHECK_GE(http_read_buffer_->offset(), 0);
49 // offset() gives the end of the data, but it also controls where data()
50 // points to, so we need to store its value and then reset it to 0.
51 int end_of_data_offset = http_read_buffer_->offset();
52 http_read_buffer_->set_offset(0);
53 if (!parser_.Decode(
54 http_read_buffer_->data(), end_of_data_offset, frame_chunks)) {
55 http_read_buffer_ = NULL;
56 return WebSocketErrorToNetError(parser_.websocket_error());
57 }
58 http_read_buffer_ = NULL;
59 }
60 // Loop until we either have at least one chunk to return, or we get
61 // ERR_IO_PENDING, or something goes wrong.
62 while (frame_chunks->empty()) {
63 // This use of base::Unretained() is safe because WebSocketChannel will
64 // delete us before deleting frame_chunks.
65 int result =
66 connection_->socket()->Read(read_buffer_.get(),
67 read_buffer_->size(),
68 base::Bind(&WebSocketBasicStream::ReadDone,
69 base::Unretained(this),
70 base::Unretained(frame_chunks),
71 callback));
72 if (result > 0) {
73 if (!parser_.Decode(read_buffer_->data(), result, frame_chunks)) {
74 return WebSocketErrorToNetError(parser_.websocket_error());
75 }
76 } else if (result == 0 && frame_chunks->empty()) {
77 return ERR_CONNECTION_CLOSED;
78 } else {
79 return result;
80 }
81 }
82 return OK;
83 }
84
85 void WebSocketBasicStream::ReadDone(
86 ScopedVector<WebSocketFrameChunk>* frame_chunks,
87 const CompletionCallback& callback,
88 int result) {
89 if (result > 0) {
90 if (parser_.Decode(read_buffer_->data(), result, frame_chunks)) {
91 if (!frame_chunks->empty()) {
92 result = OK;
93 } else {
94 result = ReadFrames(frame_chunks, callback);
95 if (result == ERR_IO_PENDING) {
96 // This method will be called back again.
97 return;
98 }
99 }
100 } else {
101 result = WebSocketErrorToNetError(parser_.websocket_error());
102 }
103 }
104 if (result == 0 && frame_chunks->empty()) {
105 result = ERR_CONNECTION_CLOSED;
106 }
107 DCHECK_NE(ERR_IO_PENDING, result);
108 callback.Run(result);
109 }
110
111 int WebSocketBasicStream::WriteFrames(
112 ScopedVector<WebSocketFrameChunk>* frame_chunks,
113 const CompletionCallback& callback) {
114 // This function always concatenates all frames into a single buffer.
115 // TODO(ricea): Investigate whether it would be better in some cases to
116 // perform multiple writes with smaller buffers.
117 //
118 // First calculate the size of the buffer we need to allocate.
119 typedef ScopedVector<WebSocketFrameChunk>::const_iterator Iterator;
120 int total_size = 0;
121 for (Iterator it = frame_chunks->begin(); it != frame_chunks->end(); ++it) {
122 WebSocketFrameChunk* chunk = *it;
123 DCHECK(chunk->header && chunk->final_chunk)
124 << "Only complete frames are supported by WebSocketBasicStream";
125 // Force the masked bit on.
126 chunk->header->masked = true;
127 // We enforce flow control so the renderer should never be able to force us
128 // to cache anywhere near 2GB of frames.
129 int chunk_size =
130 chunk->data->size() + GetWebSocketFrameHeaderSize(*(chunk->header));
131 CHECK_GE(std::numeric_limits<int>::max() - total_size, chunk_size)
132 << "Aborting to prevent overflow";
133 total_size += chunk_size;
134 }
135 scoped_refptr<IOBufferWithSize> dest(new IOBufferWithSize(total_size));
136 char* data = dest->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, data, 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 data += 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, data);
154 MaskWebSocketFramePayload(mask, 0, data, frame_size);
155 data += 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(dest, 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) {
192 buffer->DidConsume(result);
193 if (buffer->BytesRemaining() > 0) {
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);
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