OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/websockets/websocket_channel.h" | 5 #include "net/websockets/websocket_channel.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/basictypes.h" // for size_t | 9 #include "base/basictypes.h" // for size_t |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/compiler_specific.h" | |
11 #include "base/safe_numerics.h" | 12 #include "base/safe_numerics.h" |
12 #include "base/strings/string_util.h" | 13 #include "base/strings/string_util.h" |
13 #include "net/base/big_endian.h" | 14 #include "net/base/big_endian.h" |
14 #include "net/base/io_buffer.h" | 15 #include "net/base/io_buffer.h" |
15 #include "net/base/net_log.h" | 16 #include "net/base/net_log.h" |
16 #include "net/websockets/websocket_errors.h" | 17 #include "net/websockets/websocket_errors.h" |
17 #include "net/websockets/websocket_event_interface.h" | 18 #include "net/websockets/websocket_event_interface.h" |
18 #include "net/websockets/websocket_frame.h" | 19 #include "net/websockets/websocket_frame.h" |
19 #include "net/websockets/websocket_mux.h" | 20 #include "net/websockets/websocket_mux.h" |
20 #include "net/websockets/websocket_stream.h" | 21 #include "net/websockets/websocket_stream.h" |
21 | 22 |
22 namespace net { | 23 namespace net { |
23 | 24 |
24 namespace { | 25 namespace { |
25 | 26 |
26 const int kDefaultSendQuotaLowWaterMark = 1 << 16; | 27 const int kDefaultSendQuotaLowWaterMark = 1 << 16; |
27 const int kDefaultSendQuotaHighWaterMark = 1 << 17; | 28 const int kDefaultSendQuotaHighWaterMark = 1 << 17; |
28 const size_t kWebSocketCloseCodeLength = 2; | 29 const size_t kWebSocketCloseCodeLength = 2; |
30 typedef WebSocketEventInterface::ChannelState ChannelState; | |
31 const ChannelState CHANNEL_ALIVE = WebSocketEventInterface::CHANNEL_ALIVE; | |
32 const ChannelState CHANNEL_DELETED = WebSocketEventInterface::CHANNEL_DELETED; | |
29 | 33 |
30 } // namespace | 34 } // namespace |
31 | 35 |
32 // A class to encapsulate a set of frames and information about the size of | 36 // A class to encapsulate a set of frames and information about the size of |
33 // those frames. | 37 // those frames. |
34 class WebSocketChannel::SendBuffer { | 38 class WebSocketChannel::SendBuffer { |
35 public: | 39 public: |
36 SendBuffer() : total_bytes_(0) {} | 40 SendBuffer() : total_bytes_(0) {} |
37 | 41 |
38 // Add a WebSocketFrame to the buffer and increase total_bytes_. | 42 // Add a WebSocketFrame to the buffer and increase total_bytes_. |
(...skipping 19 matching lines...) Expand all Loading... | |
58 | 62 |
59 // Implementation of WebSocketStream::ConnectDelegate that simply forwards the | 63 // Implementation of WebSocketStream::ConnectDelegate that simply forwards the |
60 // calls on to the WebSocketChannel that created it. | 64 // calls on to the WebSocketChannel that created it. |
61 class WebSocketChannel::ConnectDelegate | 65 class WebSocketChannel::ConnectDelegate |
62 : public WebSocketStream::ConnectDelegate { | 66 : public WebSocketStream::ConnectDelegate { |
63 public: | 67 public: |
64 explicit ConnectDelegate(WebSocketChannel* creator) : creator_(creator) {} | 68 explicit ConnectDelegate(WebSocketChannel* creator) : creator_(creator) {} |
65 | 69 |
66 virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE { | 70 virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE { |
67 creator_->OnConnectSuccess(stream.Pass()); | 71 creator_->OnConnectSuccess(stream.Pass()); |
72 // |this| may have been deleted. | |
68 } | 73 } |
69 | 74 |
70 virtual void OnFailure(uint16 websocket_error) OVERRIDE { | 75 virtual void OnFailure(uint16 websocket_error) OVERRIDE { |
71 creator_->OnConnectFailure(websocket_error); | 76 creator_->OnConnectFailure(websocket_error); |
77 // |this| has been deleted. | |
72 } | 78 } |
73 | 79 |
74 private: | 80 private: |
75 // A pointer to the WebSocketChannel that created this object. There is no | 81 // A pointer to the WebSocketChannel that created this object. There is no |
76 // danger of this pointer being stale, because deleting the WebSocketChannel | 82 // danger of this pointer being stale, because deleting the WebSocketChannel |
77 // cancels the connect process, deleting this object and preventing its | 83 // cancels the connect process, deleting this object and preventing its |
78 // callbacks from being called. | 84 // callbacks from being called. |
79 WebSocketChannel* const creator_; | 85 WebSocketChannel* const creator_; |
80 | 86 |
81 DISALLOW_COPY_AND_ASSIGN(ConnectDelegate); | 87 DISALLOW_COPY_AND_ASSIGN(ConnectDelegate); |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
137 return; | 143 return; |
138 } | 144 } |
139 if (state_ != CONNECTED) { | 145 if (state_ != CONNECTED) { |
140 NOTREACHED() << "SendFrame() called in state " << state_; | 146 NOTREACHED() << "SendFrame() called in state " << state_; |
141 return; | 147 return; |
142 } | 148 } |
143 if (data.size() > base::checked_numeric_cast<size_t>(current_send_quota_)) { | 149 if (data.size() > base::checked_numeric_cast<size_t>(current_send_quota_)) { |
144 FailChannel(SEND_GOING_AWAY, | 150 FailChannel(SEND_GOING_AWAY, |
145 kWebSocketMuxErrorSendQuotaViolation, | 151 kWebSocketMuxErrorSendQuotaViolation, |
146 "Send quota exceeded"); | 152 "Send quota exceeded"); |
153 // |this| is deleted here. | |
147 return; | 154 return; |
148 } | 155 } |
149 if (!WebSocketFrameHeader::IsKnownDataOpCode(op_code)) { | 156 if (!WebSocketFrameHeader::IsKnownDataOpCode(op_code)) { |
150 LOG(DFATAL) << "Got SendFrame with bogus op_code " << op_code | 157 LOG(DFATAL) << "Got SendFrame with bogus op_code " << op_code |
151 << "; misbehaving renderer? fin=" << fin | 158 << "; misbehaving renderer? fin=" << fin |
152 << " data.size()=" << data.size(); | 159 << " data.size()=" << data.size(); |
153 return; | 160 return; |
154 } | 161 } |
155 current_send_quota_ -= data.size(); | 162 current_send_quota_ -= data.size(); |
156 // TODO(ricea): If current_send_quota_ has dropped below | 163 // TODO(ricea): If current_send_quota_ has dropped below |
157 // send_quota_low_water_mark_, it might be good to increase the "low | 164 // send_quota_low_water_mark_, it might be good to increase the "low |
158 // water mark" and "high water mark", but only if the link to the WebSocket | 165 // water mark" and "high water mark", but only if the link to the WebSocket |
159 // server is not saturated. | 166 // server is not saturated. |
160 // TODO(ricea): For kOpCodeText, do UTF-8 validation? | 167 // TODO(ricea): For kOpCodeText, do UTF-8 validation? |
161 scoped_refptr<IOBuffer> buffer(new IOBuffer(data.size())); | 168 scoped_refptr<IOBuffer> buffer(new IOBuffer(data.size())); |
162 std::copy(data.begin(), data.end(), buffer->data()); | 169 std::copy(data.begin(), data.end(), buffer->data()); |
163 SendIOBuffer(fin, op_code, buffer, data.size()); | 170 ChannelState unused ALLOW_UNUSED = |
171 SendIOBuffer(fin, op_code, buffer, data.size()); | |
172 // |this| may have been deleted. | |
164 } | 173 } |
165 | 174 |
166 void WebSocketChannel::SendFlowControl(int64 quota) { | 175 void WebSocketChannel::SendFlowControl(int64 quota) { |
167 DCHECK_EQ(CONNECTED, state_); | 176 DCHECK_EQ(CONNECTED, state_); |
168 // TODO(ricea): Add interface to WebSocketStream and implement. | 177 // TODO(ricea): Add interface to WebSocketStream and implement. |
169 // stream_->SendFlowControl(quota); | 178 // stream_->SendFlowControl(quota); |
170 } | 179 } |
171 | 180 |
172 void WebSocketChannel::StartClosingHandshake(uint16 code, | 181 void WebSocketChannel::StartClosingHandshake(uint16 code, |
173 const std::string& reason) { | 182 const std::string& reason) { |
174 if (InClosingState()) { | 183 if (InClosingState()) { |
175 VLOG(1) << "StartClosingHandshake called in state " << state_ | 184 VLOG(1) << "StartClosingHandshake called in state " << state_ |
176 << ". This may be a bug, or a harmless race."; | 185 << ". This may be a bug, or a harmless race."; |
177 return; | 186 return; |
178 } | 187 } |
179 if (state_ != CONNECTED) { | 188 if (state_ != CONNECTED) { |
180 NOTREACHED() << "StartClosingHandshake() called in state " << state_; | 189 NOTREACHED() << "StartClosingHandshake() called in state " << state_; |
181 return; | 190 return; |
182 } | 191 } |
183 // TODO(ricea): Validate |code|? Check that |reason| is valid UTF-8? | 192 // TODO(ricea): Validate |code| |
184 // TODO(ricea): There should be a timeout for the closing handshake. | 193 // TODO(ricea): There should be a timeout for the closing handshake. |
185 SendClose(code, reason); // Sets state_ to SEND_CLOSED | 194 ChannelState unused ALLOW_UNUSED = SendClose( |
195 code, IsStringUTF8(reason) ? reason : std::string()); // Sets state_ to | |
196 // SEND_CLOSED | |
197 // If |unused| is CHANNEL_DELETED, then |this| has been deleted. | |
186 } | 198 } |
187 | 199 |
188 void WebSocketChannel::SendAddChannelRequestForTesting( | 200 void WebSocketChannel::SendAddChannelRequestForTesting( |
189 const GURL& socket_url, | 201 const GURL& socket_url, |
190 const std::vector<std::string>& requested_subprotocols, | 202 const std::vector<std::string>& requested_subprotocols, |
191 const GURL& origin, | 203 const GURL& origin, |
192 const WebSocketStreamFactory& factory) { | 204 const WebSocketStreamFactory& factory) { |
193 SendAddChannelRequestWithFactory(socket_url, | 205 SendAddChannelRequestWithFactory( |
194 requested_subprotocols, | 206 socket_url, requested_subprotocols, origin, factory); |
195 origin, | |
196 factory); | |
197 } | 207 } |
198 | 208 |
199 void WebSocketChannel::SendAddChannelRequestWithFactory( | 209 void WebSocketChannel::SendAddChannelRequestWithFactory( |
200 const GURL& socket_url, | 210 const GURL& socket_url, |
201 const std::vector<std::string>& requested_subprotocols, | 211 const std::vector<std::string>& requested_subprotocols, |
202 const GURL& origin, | 212 const GURL& origin, |
203 const WebSocketStreamFactory& factory) { | 213 const WebSocketStreamFactory& factory) { |
204 DCHECK_EQ(FRESHLY_CONSTRUCTED, state_); | 214 DCHECK_EQ(FRESHLY_CONSTRUCTED, state_); |
205 socket_url_ = socket_url; | 215 socket_url_ = socket_url; |
206 scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate( | 216 scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate( |
207 new ConnectDelegate(this)); | 217 new ConnectDelegate(this)); |
208 stream_request_ = factory.Run(socket_url_, | 218 stream_request_ = factory.Run(socket_url_, |
209 requested_subprotocols, | 219 requested_subprotocols, |
210 origin, | 220 origin, |
211 url_request_context_, | 221 url_request_context_, |
212 BoundNetLog(), | 222 BoundNetLog(), |
213 connect_delegate.Pass()); | 223 connect_delegate.Pass()); |
214 state_ = CONNECTING; | 224 state_ = CONNECTING; |
215 } | 225 } |
216 | 226 |
217 void WebSocketChannel::OnConnectSuccess(scoped_ptr<WebSocketStream> stream) { | 227 void WebSocketChannel::OnConnectSuccess(scoped_ptr<WebSocketStream> stream) { |
218 DCHECK(stream); | 228 DCHECK(stream); |
219 DCHECK_EQ(CONNECTING, state_); | 229 DCHECK_EQ(CONNECTING, state_); |
220 stream_ = stream.Pass(); | 230 stream_ = stream.Pass(); |
221 state_ = CONNECTED; | 231 state_ = CONNECTED; |
222 event_interface_->OnAddChannelResponse(false, stream_->GetSubProtocol()); | 232 if (event_interface_->OnAddChannelResponse( |
233 false, stream_->GetSubProtocol()) == CHANNEL_DELETED) | |
234 return; | |
223 | 235 |
224 // TODO(ricea): Get flow control information from the WebSocketStream once we | 236 // TODO(ricea): Get flow control information from the WebSocketStream once we |
225 // have a multiplexing WebSocketStream. | 237 // have a multiplexing WebSocketStream. |
226 current_send_quota_ = send_quota_high_water_mark_; | 238 current_send_quota_ = send_quota_high_water_mark_; |
227 event_interface_->OnFlowControl(send_quota_high_water_mark_); | 239 if (event_interface_->OnFlowControl(send_quota_high_water_mark_) == |
240 CHANNEL_DELETED) | |
241 return; | |
228 | 242 |
229 // |stream_request_| is not used once the connection has succeeded. | 243 // |stream_request_| is not used once the connection has succeeded. |
230 stream_request_.reset(); | 244 stream_request_.reset(); |
231 ReadFrames(); | 245 ChannelState unused ALLOW_UNUSED = ReadFrames(); |
246 // |this| may have been deleted. | |
232 } | 247 } |
233 | 248 |
234 void WebSocketChannel::OnConnectFailure(uint16 websocket_error) { | 249 void WebSocketChannel::OnConnectFailure(uint16 websocket_error) { |
235 DCHECK_EQ(CONNECTING, state_); | 250 DCHECK_EQ(CONNECTING, state_); |
236 state_ = CLOSED; | 251 state_ = CLOSED; |
237 stream_request_.reset(); | 252 stream_request_.reset(); |
238 event_interface_->OnAddChannelResponse(true, ""); | 253 ChannelState unused ALLOW_UNUSED = |
254 event_interface_->OnAddChannelResponse(true, ""); | |
255 // |this| has been deleted. | |
239 } | 256 } |
240 | 257 |
241 void WebSocketChannel::WriteFrames() { | 258 ChannelState WebSocketChannel::WriteFrames() { |
242 int result = OK; | 259 int result = OK; |
243 do { | 260 do { |
244 // This use of base::Unretained is safe because this object owns the | 261 // This use of base::Unretained is safe because this object owns the |
245 // WebSocketStream and destroying it cancels all callbacks. | 262 // WebSocketStream and destroying it cancels all callbacks. |
246 result = stream_->WriteFrames( | 263 result = stream_->WriteFrames( |
247 data_being_sent_->frames(), | 264 data_being_sent_->frames(), |
248 base::Bind( | 265 base::Bind(base::IgnoreResult(&WebSocketChannel::OnWriteDone), |
249 &WebSocketChannel::OnWriteDone, base::Unretained(this), false)); | 266 base::Unretained(this), |
267 false)); | |
250 if (result != ERR_IO_PENDING) { | 268 if (result != ERR_IO_PENDING) { |
251 OnWriteDone(true, result); | 269 if (OnWriteDone(true, result) == CHANNEL_DELETED) |
270 return CHANNEL_DELETED; | |
252 } | 271 } |
253 } while (result == OK && data_being_sent_); | 272 } while (result == OK && data_being_sent_); |
273 return CHANNEL_ALIVE; | |
254 } | 274 } |
255 | 275 |
256 void WebSocketChannel::OnWriteDone(bool synchronous, int result) { | 276 ChannelState WebSocketChannel::OnWriteDone(bool synchronous, int result) { |
257 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | 277 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); |
258 DCHECK_NE(CONNECTING, state_); | 278 DCHECK_NE(CONNECTING, state_); |
259 DCHECK_NE(ERR_IO_PENDING, result); | 279 DCHECK_NE(ERR_IO_PENDING, result); |
260 DCHECK(data_being_sent_); | 280 DCHECK(data_being_sent_); |
261 switch (result) { | 281 switch (result) { |
262 case OK: | 282 case OK: |
263 if (data_to_send_next_) { | 283 if (data_to_send_next_) { |
264 data_being_sent_ = data_to_send_next_.Pass(); | 284 data_being_sent_ = data_to_send_next_.Pass(); |
265 if (!synchronous) { | 285 if (!synchronous) { |
266 WriteFrames(); | 286 if (WriteFrames() == CHANNEL_DELETED) |
287 return CHANNEL_DELETED; | |
267 } | 288 } |
268 } else { | 289 } else { |
269 data_being_sent_.reset(); | 290 data_being_sent_.reset(); |
270 if (current_send_quota_ < send_quota_low_water_mark_) { | 291 if (current_send_quota_ < send_quota_low_water_mark_) { |
271 // TODO(ricea): Increase low_water_mark and high_water_mark if | 292 // TODO(ricea): Increase low_water_mark and high_water_mark if |
272 // throughput is high, reduce them if throughput is low. Low water | 293 // throughput is high, reduce them if throughput is low. Low water |
273 // mark needs to be >= the bandwidth delay product *of the IPC | 294 // mark needs to be >= the bandwidth delay product *of the IPC |
274 // channel*. Because factors like context-switch time, thread wake-up | 295 // channel*. Because factors like context-switch time, thread wake-up |
275 // time, and bus speed come into play it is complex and probably needs | 296 // time, and bus speed come into play it is complex and probably needs |
276 // to be determined empirically. | 297 // to be determined empirically. |
277 DCHECK_LE(send_quota_low_water_mark_, send_quota_high_water_mark_); | 298 DCHECK_LE(send_quota_low_water_mark_, send_quota_high_water_mark_); |
278 // TODO(ricea): Truncate quota by the quota specified by the remote | 299 // TODO(ricea): Truncate quota by the quota specified by the remote |
279 // server, if the protocol in use supports quota. | 300 // server, if the protocol in use supports quota. |
280 int fresh_quota = send_quota_high_water_mark_ - current_send_quota_; | 301 int fresh_quota = send_quota_high_water_mark_ - current_send_quota_; |
281 current_send_quota_ += fresh_quota; | 302 current_send_quota_ += fresh_quota; |
282 event_interface_->OnFlowControl(fresh_quota); | 303 if (event_interface_->OnFlowControl(fresh_quota) == CHANNEL_DELETED) |
304 return CHANNEL_DELETED; | |
283 } | 305 } |
284 } | 306 } |
285 return; | 307 return CHANNEL_ALIVE; |
286 | 308 |
287 // If a recoverable error condition existed, it would go here. | 309 // If a recoverable error condition existed, it would go here. |
288 | 310 |
289 default: | 311 default: |
290 DCHECK_LT(result, 0) | 312 DCHECK_LT(result, 0) |
291 << "WriteFrames() should only return OK or ERR_ codes"; | 313 << "WriteFrames() should only return OK or ERR_ codes"; |
292 stream_->Close(); | 314 stream_->Close(); |
293 if (state_ != CLOSED) { | 315 if (state_ != CLOSED) { |
yhirano
2013/10/10 09:22:27
Given that event_interface_->OnDropChannel() and e
Adam Rice
2013/10/11 05:42:54
Yes. I changed all the conditions on CLOSED to DCH
| |
294 state_ = CLOSED; | 316 state_ = CLOSED; |
295 event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure, | 317 return event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure, |
296 "Abnormal Closure"); | 318 "Abnormal Closure"); |
297 } | 319 } |
298 return; | 320 return CHANNEL_ALIVE; |
299 } | 321 } |
300 } | 322 } |
301 | 323 |
302 void WebSocketChannel::ReadFrames() { | 324 ChannelState WebSocketChannel::ReadFrames() { |
303 int result = OK; | 325 int result = OK; |
304 do { | 326 do { |
305 // This use of base::Unretained is safe because this object owns the | 327 // This use of base::Unretained is safe because this object owns the |
306 // WebSocketStream, and any pending reads will be cancelled when it is | 328 // WebSocketStream, and any pending reads will be cancelled when it is |
307 // destroyed. | 329 // destroyed. |
308 result = stream_->ReadFrames( | 330 result = stream_->ReadFrames( |
309 &read_frames_, | 331 &read_frames_, |
310 base::Bind( | 332 base::Bind(base::IgnoreResult(&WebSocketChannel::OnReadDone), |
311 &WebSocketChannel::OnReadDone, base::Unretained(this), false)); | 333 base::Unretained(this), |
334 false)); | |
312 if (result != ERR_IO_PENDING) { | 335 if (result != ERR_IO_PENDING) { |
313 OnReadDone(true, result); | 336 if (OnReadDone(true, result) == CHANNEL_DELETED) |
337 return CHANNEL_DELETED; | |
314 } | 338 } |
315 } while (result == OK && state_ != CLOSED); | 339 } while (result == OK && state_ != CLOSED); |
340 return CHANNEL_ALIVE; | |
316 } | 341 } |
317 | 342 |
318 void WebSocketChannel::OnReadDone(bool synchronous, int result) { | 343 ChannelState WebSocketChannel::OnReadDone(bool synchronous, int result) { |
319 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | 344 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); |
320 DCHECK_NE(CONNECTING, state_); | 345 DCHECK_NE(CONNECTING, state_); |
321 DCHECK_NE(ERR_IO_PENDING, result); | 346 DCHECK_NE(ERR_IO_PENDING, result); |
322 switch (result) { | 347 switch (result) { |
323 case OK: | 348 case OK: |
324 // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection | 349 // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection |
325 // with no data read, not an empty response. | 350 // with no data read, not an empty response. |
326 DCHECK(!read_frames_.empty()) | 351 DCHECK(!read_frames_.empty()) |
327 << "ReadFrames() returned OK, but nothing was read."; | 352 << "ReadFrames() returned OK, but nothing was read."; |
328 for (size_t i = 0; i < read_frames_.size(); ++i) { | 353 for (size_t i = 0; i < read_frames_.size(); ++i) { |
329 scoped_ptr<WebSocketFrame> frame(read_frames_[i]); | 354 scoped_ptr<WebSocketFrame> frame(read_frames_[i]); |
330 read_frames_[i] = NULL; | 355 read_frames_[i] = NULL; |
331 ProcessFrame(frame.Pass()); | 356 if (ProcessFrame(frame.Pass()) == CHANNEL_DELETED) |
357 return CHANNEL_DELETED; | |
332 } | 358 } |
333 read_frames_.clear(); | 359 read_frames_.clear(); |
334 // There should always be a call to ReadFrames pending. | 360 // There should always be a call to ReadFrames pending. |
335 // TODO(ricea): Unless we are out of quota. | 361 // TODO(ricea): Unless we are out of quota. |
336 if (!synchronous && state_ != CLOSED) { | 362 if (!synchronous && state_ != CLOSED) { |
337 ReadFrames(); | 363 if (ReadFrames() == CHANNEL_DELETED) |
364 return CHANNEL_DELETED; | |
338 } | 365 } |
339 return; | 366 return CHANNEL_ALIVE; |
340 | 367 |
341 case ERR_WS_PROTOCOL_ERROR: | 368 case ERR_WS_PROTOCOL_ERROR: |
342 FailChannel(SEND_REAL_ERROR, | 369 return FailChannel(SEND_REAL_ERROR, |
343 kWebSocketErrorProtocolError, | 370 kWebSocketErrorProtocolError, |
344 "WebSocket Protocol Error"); | 371 "WebSocket Protocol Error"); |
345 return; | |
346 | 372 |
347 default: | 373 default: |
348 DCHECK_LT(result, 0) | 374 DCHECK_LT(result, 0) |
349 << "ReadFrames() should only return OK or ERR_ codes"; | 375 << "ReadFrames() should only return OK or ERR_ codes"; |
350 stream_->Close(); | 376 stream_->Close(); |
351 if (state_ != CLOSED) { | 377 if (state_ != CLOSED) { |
yhirano
2013/10/10 09:22:27
Same as L315
Adam Rice
2013/10/11 05:42:54
Done.
| |
352 state_ = CLOSED; | 378 state_ = CLOSED; |
353 uint16 code = kWebSocketErrorAbnormalClosure; | 379 uint16 code = kWebSocketErrorAbnormalClosure; |
354 std::string reason = "Abnormal Closure"; | 380 std::string reason = "Abnormal Closure"; |
355 if (closing_code_ != 0) { | 381 if (closing_code_ != 0) { |
356 code = closing_code_; | 382 code = closing_code_; |
357 reason = closing_reason_; | 383 reason = closing_reason_; |
358 } | 384 } |
359 event_interface_->OnDropChannel(code, reason); | 385 return event_interface_->OnDropChannel(code, reason); |
360 } | 386 } |
361 return; | 387 NOTREACHED(); |
388 return CHANNEL_ALIVE; | |
362 } | 389 } |
363 } | 390 } |
364 | 391 |
365 void WebSocketChannel::ProcessFrame(scoped_ptr<WebSocketFrame> frame) { | 392 ChannelState WebSocketChannel::ProcessFrame(scoped_ptr<WebSocketFrame> frame) { |
366 if (frame->header.masked) { | 393 if (frame->header.masked) { |
367 // RFC6455 Section 5.1 "A client MUST close a connection if it detects a | 394 // RFC6455 Section 5.1 "A client MUST close a connection if it detects a |
368 // masked frame." | 395 // masked frame." |
369 FailChannel(SEND_REAL_ERROR, | 396 return FailChannel(SEND_REAL_ERROR, |
370 kWebSocketErrorProtocolError, | 397 kWebSocketErrorProtocolError, |
371 "Masked frame from server"); | 398 "Masked frame from server"); |
372 return; | |
373 } | 399 } |
374 const WebSocketFrameHeader::OpCode opcode = frame->header.opcode; | 400 const WebSocketFrameHeader::OpCode opcode = frame->header.opcode; |
375 if (WebSocketFrameHeader::IsKnownControlOpCode(opcode) && | 401 if (WebSocketFrameHeader::IsKnownControlOpCode(opcode) && |
376 !frame->header.final) { | 402 !frame->header.final) { |
377 FailChannel(SEND_REAL_ERROR, | 403 return FailChannel(SEND_REAL_ERROR, |
378 kWebSocketErrorProtocolError, | 404 kWebSocketErrorProtocolError, |
379 "Control message with FIN bit unset received"); | 405 "Control message with FIN bit unset received"); |
380 return; | |
381 } | 406 } |
382 | 407 |
383 // Respond to the frame appropriately to its type. | 408 // Respond to the frame appropriately to its type. |
384 HandleFrame( | 409 return HandleFrame( |
385 opcode, frame->header.final, frame->data, frame->header.payload_length); | 410 opcode, frame->header.final, frame->data, frame->header.payload_length); |
386 } | 411 } |
387 | 412 |
388 void WebSocketChannel::HandleFrame(const WebSocketFrameHeader::OpCode opcode, | 413 ChannelState WebSocketChannel::HandleFrame( |
389 bool final, | 414 const WebSocketFrameHeader::OpCode opcode, |
390 const scoped_refptr<IOBuffer>& data_buffer, | 415 bool final, |
391 size_t size) { | 416 const scoped_refptr<IOBuffer>& data_buffer, |
417 size_t size) { | |
392 DCHECK_NE(RECV_CLOSED, state_) | 418 DCHECK_NE(RECV_CLOSED, state_) |
393 << "HandleFrame() does not support being called re-entrantly from within " | 419 << "HandleFrame() does not support being called re-entrantly from within " |
394 "SendClose()"; | 420 "SendClose()"; |
395 if (state_ == CLOSED || state_ == CLOSE_WAIT) { | 421 if (state_ == CLOSED || state_ == CLOSE_WAIT) { |
yhirano
2013/10/10 09:22:27
Same as L315
Adam Rice
2013/10/11 05:42:54
Done.
| |
396 DVLOG_IF(1, state_ == CLOSED) << "A frame was received while in the CLOSED " | 422 DCHECK(state_ != CLOSED); |
397 "state. This is possible after a channel " | |
398 "failed, but should be very rare."; | |
399 std::string frame_name; | 423 std::string frame_name; |
400 switch (opcode) { | 424 switch (opcode) { |
401 case WebSocketFrameHeader::kOpCodeText: // fall-thru | 425 case WebSocketFrameHeader::kOpCodeText: // fall-thru |
402 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru | 426 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru |
403 case WebSocketFrameHeader::kOpCodeContinuation: | 427 case WebSocketFrameHeader::kOpCodeContinuation: |
404 frame_name = "Data frame"; | 428 frame_name = "Data frame"; |
405 break; | 429 break; |
406 | 430 |
407 case WebSocketFrameHeader::kOpCodePing: | 431 case WebSocketFrameHeader::kOpCodePing: |
408 frame_name = "Ping"; | 432 frame_name = "Ping"; |
409 break; | 433 break; |
410 | 434 |
411 case WebSocketFrameHeader::kOpCodePong: | 435 case WebSocketFrameHeader::kOpCodePong: |
412 frame_name = "Pong"; | 436 frame_name = "Pong"; |
413 break; | 437 break; |
414 | 438 |
415 case WebSocketFrameHeader::kOpCodeClose: | 439 case WebSocketFrameHeader::kOpCodeClose: |
416 frame_name = "Close"; | 440 frame_name = "Close"; |
417 break; | 441 break; |
418 | 442 |
419 default: | 443 default: |
420 frame_name = "Unknown frame type"; | 444 frame_name = "Unknown frame type"; |
421 break; | 445 break; |
422 } | 446 } |
423 // SEND_REAL_ERROR makes no difference here, as FailChannel() won't send | 447 // SEND_REAL_ERROR makes no difference here, as FailChannel() won't send |
424 // another Close frame. | 448 // another Close frame. |
425 FailChannel(SEND_REAL_ERROR, | 449 return FailChannel(SEND_REAL_ERROR, |
426 kWebSocketErrorProtocolError, | 450 kWebSocketErrorProtocolError, |
427 frame_name + " received after close"); | 451 frame_name + " received after close"); |
428 return; | |
429 } | 452 } |
430 switch (opcode) { | 453 switch (opcode) { |
431 case WebSocketFrameHeader::kOpCodeText: // fall-thru | 454 case WebSocketFrameHeader::kOpCodeText: // fall-thru |
432 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru | 455 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru |
433 case WebSocketFrameHeader::kOpCodeContinuation: | 456 case WebSocketFrameHeader::kOpCodeContinuation: |
434 if (state_ == CONNECTED) { | 457 if (state_ == CONNECTED) { |
435 // TODO(ricea): Need to fail the connection if UTF-8 is invalid | 458 // TODO(ricea): Need to fail the connection if UTF-8 is invalid |
436 // post-reassembly. Requires a streaming UTF-8 validator. | 459 // post-reassembly. Requires a streaming UTF-8 validator. |
437 // TODO(ricea): Can this copy be eliminated? | 460 // TODO(ricea): Can this copy be eliminated? |
438 const char* const data_begin = data_buffer->data(); | 461 const char* const data_begin = data_buffer->data(); |
439 const char* const data_end = data_begin + size; | 462 const char* const data_end = data_begin + size; |
440 const std::vector<char> data(data_begin, data_end); | 463 const std::vector<char> data(data_begin, data_end); |
441 // TODO(ricea): Handle the case when ReadFrames returns far | 464 // TODO(ricea): Handle the case when ReadFrames returns far |
442 // more data at once than should be sent in a single IPC. This needs to | 465 // more data at once than should be sent in a single IPC. This needs to |
443 // be handled carefully, as an overloaded IO thread is one possible | 466 // be handled carefully, as an overloaded IO thread is one possible |
444 // cause of receiving very large chunks. | 467 // cause of receiving very large chunks. |
445 | 468 |
446 // Sends the received frame to the renderer process. | 469 // Sends the received frame to the renderer process. |
447 event_interface_->OnDataFrame(final, opcode, data); | 470 if (event_interface_->OnDataFrame(final, opcode, data) == |
471 CHANNEL_DELETED) | |
472 return CHANNEL_DELETED; | |
448 } else { | 473 } else { |
449 VLOG(3) << "Ignored data packet received in state " << state_; | 474 VLOG(3) << "Ignored data packet received in state " << state_; |
450 } | 475 } |
451 return; | 476 return CHANNEL_ALIVE; |
452 | 477 |
453 case WebSocketFrameHeader::kOpCodePing: | 478 case WebSocketFrameHeader::kOpCodePing: |
454 VLOG(1) << "Got Ping of size " << size; | 479 VLOG(1) << "Got Ping of size " << size; |
455 if (state_ == CONNECTED) { | 480 if (state_ == CONNECTED) { |
456 SendIOBuffer( | 481 if (SendIOBuffer( |
457 true, WebSocketFrameHeader::kOpCodePong, data_buffer, size); | 482 true, WebSocketFrameHeader::kOpCodePong, data_buffer, size) == |
483 CHANNEL_DELETED) | |
484 return CHANNEL_DELETED; | |
458 } else { | 485 } else { |
459 VLOG(3) << "Ignored ping in state " << state_; | 486 VLOG(3) << "Ignored ping in state " << state_; |
460 } | 487 } |
461 return; | 488 return CHANNEL_ALIVE; |
462 | 489 |
463 case WebSocketFrameHeader::kOpCodePong: | 490 case WebSocketFrameHeader::kOpCodePong: |
464 VLOG(1) << "Got Pong of size " << size; | 491 VLOG(1) << "Got Pong of size " << size; |
465 // There is no need to do anything with pong messages. | 492 // There is no need to do anything with pong messages. |
466 return; | 493 return CHANNEL_ALIVE; |
467 | 494 |
468 case WebSocketFrameHeader::kOpCodeClose: { | 495 case WebSocketFrameHeader::kOpCodeClose: { |
469 uint16 code = kWebSocketNormalClosure; | 496 uint16 code = kWebSocketNormalClosure; |
470 std::string reason; | 497 std::string reason; |
471 ParseClose(data_buffer, size, &code, &reason); | 498 ParseClose(data_buffer, size, &code, &reason); |
472 // TODO(ricea): Find a way to safely log the message from the close | 499 // TODO(ricea): Find a way to safely log the message from the close |
473 // message (escape control codes and so on). | 500 // message (escape control codes and so on). |
474 VLOG(1) << "Got Close with code " << code; | 501 VLOG(1) << "Got Close with code " << code; |
475 switch (state_) { | 502 switch (state_) { |
476 case CONNECTED: | 503 case CONNECTED: |
477 state_ = RECV_CLOSED; | 504 state_ = RECV_CLOSED; |
478 SendClose(code, reason); // Sets state_ to CLOSE_WAIT | 505 if (SendClose(code, reason) // Sets state_ to CLOSE_WAIT |
479 event_interface_->OnClosingHandshake(); | 506 == CHANNEL_DELETED) |
507 return CHANNEL_DELETED; | |
508 if (event_interface_->OnClosingHandshake() == CHANNEL_DELETED) | |
509 return CHANNEL_DELETED; | |
480 closing_code_ = code; | 510 closing_code_ = code; |
481 closing_reason_ = reason; | 511 closing_reason_ = reason; |
482 break; | 512 break; |
483 | 513 |
484 case SEND_CLOSED: | 514 case SEND_CLOSED: |
485 state_ = CLOSE_WAIT; | 515 state_ = CLOSE_WAIT; |
486 // From RFC6455 section 7.1.5: "Each endpoint | 516 // From RFC6455 section 7.1.5: "Each endpoint |
487 // will see the status code sent by the other end as _The WebSocket | 517 // will see the status code sent by the other end as _The WebSocket |
488 // Connection Close Code_." | 518 // Connection Close Code_." |
489 closing_code_ = code; | 519 closing_code_ = code; |
490 closing_reason_ = reason; | 520 closing_reason_ = reason; |
491 break; | 521 break; |
492 | 522 |
493 default: | 523 default: |
494 LOG(DFATAL) << "Got Close in unexpected state " << state_; | 524 LOG(DFATAL) << "Got Close in unexpected state " << state_; |
495 break; | 525 break; |
496 } | 526 } |
497 return; | 527 return CHANNEL_ALIVE; |
498 } | 528 } |
499 | 529 |
500 default: | 530 default: |
501 FailChannel( | 531 return FailChannel( |
502 SEND_REAL_ERROR, kWebSocketErrorProtocolError, "Unknown opcode"); | 532 SEND_REAL_ERROR, kWebSocketErrorProtocolError, "Unknown opcode"); |
503 return; | |
504 } | 533 } |
505 } | 534 } |
506 | 535 |
507 void WebSocketChannel::SendIOBuffer(bool fin, | 536 ChannelState WebSocketChannel::SendIOBuffer( |
508 WebSocketFrameHeader::OpCode op_code, | 537 bool fin, |
509 const scoped_refptr<IOBuffer>& buffer, | 538 WebSocketFrameHeader::OpCode op_code, |
510 size_t size) { | 539 const scoped_refptr<IOBuffer>& buffer, |
540 size_t size) { | |
511 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); | 541 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); |
512 DCHECK(stream_); | 542 DCHECK(stream_); |
513 scoped_ptr<WebSocketFrame> frame(new WebSocketFrame(op_code)); | 543 scoped_ptr<WebSocketFrame> frame(new WebSocketFrame(op_code)); |
514 WebSocketFrameHeader& header = frame->header; | 544 WebSocketFrameHeader& header = frame->header; |
515 header.final = fin; | 545 header.final = fin; |
516 header.masked = true; | 546 header.masked = true; |
517 header.payload_length = size; | 547 header.payload_length = size; |
518 frame->data = buffer; | 548 frame->data = buffer; |
519 if (data_being_sent_) { | 549 if (data_being_sent_) { |
520 // Either the link to the WebSocket server is saturated, or several messages | 550 // Either the link to the WebSocket server is saturated, or several messages |
521 // are being sent in a batch. | 551 // are being sent in a batch. |
522 // TODO(ricea): Keep some statistics to work out the situation and adjust | 552 // TODO(ricea): Keep some statistics to work out the situation and adjust |
523 // quota appropriately. | 553 // quota appropriately. |
524 if (!data_to_send_next_) | 554 if (!data_to_send_next_) |
525 data_to_send_next_.reset(new SendBuffer); | 555 data_to_send_next_.reset(new SendBuffer); |
526 data_to_send_next_->AddFrame(frame.Pass()); | 556 data_to_send_next_->AddFrame(frame.Pass()); |
527 } else { | 557 } else { |
528 data_being_sent_.reset(new SendBuffer); | 558 data_being_sent_.reset(new SendBuffer); |
529 data_being_sent_->AddFrame(frame.Pass()); | 559 data_being_sent_->AddFrame(frame.Pass()); |
530 WriteFrames(); | 560 if (WriteFrames() == CHANNEL_DELETED) |
561 return CHANNEL_DELETED; | |
531 } | 562 } |
563 return CHANNEL_ALIVE; | |
532 } | 564 } |
533 | 565 |
534 void WebSocketChannel::FailChannel(ExposeError expose, | 566 ChannelState WebSocketChannel::FailChannel(ExposeError expose, |
535 uint16 code, | 567 uint16 code, |
536 const std::string& reason) { | 568 const std::string& reason) { |
537 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | 569 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); |
538 DCHECK_NE(CONNECTING, state_); | 570 DCHECK_NE(CONNECTING, state_); |
539 // TODO(ricea): Logging. | 571 // TODO(ricea): Logging. |
540 State old_state = state_; | 572 State old_state = state_; |
541 if (state_ == CONNECTED) { | 573 if (state_ == CONNECTED) { |
542 uint16 send_code = kWebSocketErrorGoingAway; | 574 uint16 send_code = kWebSocketErrorGoingAway; |
543 std::string send_reason = "Internal Error"; | 575 std::string send_reason = "Internal Error"; |
544 if (expose == SEND_REAL_ERROR) { | 576 if (expose == SEND_REAL_ERROR) { |
545 send_code = code; | 577 send_code = code; |
546 send_reason = reason; | 578 send_reason = reason; |
547 } | 579 } |
548 SendClose(send_code, send_reason); // Sets state_ to SEND_CLOSED | 580 if (SendClose(send_code, send_reason) // Sets state_ to SEND_CLOSED |
581 == CHANNEL_DELETED) | |
582 return CHANNEL_DELETED; | |
549 } | 583 } |
550 // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates the browser | 584 // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates the browser |
551 // should close the connection itself without waiting for the closing | 585 // should close the connection itself without waiting for the closing |
552 // handshake. | 586 // handshake. |
553 stream_->Close(); | 587 stream_->Close(); |
554 state_ = CLOSED; | 588 state_ = CLOSED; |
555 | 589 |
556 if (old_state != CLOSED) { | 590 if (old_state != CLOSED) { |
yhirano
2013/10/10 09:22:27
Same as L315
Adam Rice
2013/10/11 05:42:54
Done.
| |
557 event_interface_->OnDropChannel(code, reason); | 591 return event_interface_->OnDropChannel(code, reason); |
558 } | 592 } |
593 NOTREACHED(); | |
594 return CHANNEL_ALIVE; | |
559 } | 595 } |
560 | 596 |
561 void WebSocketChannel::SendClose(uint16 code, const std::string& reason) { | 597 ChannelState WebSocketChannel::SendClose(uint16 code, |
598 const std::string& reason) { | |
562 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); | 599 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); |
563 // TODO(ricea): Ensure reason.length() <= 123 | 600 // TODO(ricea): Ensure reason.length() <= 123 |
564 scoped_refptr<IOBuffer> body; | 601 scoped_refptr<IOBuffer> body; |
565 size_t size = 0; | 602 size_t size = 0; |
566 if (code == kWebSocketErrorNoStatusReceived) { | 603 if (code == kWebSocketErrorNoStatusReceived) { |
567 // Special case: translate kWebSocketErrorNoStatusReceived into a Close | 604 // Special case: translate kWebSocketErrorNoStatusReceived into a Close |
568 // frame with no payload. | 605 // frame with no payload. |
569 body = new IOBuffer(0); | 606 body = new IOBuffer(0); |
570 } else { | 607 } else { |
571 const size_t payload_length = kWebSocketCloseCodeLength + reason.length(); | 608 const size_t payload_length = kWebSocketCloseCodeLength + reason.length(); |
572 body = new IOBuffer(payload_length); | 609 body = new IOBuffer(payload_length); |
573 size = payload_length; | 610 size = payload_length; |
574 WriteBigEndian(body->data(), code); | 611 WriteBigEndian(body->data(), code); |
575 COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength, | 612 COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength, |
576 they_should_both_be_two); | 613 they_should_both_be_two); |
577 std::copy( | 614 std::copy( |
578 reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength); | 615 reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength); |
579 } | 616 } |
580 SendIOBuffer(true, WebSocketFrameHeader::kOpCodeClose, body, size); | 617 if (SendIOBuffer(true, WebSocketFrameHeader::kOpCodeClose, body, size) == |
618 CHANNEL_DELETED) | |
619 return CHANNEL_DELETED; | |
581 state_ = (state_ == CONNECTED) ? SEND_CLOSED : CLOSE_WAIT; | 620 state_ = (state_ == CONNECTED) ? SEND_CLOSED : CLOSE_WAIT; |
621 return CHANNEL_ALIVE; | |
582 } | 622 } |
583 | 623 |
584 void WebSocketChannel::ParseClose(const scoped_refptr<IOBuffer>& buffer, | 624 void WebSocketChannel::ParseClose(const scoped_refptr<IOBuffer>& buffer, |
585 size_t size, | 625 size_t size, |
586 uint16* code, | 626 uint16* code, |
587 std::string* reason) { | 627 std::string* reason) { |
588 const char* data = buffer->data(); | 628 const char* data = buffer->data(); |
589 reason->clear(); | 629 reason->clear(); |
590 if (size < kWebSocketCloseCodeLength) { | 630 if (size < kWebSocketCloseCodeLength) { |
591 *code = kWebSocketErrorNoStatusReceived; | 631 *code = kWebSocketErrorNoStatusReceived; |
(...skipping 12 matching lines...) Expand all Loading... | |
604 if (unchecked_code >= static_cast<uint16>(kWebSocketNormalClosure) && | 644 if (unchecked_code >= static_cast<uint16>(kWebSocketNormalClosure) && |
605 unchecked_code <= | 645 unchecked_code <= |
606 static_cast<uint16>(kWebSocketErrorPrivateReservedMax)) { | 646 static_cast<uint16>(kWebSocketErrorPrivateReservedMax)) { |
607 *code = unchecked_code; | 647 *code = unchecked_code; |
608 } else { | 648 } else { |
609 VLOG(1) << "Close frame contained code outside of the valid range: " | 649 VLOG(1) << "Close frame contained code outside of the valid range: " |
610 << unchecked_code; | 650 << unchecked_code; |
611 *code = kWebSocketErrorAbnormalClosure; | 651 *code = kWebSocketErrorAbnormalClosure; |
612 } | 652 } |
613 std::string text(data + kWebSocketCloseCodeLength, data + size); | 653 std::string text(data + kWebSocketCloseCodeLength, data + size); |
614 // TODO(ricea): Is this check strict enough? In particular, check the | 654 // IsStringUTF8() blocks surrogate pairs and non-characters, so it is strictly |
615 // "Security Considerations" from RFC3629. | 655 // stronger than required by RFC3629. |
616 if (IsStringUTF8(text)) { | 656 if (IsStringUTF8(text)) { |
617 reason->swap(text); | 657 reason->swap(text); |
618 } | 658 } |
619 } | 659 } |
620 | 660 |
621 } // namespace net | 661 } // namespace net |
OLD | NEW |