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 |
342 default: | 338 default: |
343 DCHECK_LT(result, 0) | 339 DCHECK_LT(result, 0) |
344 << "ReadFrames() should only return OK or ERR_ codes"; | 340 << "ReadFrames() should only return OK or ERR_ codes"; |
345 stream_->Close(); | 341 stream_->Close(); |
346 if (state_ != CLOSED) { | 342 if (state_ != CLOSED) { |
347 state_ = CLOSED; | 343 state_ = CLOSED; |
348 uint16 code = kWebSocketErrorAbnormalClosure; | 344 uint16 code = kWebSocketErrorAbnormalClosure; |
349 std::string reason = "Abnormal Closure"; | 345 std::string reason = "Abnormal Closure"; |
350 if (closing_code_ != 0) { | 346 if (closing_code_ != 0) { |
351 code = closing_code_; | 347 code = closing_code_; |
352 reason = closing_reason_; | 348 reason = closing_reason_; |
353 } | 349 } |
354 event_interface_->OnDropChannel(code, reason); | 350 event_interface_->OnDropChannel(code, reason); |
355 } | 351 } |
356 return; | 352 return; |
357 } | 353 } |
358 } | 354 } |
359 | 355 |
360 void WebSocketChannel::ProcessFrameChunk( | 356 void WebSocketChannel::ProcessFrame(scoped_ptr<WebSocketFrame> frame) { |
361 scoped_ptr<WebSocketFrameChunk> chunk) { | 357 if (frame->header.masked) { |
362 bool is_first_chunk = false; | 358 // RFC6455 Section 5.1 "A client MUST close a connection if it detects a |
363 if (chunk->header) { | 359 // masked frame." |
364 DCHECK(current_frame_header_ == NULL) | 360 FailChannel(SEND_REAL_ERROR, |
365 << "Received the header for a new frame without notification that " | 361 kWebSocketErrorProtocolError, |
366 << "the previous frame was complete."; | 362 "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; | 363 return; |
389 } | 364 } |
390 scoped_refptr<IOBufferWithSize> data_buffer; | 365 scoped_refptr<IOBuffer> data_buffer; |
391 data_buffer.swap(chunk->data); | 366 data_buffer.swap(frame->data); |
tyoshino (SeeGerritForStatus)
2013/09/25 08:39:23
you can move these 2 lines down to L377
Adam Rice
2013/09/26 03:07:58
Thank you. Actually, it turned out I could remove
| |
392 const bool is_final_chunk = chunk->final_chunk; | 367 const WebSocketFrameHeader::OpCode opcode = frame->header.opcode; |
393 chunk.reset(); | 368 if (WebSocketFrameHeader::IsKnownControlOpCode(opcode) && |
394 const WebSocketFrameHeader::OpCode opcode = current_frame_header_->opcode; | 369 !frame->header.final) { |
395 if (WebSocketFrameHeader::IsKnownControlOpCode(opcode)) { | 370 FailChannel(SEND_REAL_ERROR, |
396 if (!current_frame_header_->final) { | 371 kWebSocketErrorProtocolError, |
397 FailChannel(SEND_REAL_ERROR, | 372 "Control message with FIN bit unset received"); |
398 kWebSocketErrorProtocolError, | 373 return; |
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 } | |
tyoshino (SeeGerritForStatus)
2013/09/25 08:39:23
Looks like these two FailChannel() calls has been
Adam Rice
2013/09/26 03:07:58
No. It closes the connection and sends code 1006,
| |
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 } | 374 } |
436 | 375 |
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. | 376 // Respond to the frame appropriately to its type. |
447 HandleFrame(opcode, is_first_chunk, is_final_chunk, data_buffer); | 377 HandleFrame( |
448 | 378 opcode, frame->header.final, data_buffer, 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 } | 379 } |
454 | 380 |
455 void WebSocketChannel::AddToIncompleteControlFrameBody( | 381 void WebSocketChannel::HandleFrame(const WebSocketFrameHeader::OpCode opcode, |
456 const scoped_refptr<IOBufferWithSize>& data_buffer) { | 382 bool final, |
457 const int new_offset = | 383 const scoped_refptr<IOBuffer>& data_buffer, |
458 incomplete_control_frame_body_->offset() + data_buffer->size(); | 384 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_) | 385 DCHECK_NE(RECV_CLOSED, state_) |
474 << "HandleFrame() does not support being called re-entrantly from within " | 386 << "HandleFrame() does not support being called re-entrantly from within " |
475 "SendClose()"; | 387 "SendClose()"; |
476 if (state_ == CLOSED || state_ == CLOSE_WAIT) { | 388 if (state_ == CLOSED || state_ == CLOSE_WAIT) { |
477 DVLOG_IF(1, state_ == CLOSED) << "A frame was received while in the CLOSED " | 389 DVLOG_IF(1, state_ == CLOSED) << "A frame was received while in the CLOSED " |
478 "state. This is possible after a channel " | 390 "state. This is possible after a channel " |
479 "failed, but should be very rare."; | 391 "failed, but should be very rare."; |
480 std::string frame_name; | 392 std::string frame_name; |
481 switch (opcode) { | 393 switch (opcode) { |
482 case WebSocketFrameHeader::kOpCodeText: // fall-thru | 394 case WebSocketFrameHeader::kOpCodeText: // fall-thru |
(...skipping 23 matching lines...) Expand all Loading... | |
506 FailChannel(SEND_REAL_ERROR, | 418 FailChannel(SEND_REAL_ERROR, |
507 kWebSocketErrorProtocolError, | 419 kWebSocketErrorProtocolError, |
508 frame_name + " received after close"); | 420 frame_name + " received after close"); |
509 return; | 421 return; |
510 } | 422 } |
511 switch (opcode) { | 423 switch (opcode) { |
512 case WebSocketFrameHeader::kOpCodeText: // fall-thru | 424 case WebSocketFrameHeader::kOpCodeText: // fall-thru |
513 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru | 425 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru |
514 case WebSocketFrameHeader::kOpCodeContinuation: | 426 case WebSocketFrameHeader::kOpCodeContinuation: |
515 if (state_ == CONNECTED) { | 427 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 | 428 // TODO(ricea): Need to fail the connection if UTF-8 is invalid |
518 // post-reassembly. Requires a streaming UTF-8 validator. | 429 // post-reassembly. Requires a streaming UTF-8 validator. |
519 // TODO(ricea): Can this copy be eliminated? | 430 // TODO(ricea): Can this copy be eliminated? |
520 const char* const data_begin = data_buffer->data(); | 431 const char* const data_begin = data_buffer->data(); |
521 const char* const data_end = data_begin + data_buffer->size(); | 432 const char* const data_end = data_begin + size; |
522 const std::vector<char> data(data_begin, data_end); | 433 const std::vector<char> data(data_begin, data_end); |
523 // TODO(ricea): Handle the case when ReadFrames returns far | 434 // 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 | 435 // 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 | 436 // be handled carefully, as an overloaded IO thread is one possible |
526 // cause of receiving very large chunks. | 437 // cause of receiving very large chunks. |
527 | 438 |
528 // Sends the received frame to the renderer process. | 439 // Sends the received frame to the renderer process. |
529 event_interface_->OnDataFrame( | 440 event_interface_->OnDataFrame(final, opcode, data); |
530 final, | |
531 is_first_chunk ? opcode : WebSocketFrameHeader::kOpCodeContinuation, | |
532 data); | |
533 } else { | 441 } else { |
534 VLOG(3) << "Ignored data packet received in state " << state_; | 442 VLOG(3) << "Ignored data packet received in state " << state_; |
535 } | 443 } |
536 return; | 444 return; |
537 | 445 |
538 case WebSocketFrameHeader::kOpCodePing: | 446 case WebSocketFrameHeader::kOpCodePing: |
539 VLOG(1) << "Got Ping of size " << data_buffer->size(); | 447 VLOG(1) << "Got Ping of size " << size; |
540 if (state_ == CONNECTED) { | 448 if (state_ == CONNECTED) { |
541 SendIOBufferWithSize( | 449 SendIOBuffer( |
542 true, WebSocketFrameHeader::kOpCodePong, data_buffer); | 450 true, WebSocketFrameHeader::kOpCodePong, data_buffer, size); |
543 } else { | 451 } else { |
544 VLOG(3) << "Ignored ping in state " << state_; | 452 VLOG(3) << "Ignored ping in state " << state_; |
545 } | 453 } |
546 return; | 454 return; |
547 | 455 |
548 case WebSocketFrameHeader::kOpCodePong: | 456 case WebSocketFrameHeader::kOpCodePong: |
549 VLOG(1) << "Got Pong of size " << data_buffer->size(); | 457 VLOG(1) << "Got Pong of size " << size; |
550 // There is no need to do anything with pong messages. | 458 // There is no need to do anything with pong messages. |
551 return; | 459 return; |
552 | 460 |
553 case WebSocketFrameHeader::kOpCodeClose: { | 461 case WebSocketFrameHeader::kOpCodeClose: { |
554 uint16 code = kWebSocketNormalClosure; | 462 uint16 code = kWebSocketNormalClosure; |
555 std::string reason; | 463 std::string reason; |
556 ParseClose(data_buffer, &code, &reason); | 464 ParseClose(data_buffer, size, &code, &reason); |
557 // TODO(ricea): Find a way to safely log the message from the close | 465 // TODO(ricea): Find a way to safely log the message from the close |
558 // message (escape control codes and so on). | 466 // message (escape control codes and so on). |
559 VLOG(1) << "Got Close with code " << code; | 467 VLOG(1) << "Got Close with code " << code; |
560 switch (state_) { | 468 switch (state_) { |
561 case CONNECTED: | 469 case CONNECTED: |
562 state_ = RECV_CLOSED; | 470 state_ = RECV_CLOSED; |
563 SendClose(code, reason); // Sets state_ to CLOSE_WAIT | 471 SendClose(code, reason); // Sets state_ to CLOSE_WAIT |
564 event_interface_->OnClosingHandshake(); | 472 event_interface_->OnClosingHandshake(); |
565 closing_code_ = code; | 473 closing_code_ = code; |
566 closing_reason_ = reason; | 474 closing_reason_ = reason; |
(...skipping 15 matching lines...) Expand all Loading... | |
582 return; | 490 return; |
583 } | 491 } |
584 | 492 |
585 default: | 493 default: |
586 FailChannel( | 494 FailChannel( |
587 SEND_REAL_ERROR, kWebSocketErrorProtocolError, "Unknown opcode"); | 495 SEND_REAL_ERROR, kWebSocketErrorProtocolError, "Unknown opcode"); |
588 return; | 496 return; |
589 } | 497 } |
590 } | 498 } |
591 | 499 |
592 void WebSocketChannel::SendIOBufferWithSize( | 500 void WebSocketChannel::SendIOBuffer(bool fin, |
593 bool fin, | 501 WebSocketFrameHeader::OpCode op_code, |
594 WebSocketFrameHeader::OpCode op_code, | 502 const scoped_refptr<IOBuffer>& buffer, |
595 const scoped_refptr<IOBufferWithSize>& buffer) { | 503 size_t size) { |
596 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); | 504 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); |
597 DCHECK(stream_); | 505 DCHECK(stream_); |
598 scoped_ptr<WebSocketFrameHeader> header(new WebSocketFrameHeader(op_code)); | 506 scoped_ptr<WebSocketFrame> frame(new WebSocketFrame(op_code)); |
599 header->final = fin; | 507 WebSocketFrameHeader& header = frame->header; |
600 header->masked = true; | 508 header.final = fin; |
601 header->payload_length = buffer->size(); | 509 header.masked = true; |
602 scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk()); | 510 header.payload_length = size; |
603 chunk->header = header.Pass(); | 511 frame->data = buffer; |
604 chunk->final_chunk = true; | |
605 chunk->data = buffer; | |
606 if (data_being_sent_) { | 512 if (data_being_sent_) { |
607 // Either the link to the WebSocket server is saturated, or several messages | 513 // Either the link to the WebSocket server is saturated, or several messages |
608 // are being sent in a batch. | 514 // are being sent in a batch. |
609 // TODO(ricea): Keep some statistics to work out the situation and adjust | 515 // TODO(ricea): Keep some statistics to work out the situation and adjust |
610 // quota appropriately. | 516 // quota appropriately. |
611 if (!data_to_send_next_) | 517 if (!data_to_send_next_) |
612 data_to_send_next_.reset(new SendBuffer); | 518 data_to_send_next_.reset(new SendBuffer); |
613 data_to_send_next_->AddFrame(chunk.Pass()); | 519 data_to_send_next_->AddFrame(frame.Pass()); |
614 } else { | 520 } else { |
615 data_being_sent_.reset(new SendBuffer); | 521 data_being_sent_.reset(new SendBuffer); |
616 data_being_sent_->AddFrame(chunk.Pass()); | 522 data_being_sent_->AddFrame(frame.Pass()); |
617 WriteFrames(); | 523 WriteFrames(); |
618 } | 524 } |
619 } | 525 } |
620 | 526 |
621 void WebSocketChannel::FailChannel(ExposeError expose, | 527 void WebSocketChannel::FailChannel(ExposeError expose, |
622 uint16 code, | 528 uint16 code, |
623 const std::string& reason) { | 529 const std::string& reason) { |
624 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | 530 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); |
625 DCHECK_NE(CONNECTING, state_); | 531 DCHECK_NE(CONNECTING, state_); |
626 // TODO(ricea): Logging. | 532 // TODO(ricea): Logging. |
627 State old_state = state_; | 533 State old_state = state_; |
628 if (state_ == CONNECTED) { | 534 if (state_ == CONNECTED) { |
629 uint16 send_code = kWebSocketErrorGoingAway; | 535 uint16 send_code = kWebSocketErrorGoingAway; |
630 std::string send_reason = "Internal Error"; | 536 std::string send_reason = "Internal Error"; |
631 if (expose == SEND_REAL_ERROR) { | 537 if (expose == SEND_REAL_ERROR) { |
632 send_code = code; | 538 send_code = code; |
633 send_reason = reason; | 539 send_reason = reason; |
634 } | 540 } |
635 SendClose(send_code, send_reason); // Sets state_ to SEND_CLOSED | 541 SendClose(send_code, send_reason); // Sets state_ to SEND_CLOSED |
636 } | 542 } |
637 // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates the browser | 543 // 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 | 544 // should close the connection itself without waiting for the closing |
639 // handshake. | 545 // handshake. |
640 stream_->Close(); | 546 stream_->Close(); |
641 state_ = CLOSED; | 547 state_ = CLOSED; |
642 | 548 |
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) { | 549 if (old_state != CLOSED) { |
647 event_interface_->OnDropChannel(code, reason); | 550 event_interface_->OnDropChannel(code, reason); |
648 } | 551 } |
649 } | 552 } |
650 | 553 |
651 void WebSocketChannel::SendClose(uint16 code, const std::string& reason) { | 554 void WebSocketChannel::SendClose(uint16 code, const std::string& reason) { |
652 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); | 555 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); |
653 // TODO(ricea): Ensure reason.length() <= 123 | 556 // TODO(ricea): Ensure reason.length() <= 123 |
654 scoped_refptr<IOBufferWithSize> body; | 557 scoped_refptr<IOBuffer> body; |
558 size_t size = 0; | |
655 if (code == kWebSocketErrorNoStatusReceived) { | 559 if (code == kWebSocketErrorNoStatusReceived) { |
656 // Special case: translate kWebSocketErrorNoStatusReceived into a Close | 560 // Special case: translate kWebSocketErrorNoStatusReceived into a Close |
657 // frame with no payload. | 561 // frame with no payload. |
658 body = new IOBufferWithSize(0); | 562 body = new IOBuffer(0); |
659 } else { | 563 } else { |
660 const size_t payload_length = kWebSocketCloseCodeLength + reason.length(); | 564 const size_t payload_length = kWebSocketCloseCodeLength + reason.length(); |
661 body = new IOBufferWithSize(payload_length); | 565 body = new IOBuffer(payload_length); |
566 size = payload_length; | |
662 WriteBigEndian(body->data(), code); | 567 WriteBigEndian(body->data(), code); |
663 COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength, | 568 COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength, |
664 they_should_both_be_two); | 569 they_should_both_be_two); |
665 std::copy( | 570 std::copy( |
666 reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength); | 571 reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength); |
667 } | 572 } |
668 SendIOBufferWithSize(true, WebSocketFrameHeader::kOpCodeClose, body); | 573 SendIOBuffer(true, WebSocketFrameHeader::kOpCodeClose, body, size); |
669 state_ = (state_ == CONNECTED) ? SEND_CLOSED : CLOSE_WAIT; | 574 state_ = (state_ == CONNECTED) ? SEND_CLOSED : CLOSE_WAIT; |
670 } | 575 } |
671 | 576 |
672 void WebSocketChannel::ParseClose(const scoped_refptr<IOBufferWithSize>& buffer, | 577 void WebSocketChannel::ParseClose(const scoped_refptr<IOBuffer>& buffer, |
578 size_t size, | |
673 uint16* code, | 579 uint16* code, |
674 std::string* reason) { | 580 std::string* reason) { |
675 const char* data = buffer->data(); | 581 const char* data = buffer->data(); |
676 size_t size = base::checked_numeric_cast<size_t>(buffer->size()); | |
677 reason->clear(); | 582 reason->clear(); |
678 if (size < kWebSocketCloseCodeLength) { | 583 if (size < kWebSocketCloseCodeLength) { |
679 *code = kWebSocketErrorNoStatusReceived; | 584 *code = kWebSocketErrorNoStatusReceived; |
680 if (size != 0) { | 585 if (size != 0) { |
681 VLOG(1) << "Close frame with payload size " << size << " received " | 586 VLOG(1) << "Close frame with payload size " << size << " received " |
682 << "(the first byte is " << std::hex << static_cast<int>(data[0]) | 587 << "(the first byte is " << std::hex << static_cast<int>(data[0]) |
683 << ")"; | 588 << ")"; |
684 return; | 589 return; |
685 } | 590 } |
686 return; | 591 return; |
(...skipping 13 matching lines...) Expand all Loading... | |
700 } | 605 } |
701 std::string text(data + kWebSocketCloseCodeLength, data + size); | 606 std::string text(data + kWebSocketCloseCodeLength, data + size); |
702 // TODO(ricea): Is this check strict enough? In particular, check the | 607 // TODO(ricea): Is this check strict enough? In particular, check the |
703 // "Security Considerations" from RFC3629. | 608 // "Security Considerations" from RFC3629. |
704 if (IsStringUTF8(text)) { | 609 if (IsStringUTF8(text)) { |
705 reason->swap(text); | 610 reason->swap(text); |
706 } | 611 } |
707 } | 612 } |
708 | 613 |
709 } // namespace net | 614 } // namespace net |
OLD | NEW |