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

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

Issue 12764006: WebSocketChannel implementation (Closed) Base URL: http://git.chromium.org/chromium/src.git@web_socket_dispatcher
Patch Set: Explain why operator<< needs to be in ::net Created 7 years, 5 months 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
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "net/websockets/websocket_channel.h"
6
7 #include <algorithm>
8
9 #include "base/basictypes.h" // for size_t
10 #include "base/bind.h"
11 #include "base/safe_numerics.h"
12 #include "base/strings/string_util.h"
13 #include "net/base/big_endian.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/net_log.h"
16 #include "net/websockets/websocket_errors.h"
17 #include "net/websockets/websocket_event_interface.h"
18 #include "net/websockets/websocket_frame.h"
19 #include "net/websockets/websocket_mux.h"
20 #include "net/websockets/websocket_stream.h"
21
22 namespace net {
23
24 namespace {
25
26 const int kDefaultSendQuotaLowWaterMark = 1 << 16;
27 const int kDefaultSendQuotaHighWaterMark = 1 << 17;
28 const size_t kWebSocketCloseCodeLength = 2;
29
30 // Concatenate the data from two IOBufferWithSize objects into a single one.
31 IOBufferWithSize* ConcatenateIOBuffers(
32 const scoped_refptr<IOBufferWithSize>& part1,
33 const scoped_refptr<IOBufferWithSize>& part2) {
34 int newsize = part1->size() + part2->size();
35 IOBufferWithSize* newbuffer = new IOBufferWithSize(newsize);
36 std::copy(part1->data(), part1->data() + part1->size(), newbuffer->data());
37 std::copy(part2->data(),
38 part2->data() + part2->size(),
39 newbuffer->data() + part1->size());
40 return newbuffer;
41 }
42
43 } // namespace
44
45 // A class to encapsulate a set of frames and information about the size of
46 // those frames.
47 class WebSocketChannel::SendBuffer {
48 public:
49 SendBuffer() : total_bytes_(0) {}
50
51 // Add a WebSocketFrameChunk to the buffer and increase total_bytes_.
52 void AddFrame(scoped_ptr<WebSocketFrameChunk> chunk);
53
54 // Return a pointer to the frames_ for write purposes.
55 ScopedVector<WebSocketFrameChunk>* GetFrames() { return &frames_; }
56
57 private:
58 // The frames_ that will be sent in the next call to WriteFrames().
59 ScopedVector<WebSocketFrameChunk> frames_;
60
61 // The total size of the buffers in |frames_|. This will be used to measure
62 // the throughput of the link.
63 // TODO(ricea): Measure the throughput of the link.
64 size_t total_bytes_;
65 };
66
67 void WebSocketChannel::SendBuffer::AddFrame(
68 scoped_ptr<WebSocketFrameChunk> chunk) {
69 total_bytes_ += chunk->data->size();
70 frames_.push_back(chunk.release());
71 }
72
73 // Implementation of WebSocketStream::ConnectDelegate that simply forwards the
74 // calls on to the WebSocketChannel that created it.
75 class WebSocketChannel::ConnectDelegate
76 : public WebSocketStream::ConnectDelegate {
77 public:
78 explicit ConnectDelegate(WebSocketChannel* creator) : creator_(creator) {}
79
80 virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE {
81 creator_->OnConnectSuccess(stream.Pass());
82 }
83
84 virtual void OnFailure(uint16 websocket_error) OVERRIDE {
85 creator_->OnConnectFailure(websocket_error);
86 }
87
88 private:
89 // A pointer to the WebSocketChannel that created us. We do not need to worry
90 // about this pointer being stale, because deleting WebSocketChannel cancels
91 // the connect process, deleting this object and preventing its callbacks from
92 // being called.
93 WebSocketChannel* const creator_;
94
95 DISALLOW_COPY_AND_ASSIGN(ConnectDelegate);
96 };
97
98 WebSocketChannel::WebSocketChannel(
99 const GURL& socket_url,
100 scoped_ptr<WebSocketEventInterface> event_interface)
101 : socket_url_(socket_url),
102 event_interface_(event_interface.Pass()),
103 send_quota_low_water_mark_(kDefaultSendQuotaLowWaterMark),
104 send_quota_high_water_mark_(kDefaultSendQuotaHighWaterMark),
105 current_send_quota_(0),
106 closing_code_(0),
107 state_(FRESHLY_CONSTRUCTED) {}
108
109 WebSocketChannel::~WebSocketChannel() {
110 // The stream may hold a pointer to read_frame_chunks_, and so it needs to be
111 // destroyed first.
112 stream_.reset();
113 }
114
115 void WebSocketChannel::SendAddChannelRequest(
116 const std::vector<std::string>& requested_subprotocols,
117 const GURL& origin,
118 URLRequestContext* url_request_context) {
119 // Delegate to the tested version.
120 SendAddChannelRequestWithFactory(
121 requested_subprotocols,
122 origin,
123 url_request_context,
124 base::Bind(&WebSocketStream::CreateAndConnectStream));
125 }
126
127 void WebSocketChannel::SendFrame(bool fin,
128 WebSocketFrameHeader::OpCode op_code,
129 const std::vector<char>& data) {
130 if (data.size() > INT_MAX) {
131 NOTREACHED() << "Frame size sanity check failed";
132 return;
133 }
134 if (stream_ == NULL) {
135 LOG(DFATAL) << "Got SendFrame without a connection established; "
136 << "misbehaving renderer? fin=" << fin << " op_code=" << op_code
137 << " data.size()=" << data.size();
138 return;
139 }
140 if (state_ == SEND_CLOSED || state_ == CLOSED) {
141 VLOG(1) << "SendFrame called in state " << state_
142 << ". This may be a bug, or a harmless race.";
143 return;
144 }
145 if (state_ != CONNECTED) {
146 NOTREACHED() << "SendFrame() called in state " << state_;
147 return;
148 }
149 if (data.size() > base::checked_numeric_cast<size_t>(current_send_quota_)) {
150 FailChannel(SEND_GOING_AWAY,
151 kWebSocketMuxErrorSendQuotaViolation,
152 "Send quota exceeded");
153 return;
154 }
155 if (!WebSocketFrameHeader::IsKnownDataOpCode(op_code)) {
156 LOG(DFATAL) << "Got SendFrame with bogus op_code " << op_code
157 << "; misbehaving renderer? fin=" << fin
158 << " data.size()=" << data.size();
159 return;
160 }
161 current_send_quota_ -= data.size();
162 // TODO(ricea): If current_send_quota_ has dropped below
163 // send_quota_low_water_mark_, we may want to consider increasing the "low
164 // water mark" and "high water mark", but only if we think we are not
165 // saturating the link to the WebSocket server.
166 // TODO(ricea): For kOpCodeText, do UTF-8 validation?
167 scoped_refptr<IOBufferWithSize> buffer(new IOBufferWithSize(data.size()));
168 std::copy(data.begin(), data.end(), buffer->data());
169 SendIOBufferWithSize(fin, op_code, buffer);
170 }
171
172 void WebSocketChannel::SendFlowControl(int64 quota) {
173 DCHECK_EQ(CONNECTED, state_);
174 // TODO(ricea): Add interface to WebSocketStream and implement.
175 // stream_->SendFlowControl(quota);
176 }
177
178 void WebSocketChannel::StartClosingHandshake(uint16 code,
179 const std::string& reason) {
180 if (state_ == SEND_CLOSED || state_ == CLOSED) {
181 VLOG(1) << "StartClosingHandshake called in state " << state_
182 << ". This may be a bug, or a harmless race.";
183 return;
184 }
185 if (state_ != CONNECTED) {
186 NOTREACHED() << "StartClosingHandshake() called in state " << state_;
187 return;
188 }
189 // TODO(ricea): Validate |code|? Check that |reason| is valid UTF-8?
190 // TODO(ricea): There should be a timeout for the closing handshake.
191 SendClose(code, reason); // Sets state_ to SEND_CLOSED
192 }
193
194 void WebSocketChannel::SendAddChannelRequestForTesting(
195 const std::vector<std::string>& requested_subprotocols,
196 const GURL& origin,
197 URLRequestContext* url_request_context,
198 const WebSocketStreamFactory& factory) {
199 SendAddChannelRequestWithFactory(
200 requested_subprotocols, origin, url_request_context, factory);
201 }
202
203 void WebSocketChannel::SendAddChannelRequestWithFactory(
204 const std::vector<std::string>& requested_subprotocols,
205 const GURL& origin,
206 URLRequestContext* url_request_context,
207 const WebSocketStreamFactory& factory) {
208 DCHECK_EQ(FRESHLY_CONSTRUCTED, state_);
209 scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate(
210 new ConnectDelegate(this));
211 stream_request_ = factory.Run(socket_url_,
212 requested_subprotocols,
213 origin,
214 url_request_context,
215 BoundNetLog(),
216 connect_delegate.Pass());
217 state_ = CONNECTING;
218 }
219
220 void WebSocketChannel::OnConnectSuccess(scoped_ptr<WebSocketStream> stream) {
221 DCHECK(stream);
222 DCHECK_EQ(CONNECTING, state_);
223 stream_ = stream.Pass();
224 state_ = CONNECTED;
225 event_interface_->OnAddChannelResponse(false, stream_->GetSubProtocol());
226
227 // TODO(ricea): Get flow control information from the WebSocketStream once we
228 // have a multiplexing WebSocketStream.
229 current_send_quota_ = send_quota_high_water_mark_;
230 event_interface_->OnFlowControl(send_quota_high_water_mark_);
231
232 // We don't need this any more.
233 stream_request_.reset();
234 ReadFrames();
235 }
236
237 void WebSocketChannel::OnConnectFailure(uint16 websocket_error) {
238 DCHECK_EQ(CONNECTING, state_);
239 state_ = CLOSED;
240 stream_request_.reset();
241 event_interface_->OnAddChannelResponse(true, "");
242 }
243
244 void WebSocketChannel::WriteFrames() {
245 int result = OK;
246 do {
247 // This use of base::Unretained is safe because we own the WebSocketStream
248 // and destroying it cancels all callbacks.
249 result = stream_->WriteFrames(
250 data_being_sent_->GetFrames(),
251 base::Bind(
252 &WebSocketChannel::OnWriteDone, base::Unretained(this), false));
253 if (result != ERR_IO_PENDING) {
254 OnWriteDone(true, result);
255 }
256 } while (result == OK && data_being_sent_);
257 }
258
259 void WebSocketChannel::OnWriteDone(bool synchronous, int result) {
260 DCHECK_NE(FRESHLY_CONSTRUCTED, state_);
261 DCHECK_NE(CONNECTING, state_);
262 DCHECK_NE(ERR_IO_PENDING, result);
263 DCHECK(data_being_sent_);
264 switch (result) {
265 case OK:
266 if (data_to_send_next_) {
267 data_being_sent_ = data_to_send_next_.Pass();
268 if (!synchronous) {
269 WriteFrames();
270 }
271 } else {
272 data_being_sent_.reset();
273 if (current_send_quota_ < send_quota_low_water_mark_) {
274 // TODO(ricea): Increase low_water_mark and high_water_mark if
275 // throughput is high, reduce them if throughput is low. Low water
276 // mark needs to be >= the bandwidth delay product *of the IPC
277 // channel*. Because factors like context-switch time, thread wake-up
278 // time, and bus speed come into play it is complex and probably needs
279 // to be determined empirically.
280 DCHECK_LE(send_quota_low_water_mark_, send_quota_high_water_mark_);
281 // TODO(ricea): Truncate quota by the quota specified by the remote
282 // server, if the protocol in use supports quota.
283 int fresh_quota = send_quota_high_water_mark_ - current_send_quota_;
284 current_send_quota_ += fresh_quota;
285 event_interface_->OnFlowControl(fresh_quota);
286 }
287 }
288 return;
289
290 // If a recoverable error condition existed, it would go here.
291
292 default:
293 DCHECK_LT(result, 0)
294 << "WriteFrames() should only return OK or ERR_ codes";
295 stream_->Close();
296 state_ = CLOSED;
297 event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
298 "Abnormal Closure");
299 return;
300 }
301 }
302
303 void WebSocketChannel::ReadFrames() {
304 int result = OK;
305 do {
306 // This use of base::Unretained is safe because we own the WebSocketStream,
307 // and any pending reads will be cancelled when it is destroyed.
308 result = stream_->ReadFrames(
309 &read_frame_chunks_,
310 base::Bind(
311 &WebSocketChannel::OnReadDone, base::Unretained(this), false));
312 if (result != ERR_IO_PENDING) {
313 OnReadDone(true, result);
314 }
315 } while (result == OK);
316 }
317
318 void WebSocketChannel::OnReadDone(bool synchronous, int result) {
319 DCHECK_NE(FRESHLY_CONSTRUCTED, state_);
320 DCHECK_NE(CONNECTING, state_);
321 DCHECK_NE(ERR_IO_PENDING, result);
322 switch (result) {
323 case OK:
324 // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection
325 // with no data read, not an empty response.
326 DCHECK(!read_frame_chunks_.empty())
327 << "ReadFrames() returned OK, but nothing was read.";
328 for (size_t i = 0; i < read_frame_chunks_.size(); ++i) {
329 scoped_ptr<WebSocketFrameChunk> chunk(read_frame_chunks_[i]);
330 read_frame_chunks_[i] = NULL;
331 ProcessFrameChunk(chunk.Pass());
332 }
333 read_frame_chunks_.clear();
334 // We need to always keep a call to ReadFrames pending.
335 if (!synchronous) {
336 ReadFrames();
337 }
338 return;
339
340 default: {
341 DCHECK_LT(result, 0)
342 << "ReadFrames() should only return OK or ERR_ codes";
343 stream_->Close();
344 state_ = CLOSED;
345 uint16 code = kWebSocketErrorAbnormalClosure;
346 std::string reason = "Abnormal Closure";
347 if (closing_code_ != 0) {
348 code = closing_code_;
349 reason = closing_reason_;
350 }
351 event_interface_->OnDropChannel(code, reason);
352 return;
353 }
354 }
355 }
356
357 void WebSocketChannel::ProcessFrameChunk(
358 scoped_ptr<WebSocketFrameChunk> chunk) {
359 bool is_first_chunk = false;
360 if (chunk->header) {
361 DCHECK(current_frame_header_ == NULL)
362 << "Received the header for a new frame without notification that "
363 << "the previous frame was complete.";
364 is_first_chunk = true;
365 current_frame_header_.swap(chunk->header);
366 if (current_frame_header_->masked) {
367 // RFC6455 Section 5.1 "A client MUST close a connection if it detects a
368 // masked frame."
369 FailChannel(SEND_REAL_ERROR,
370 kWebSocketErrorProtocolError,
371 "Masked frame from server");
372 return;
373 }
374 }
375 if (!current_frame_header_) {
376 // If we rejected the previous chunk as invalid, then we will have reset
377 // current_frame_header_ to avoid using it. More chunks of the invalid frame
378 // may still arrive, so this is not necessarily a bug on our side. However,
379 // if this happens when state_ is CONNECTED, it is definitely a bug.
380 DCHECK(state_ != CONNECTED) << "Unexpected header-less frame received "
381 << "(final_chunk = " << chunk->final_chunk
382 << ", data size = " << chunk->data->size()
383 << ")";
384 return;
385 }
386 scoped_refptr<IOBufferWithSize> data_buffer;
387 data_buffer.swap(chunk->data);
388 const bool is_final_chunk = chunk->final_chunk;
389 chunk.reset();
390 WebSocketFrameHeader::OpCode opcode = current_frame_header_->opcode;
391 if (WebSocketFrameHeader::IsKnownControlOpCode(opcode)) {
392 if (!is_final_chunk) {
393 // TODO(ricea): Enforce a maximum size of 125 bytes on the control frames
394 // we accept.
395 VLOG(2) << "Encountered a split control frame, opcode " << opcode;
396 if (incomplete_control_frame_body_) {
397 // The really horrid case. We need to create a new IOBufferWithSize
398 // combining the new one and the old one. This should virtually never
399 // happen.
400 // TODO(ricea): This algorithm is O(N^2). Use a fixed 127-byte buffer
401 // instead.
402 VLOG(3) << "Hit the really horrid case";
403 incomplete_control_frame_body_ =
404 ConcatenateIOBuffers(incomplete_control_frame_body_, data_buffer);
405 } else {
406 // The merely horrid case. Store the IOBufferWithSize to use when the
407 // rest of the control frame arrives.
408 incomplete_control_frame_body_.swap(data_buffer);
409 }
410 return; // Handle when complete.
411 }
412 if (incomplete_control_frame_body_) {
413 VLOG(2) << "Rejoining a split control frame, opcode " << opcode;
414 data_buffer =
415 ConcatenateIOBuffers(incomplete_control_frame_body_, data_buffer);
416 incomplete_control_frame_body_ = NULL; // Frame now complete.
417 }
418 }
419
420 // Apply basic sanity checks to the |payload_length| field from the frame
421 // header. We can only apply a strict check when we know we have the whole
422 // frame in one chunk.
423 DCHECK_GE(current_frame_header_->payload_length,
424 base::checked_numeric_cast<uint64>(data_buffer->size()));
425 DCHECK(!is_first_chunk || !is_final_chunk ||
426 current_frame_header_->payload_length ==
427 base::checked_numeric_cast<uint64>(data_buffer->size()));
428
429 // Respond to the frame appropriately to its type.
430 HandleFrame(opcode, is_first_chunk, is_final_chunk, data_buffer);
431
432 if (is_final_chunk) {
433 // Make sure we do not apply this frame header to any future chunks.
434 current_frame_header_.reset();
435 }
436 }
437
438 void WebSocketChannel::HandleFrame(
439 const WebSocketFrameHeader::OpCode opcode,
440 bool is_first_chunk,
441 bool is_final_chunk,
442 const scoped_refptr<IOBufferWithSize>& data_buffer) {
443 DCHECK_NE(RECV_CLOSED, state_)
444 << "HandleFrame() does not support being called re-entrantly from within "
445 "SendClose()";
446 if (state_ == CLOSED) {
447 std::string frame_name;
448 switch (opcode) {
449 case WebSocketFrameHeader::kOpCodeText: // fall-thru
450 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru
451 case WebSocketFrameHeader::kOpCodeContinuation:
452 frame_name = "Data frame";
453 break;
454
455 case WebSocketFrameHeader::kOpCodePing:
456 frame_name = "Ping";
457 break;
458
459 case WebSocketFrameHeader::kOpCodePong:
460 frame_name = "Pong";
461 break;
462
463 case WebSocketFrameHeader::kOpCodeClose:
464 frame_name = "Close";
465 break;
466
467 default:
468 frame_name = "Unknown frame type";
469 break;
470 }
471 // SEND_REAL_ERROR makes no difference here, as we won't send another Close
472 // frame.
473 FailChannel(SEND_REAL_ERROR,
474 kWebSocketErrorProtocolError,
475 frame_name + " received after close");
476 return;
477 }
478 switch (opcode) {
479 case WebSocketFrameHeader::kOpCodeText: // fall-thru
480 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru
481 case WebSocketFrameHeader::kOpCodeContinuation:
482 if (state_ == CONNECTED) {
483 const bool final = is_final_chunk && current_frame_header_->final;
484 // TODO(ricea): Can this copy be eliminated?
485 const char* const data_begin = data_buffer->data();
486 const char* const data_end = data_begin + data_buffer->size();
487 const std::vector<char> data(data_begin, data_end);
488 // TODO(ricea): Handle the (improbable) case when ReadFrames returns far
489 // more data at once than we want to send in a single IPC (in which case
490 // we need to buffer the data and return to the event loop with a
491 // callback to send the rest in 32K chunks).
492
493 // Send the received frame to the renderer process.
494 event_interface_->OnDataFrame(
495 final,
496 is_first_chunk ? opcode : WebSocketFrameHeader::kOpCodeContinuation,
497 data);
498 } else {
499 VLOG(3) << "Ignored data packet received in state " << state_;
500 }
501 return;
502
503 case WebSocketFrameHeader::kOpCodePing:
504 VLOG(1) << "Got Ping of size " << data_buffer->size();
505 if (state_ == CONNECTED) {
506 SendIOBufferWithSize(
507 true, WebSocketFrameHeader::kOpCodePong, data_buffer);
508 } else {
509 VLOG(3) << "Ignored ping in state " << state_;
510 }
511 return;
512
513 case WebSocketFrameHeader::kOpCodePong:
514 VLOG(1) << "Got Pong of size " << data_buffer->size();
515 // We do not need to do anything with pong messages.
516 return;
517
518 case WebSocketFrameHeader::kOpCodeClose: {
519 uint16 code = kWebSocketNormalClosure;
520 std::string reason;
521 ParseClose(data_buffer, &code, &reason);
522 // TODO(ricea): Find a way to safely log the message from the close
523 // message (escape control codes and so on).
524 VLOG(1) << "Got Close with code " << code;
525 switch (state_) {
526 case CONNECTED:
527 state_ = RECV_CLOSED;
528 SendClose(code, reason); // Sets state_ to CLOSED
529 event_interface_->OnClosingHandshake();
530 closing_code_ = code;
531 closing_reason_ = reason;
532 break;
533
534 case SEND_CLOSED:
535 state_ = CLOSED;
536 // From RFC6455 section 7.1.5: "Each endpoint
537 // will see the status code sent by the other end as _The WebSocket
538 // Connection Close Code_."
539 closing_code_ = code;
540 closing_reason_ = reason;
541 break;
542
543 default:
544 LOG(DFATAL) << "Got Close in unexpected state " << state_;
545 break;
546 }
547 return;
548 }
549
550 default:
551 FailChannel(
552 SEND_REAL_ERROR, kWebSocketErrorProtocolError, "Unknown opcode");
553 return;
554 }
555 }
556
557 void WebSocketChannel::SendIOBufferWithSize(
558 bool fin,
559 WebSocketFrameHeader::OpCode op_code,
560 const scoped_refptr<IOBufferWithSize>& buffer) {
561 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
562 DCHECK(stream_);
563 scoped_ptr<WebSocketFrameHeader> header(new WebSocketFrameHeader(op_code));
564 header->final = fin;
565 header->masked = true;
566 header->payload_length = buffer->size();
567 scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk());
568 chunk->header = header.Pass();
569 chunk->final_chunk = true;
570 chunk->data = buffer;
571 if (data_being_sent_) {
572 // Either the link to the WebSocket server is saturated, or we are simply
573 // processing a batch of messages.
574 // TODO(ricea): We need to keep some statistics to work out which situation
575 // we are in and adjust quota appropriately.
576 if (!data_to_send_next_)
577 data_to_send_next_.reset(new SendBuffer);
578 data_to_send_next_->AddFrame(chunk.Pass());
579 } else {
580 data_being_sent_.reset(new SendBuffer);
581 data_being_sent_->AddFrame(chunk.Pass());
582 WriteFrames();
583 }
584 }
585
586 void WebSocketChannel::FailChannel(ExposeError expose,
587 uint16 code,
588 const std::string& reason) {
589 DCHECK_NE(FRESHLY_CONSTRUCTED, state_);
590 DCHECK_NE(CONNECTING, state_);
591 // TODO(ricea): Logging.
592 State old_state = state_;
593 if (state_ == CONNECTED) {
594 uint16 send_code = kWebSocketErrorGoingAway;
595 std::string send_reason = "Internal Error";
596 if (expose == SEND_REAL_ERROR) {
597 send_code = code;
598 send_reason = reason;
599 }
600 SendClose(send_code, send_reason); // Sets state_ to SEND_CLOSED
601 }
602 // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates we should close
603 // the connection ourselves without waiting for the closing handshake.
604 stream_->Close();
605 state_ = CLOSED;
606
607 // We may be in the middle of processing several chunks. We should not re-use
608 // the frame header.
609 current_frame_header_.reset();
610 if (old_state != CLOSED) {
611 event_interface_->OnDropChannel(code, reason);
612 }
613 }
614
615 void WebSocketChannel::SendClose(uint16 code, const std::string& reason) {
616 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
617 // TODO(ricea): Ensure reason.length() <= 123
618 size_t payload_length = kWebSocketCloseCodeLength + reason.length();
619 scoped_refptr<IOBufferWithSize> body =
620 new IOBufferWithSize(base::checked_numeric_cast<int>(payload_length));
621 WriteBigEndian(body->data(), code);
622 COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength,
623 they_should_both_be_two);
624 std::copy(
625 reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength);
626 SendIOBufferWithSize(true, WebSocketFrameHeader::kOpCodeClose, body);
627 state_ = (state_ == CONNECTED) ? SEND_CLOSED : CLOSED;
628 }
629
630 void WebSocketChannel::ParseClose(const scoped_refptr<IOBufferWithSize>& buffer,
631 uint16* code,
632 std::string* reason) {
633 const char* data = buffer->data();
634 size_t size = base::checked_numeric_cast<size_t>(buffer->size());
635 reason->clear();
636 if (size < kWebSocketCloseCodeLength) {
637 *code = kWebSocketErrorNoStatusReceived;
638 if (size != 0) {
639 VLOG(1) << "Close frame with payload size " << size << " received "
640 << "(the first byte is " << std::hex << static_cast<int>(data[0])
641 << ")";
642 return;
643 }
644 return;
645 }
646 uint16 unchecked_code = 0;
647 ReadBigEndian(data, &unchecked_code);
648 COMPILE_ASSERT(sizeof(unchecked_code) == kWebSocketCloseCodeLength,
649 they_should_both_be_two_bytes);
650 if (unchecked_code >= static_cast<uint16>(kWebSocketNormalClosure) &&
651 unchecked_code <=
652 static_cast<uint16>(kWebSocketErrorPrivateReservedMax)) {
653 *code = unchecked_code;
654 } else {
655 VLOG(1) << "Close frame contained code outside of the valid range: "
656 << unchecked_code;
657 *code = kWebSocketErrorAbnormalClosure;
658 }
659 std::string text(data + kWebSocketCloseCodeLength, data + size);
660 // TODO(ricea): Is this check strict enough? In particular, check the
661 // "Security Considerations" from RFC3629.
662 if (IsStringUTF8(text)) {
663 reason->swap(text);
664 }
665 }
666
667 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698