OLD | NEW |
---|---|
(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 | |
OLD | NEW |