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 "base/time/time.h" | 14 #include "base/time/time.h" |
14 #include "net/base/big_endian.h" | 15 #include "net/base/big_endian.h" |
15 #include "net/base/io_buffer.h" | 16 #include "net/base/io_buffer.h" |
16 #include "net/base/net_log.h" | 17 #include "net/base/net_log.h" |
17 #include "net/websockets/websocket_errors.h" | 18 #include "net/websockets/websocket_errors.h" |
18 #include "net/websockets/websocket_event_interface.h" | 19 #include "net/websockets/websocket_event_interface.h" |
19 #include "net/websockets/websocket_frame.h" | 20 #include "net/websockets/websocket_frame.h" |
20 #include "net/websockets/websocket_mux.h" | 21 #include "net/websockets/websocket_mux.h" |
21 #include "net/websockets/websocket_stream.h" | 22 #include "net/websockets/websocket_stream.h" |
22 | 23 |
23 namespace net { | 24 namespace net { |
24 | 25 |
25 namespace { | 26 namespace { |
26 | 27 |
27 const int kDefaultSendQuotaLowWaterMark = 1 << 16; | 28 const int kDefaultSendQuotaLowWaterMark = 1 << 16; |
28 const int kDefaultSendQuotaHighWaterMark = 1 << 17; | 29 const int kDefaultSendQuotaHighWaterMark = 1 << 17; |
29 const size_t kWebSocketCloseCodeLength = 2; | 30 const size_t kWebSocketCloseCodeLength = 2; |
30 // This timeout is based on TCPMaximumSegmentLifetime * 2 from | 31 // This timeout is based on TCPMaximumSegmentLifetime * 2 from |
31 // MainThreadWebSocketChannel.cpp in Blink. | 32 // MainThreadWebSocketChannel.cpp in Blink. |
32 const int kClosingHandshakeTimeoutSeconds = 2 * 2 * 60; | 33 const int kClosingHandshakeTimeoutSeconds = 2 * 2 * 60; |
33 | 34 |
| 35 typedef WebSocketEventInterface::ChannelState ChannelState; |
| 36 const ChannelState CHANNEL_ALIVE = WebSocketEventInterface::CHANNEL_ALIVE; |
| 37 const ChannelState CHANNEL_DELETED = WebSocketEventInterface::CHANNEL_DELETED; |
| 38 |
34 // Maximum close reason length = max control frame payload - | 39 // Maximum close reason length = max control frame payload - |
35 // status code length | 40 // status code length |
36 // = 125 - 2 | 41 // = 125 - 2 |
37 const size_t kMaximumCloseReasonLength = 125 - kWebSocketCloseCodeLength; | 42 const size_t kMaximumCloseReasonLength = 125 - kWebSocketCloseCodeLength; |
38 | 43 |
39 // Check a close status code for strict compliance with RFC6455. This is only | 44 // Check a close status code for strict compliance with RFC6455. This is only |
40 // used for close codes received from a renderer that we are intending to send | 45 // used for close codes received from a renderer that we are intending to send |
41 // out over the network. See ParseClose() for the restrictions on incoming close | 46 // out over the network. See ParseClose() for the restrictions on incoming close |
42 // codes. The |code| parameter is type int for convenience of implementation; | 47 // codes. The |code| parameter is type int for convenience of implementation; |
43 // the real type is uint16. | 48 // the real type is uint16. |
(...skipping 11 matching lines...) Expand all Loading... |
55 DCHECK_GE(code, 0); | 60 DCHECK_GE(code, 0); |
56 DCHECK_LT(code, 65536); | 61 DCHECK_LT(code, 65536); |
57 const int* upper = std::upper_bound(kInvalidRanges, kInvalidRangesEnd, code); | 62 const int* upper = std::upper_bound(kInvalidRanges, kInvalidRangesEnd, code); |
58 DCHECK_NE(kInvalidRangesEnd, upper); | 63 DCHECK_NE(kInvalidRangesEnd, upper); |
59 DCHECK_GT(upper, kInvalidRanges); | 64 DCHECK_GT(upper, kInvalidRanges); |
60 DCHECK_GT(*upper, code); | 65 DCHECK_GT(*upper, code); |
61 DCHECK_LE(*(upper - 1), code); | 66 DCHECK_LE(*(upper - 1), code); |
62 return ((upper - kInvalidRanges) % 2) == 0; | 67 return ((upper - kInvalidRanges) % 2) == 0; |
63 } | 68 } |
64 | 69 |
| 70 // This function avoids a bunch of boilerplate code. |
| 71 void AllowUnused(ChannelState ALLOW_UNUSED unused) {} |
| 72 |
65 } // namespace | 73 } // namespace |
66 | 74 |
67 // A class to encapsulate a set of frames and information about the size of | 75 // A class to encapsulate a set of frames and information about the size of |
68 // those frames. | 76 // those frames. |
69 class WebSocketChannel::SendBuffer { | 77 class WebSocketChannel::SendBuffer { |
70 public: | 78 public: |
71 SendBuffer() : total_bytes_(0) {} | 79 SendBuffer() : total_bytes_(0) {} |
72 | 80 |
73 // Add a WebSocketFrame to the buffer and increase total_bytes_. | 81 // Add a WebSocketFrame to the buffer and increase total_bytes_. |
74 void AddFrame(scoped_ptr<WebSocketFrame> chunk); | 82 void AddFrame(scoped_ptr<WebSocketFrame> chunk); |
(...skipping 18 matching lines...) Expand all Loading... |
93 | 101 |
94 // Implementation of WebSocketStream::ConnectDelegate that simply forwards the | 102 // Implementation of WebSocketStream::ConnectDelegate that simply forwards the |
95 // calls on to the WebSocketChannel that created it. | 103 // calls on to the WebSocketChannel that created it. |
96 class WebSocketChannel::ConnectDelegate | 104 class WebSocketChannel::ConnectDelegate |
97 : public WebSocketStream::ConnectDelegate { | 105 : public WebSocketStream::ConnectDelegate { |
98 public: | 106 public: |
99 explicit ConnectDelegate(WebSocketChannel* creator) : creator_(creator) {} | 107 explicit ConnectDelegate(WebSocketChannel* creator) : creator_(creator) {} |
100 | 108 |
101 virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE { | 109 virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE { |
102 creator_->OnConnectSuccess(stream.Pass()); | 110 creator_->OnConnectSuccess(stream.Pass()); |
| 111 // |this| may have been deleted. |
103 } | 112 } |
104 | 113 |
105 virtual void OnFailure(uint16 websocket_error) OVERRIDE { | 114 virtual void OnFailure(uint16 websocket_error) OVERRIDE { |
106 creator_->OnConnectFailure(websocket_error); | 115 creator_->OnConnectFailure(websocket_error); |
| 116 // |this| has been deleted. |
107 } | 117 } |
108 | 118 |
109 private: | 119 private: |
110 // A pointer to the WebSocketChannel that created this object. There is no | 120 // A pointer to the WebSocketChannel that created this object. There is no |
111 // danger of this pointer being stale, because deleting the WebSocketChannel | 121 // danger of this pointer being stale, because deleting the WebSocketChannel |
112 // cancels the connect process, deleting this object and preventing its | 122 // cancels the connect process, deleting this object and preventing its |
113 // callbacks from being called. | 123 // callbacks from being called. |
114 WebSocketChannel* const creator_; | 124 WebSocketChannel* const creator_; |
115 | 125 |
116 DISALLOW_COPY_AND_ASSIGN(ConnectDelegate); | 126 DISALLOW_COPY_AND_ASSIGN(ConnectDelegate); |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
173 if (InClosingState()) { | 183 if (InClosingState()) { |
174 VLOG(1) << "SendFrame called in state " << state_ | 184 VLOG(1) << "SendFrame called in state " << state_ |
175 << ". This may be a bug, or a harmless race."; | 185 << ". This may be a bug, or a harmless race."; |
176 return; | 186 return; |
177 } | 187 } |
178 if (state_ != CONNECTED) { | 188 if (state_ != CONNECTED) { |
179 NOTREACHED() << "SendFrame() called in state " << state_; | 189 NOTREACHED() << "SendFrame() called in state " << state_; |
180 return; | 190 return; |
181 } | 191 } |
182 if (data.size() > base::checked_numeric_cast<size_t>(current_send_quota_)) { | 192 if (data.size() > base::checked_numeric_cast<size_t>(current_send_quota_)) { |
183 FailChannel(SEND_GOING_AWAY, | 193 AllowUnused(FailChannel(SEND_GOING_AWAY, |
184 kWebSocketMuxErrorSendQuotaViolation, | 194 kWebSocketMuxErrorSendQuotaViolation, |
185 "Send quota exceeded"); | 195 "Send quota exceeded")); |
| 196 // |this| is deleted here. |
186 return; | 197 return; |
187 } | 198 } |
188 if (!WebSocketFrameHeader::IsKnownDataOpCode(op_code)) { | 199 if (!WebSocketFrameHeader::IsKnownDataOpCode(op_code)) { |
189 LOG(DFATAL) << "Got SendFrame with bogus op_code " << op_code | 200 LOG(DFATAL) << "Got SendFrame with bogus op_code " << op_code |
190 << "; misbehaving renderer? fin=" << fin | 201 << "; misbehaving renderer? fin=" << fin |
191 << " data.size()=" << data.size(); | 202 << " data.size()=" << data.size(); |
192 return; | 203 return; |
193 } | 204 } |
194 current_send_quota_ -= data.size(); | 205 current_send_quota_ -= data.size(); |
195 // TODO(ricea): If current_send_quota_ has dropped below | 206 // TODO(ricea): If current_send_quota_ has dropped below |
196 // send_quota_low_water_mark_, it might be good to increase the "low | 207 // send_quota_low_water_mark_, it might be good to increase the "low |
197 // water mark" and "high water mark", but only if the link to the WebSocket | 208 // water mark" and "high water mark", but only if the link to the WebSocket |
198 // server is not saturated. | 209 // server is not saturated. |
199 // TODO(ricea): For kOpCodeText, do UTF-8 validation? | 210 // TODO(ricea): For kOpCodeText, do UTF-8 validation? |
200 scoped_refptr<IOBuffer> buffer(new IOBuffer(data.size())); | 211 scoped_refptr<IOBuffer> buffer(new IOBuffer(data.size())); |
201 std::copy(data.begin(), data.end(), buffer->data()); | 212 std::copy(data.begin(), data.end(), buffer->data()); |
202 SendIOBuffer(fin, op_code, buffer, data.size()); | 213 AllowUnused(SendIOBuffer(fin, op_code, buffer, data.size())); |
| 214 // |this| may have been deleted. |
203 } | 215 } |
204 | 216 |
205 void WebSocketChannel::SendFlowControl(int64 quota) { | 217 void WebSocketChannel::SendFlowControl(int64 quota) { |
206 DCHECK_EQ(CONNECTED, state_); | 218 DCHECK_EQ(CONNECTED, state_); |
207 // TODO(ricea): Add interface to WebSocketStream and implement. | 219 // TODO(ricea): Add interface to WebSocketStream and implement. |
208 // stream_->SendFlowControl(quota); | 220 // stream_->SendFlowControl(quota); |
209 } | 221 } |
210 | 222 |
211 void WebSocketChannel::StartClosingHandshake(uint16 code, | 223 void WebSocketChannel::StartClosingHandshake(uint16 code, |
212 const std::string& reason) { | 224 const std::string& reason) { |
213 if (InClosingState()) { | 225 if (InClosingState()) { |
214 VLOG(1) << "StartClosingHandshake called in state " << state_ | 226 VLOG(1) << "StartClosingHandshake called in state " << state_ |
215 << ". This may be a bug, or a harmless race."; | 227 << ". This may be a bug, or a harmless race."; |
216 return; | 228 return; |
217 } | 229 } |
218 if (state_ != CONNECTED) { | 230 if (state_ != CONNECTED) { |
219 NOTREACHED() << "StartClosingHandshake() called in state " << state_; | 231 NOTREACHED() << "StartClosingHandshake() called in state " << state_; |
220 return; | 232 return; |
221 } | 233 } |
222 // Javascript actually only permits 1000 and 3000-4999, but the implementation | 234 // Javascript actually only permits 1000 and 3000-4999, but the implementation |
223 // itself may produce different codes. The length of |reason| is also checked | 235 // itself may produce different codes. The length of |reason| is also checked |
224 // by Javascript. | 236 // by Javascript. |
225 if (!IsStrictlyValidCloseStatusCode(code) || | 237 if (!IsStrictlyValidCloseStatusCode(code) || |
226 reason.size() > kMaximumCloseReasonLength) { | 238 reason.size() > kMaximumCloseReasonLength) { |
227 // "InternalServerError" is actually used for errors from any endpoint, per | 239 // "InternalServerError" is actually used for errors from any endpoint, per |
228 // errata 3227 to RFC6455. If the renderer is sending us an invalid code or | 240 // errata 3227 to RFC6455. If the renderer is sending us an invalid code or |
229 // reason it must be malfunctioning in some way, and based on that we | 241 // reason it must be malfunctioning in some way, and based on that we |
230 // interpret this as an internal error. | 242 // interpret this as an internal error. |
231 SendClose(kWebSocketErrorInternalServerError, "Internal Error"); | 243 AllowUnused( |
| 244 SendClose(kWebSocketErrorInternalServerError, "Internal Error")); |
| 245 // |this| may have been deleted. |
232 return; | 246 return; |
233 } | 247 } |
234 // TODO(ricea): Check that |reason| is valid UTF-8. | 248 AllowUnused(SendClose(code, IsStringUTF8(reason) ? reason : std::string())); |
235 SendClose(code, reason); // Sets state_ to SEND_CLOSED | 249 // |this| may have been deleted. |
236 } | 250 } |
237 | 251 |
238 void WebSocketChannel::SendAddChannelRequestForTesting( | 252 void WebSocketChannel::SendAddChannelRequestForTesting( |
239 const GURL& socket_url, | 253 const GURL& socket_url, |
240 const std::vector<std::string>& requested_subprotocols, | 254 const std::vector<std::string>& requested_subprotocols, |
241 const GURL& origin, | 255 const GURL& origin, |
242 const WebSocketStreamFactory& factory) { | 256 const WebSocketStreamFactory& factory) { |
243 SendAddChannelRequestWithFactory( | 257 SendAddChannelRequestWithFactory( |
244 socket_url, requested_subprotocols, origin, factory); | 258 socket_url, requested_subprotocols, origin, factory); |
245 } | 259 } |
(...skipping 19 matching lines...) Expand all Loading... |
265 BoundNetLog(), | 279 BoundNetLog(), |
266 connect_delegate.Pass()); | 280 connect_delegate.Pass()); |
267 state_ = CONNECTING; | 281 state_ = CONNECTING; |
268 } | 282 } |
269 | 283 |
270 void WebSocketChannel::OnConnectSuccess(scoped_ptr<WebSocketStream> stream) { | 284 void WebSocketChannel::OnConnectSuccess(scoped_ptr<WebSocketStream> stream) { |
271 DCHECK(stream); | 285 DCHECK(stream); |
272 DCHECK_EQ(CONNECTING, state_); | 286 DCHECK_EQ(CONNECTING, state_); |
273 stream_ = stream.Pass(); | 287 stream_ = stream.Pass(); |
274 state_ = CONNECTED; | 288 state_ = CONNECTED; |
275 event_interface_->OnAddChannelResponse(false, stream_->GetSubProtocol()); | 289 if (event_interface_->OnAddChannelResponse( |
| 290 false, stream_->GetSubProtocol()) == CHANNEL_DELETED) |
| 291 return; |
276 | 292 |
277 // TODO(ricea): Get flow control information from the WebSocketStream once we | 293 // TODO(ricea): Get flow control information from the WebSocketStream once we |
278 // have a multiplexing WebSocketStream. | 294 // have a multiplexing WebSocketStream. |
279 current_send_quota_ = send_quota_high_water_mark_; | 295 current_send_quota_ = send_quota_high_water_mark_; |
280 event_interface_->OnFlowControl(send_quota_high_water_mark_); | 296 if (event_interface_->OnFlowControl(send_quota_high_water_mark_) == |
| 297 CHANNEL_DELETED) |
| 298 return; |
281 | 299 |
282 // |stream_request_| is not used once the connection has succeeded. | 300 // |stream_request_| is not used once the connection has succeeded. |
283 stream_request_.reset(); | 301 stream_request_.reset(); |
284 ReadFrames(); | 302 AllowUnused(ReadFrames()); |
| 303 // |this| may have been deleted. |
285 } | 304 } |
286 | 305 |
287 void WebSocketChannel::OnConnectFailure(uint16 websocket_error) { | 306 void WebSocketChannel::OnConnectFailure(uint16 websocket_error) { |
288 DCHECK_EQ(CONNECTING, state_); | 307 DCHECK_EQ(CONNECTING, state_); |
289 state_ = CLOSED; | 308 state_ = CLOSED; |
290 stream_request_.reset(); | 309 stream_request_.reset(); |
291 event_interface_->OnAddChannelResponse(true, ""); | 310 AllowUnused(event_interface_->OnAddChannelResponse(true, "")); |
| 311 // |this| has been deleted. |
292 } | 312 } |
293 | 313 |
294 void WebSocketChannel::WriteFrames() { | 314 ChannelState WebSocketChannel::WriteFrames() { |
295 int result = OK; | 315 int result = OK; |
296 do { | 316 do { |
297 // This use of base::Unretained is safe because this object owns the | 317 // This use of base::Unretained is safe because this object owns the |
298 // WebSocketStream and destroying it cancels all callbacks. | 318 // WebSocketStream and destroying it cancels all callbacks. |
299 result = stream_->WriteFrames( | 319 result = stream_->WriteFrames( |
300 data_being_sent_->frames(), | 320 data_being_sent_->frames(), |
301 base::Bind( | 321 base::Bind(base::IgnoreResult(&WebSocketChannel::OnWriteDone), |
302 &WebSocketChannel::OnWriteDone, base::Unretained(this), false)); | 322 base::Unretained(this), |
| 323 false)); |
303 if (result != ERR_IO_PENDING) { | 324 if (result != ERR_IO_PENDING) { |
304 OnWriteDone(true, result); | 325 if (OnWriteDone(true, result) == CHANNEL_DELETED) |
| 326 return CHANNEL_DELETED; |
305 } | 327 } |
306 } while (result == OK && data_being_sent_); | 328 } while (result == OK && data_being_sent_); |
| 329 return CHANNEL_ALIVE; |
307 } | 330 } |
308 | 331 |
309 void WebSocketChannel::OnWriteDone(bool synchronous, int result) { | 332 ChannelState WebSocketChannel::OnWriteDone(bool synchronous, int result) { |
310 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | 333 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); |
311 DCHECK_NE(CONNECTING, state_); | 334 DCHECK_NE(CONNECTING, state_); |
312 DCHECK_NE(ERR_IO_PENDING, result); | 335 DCHECK_NE(ERR_IO_PENDING, result); |
313 DCHECK(data_being_sent_); | 336 DCHECK(data_being_sent_); |
314 switch (result) { | 337 switch (result) { |
315 case OK: | 338 case OK: |
316 if (data_to_send_next_) { | 339 if (data_to_send_next_) { |
317 data_being_sent_ = data_to_send_next_.Pass(); | 340 data_being_sent_ = data_to_send_next_.Pass(); |
318 if (!synchronous) { | 341 if (!synchronous) |
319 WriteFrames(); | 342 return WriteFrames(); |
320 } | |
321 } else { | 343 } else { |
322 data_being_sent_.reset(); | 344 data_being_sent_.reset(); |
323 if (current_send_quota_ < send_quota_low_water_mark_) { | 345 if (current_send_quota_ < send_quota_low_water_mark_) { |
324 // TODO(ricea): Increase low_water_mark and high_water_mark if | 346 // TODO(ricea): Increase low_water_mark and high_water_mark if |
325 // throughput is high, reduce them if throughput is low. Low water | 347 // throughput is high, reduce them if throughput is low. Low water |
326 // mark needs to be >= the bandwidth delay product *of the IPC | 348 // mark needs to be >= the bandwidth delay product *of the IPC |
327 // channel*. Because factors like context-switch time, thread wake-up | 349 // channel*. Because factors like context-switch time, thread wake-up |
328 // time, and bus speed come into play it is complex and probably needs | 350 // time, and bus speed come into play it is complex and probably needs |
329 // to be determined empirically. | 351 // to be determined empirically. |
330 DCHECK_LE(send_quota_low_water_mark_, send_quota_high_water_mark_); | 352 DCHECK_LE(send_quota_low_water_mark_, send_quota_high_water_mark_); |
331 // TODO(ricea): Truncate quota by the quota specified by the remote | 353 // TODO(ricea): Truncate quota by the quota specified by the remote |
332 // server, if the protocol in use supports quota. | 354 // server, if the protocol in use supports quota. |
333 int fresh_quota = send_quota_high_water_mark_ - current_send_quota_; | 355 int fresh_quota = send_quota_high_water_mark_ - current_send_quota_; |
334 current_send_quota_ += fresh_quota; | 356 current_send_quota_ += fresh_quota; |
335 event_interface_->OnFlowControl(fresh_quota); | 357 return event_interface_->OnFlowControl(fresh_quota); |
336 } | 358 } |
337 } | 359 } |
338 return; | 360 return CHANNEL_ALIVE; |
339 | 361 |
340 // If a recoverable error condition existed, it would go here. | 362 // If a recoverable error condition existed, it would go here. |
341 | 363 |
342 default: | 364 default: |
343 DCHECK_LT(result, 0) | 365 DCHECK_LT(result, 0) |
344 << "WriteFrames() should only return OK or ERR_ codes"; | 366 << "WriteFrames() should only return OK or ERR_ codes"; |
345 stream_->Close(); | 367 stream_->Close(); |
346 if (state_ != CLOSED) { | 368 DCHECK_NE(CLOSED, state_); |
347 state_ = CLOSED; | 369 state_ = CLOSED; |
348 event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure, | 370 return event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure, |
349 "Abnormal Closure"); | 371 "Abnormal Closure"); |
350 } | |
351 return; | |
352 } | 372 } |
353 } | 373 } |
354 | 374 |
355 void WebSocketChannel::ReadFrames() { | 375 ChannelState WebSocketChannel::ReadFrames() { |
356 int result = OK; | 376 int result = OK; |
357 do { | 377 do { |
358 // This use of base::Unretained is safe because this object owns the | 378 // This use of base::Unretained is safe because this object owns the |
359 // WebSocketStream, and any pending reads will be cancelled when it is | 379 // WebSocketStream, and any pending reads will be cancelled when it is |
360 // destroyed. | 380 // destroyed. |
361 result = stream_->ReadFrames( | 381 result = stream_->ReadFrames( |
362 &read_frames_, | 382 &read_frames_, |
363 base::Bind( | 383 base::Bind(base::IgnoreResult(&WebSocketChannel::OnReadDone), |
364 &WebSocketChannel::OnReadDone, base::Unretained(this), false)); | 384 base::Unretained(this), |
| 385 false)); |
365 if (result != ERR_IO_PENDING) { | 386 if (result != ERR_IO_PENDING) { |
366 OnReadDone(true, result); | 387 if (OnReadDone(true, result) == CHANNEL_DELETED) |
| 388 return CHANNEL_DELETED; |
367 } | 389 } |
368 } while (result == OK && state_ != CLOSED); | 390 DCHECK_NE(CLOSED, state_); |
| 391 } while (result == OK); |
| 392 return CHANNEL_ALIVE; |
369 } | 393 } |
370 | 394 |
371 void WebSocketChannel::OnReadDone(bool synchronous, int result) { | 395 ChannelState WebSocketChannel::OnReadDone(bool synchronous, int result) { |
372 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | 396 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); |
373 DCHECK_NE(CONNECTING, state_); | 397 DCHECK_NE(CONNECTING, state_); |
374 DCHECK_NE(ERR_IO_PENDING, result); | 398 DCHECK_NE(ERR_IO_PENDING, result); |
375 switch (result) { | 399 switch (result) { |
376 case OK: | 400 case OK: |
377 // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection | 401 // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection |
378 // with no data read, not an empty response. | 402 // with no data read, not an empty response. |
379 DCHECK(!read_frames_.empty()) | 403 DCHECK(!read_frames_.empty()) |
380 << "ReadFrames() returned OK, but nothing was read."; | 404 << "ReadFrames() returned OK, but nothing was read."; |
381 for (size_t i = 0; i < read_frames_.size(); ++i) { | 405 for (size_t i = 0; i < read_frames_.size(); ++i) { |
382 scoped_ptr<WebSocketFrame> frame(read_frames_[i]); | 406 scoped_ptr<WebSocketFrame> frame(read_frames_[i]); |
383 read_frames_[i] = NULL; | 407 read_frames_[i] = NULL; |
384 ProcessFrame(frame.Pass()); | 408 if (ProcessFrame(frame.Pass()) == CHANNEL_DELETED) |
| 409 return CHANNEL_DELETED; |
385 } | 410 } |
386 read_frames_.clear(); | 411 read_frames_.clear(); |
387 // There should always be a call to ReadFrames pending. | 412 // There should always be a call to ReadFrames pending. |
388 // TODO(ricea): Unless we are out of quota. | 413 // TODO(ricea): Unless we are out of quota. |
389 if (!synchronous && state_ != CLOSED) { | 414 DCHECK_NE(CLOSED, state_); |
390 ReadFrames(); | 415 if (!synchronous) |
391 } | 416 return ReadFrames(); |
392 return; | 417 return CHANNEL_ALIVE; |
393 | 418 |
394 case ERR_WS_PROTOCOL_ERROR: | 419 case ERR_WS_PROTOCOL_ERROR: |
395 FailChannel(SEND_REAL_ERROR, | 420 return FailChannel(SEND_REAL_ERROR, |
396 kWebSocketErrorProtocolError, | 421 kWebSocketErrorProtocolError, |
397 "WebSocket Protocol Error"); | 422 "WebSocket Protocol Error"); |
398 return; | |
399 | 423 |
400 default: | 424 default: |
401 DCHECK_LT(result, 0) | 425 DCHECK_LT(result, 0) |
402 << "ReadFrames() should only return OK or ERR_ codes"; | 426 << "ReadFrames() should only return OK or ERR_ codes"; |
403 stream_->Close(); | 427 stream_->Close(); |
404 if (state_ != CLOSED) { | 428 DCHECK_NE(CLOSED, state_); |
405 state_ = CLOSED; | 429 state_ = CLOSED; |
406 uint16 code = kWebSocketErrorAbnormalClosure; | 430 uint16 code = kWebSocketErrorAbnormalClosure; |
407 std::string reason = "Abnormal Closure"; | 431 std::string reason = "Abnormal Closure"; |
408 if (closing_code_ != 0) { | 432 if (closing_code_ != 0) { |
409 code = closing_code_; | 433 code = closing_code_; |
410 reason = closing_reason_; | 434 reason = closing_reason_; |
411 } | |
412 event_interface_->OnDropChannel(code, reason); | |
413 } | 435 } |
414 return; | 436 return event_interface_->OnDropChannel(code, reason); |
415 } | 437 } |
416 } | 438 } |
417 | 439 |
418 void WebSocketChannel::ProcessFrame(scoped_ptr<WebSocketFrame> frame) { | 440 ChannelState WebSocketChannel::ProcessFrame(scoped_ptr<WebSocketFrame> frame) { |
419 if (frame->header.masked) { | 441 if (frame->header.masked) { |
420 // RFC6455 Section 5.1 "A client MUST close a connection if it detects a | 442 // RFC6455 Section 5.1 "A client MUST close a connection if it detects a |
421 // masked frame." | 443 // masked frame." |
422 FailChannel(SEND_REAL_ERROR, | 444 return FailChannel(SEND_REAL_ERROR, |
423 kWebSocketErrorProtocolError, | 445 kWebSocketErrorProtocolError, |
424 "Masked frame from server"); | 446 "Masked frame from server"); |
425 return; | |
426 } | 447 } |
427 const WebSocketFrameHeader::OpCode opcode = frame->header.opcode; | 448 const WebSocketFrameHeader::OpCode opcode = frame->header.opcode; |
428 if (WebSocketFrameHeader::IsKnownControlOpCode(opcode) && | 449 if (WebSocketFrameHeader::IsKnownControlOpCode(opcode) && |
429 !frame->header.final) { | 450 !frame->header.final) { |
430 FailChannel(SEND_REAL_ERROR, | 451 return FailChannel(SEND_REAL_ERROR, |
431 kWebSocketErrorProtocolError, | 452 kWebSocketErrorProtocolError, |
432 "Control message with FIN bit unset received"); | 453 "Control message with FIN bit unset received"); |
433 return; | |
434 } | 454 } |
435 | 455 |
436 // Respond to the frame appropriately to its type. | 456 // Respond to the frame appropriately to its type. |
437 HandleFrame( | 457 return HandleFrame( |
438 opcode, frame->header.final, frame->data, frame->header.payload_length); | 458 opcode, frame->header.final, frame->data, frame->header.payload_length); |
439 } | 459 } |
440 | 460 |
441 void WebSocketChannel::HandleFrame(const WebSocketFrameHeader::OpCode opcode, | 461 ChannelState WebSocketChannel::HandleFrame( |
442 bool final, | 462 const WebSocketFrameHeader::OpCode opcode, |
443 const scoped_refptr<IOBuffer>& data_buffer, | 463 bool final, |
444 size_t size) { | 464 const scoped_refptr<IOBuffer>& data_buffer, |
| 465 size_t size) { |
445 DCHECK_NE(RECV_CLOSED, state_) | 466 DCHECK_NE(RECV_CLOSED, state_) |
446 << "HandleFrame() does not support being called re-entrantly from within " | 467 << "HandleFrame() does not support being called re-entrantly from within " |
447 "SendClose()"; | 468 "SendClose()"; |
448 if (state_ == CLOSED || state_ == CLOSE_WAIT) { | 469 DCHECK_NE(CLOSED, state_); |
449 DVLOG_IF(1, state_ == CLOSED) << "A frame was received while in the CLOSED " | 470 if (state_ == CLOSE_WAIT) { |
450 "state. This is possible after a channel " | |
451 "failed, but should be very rare."; | |
452 std::string frame_name; | 471 std::string frame_name; |
453 switch (opcode) { | 472 switch (opcode) { |
454 case WebSocketFrameHeader::kOpCodeText: // fall-thru | 473 case WebSocketFrameHeader::kOpCodeText: // fall-thru |
455 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru | 474 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru |
456 case WebSocketFrameHeader::kOpCodeContinuation: | 475 case WebSocketFrameHeader::kOpCodeContinuation: |
457 frame_name = "Data frame"; | 476 frame_name = "Data frame"; |
458 break; | 477 break; |
459 | 478 |
460 case WebSocketFrameHeader::kOpCodePing: | 479 case WebSocketFrameHeader::kOpCodePing: |
461 frame_name = "Ping"; | 480 frame_name = "Ping"; |
462 break; | 481 break; |
463 | 482 |
464 case WebSocketFrameHeader::kOpCodePong: | 483 case WebSocketFrameHeader::kOpCodePong: |
465 frame_name = "Pong"; | 484 frame_name = "Pong"; |
466 break; | 485 break; |
467 | 486 |
468 case WebSocketFrameHeader::kOpCodeClose: | 487 case WebSocketFrameHeader::kOpCodeClose: |
469 frame_name = "Close"; | 488 frame_name = "Close"; |
470 break; | 489 break; |
471 | 490 |
472 default: | 491 default: |
473 frame_name = "Unknown frame type"; | 492 frame_name = "Unknown frame type"; |
474 break; | 493 break; |
475 } | 494 } |
476 // SEND_REAL_ERROR makes no difference here, as FailChannel() won't send | 495 // SEND_REAL_ERROR makes no difference here, as FailChannel() won't send |
477 // another Close frame. | 496 // another Close frame. |
478 FailChannel(SEND_REAL_ERROR, | 497 return FailChannel(SEND_REAL_ERROR, |
479 kWebSocketErrorProtocolError, | 498 kWebSocketErrorProtocolError, |
480 frame_name + " received after close"); | 499 frame_name + " received after close"); |
481 return; | |
482 } | 500 } |
483 switch (opcode) { | 501 switch (opcode) { |
484 case WebSocketFrameHeader::kOpCodeText: // fall-thru | 502 case WebSocketFrameHeader::kOpCodeText: // fall-thru |
485 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru | 503 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru |
486 case WebSocketFrameHeader::kOpCodeContinuation: | 504 case WebSocketFrameHeader::kOpCodeContinuation: |
487 if (state_ == CONNECTED) { | 505 if (state_ == CONNECTED) { |
488 // TODO(ricea): Need to fail the connection if UTF-8 is invalid | 506 // TODO(ricea): Need to fail the connection if UTF-8 is invalid |
489 // post-reassembly. Requires a streaming UTF-8 validator. | 507 // post-reassembly. Requires a streaming UTF-8 validator. |
490 // TODO(ricea): Can this copy be eliminated? | 508 // TODO(ricea): Can this copy be eliminated? |
491 const char* const data_begin = data_buffer->data(); | 509 const char* const data_begin = data_buffer->data(); |
492 const char* const data_end = data_begin + size; | 510 const char* const data_end = data_begin + size; |
493 const std::vector<char> data(data_begin, data_end); | 511 const std::vector<char> data(data_begin, data_end); |
494 // TODO(ricea): Handle the case when ReadFrames returns far | 512 // TODO(ricea): Handle the case when ReadFrames returns far |
495 // more data at once than should be sent in a single IPC. This needs to | 513 // more data at once than should be sent in a single IPC. This needs to |
496 // be handled carefully, as an overloaded IO thread is one possible | 514 // be handled carefully, as an overloaded IO thread is one possible |
497 // cause of receiving very large chunks. | 515 // cause of receiving very large chunks. |
498 | 516 |
499 // Sends the received frame to the renderer process. | 517 // Sends the received frame to the renderer process. |
500 event_interface_->OnDataFrame(final, opcode, data); | 518 return event_interface_->OnDataFrame(final, opcode, data); |
501 } else { | |
502 VLOG(3) << "Ignored data packet received in state " << state_; | |
503 } | 519 } |
504 return; | 520 VLOG(3) << "Ignored data packet received in state " << state_; |
| 521 return CHANNEL_ALIVE; |
505 | 522 |
506 case WebSocketFrameHeader::kOpCodePing: | 523 case WebSocketFrameHeader::kOpCodePing: |
507 VLOG(1) << "Got Ping of size " << size; | 524 VLOG(1) << "Got Ping of size " << size; |
508 if (state_ == CONNECTED) { | 525 if (state_ == CONNECTED) |
509 SendIOBuffer( | 526 return SendIOBuffer( |
510 true, WebSocketFrameHeader::kOpCodePong, data_buffer, size); | 527 true, WebSocketFrameHeader::kOpCodePong, data_buffer, size); |
511 } else { | 528 VLOG(3) << "Ignored ping in state " << state_; |
512 VLOG(3) << "Ignored ping in state " << state_; | 529 return CHANNEL_ALIVE; |
513 } | |
514 return; | |
515 | 530 |
516 case WebSocketFrameHeader::kOpCodePong: | 531 case WebSocketFrameHeader::kOpCodePong: |
517 VLOG(1) << "Got Pong of size " << size; | 532 VLOG(1) << "Got Pong of size " << size; |
518 // There is no need to do anything with pong messages. | 533 // There is no need to do anything with pong messages. |
519 return; | 534 return CHANNEL_ALIVE; |
520 | 535 |
521 case WebSocketFrameHeader::kOpCodeClose: { | 536 case WebSocketFrameHeader::kOpCodeClose: { |
522 uint16 code = kWebSocketNormalClosure; | 537 uint16 code = kWebSocketNormalClosure; |
523 std::string reason; | 538 std::string reason; |
524 ParseClose(data_buffer, size, &code, &reason); | 539 ParseClose(data_buffer, size, &code, &reason); |
525 // TODO(ricea): Find a way to safely log the message from the close | 540 // TODO(ricea): Find a way to safely log the message from the close |
526 // message (escape control codes and so on). | 541 // message (escape control codes and so on). |
527 VLOG(1) << "Got Close with code " << code; | 542 VLOG(1) << "Got Close with code " << code; |
528 switch (state_) { | 543 switch (state_) { |
529 case CONNECTED: | 544 case CONNECTED: |
530 state_ = RECV_CLOSED; | 545 state_ = RECV_CLOSED; |
531 SendClose(code, reason); // Sets state_ to CLOSE_WAIT | 546 if (SendClose(code, reason) == // Sets state_ to CLOSE_WAIT |
532 event_interface_->OnClosingHandshake(); | 547 CHANNEL_DELETED) |
| 548 return CHANNEL_DELETED; |
| 549 if (event_interface_->OnClosingHandshake() == CHANNEL_DELETED) |
| 550 return CHANNEL_DELETED; |
533 closing_code_ = code; | 551 closing_code_ = code; |
534 closing_reason_ = reason; | 552 closing_reason_ = reason; |
535 break; | 553 break; |
536 | 554 |
537 case SEND_CLOSED: | 555 case SEND_CLOSED: |
538 // Give them another few minutes to actually close the connection. | |
539 DCHECK(timer_.IsRunning()); | |
540 timer_.Reset(); | |
541 | |
542 state_ = CLOSE_WAIT; | 556 state_ = CLOSE_WAIT; |
543 // From RFC6455 section 7.1.5: "Each endpoint | 557 // From RFC6455 section 7.1.5: "Each endpoint |
544 // will see the status code sent by the other end as _The WebSocket | 558 // will see the status code sent by the other end as _The WebSocket |
545 // Connection Close Code_." | 559 // Connection Close Code_." |
546 closing_code_ = code; | 560 closing_code_ = code; |
547 closing_reason_ = reason; | 561 closing_reason_ = reason; |
548 break; | 562 break; |
549 | 563 |
550 default: | 564 default: |
551 LOG(DFATAL) << "Got Close in unexpected state " << state_; | 565 LOG(DFATAL) << "Got Close in unexpected state " << state_; |
552 break; | 566 break; |
553 } | 567 } |
554 return; | 568 return CHANNEL_ALIVE; |
555 } | 569 } |
556 | 570 |
557 default: | 571 default: |
558 FailChannel( | 572 return FailChannel( |
559 SEND_REAL_ERROR, kWebSocketErrorProtocolError, "Unknown opcode"); | 573 SEND_REAL_ERROR, kWebSocketErrorProtocolError, "Unknown opcode"); |
560 return; | |
561 } | 574 } |
562 } | 575 } |
563 | 576 |
564 void WebSocketChannel::SendIOBuffer(bool fin, | 577 ChannelState WebSocketChannel::SendIOBuffer( |
565 WebSocketFrameHeader::OpCode op_code, | 578 bool fin, |
566 const scoped_refptr<IOBuffer>& buffer, | 579 WebSocketFrameHeader::OpCode op_code, |
567 size_t size) { | 580 const scoped_refptr<IOBuffer>& buffer, |
| 581 size_t size) { |
568 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); | 582 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); |
569 DCHECK(stream_); | 583 DCHECK(stream_); |
570 scoped_ptr<WebSocketFrame> frame(new WebSocketFrame(op_code)); | 584 scoped_ptr<WebSocketFrame> frame(new WebSocketFrame(op_code)); |
571 WebSocketFrameHeader& header = frame->header; | 585 WebSocketFrameHeader& header = frame->header; |
572 header.final = fin; | 586 header.final = fin; |
573 header.masked = true; | 587 header.masked = true; |
574 header.payload_length = size; | 588 header.payload_length = size; |
575 frame->data = buffer; | 589 frame->data = buffer; |
576 if (data_being_sent_) { | 590 if (data_being_sent_) { |
577 // Either the link to the WebSocket server is saturated, or several messages | 591 // Either the link to the WebSocket server is saturated, or several messages |
578 // are being sent in a batch. | 592 // are being sent in a batch. |
579 // TODO(ricea): Keep some statistics to work out the situation and adjust | 593 // TODO(ricea): Keep some statistics to work out the situation and adjust |
580 // quota appropriately. | 594 // quota appropriately. |
581 if (!data_to_send_next_) | 595 if (!data_to_send_next_) |
582 data_to_send_next_.reset(new SendBuffer); | 596 data_to_send_next_.reset(new SendBuffer); |
583 data_to_send_next_->AddFrame(frame.Pass()); | 597 data_to_send_next_->AddFrame(frame.Pass()); |
584 } else { | 598 return CHANNEL_ALIVE; |
585 data_being_sent_.reset(new SendBuffer); | |
586 data_being_sent_->AddFrame(frame.Pass()); | |
587 WriteFrames(); | |
588 } | 599 } |
| 600 data_being_sent_.reset(new SendBuffer); |
| 601 data_being_sent_->AddFrame(frame.Pass()); |
| 602 return WriteFrames(); |
589 } | 603 } |
590 | 604 |
591 void WebSocketChannel::FailChannel(ExposeError expose, | 605 ChannelState WebSocketChannel::FailChannel(ExposeError expose, |
592 uint16 code, | 606 uint16 code, |
593 const std::string& reason) { | 607 const std::string& reason) { |
594 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | 608 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); |
595 DCHECK_NE(CONNECTING, state_); | 609 DCHECK_NE(CONNECTING, state_); |
| 610 DCHECK_NE(CLOSED, state_); |
596 // TODO(ricea): Logging. | 611 // TODO(ricea): Logging. |
597 State old_state = state_; | |
598 if (state_ == CONNECTED) { | 612 if (state_ == CONNECTED) { |
599 uint16 send_code = kWebSocketErrorGoingAway; | 613 uint16 send_code = kWebSocketErrorGoingAway; |
600 std::string send_reason = "Internal Error"; | 614 std::string send_reason = "Internal Error"; |
601 if (expose == SEND_REAL_ERROR) { | 615 if (expose == SEND_REAL_ERROR) { |
602 send_code = code; | 616 send_code = code; |
603 send_reason = reason; | 617 send_reason = reason; |
604 } | 618 } |
605 SendClose(send_code, send_reason); // Sets state_ to SEND_CLOSED | 619 if (SendClose(send_code, send_reason) == // Sets state_ to SEND_CLOSED |
| 620 CHANNEL_DELETED) |
| 621 return CHANNEL_DELETED; |
606 } | 622 } |
607 // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates the browser | 623 // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates the browser |
608 // should close the connection itself without waiting for the closing | 624 // should close the connection itself without waiting for the closing |
609 // handshake. | 625 // handshake. |
610 stream_->Close(); | 626 stream_->Close(); |
611 state_ = CLOSED; | 627 state_ = CLOSED; |
612 | 628 |
613 if (old_state != CLOSED) { | 629 return event_interface_->OnDropChannel(code, reason); |
614 event_interface_->OnDropChannel(code, reason); | |
615 } | |
616 } | 630 } |
617 | 631 |
618 void WebSocketChannel::SendClose(uint16 code, const std::string& reason) { | 632 ChannelState WebSocketChannel::SendClose(uint16 code, |
| 633 const std::string& reason) { |
619 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); | 634 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); |
620 DCHECK_LE(reason.size(), kMaximumCloseReasonLength); | 635 DCHECK_LE(reason.size(), kMaximumCloseReasonLength); |
621 scoped_refptr<IOBuffer> body; | 636 scoped_refptr<IOBuffer> body; |
622 size_t size = 0; | 637 size_t size = 0; |
623 if (code == kWebSocketErrorNoStatusReceived) { | 638 if (code == kWebSocketErrorNoStatusReceived) { |
624 // Special case: translate kWebSocketErrorNoStatusReceived into a Close | 639 // Special case: translate kWebSocketErrorNoStatusReceived into a Close |
625 // frame with no payload. | 640 // frame with no payload. |
626 body = new IOBuffer(0); | 641 body = new IOBuffer(0); |
627 } else { | 642 } else { |
628 const size_t payload_length = kWebSocketCloseCodeLength + reason.length(); | 643 const size_t payload_length = kWebSocketCloseCodeLength + reason.length(); |
629 body = new IOBuffer(payload_length); | 644 body = new IOBuffer(payload_length); |
630 size = payload_length; | 645 size = payload_length; |
631 WriteBigEndian(body->data(), code); | 646 WriteBigEndian(body->data(), code); |
632 COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength, | 647 COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength, |
633 they_should_both_be_two); | 648 they_should_both_be_two); |
634 std::copy( | 649 std::copy( |
635 reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength); | 650 reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength); |
636 } | 651 } |
637 // This use of base::Unretained() is safe because we stop the timer in the | 652 // This use of base::Unretained() is safe because we stop the timer in the |
638 // destructor. | 653 // destructor. |
639 timer_.Start( | 654 timer_.Start( |
640 FROM_HERE, | 655 FROM_HERE, |
641 timeout_, | 656 timeout_, |
642 base::Bind(&WebSocketChannel::CloseTimeout, base::Unretained(this))); | 657 base::Bind(&WebSocketChannel::CloseTimeout, base::Unretained(this))); |
643 SendIOBuffer(true, WebSocketFrameHeader::kOpCodeClose, body, size); | 658 if (SendIOBuffer(true, WebSocketFrameHeader::kOpCodeClose, body, size) == |
| 659 CHANNEL_DELETED) |
| 660 return CHANNEL_DELETED; |
| 661 // SendIOBuffer() checks |state_|, so it is best not to change it until after |
| 662 // SendIOBuffer() returns. |
644 state_ = (state_ == CONNECTED) ? SEND_CLOSED : CLOSE_WAIT; | 663 state_ = (state_ == CONNECTED) ? SEND_CLOSED : CLOSE_WAIT; |
| 664 return CHANNEL_ALIVE; |
645 } | 665 } |
646 | 666 |
647 void WebSocketChannel::ParseClose(const scoped_refptr<IOBuffer>& buffer, | 667 void WebSocketChannel::ParseClose(const scoped_refptr<IOBuffer>& buffer, |
648 size_t size, | 668 size_t size, |
649 uint16* code, | 669 uint16* code, |
650 std::string* reason) { | 670 std::string* reason) { |
651 const char* data = buffer->data(); | 671 const char* data = buffer->data(); |
652 reason->clear(); | 672 reason->clear(); |
653 if (size < kWebSocketCloseCodeLength) { | 673 if (size < kWebSocketCloseCodeLength) { |
654 *code = kWebSocketErrorNoStatusReceived; | 674 *code = kWebSocketErrorNoStatusReceived; |
(...skipping 12 matching lines...) Expand all Loading... |
667 if (unchecked_code >= static_cast<uint16>(kWebSocketNormalClosure) && | 687 if (unchecked_code >= static_cast<uint16>(kWebSocketNormalClosure) && |
668 unchecked_code <= | 688 unchecked_code <= |
669 static_cast<uint16>(kWebSocketErrorPrivateReservedMax)) { | 689 static_cast<uint16>(kWebSocketErrorPrivateReservedMax)) { |
670 *code = unchecked_code; | 690 *code = unchecked_code; |
671 } else { | 691 } else { |
672 VLOG(1) << "Close frame contained code outside of the valid range: " | 692 VLOG(1) << "Close frame contained code outside of the valid range: " |
673 << unchecked_code; | 693 << unchecked_code; |
674 *code = kWebSocketErrorAbnormalClosure; | 694 *code = kWebSocketErrorAbnormalClosure; |
675 } | 695 } |
676 std::string text(data + kWebSocketCloseCodeLength, data + size); | 696 std::string text(data + kWebSocketCloseCodeLength, data + size); |
677 // TODO(ricea): Is this check strict enough? In particular, check the | 697 // IsStringUTF8() blocks surrogate pairs and non-characters, so it is strictly |
678 // "Security Considerations" from RFC3629. | 698 // stronger than required by RFC3629. |
679 if (IsStringUTF8(text)) { | 699 if (IsStringUTF8(text)) { |
680 reason->swap(text); | 700 reason->swap(text); |
681 } | 701 } |
682 } | 702 } |
683 | 703 |
684 void WebSocketChannel::CloseTimeout() { | 704 void WebSocketChannel::CloseTimeout() { |
685 stream_->Close(); | 705 stream_->Close(); |
686 // TODO(ricea): This becomes a DCHECK() once | 706 DCHECK_NE(CLOSED, state_); |
687 // https://codereview.chromium.org/26544003/ has landed. | 707 state_ = CLOSED; |
688 if (state_ != CLOSED) { | 708 AllowUnused(event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure, |
689 state_ = CLOSED; | 709 "Abnormal Closure")); |
690 event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure, | 710 // |this| has been deleted. |
691 "Abnormal Closure"); | |
692 } | |
693 } | 711 } |
694 | 712 |
695 } // namespace net | 713 } // namespace net |
OLD | NEW |