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

Side by Side Diff: net/websockets/websocket_channel.cc

Issue 26544003: Make net::WebSocketChannel deletion safe and enable new IPCs (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Rebase (again). Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/websockets/websocket_channel.h ('k') | net/websockets/websocket_channel_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « net/websockets/websocket_channel.h ('k') | net/websockets/websocket_channel_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698