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/safe_numerics.h" | 11 #include "base/safe_numerics.h" |
12 #include "base/strings/string_util.h" | 12 #include "base/strings/string_util.h" |
13 #include "net/base/big_endian.h" | 13 #include "net/base/big_endian.h" |
14 #include "net/base/io_buffer.h" | 14 #include "net/base/io_buffer.h" |
15 #include "net/base/net_log.h" | 15 #include "net/base/net_log.h" |
16 #include "net/websockets/websocket_errors.h" | 16 #include "net/websockets/websocket_errors.h" |
17 #include "net/websockets/websocket_event_interface.h" | 17 #include "net/websockets/websocket_event_interface.h" |
18 #include "net/websockets/websocket_frame.h" | 18 #include "net/websockets/websocket_frame.h" |
19 #include "net/websockets/websocket_mux.h" | 19 #include "net/websockets/websocket_mux.h" |
20 #include "net/websockets/websocket_stream.h" | 20 #include "net/websockets/websocket_stream.h" |
21 | 21 |
22 namespace net { | 22 namespace net { |
23 | 23 |
24 namespace { | 24 namespace { |
25 | 25 |
26 const int kDefaultSendQuotaLowWaterMark = 1 << 16; | 26 const int kDefaultSendQuotaLowWaterMark = 1 << 16; |
27 const int kDefaultSendQuotaHighWaterMark = 1 << 17; | 27 const int kDefaultSendQuotaHighWaterMark = 1 << 17; |
28 const size_t kWebSocketCloseCodeLength = 2; | 28 const size_t kWebSocketCloseCodeLength = 2; |
29 | 29 |
30 // This uses type uint64 to match the definition of | |
31 // WebSocketFrameHeader::payload_length in websocket_frame.h. | |
32 const uint64 kMaxControlFramePayload = 125; | |
33 | |
34 } // namespace | 30 } // namespace |
35 | 31 |
36 // A class to encapsulate a set of frames and information about the size of | 32 // A class to encapsulate a set of frames and information about the size of |
37 // those frames. | 33 // those frames. |
38 class WebSocketChannel::SendBuffer { | 34 class WebSocketChannel::SendBuffer { |
39 public: | 35 public: |
40 SendBuffer() : total_bytes_(0) {} | 36 SendBuffer() : total_bytes_(0) {} |
41 | 37 |
42 // Add a WebSocketFrameChunk to the buffer and increase total_bytes_. | 38 // Add a WebSocketFrame to the buffer and increase total_bytes_. |
43 void AddFrame(scoped_ptr<WebSocketFrameChunk> chunk); | 39 void AddFrame(scoped_ptr<WebSocketFrame> chunk); |
44 | 40 |
45 // Return a pointer to the frames_ for write purposes. | 41 // Return a pointer to the frames_ for write purposes. |
46 ScopedVector<WebSocketFrameChunk>* frames() { return &frames_; } | 42 ScopedVector<WebSocketFrame>* frames() { return &frames_; } |
47 | 43 |
48 private: | 44 private: |
49 // The frames_ that will be sent in the next call to WriteFrames(). | 45 // The frames_ that will be sent in the next call to WriteFrames(). |
50 ScopedVector<WebSocketFrameChunk> frames_; | 46 ScopedVector<WebSocketFrame> frames_; |
51 | 47 |
52 // The total size of the buffers in |frames_|. This will be used to measure | 48 // The total size of the payload data in |frames_|. This will be used to |
53 // the throughput of the link. | 49 // measure the throughput of the link. |
54 // TODO(ricea): Measure the throughput of the link. | 50 // TODO(ricea): Measure the throughput of the link. |
55 size_t total_bytes_; | 51 size_t total_bytes_; |
56 }; | 52 }; |
57 | 53 |
58 void WebSocketChannel::SendBuffer::AddFrame( | 54 void WebSocketChannel::SendBuffer::AddFrame(scoped_ptr<WebSocketFrame> frame) { |
59 scoped_ptr<WebSocketFrameChunk> chunk) { | 55 total_bytes_ += frame->header.payload_length; |
60 total_bytes_ += chunk->data->size(); | 56 frames_.push_back(frame.release()); |
61 frames_.push_back(chunk.release()); | |
62 } | 57 } |
63 | 58 |
64 // Implementation of WebSocketStream::ConnectDelegate that simply forwards the | 59 // Implementation of WebSocketStream::ConnectDelegate that simply forwards the |
65 // calls on to the WebSocketChannel that created it. | 60 // calls on to the WebSocketChannel that created it. |
66 class WebSocketChannel::ConnectDelegate | 61 class WebSocketChannel::ConnectDelegate |
67 : public WebSocketStream::ConnectDelegate { | 62 : public WebSocketStream::ConnectDelegate { |
68 public: | 63 public: |
69 explicit ConnectDelegate(WebSocketChannel* creator) : creator_(creator) {} | 64 explicit ConnectDelegate(WebSocketChannel* creator) : creator_(creator) {} |
70 | 65 |
71 virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE { | 66 virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE { |
(...skipping 19 matching lines...) Expand all Loading... |
91 scoped_ptr<WebSocketEventInterface> event_interface) | 86 scoped_ptr<WebSocketEventInterface> event_interface) |
92 : socket_url_(socket_url), | 87 : socket_url_(socket_url), |
93 event_interface_(event_interface.Pass()), | 88 event_interface_(event_interface.Pass()), |
94 send_quota_low_water_mark_(kDefaultSendQuotaLowWaterMark), | 89 send_quota_low_water_mark_(kDefaultSendQuotaLowWaterMark), |
95 send_quota_high_water_mark_(kDefaultSendQuotaHighWaterMark), | 90 send_quota_high_water_mark_(kDefaultSendQuotaHighWaterMark), |
96 current_send_quota_(0), | 91 current_send_quota_(0), |
97 closing_code_(0), | 92 closing_code_(0), |
98 state_(FRESHLY_CONSTRUCTED) {} | 93 state_(FRESHLY_CONSTRUCTED) {} |
99 | 94 |
100 WebSocketChannel::~WebSocketChannel() { | 95 WebSocketChannel::~WebSocketChannel() { |
101 // The stream may hold a pointer to read_frame_chunks_, and so it needs to be | 96 // The stream may hold a pointer to read_frames_, and so it needs to be |
102 // destroyed first. | 97 // destroyed first. |
103 stream_.reset(); | 98 stream_.reset(); |
104 } | 99 } |
105 | 100 |
106 void WebSocketChannel::SendAddChannelRequest( | 101 void WebSocketChannel::SendAddChannelRequest( |
107 const std::vector<std::string>& requested_subprotocols, | 102 const std::vector<std::string>& requested_subprotocols, |
108 const GURL& origin, | 103 const GURL& origin, |
109 URLRequestContext* url_request_context) { | 104 URLRequestContext* url_request_context) { |
110 // Delegate to the tested version. | 105 // Delegate to the tested version. |
111 SendAddChannelRequestWithFactory( | 106 SendAddChannelRequestWithFactory( |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
156 << "; misbehaving renderer? fin=" << fin | 151 << "; misbehaving renderer? fin=" << fin |
157 << " data.size()=" << data.size(); | 152 << " data.size()=" << data.size(); |
158 return; | 153 return; |
159 } | 154 } |
160 current_send_quota_ -= data.size(); | 155 current_send_quota_ -= data.size(); |
161 // TODO(ricea): If current_send_quota_ has dropped below | 156 // TODO(ricea): If current_send_quota_ has dropped below |
162 // send_quota_low_water_mark_, it might be good to increase the "low | 157 // send_quota_low_water_mark_, it might be good to increase the "low |
163 // water mark" and "high water mark", but only if the link to the WebSocket | 158 // water mark" and "high water mark", but only if the link to the WebSocket |
164 // server is not saturated. | 159 // server is not saturated. |
165 // TODO(ricea): For kOpCodeText, do UTF-8 validation? | 160 // TODO(ricea): For kOpCodeText, do UTF-8 validation? |
166 scoped_refptr<IOBufferWithSize> buffer(new IOBufferWithSize(data.size())); | 161 scoped_refptr<IOBuffer> buffer(new IOBuffer(data.size())); |
167 std::copy(data.begin(), data.end(), buffer->data()); | 162 std::copy(data.begin(), data.end(), buffer->data()); |
168 SendIOBufferWithSize(fin, op_code, buffer); | 163 SendIOBuffer(fin, op_code, buffer, data.size()); |
169 } | 164 } |
170 | 165 |
171 void WebSocketChannel::SendFlowControl(int64 quota) { | 166 void WebSocketChannel::SendFlowControl(int64 quota) { |
172 DCHECK_EQ(CONNECTED, state_); | 167 DCHECK_EQ(CONNECTED, state_); |
173 // TODO(ricea): Add interface to WebSocketStream and implement. | 168 // TODO(ricea): Add interface to WebSocketStream and implement. |
174 // stream_->SendFlowControl(quota); | 169 // stream_->SendFlowControl(quota); |
175 } | 170 } |
176 | 171 |
177 void WebSocketChannel::StartClosingHandshake(uint16 code, | 172 void WebSocketChannel::StartClosingHandshake(uint16 code, |
178 const std::string& reason) { | 173 const std::string& reason) { |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
301 } | 296 } |
302 } | 297 } |
303 | 298 |
304 void WebSocketChannel::ReadFrames() { | 299 void WebSocketChannel::ReadFrames() { |
305 int result = OK; | 300 int result = OK; |
306 do { | 301 do { |
307 // This use of base::Unretained is safe because this object owns the | 302 // This use of base::Unretained is safe because this object owns the |
308 // WebSocketStream, and any pending reads will be cancelled when it is | 303 // WebSocketStream, and any pending reads will be cancelled when it is |
309 // destroyed. | 304 // destroyed. |
310 result = stream_->ReadFrames( | 305 result = stream_->ReadFrames( |
311 &read_frame_chunks_, | 306 &read_frames_, |
312 base::Bind( | 307 base::Bind( |
313 &WebSocketChannel::OnReadDone, base::Unretained(this), false)); | 308 &WebSocketChannel::OnReadDone, base::Unretained(this), false)); |
314 if (result != ERR_IO_PENDING) { | 309 if (result != ERR_IO_PENDING) { |
315 OnReadDone(true, result); | 310 OnReadDone(true, result); |
316 } | 311 } |
317 } while (result == OK && state_ != CLOSED); | 312 } while (result == OK && state_ != CLOSED); |
318 } | 313 } |
319 | 314 |
320 void WebSocketChannel::OnReadDone(bool synchronous, int result) { | 315 void WebSocketChannel::OnReadDone(bool synchronous, int result) { |
321 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | 316 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); |
322 DCHECK_NE(CONNECTING, state_); | 317 DCHECK_NE(CONNECTING, state_); |
323 DCHECK_NE(ERR_IO_PENDING, result); | 318 DCHECK_NE(ERR_IO_PENDING, result); |
324 switch (result) { | 319 switch (result) { |
325 case OK: | 320 case OK: |
326 // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection | 321 // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection |
327 // with no data read, not an empty response. | 322 // with no data read, not an empty response. |
328 DCHECK(!read_frame_chunks_.empty()) | 323 DCHECK(!read_frames_.empty()) |
329 << "ReadFrames() returned OK, but nothing was read."; | 324 << "ReadFrames() returned OK, but nothing was read."; |
330 for (size_t i = 0; i < read_frame_chunks_.size(); ++i) { | 325 for (size_t i = 0; i < read_frames_.size(); ++i) { |
331 scoped_ptr<WebSocketFrameChunk> chunk(read_frame_chunks_[i]); | 326 scoped_ptr<WebSocketFrame> frame(read_frames_[i]); |
332 read_frame_chunks_[i] = NULL; | 327 read_frames_[i] = NULL; |
333 ProcessFrameChunk(chunk.Pass()); | 328 ProcessFrame(frame.Pass()); |
334 } | 329 } |
335 read_frame_chunks_.clear(); | 330 read_frames_.clear(); |
336 // There should always be a call to ReadFrames pending. | 331 // There should always be a call to ReadFrames pending. |
| 332 // TODO(ricea): Unless we are out of quota. |
337 if (!synchronous && state_ != CLOSED) { | 333 if (!synchronous && state_ != CLOSED) { |
338 ReadFrames(); | 334 ReadFrames(); |
339 } | 335 } |
340 return; | 336 return; |
341 | 337 |
| 338 case ERR_WS_PROTOCOL_ERROR: |
| 339 FailChannel(SEND_REAL_ERROR, |
| 340 kWebSocketErrorProtocolError, |
| 341 "WebSocket Protocol Error"); |
| 342 return; |
| 343 |
342 default: | 344 default: |
343 DCHECK_LT(result, 0) | 345 DCHECK_LT(result, 0) |
344 << "ReadFrames() should only return OK or ERR_ codes"; | 346 << "ReadFrames() should only return OK or ERR_ codes"; |
345 stream_->Close(); | 347 stream_->Close(); |
346 if (state_ != CLOSED) { | 348 if (state_ != CLOSED) { |
347 state_ = CLOSED; | 349 state_ = CLOSED; |
348 uint16 code = kWebSocketErrorAbnormalClosure; | 350 uint16 code = kWebSocketErrorAbnormalClosure; |
349 std::string reason = "Abnormal Closure"; | 351 std::string reason = "Abnormal Closure"; |
350 if (closing_code_ != 0) { | 352 if (closing_code_ != 0) { |
351 code = closing_code_; | 353 code = closing_code_; |
352 reason = closing_reason_; | 354 reason = closing_reason_; |
353 } | 355 } |
354 event_interface_->OnDropChannel(code, reason); | 356 event_interface_->OnDropChannel(code, reason); |
355 } | 357 } |
356 return; | 358 return; |
357 } | 359 } |
358 } | 360 } |
359 | 361 |
360 void WebSocketChannel::ProcessFrameChunk( | 362 void WebSocketChannel::ProcessFrame(scoped_ptr<WebSocketFrame> frame) { |
361 scoped_ptr<WebSocketFrameChunk> chunk) { | 363 if (frame->header.masked) { |
362 bool is_first_chunk = false; | 364 // RFC6455 Section 5.1 "A client MUST close a connection if it detects a |
363 if (chunk->header) { | 365 // masked frame." |
364 DCHECK(current_frame_header_ == NULL) | 366 FailChannel(SEND_REAL_ERROR, |
365 << "Received the header for a new frame without notification that " | 367 kWebSocketErrorProtocolError, |
366 << "the previous frame was complete."; | 368 "Masked frame from server"); |
367 is_first_chunk = true; | |
368 current_frame_header_.swap(chunk->header); | |
369 if (current_frame_header_->masked) { | |
370 // RFC6455 Section 5.1 "A client MUST close a connection if it detects a | |
371 // masked frame." | |
372 FailChannel(SEND_REAL_ERROR, | |
373 kWebSocketErrorProtocolError, | |
374 "Masked frame from server"); | |
375 return; | |
376 } | |
377 } | |
378 if (!current_frame_header_) { | |
379 // If this channel rejected the previous chunk as invalid, then it will have | |
380 // reset |current_frame_header_| and closed the channel. More chunks of the | |
381 // invalid frame may still arrive, and it is not necessarily a bug for that | |
382 // to happen. However, if this happens when state_ is CONNECTED, it is | |
383 // definitely a bug. | |
384 DCHECK(state_ != CONNECTED) << "Unexpected header-less frame received " | |
385 << "(final_chunk = " << chunk->final_chunk | |
386 << ", data size = " << chunk->data->size() | |
387 << ")"; | |
388 return; | 369 return; |
389 } | 370 } |
390 scoped_refptr<IOBufferWithSize> data_buffer; | 371 const WebSocketFrameHeader::OpCode opcode = frame->header.opcode; |
391 data_buffer.swap(chunk->data); | 372 if (WebSocketFrameHeader::IsKnownControlOpCode(opcode) && |
392 const bool is_final_chunk = chunk->final_chunk; | 373 !frame->header.final) { |
393 chunk.reset(); | 374 FailChannel(SEND_REAL_ERROR, |
394 const WebSocketFrameHeader::OpCode opcode = current_frame_header_->opcode; | 375 kWebSocketErrorProtocolError, |
395 if (WebSocketFrameHeader::IsKnownControlOpCode(opcode)) { | 376 "Control message with FIN bit unset received"); |
396 if (!current_frame_header_->final) { | 377 return; |
397 FailChannel(SEND_REAL_ERROR, | |
398 kWebSocketErrorProtocolError, | |
399 "Control message with FIN bit unset received"); | |
400 return; | |
401 } | |
402 if (current_frame_header_->payload_length > kMaxControlFramePayload) { | |
403 FailChannel(SEND_REAL_ERROR, | |
404 kWebSocketErrorProtocolError, | |
405 "Control message has payload over 125 bytes"); | |
406 return; | |
407 } | |
408 if (!is_final_chunk) { | |
409 VLOG(2) << "Encountered a split control frame, opcode " << opcode; | |
410 if (incomplete_control_frame_body_) { | |
411 VLOG(3) << "Appending to an existing split control frame."; | |
412 AddToIncompleteControlFrameBody(data_buffer); | |
413 } else { | |
414 VLOG(3) << "Creating new storage for an incomplete control frame."; | |
415 incomplete_control_frame_body_ = new GrowableIOBuffer(); | |
416 // This method checks for oversize control frames above, so as long as | |
417 // the frame parser is working correctly, this won't overflow. If a bug | |
418 // does cause it to overflow, it will CHECK() in | |
419 // AddToIncompleteControlFrameBody() without writing outside the buffer. | |
420 incomplete_control_frame_body_->SetCapacity(kMaxControlFramePayload); | |
421 AddToIncompleteControlFrameBody(data_buffer); | |
422 } | |
423 return; // Handle when complete. | |
424 } | |
425 if (incomplete_control_frame_body_) { | |
426 VLOG(2) << "Rejoining a split control frame, opcode " << opcode; | |
427 AddToIncompleteControlFrameBody(data_buffer); | |
428 const int body_size = incomplete_control_frame_body_->offset(); | |
429 data_buffer = new IOBufferWithSize(body_size); | |
430 memcpy(data_buffer->data(), | |
431 incomplete_control_frame_body_->StartOfBuffer(), | |
432 body_size); | |
433 incomplete_control_frame_body_ = NULL; // Frame now complete. | |
434 } | |
435 } | 378 } |
436 | 379 |
437 // Apply basic sanity checks to the |payload_length| field from the frame | |
438 // header. A check for exact equality can only be used when the whole frame | |
439 // arrives in one chunk. | |
440 DCHECK_GE(current_frame_header_->payload_length, | |
441 base::checked_numeric_cast<uint64>(data_buffer->size())); | |
442 DCHECK(!is_first_chunk || !is_final_chunk || | |
443 current_frame_header_->payload_length == | |
444 base::checked_numeric_cast<uint64>(data_buffer->size())); | |
445 | |
446 // Respond to the frame appropriately to its type. | 380 // Respond to the frame appropriately to its type. |
447 HandleFrame(opcode, is_first_chunk, is_final_chunk, data_buffer); | 381 HandleFrame( |
448 | 382 opcode, frame->header.final, frame->data, frame->header.payload_length); |
449 if (is_final_chunk) { | |
450 // Make sure that this frame header is not applied to any future chunks. | |
451 current_frame_header_.reset(); | |
452 } | |
453 } | 383 } |
454 | 384 |
455 void WebSocketChannel::AddToIncompleteControlFrameBody( | 385 void WebSocketChannel::HandleFrame(const WebSocketFrameHeader::OpCode opcode, |
456 const scoped_refptr<IOBufferWithSize>& data_buffer) { | 386 bool final, |
457 const int new_offset = | 387 const scoped_refptr<IOBuffer>& data_buffer, |
458 incomplete_control_frame_body_->offset() + data_buffer->size(); | 388 size_t size) { |
459 CHECK_GE(incomplete_control_frame_body_->capacity(), new_offset) | |
460 << "Control frame body larger than frame header indicates; frame parser " | |
461 "bug?"; | |
462 memcpy(incomplete_control_frame_body_->data(), | |
463 data_buffer->data(), | |
464 data_buffer->size()); | |
465 incomplete_control_frame_body_->set_offset(new_offset); | |
466 } | |
467 | |
468 void WebSocketChannel::HandleFrame( | |
469 const WebSocketFrameHeader::OpCode opcode, | |
470 bool is_first_chunk, | |
471 bool is_final_chunk, | |
472 const scoped_refptr<IOBufferWithSize>& data_buffer) { | |
473 DCHECK_NE(RECV_CLOSED, state_) | 389 DCHECK_NE(RECV_CLOSED, state_) |
474 << "HandleFrame() does not support being called re-entrantly from within " | 390 << "HandleFrame() does not support being called re-entrantly from within " |
475 "SendClose()"; | 391 "SendClose()"; |
476 if (state_ == CLOSED || state_ == CLOSE_WAIT) { | 392 if (state_ == CLOSED || state_ == CLOSE_WAIT) { |
477 DVLOG_IF(1, state_ == CLOSED) << "A frame was received while in the CLOSED " | 393 DVLOG_IF(1, state_ == CLOSED) << "A frame was received while in the CLOSED " |
478 "state. This is possible after a channel " | 394 "state. This is possible after a channel " |
479 "failed, but should be very rare."; | 395 "failed, but should be very rare."; |
480 std::string frame_name; | 396 std::string frame_name; |
481 switch (opcode) { | 397 switch (opcode) { |
482 case WebSocketFrameHeader::kOpCodeText: // fall-thru | 398 case WebSocketFrameHeader::kOpCodeText: // fall-thru |
(...skipping 23 matching lines...) Expand all Loading... |
506 FailChannel(SEND_REAL_ERROR, | 422 FailChannel(SEND_REAL_ERROR, |
507 kWebSocketErrorProtocolError, | 423 kWebSocketErrorProtocolError, |
508 frame_name + " received after close"); | 424 frame_name + " received after close"); |
509 return; | 425 return; |
510 } | 426 } |
511 switch (opcode) { | 427 switch (opcode) { |
512 case WebSocketFrameHeader::kOpCodeText: // fall-thru | 428 case WebSocketFrameHeader::kOpCodeText: // fall-thru |
513 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru | 429 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru |
514 case WebSocketFrameHeader::kOpCodeContinuation: | 430 case WebSocketFrameHeader::kOpCodeContinuation: |
515 if (state_ == CONNECTED) { | 431 if (state_ == CONNECTED) { |
516 const bool final = is_final_chunk && current_frame_header_->final; | |
517 // TODO(ricea): Need to fail the connection if UTF-8 is invalid | 432 // TODO(ricea): Need to fail the connection if UTF-8 is invalid |
518 // post-reassembly. Requires a streaming UTF-8 validator. | 433 // post-reassembly. Requires a streaming UTF-8 validator. |
519 // TODO(ricea): Can this copy be eliminated? | 434 // TODO(ricea): Can this copy be eliminated? |
520 const char* const data_begin = data_buffer->data(); | 435 const char* const data_begin = data_buffer->data(); |
521 const char* const data_end = data_begin + data_buffer->size(); | 436 const char* const data_end = data_begin + size; |
522 const std::vector<char> data(data_begin, data_end); | 437 const std::vector<char> data(data_begin, data_end); |
523 // TODO(ricea): Handle the case when ReadFrames returns far | 438 // TODO(ricea): Handle the case when ReadFrames returns far |
524 // more data at once than should be sent in a single IPC. This needs to | 439 // more data at once than should be sent in a single IPC. This needs to |
525 // be handled carefully, as an overloaded IO thread is one possible | 440 // be handled carefully, as an overloaded IO thread is one possible |
526 // cause of receiving very large chunks. | 441 // cause of receiving very large chunks. |
527 | 442 |
528 // Sends the received frame to the renderer process. | 443 // Sends the received frame to the renderer process. |
529 event_interface_->OnDataFrame( | 444 event_interface_->OnDataFrame(final, opcode, data); |
530 final, | |
531 is_first_chunk ? opcode : WebSocketFrameHeader::kOpCodeContinuation, | |
532 data); | |
533 } else { | 445 } else { |
534 VLOG(3) << "Ignored data packet received in state " << state_; | 446 VLOG(3) << "Ignored data packet received in state " << state_; |
535 } | 447 } |
536 return; | 448 return; |
537 | 449 |
538 case WebSocketFrameHeader::kOpCodePing: | 450 case WebSocketFrameHeader::kOpCodePing: |
539 VLOG(1) << "Got Ping of size " << data_buffer->size(); | 451 VLOG(1) << "Got Ping of size " << size; |
540 if (state_ == CONNECTED) { | 452 if (state_ == CONNECTED) { |
541 SendIOBufferWithSize( | 453 SendIOBuffer( |
542 true, WebSocketFrameHeader::kOpCodePong, data_buffer); | 454 true, WebSocketFrameHeader::kOpCodePong, data_buffer, size); |
543 } else { | 455 } else { |
544 VLOG(3) << "Ignored ping in state " << state_; | 456 VLOG(3) << "Ignored ping in state " << state_; |
545 } | 457 } |
546 return; | 458 return; |
547 | 459 |
548 case WebSocketFrameHeader::kOpCodePong: | 460 case WebSocketFrameHeader::kOpCodePong: |
549 VLOG(1) << "Got Pong of size " << data_buffer->size(); | 461 VLOG(1) << "Got Pong of size " << size; |
550 // There is no need to do anything with pong messages. | 462 // There is no need to do anything with pong messages. |
551 return; | 463 return; |
552 | 464 |
553 case WebSocketFrameHeader::kOpCodeClose: { | 465 case WebSocketFrameHeader::kOpCodeClose: { |
554 uint16 code = kWebSocketNormalClosure; | 466 uint16 code = kWebSocketNormalClosure; |
555 std::string reason; | 467 std::string reason; |
556 ParseClose(data_buffer, &code, &reason); | 468 ParseClose(data_buffer, size, &code, &reason); |
557 // TODO(ricea): Find a way to safely log the message from the close | 469 // TODO(ricea): Find a way to safely log the message from the close |
558 // message (escape control codes and so on). | 470 // message (escape control codes and so on). |
559 VLOG(1) << "Got Close with code " << code; | 471 VLOG(1) << "Got Close with code " << code; |
560 switch (state_) { | 472 switch (state_) { |
561 case CONNECTED: | 473 case CONNECTED: |
562 state_ = RECV_CLOSED; | 474 state_ = RECV_CLOSED; |
563 SendClose(code, reason); // Sets state_ to CLOSE_WAIT | 475 SendClose(code, reason); // Sets state_ to CLOSE_WAIT |
564 event_interface_->OnClosingHandshake(); | 476 event_interface_->OnClosingHandshake(); |
565 closing_code_ = code; | 477 closing_code_ = code; |
566 closing_reason_ = reason; | 478 closing_reason_ = reason; |
(...skipping 15 matching lines...) Expand all Loading... |
582 return; | 494 return; |
583 } | 495 } |
584 | 496 |
585 default: | 497 default: |
586 FailChannel( | 498 FailChannel( |
587 SEND_REAL_ERROR, kWebSocketErrorProtocolError, "Unknown opcode"); | 499 SEND_REAL_ERROR, kWebSocketErrorProtocolError, "Unknown opcode"); |
588 return; | 500 return; |
589 } | 501 } |
590 } | 502 } |
591 | 503 |
592 void WebSocketChannel::SendIOBufferWithSize( | 504 void WebSocketChannel::SendIOBuffer(bool fin, |
593 bool fin, | 505 WebSocketFrameHeader::OpCode op_code, |
594 WebSocketFrameHeader::OpCode op_code, | 506 const scoped_refptr<IOBuffer>& buffer, |
595 const scoped_refptr<IOBufferWithSize>& buffer) { | 507 size_t size) { |
596 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); | 508 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); |
597 DCHECK(stream_); | 509 DCHECK(stream_); |
598 scoped_ptr<WebSocketFrameHeader> header(new WebSocketFrameHeader(op_code)); | 510 scoped_ptr<WebSocketFrame> frame(new WebSocketFrame(op_code)); |
599 header->final = fin; | 511 WebSocketFrameHeader& header = frame->header; |
600 header->masked = true; | 512 header.final = fin; |
601 header->payload_length = buffer->size(); | 513 header.masked = true; |
602 scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk()); | 514 header.payload_length = size; |
603 chunk->header = header.Pass(); | 515 frame->data = buffer; |
604 chunk->final_chunk = true; | |
605 chunk->data = buffer; | |
606 if (data_being_sent_) { | 516 if (data_being_sent_) { |
607 // Either the link to the WebSocket server is saturated, or several messages | 517 // Either the link to the WebSocket server is saturated, or several messages |
608 // are being sent in a batch. | 518 // are being sent in a batch. |
609 // TODO(ricea): Keep some statistics to work out the situation and adjust | 519 // TODO(ricea): Keep some statistics to work out the situation and adjust |
610 // quota appropriately. | 520 // quota appropriately. |
611 if (!data_to_send_next_) | 521 if (!data_to_send_next_) |
612 data_to_send_next_.reset(new SendBuffer); | 522 data_to_send_next_.reset(new SendBuffer); |
613 data_to_send_next_->AddFrame(chunk.Pass()); | 523 data_to_send_next_->AddFrame(frame.Pass()); |
614 } else { | 524 } else { |
615 data_being_sent_.reset(new SendBuffer); | 525 data_being_sent_.reset(new SendBuffer); |
616 data_being_sent_->AddFrame(chunk.Pass()); | 526 data_being_sent_->AddFrame(frame.Pass()); |
617 WriteFrames(); | 527 WriteFrames(); |
618 } | 528 } |
619 } | 529 } |
620 | 530 |
621 void WebSocketChannel::FailChannel(ExposeError expose, | 531 void WebSocketChannel::FailChannel(ExposeError expose, |
622 uint16 code, | 532 uint16 code, |
623 const std::string& reason) { | 533 const std::string& reason) { |
624 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | 534 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); |
625 DCHECK_NE(CONNECTING, state_); | 535 DCHECK_NE(CONNECTING, state_); |
626 // TODO(ricea): Logging. | 536 // TODO(ricea): Logging. |
627 State old_state = state_; | 537 State old_state = state_; |
628 if (state_ == CONNECTED) { | 538 if (state_ == CONNECTED) { |
629 uint16 send_code = kWebSocketErrorGoingAway; | 539 uint16 send_code = kWebSocketErrorGoingAway; |
630 std::string send_reason = "Internal Error"; | 540 std::string send_reason = "Internal Error"; |
631 if (expose == SEND_REAL_ERROR) { | 541 if (expose == SEND_REAL_ERROR) { |
632 send_code = code; | 542 send_code = code; |
633 send_reason = reason; | 543 send_reason = reason; |
634 } | 544 } |
635 SendClose(send_code, send_reason); // Sets state_ to SEND_CLOSED | 545 SendClose(send_code, send_reason); // Sets state_ to SEND_CLOSED |
636 } | 546 } |
637 // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates the browser | 547 // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates the browser |
638 // should close the connection itself without waiting for the closing | 548 // should close the connection itself without waiting for the closing |
639 // handshake. | 549 // handshake. |
640 stream_->Close(); | 550 stream_->Close(); |
641 state_ = CLOSED; | 551 state_ = CLOSED; |
642 | 552 |
643 // The channel may be in the middle of processing several chunks. It should | |
644 // not use this frame header for subsequent chunks. | |
645 current_frame_header_.reset(); | |
646 if (old_state != CLOSED) { | 553 if (old_state != CLOSED) { |
647 event_interface_->OnDropChannel(code, reason); | 554 event_interface_->OnDropChannel(code, reason); |
648 } | 555 } |
649 } | 556 } |
650 | 557 |
651 void WebSocketChannel::SendClose(uint16 code, const std::string& reason) { | 558 void WebSocketChannel::SendClose(uint16 code, const std::string& reason) { |
652 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); | 559 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); |
653 // TODO(ricea): Ensure reason.length() <= 123 | 560 // TODO(ricea): Ensure reason.length() <= 123 |
654 scoped_refptr<IOBufferWithSize> body; | 561 scoped_refptr<IOBuffer> body; |
| 562 size_t size = 0; |
655 if (code == kWebSocketErrorNoStatusReceived) { | 563 if (code == kWebSocketErrorNoStatusReceived) { |
656 // Special case: translate kWebSocketErrorNoStatusReceived into a Close | 564 // Special case: translate kWebSocketErrorNoStatusReceived into a Close |
657 // frame with no payload. | 565 // frame with no payload. |
658 body = new IOBufferWithSize(0); | 566 body = new IOBuffer(0); |
659 } else { | 567 } else { |
660 const size_t payload_length = kWebSocketCloseCodeLength + reason.length(); | 568 const size_t payload_length = kWebSocketCloseCodeLength + reason.length(); |
661 body = new IOBufferWithSize(payload_length); | 569 body = new IOBuffer(payload_length); |
| 570 size = payload_length; |
662 WriteBigEndian(body->data(), code); | 571 WriteBigEndian(body->data(), code); |
663 COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength, | 572 COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength, |
664 they_should_both_be_two); | 573 they_should_both_be_two); |
665 std::copy( | 574 std::copy( |
666 reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength); | 575 reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength); |
667 } | 576 } |
668 SendIOBufferWithSize(true, WebSocketFrameHeader::kOpCodeClose, body); | 577 SendIOBuffer(true, WebSocketFrameHeader::kOpCodeClose, body, size); |
669 state_ = (state_ == CONNECTED) ? SEND_CLOSED : CLOSE_WAIT; | 578 state_ = (state_ == CONNECTED) ? SEND_CLOSED : CLOSE_WAIT; |
670 } | 579 } |
671 | 580 |
672 void WebSocketChannel::ParseClose(const scoped_refptr<IOBufferWithSize>& buffer, | 581 void WebSocketChannel::ParseClose(const scoped_refptr<IOBuffer>& buffer, |
| 582 size_t size, |
673 uint16* code, | 583 uint16* code, |
674 std::string* reason) { | 584 std::string* reason) { |
675 const char* data = buffer->data(); | 585 const char* data = buffer->data(); |
676 size_t size = base::checked_numeric_cast<size_t>(buffer->size()); | |
677 reason->clear(); | 586 reason->clear(); |
678 if (size < kWebSocketCloseCodeLength) { | 587 if (size < kWebSocketCloseCodeLength) { |
679 *code = kWebSocketErrorNoStatusReceived; | 588 *code = kWebSocketErrorNoStatusReceived; |
680 if (size != 0) { | 589 if (size != 0) { |
681 VLOG(1) << "Close frame with payload size " << size << " received " | 590 VLOG(1) << "Close frame with payload size " << size << " received " |
682 << "(the first byte is " << std::hex << static_cast<int>(data[0]) | 591 << "(the first byte is " << std::hex << static_cast<int>(data[0]) |
683 << ")"; | 592 << ")"; |
684 return; | 593 return; |
685 } | 594 } |
686 return; | 595 return; |
(...skipping 13 matching lines...) Expand all Loading... |
700 } | 609 } |
701 std::string text(data + kWebSocketCloseCodeLength, data + size); | 610 std::string text(data + kWebSocketCloseCodeLength, data + size); |
702 // TODO(ricea): Is this check strict enough? In particular, check the | 611 // TODO(ricea): Is this check strict enough? In particular, check the |
703 // "Security Considerations" from RFC3629. | 612 // "Security Considerations" from RFC3629. |
704 if (IsStringUTF8(text)) { | 613 if (IsStringUTF8(text)) { |
705 reason->swap(text); | 614 reason->swap(text); |
706 } | 615 } |
707 } | 616 } |
708 | 617 |
709 } // namespace net | 618 } // namespace net |
OLD | NEW |