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

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: Comment fixes. 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 "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 // independently of HTTP Upgrade handshake functionality.
21 #define WEBSOCKET_HANDSHAKE_UNIMPLEMENTED() \
22 NOTREACHED() << "WebSocket Handshake-related functionality will be " \
23 "implemented in another CL."; \
tyoshino (SeeGerritForStatus) 2013/08/22 07:56:10 ok but you can just put "NOTREACHED();" and put "/
Adam Rice 2013/08/22 08:13:04 Done.
24 return ERR_NOT_IMPLEMENTED
25
26 namespace net {
27
28 namespace {
29
30 // The number of bytes to attempt to read at a time.
31 // TODO(ricea): See if there is a better number. Should it start small, and get
32 // bigger if needed?
33 const int kReadAtATime = 32 * 1024;
34
35 } // namespace
36
37 WebSocketBasicStream::WebSocketBasicStream(
38 scoped_ptr<ClientSocketHandle> connection)
39 : read_buffer_(new IOBufferWithSize(kReadAtATime)),
40 connection_(connection.Pass()),
41 generate_websocket_masking_key_(&GenerateWebSocketMaskingKey) {}
42
43 WebSocketBasicStream::~WebSocketBasicStream() {
44 connection_->socket()->Disconnect();
45 }
46
47 int WebSocketBasicStream::ReadFrames(
48 ScopedVector<WebSocketFrameChunk>* frame_chunks,
49 const CompletionCallback& callback) {
50 DCHECK(frame_chunks->empty());
51 // If there is data left over after parsing the HTTP headers, attempt to parse
52 // it as WebSocket frames.
53 if (http_read_buffer_) {
54 DCHECK_GE(http_read_buffer_->offset(), 0);
55 // offset() gives the end of the data, but it also controls where data()
56 // points to, so we need to store its value and then reset it to 0.
57 int end_of_data_offset = http_read_buffer_->offset();
58 http_read_buffer_->set_offset(0);
59 if (!parser_.Decode(
60 http_read_buffer_->data(), end_of_data_offset, frame_chunks)) {
61 http_read_buffer_ = NULL;
62 return WebSocketErrorToNetError(parser_.websocket_error());
63 }
64 http_read_buffer_ = NULL;
65 }
66 // Loop until we either have at least one chunk to return, or we get
67 // ERR_IO_PENDING, or something goes wrong.
68 while (frame_chunks->empty()) {
69 // This use of base::Unretained() is safe because WebSocketChannel will
70 // delete us before deleting frame_chunks.
71 int result =
72 connection_->socket()->Read(read_buffer_.get(),
73 read_buffer_->size(),
74 base::Bind(&WebSocketBasicStream::ReadDone,
75 base::Unretained(this),
76 base::Unretained(frame_chunks),
77 callback));
78 if (result > 0) {
79 if (!parser_.Decode(read_buffer_->data(), result, frame_chunks)) {
80 return WebSocketErrorToNetError(parser_.websocket_error());
81 }
82 } else if (result == 0 && frame_chunks->empty()) {
83 return ERR_CONNECTION_CLOSED;
84 } else {
85 return result;
86 }
87 }
88 return OK;
89 }
90
91 void WebSocketBasicStream::ReadDone(
92 ScopedVector<WebSocketFrameChunk>* frame_chunks,
93 const CompletionCallback& callback,
94 int result) {
95 if (result > 0) {
96 if (parser_.Decode(read_buffer_->data(), result, frame_chunks)) {
97 if (!frame_chunks->empty()) {
98 result = OK;
99 } else {
100 result = ReadFrames(frame_chunks, callback);
101 if (result == ERR_IO_PENDING) {
102 // This method will be called back again.
103 return;
104 }
105 }
106 } else {
107 result = WebSocketErrorToNetError(parser_.websocket_error());
108 }
109 }
110 if (result == 0 && frame_chunks->empty()) {
111 result = ERR_CONNECTION_CLOSED;
112 }
113 DCHECK_NE(ERR_IO_PENDING, result);
114 callback.Run(result);
115 }
116
117 int WebSocketBasicStream::WriteFrames(
118 ScopedVector<WebSocketFrameChunk>* frame_chunks,
119 const CompletionCallback& callback) {
120 // This function always concatenates all frames into a single buffer.
121 // TODO(ricea): Investigate whether it would be better in some cases to
122 // perform multiple writes with smaller buffers.
123 //
124 // First calculate the size of the buffer we need to allocate.
125 typedef ScopedVector<WebSocketFrameChunk>::const_iterator Iterator;
126 int total_size = 0;
127 for (Iterator it = frame_chunks->begin(); it != frame_chunks->end(); ++it) {
128 WebSocketFrameChunk* chunk = *it;
129 DCHECK(chunk->header && chunk->final_chunk)
130 << "Only complete frames are supported by WebSocketBasicStream";
131 // Force the masked bit on.
132 chunk->header->masked = true;
133 // We enforce flow control so the renderer should never be able to force us
134 // to cache anywhere near 2GB of frames.
135 int chunk_size =
136 chunk->data->size() + GetWebSocketFrameHeaderSize(*(chunk->header));
137 CHECK_GE(std::numeric_limits<int>::max() - total_size, chunk_size)
138 << "Aborting to prevent overflow";
139 total_size += chunk_size;
140 }
141 scoped_refptr<IOBufferWithSize> total(new IOBufferWithSize(total_size));
142 char* data = total->data();
tyoshino (SeeGerritForStatus) 2013/08/22 07:56:10 how about naming this "dest"?
Adam Rice 2013/08/22 08:13:04 Done.
tyoshino (SeeGerritForStatus) 2013/08/22 21:08:43 sorry. i meant s/data/dest/
143 int remaining_size = total_size;
144 for (Iterator it = frame_chunks->begin(); it != frame_chunks->end(); ++it) {
145 WebSocketFrameChunk* chunk = *it;
146 WebSocketMaskingKey mask = generate_websocket_masking_key_();
147 int result = WriteWebSocketFrameHeader(
148 *(chunk->header), &mask, data, remaining_size);
149 DCHECK(result != ERR_INVALID_ARGUMENT)
150 << "WriteWebSocketFrameHeader() says that " << remaining_size
151 << " is not enough to write the header in. This should not happen.";
152 CHECK_GE(result, 0) << "Potentially security-critical check failed";
153 data += result;
154 remaining_size -= result;
tyoshino (SeeGerritForStatus) 2013/08/22 07:56:10 put one blank line to separate header writing code
Adam Rice 2013/08/22 08:13:04 Done.
155 const char* const frame_data = chunk->data->data();
156 const int frame_size = chunk->data->size();
157 CHECK_GE(remaining_size, frame_size);
158 std::copy(frame_data, frame_data + frame_size, data);
159 MaskWebSocketFramePayload(mask, 0, data, frame_size);
160 data += frame_size;
161 remaining_size -= frame_size;
162 }
163 DCHECK_EQ(0, remaining_size) << "Buffer size calculation was wrong; "
164 << remaining_size << " bytes left over.";
165 scoped_refptr<DrainableIOBuffer> drainable_buffer(
166 new DrainableIOBuffer(total, total_size));
167 return WriteEverything(drainable_buffer, callback);
168 }
169
170 int WebSocketBasicStream::WriteEverything(
171 const scoped_refptr<DrainableIOBuffer>& buffer,
172 const CompletionCallback& callback) {
173 while (buffer->BytesRemaining() > 0) {
174 // The use of base::Unretained() here is safe because on destruction we
175 // disconnect the socket, preventing any further callbacks.
176 int result = connection_->socket()->Write(
177 buffer.get(),
178 buffer->BytesRemaining(),
179 base::Bind(&WebSocketBasicStream::WriteDone,
180 base::Unretained(this),
181 buffer,
182 callback));
183 if (result > 0) {
184 buffer->DidConsume(result);
185 } else {
186 return result;
187 }
188 }
189 return OK;
190 }
191
192 void WebSocketBasicStream::WriteDone(
193 const scoped_refptr<DrainableIOBuffer>& buffer,
194 const CompletionCallback& callback,
195 int result) {
196 if (result > 0) {
197 buffer->DidConsume(result);
198 if (buffer->BytesRemaining() > 0) {
199 int result = WriteEverything(buffer, callback);
200 if (result != ERR_IO_PENDING) {
201 callback.Run(result);
202 }
203 } else {
204 callback.Run(OK);
205 }
206 } else {
207 DCHECK(result != ERR_IO_PENDING);
208 callback.Run(result);
209 }
210 }
211
212 void WebSocketBasicStream::Close() { connection_->socket()->Disconnect(); }
213
214 std::string WebSocketBasicStream::GetSubProtocol() const {
215 return sub_protocol_;
216 }
217
218 std::string WebSocketBasicStream::GetExtensions() const { return extensions_; }
219
220 int WebSocketBasicStream::SendHandshakeRequest(
221 const GURL& url,
222 const HttpRequestHeaders& headers,
223 HttpResponseInfo* response_info,
224 const CompletionCallback& callback) {
225 WEBSOCKET_HANDSHAKE_UNIMPLEMENTED();
226 }
227
228 int WebSocketBasicStream::ReadHandshakeResponse(
229 const CompletionCallback& callback) {
230 WEBSOCKET_HANDSHAKE_UNIMPLEMENTED();
231 }
232
233 /*static*/
234 scoped_ptr<WebSocketBasicStream>
235 WebSocketBasicStream::CreateWebSocketBasicStreamForTesting(
236 scoped_ptr<ClientSocketHandle> connection,
237 const scoped_refptr<GrowableIOBuffer>& http_read_buffer,
238 const std::string& sub_protocol,
239 const std::string& extensions,
240 WebSocketMaskingKeyGeneratorFunction key_generator_function) {
241 scoped_ptr<WebSocketBasicStream> stream(
242 new WebSocketBasicStream(connection.Pass()));
243 if (http_read_buffer) {
244 stream->http_read_buffer_ = http_read_buffer;
245 }
246 stream->sub_protocol_ = sub_protocol;
247 stream->extensions_ = extensions;
248 stream->generate_websocket_masking_key_ = key_generator_function;
249 return stream.Pass();
250 }
251
252 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698