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

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: Fix potential NULL-pointer dereference in ProcessFrameChunks. 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
« 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
(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/string_util.h"
12 #include "net/base/big_endian.h"
13 #include "net/base/io_buffer.h"
14 #include "net/http/http_request_info.h"
15 #include "net/http/http_stream_factory.h"
16 #include "net/ssl/ssl_config_service.h"
17 #include "net/websockets/websocket_errors.h"
18 #include "net/websockets/websocket_event_interface.h"
19 #include "net/websockets/websocket_frame.h"
20 #include "net/websockets/websocket_mux.h"
21 #include "net/websockets/websocket_stream.h"
22
23 namespace net {
24
25 namespace {
26
27 const int kDefaultSendQuotaLowWaterMark = 1 << 16;
28 const int kDefaultSendQuotaHighWaterMark = 1 << 17;
29 const size_t kWebSocketCloseCodeLength = 2;
30
31 // IOBuffer is too hardcore to offer a const accessor. Make our own.
32 const char* GetConstData(const IOBuffer* iobuffer) {
33 return const_cast<IOBuffer*>(iobuffer)->data();
34 }
35
36 // Concatenate the data from two IOBufferWithSize objects into a single one.
37 IOBufferWithSize* ConcatenateIOBuffers(const IOBufferWithSize* part1,
38 const IOBufferWithSize* part2) {
39 int newsize = part1->size() + part2->size();
40 IOBufferWithSize* newbuffer = new IOBufferWithSize(newsize);
41 std::copy(GetConstData(part1),
42 GetConstData(part1) + part1->size(),
43 newbuffer->data());
44 std::copy(GetConstData(part2),
45 GetConstData(part2) + part2->size(),
46 newbuffer->data() + part1->size());
47 return newbuffer;
48 }
49
50 } // namespace
51
52 struct WebSocketChannel::SendBuffer {
53 SendBuffer() : total_bytes(0) {}
54 ScopedVector<WebSocketFrameChunk> frames;
55 size_t total_bytes;
56 };
57
58 // Implementation of WebSocketStream::ConnectDelegate that simply forwards the
59 // calls on to the WebSocketChannel that created it.
60 class WebSocketChannel::ConnectDelegate
61 : public WebSocketStream::ConnectDelegate {
62 public:
63 ConnectDelegate(WebSocketChannel* creator) : creator_(creator) {}
64
65 virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE {
66 creator_->OnConnectSuccess(stream.Pass());
67 }
68
69 virtual void OnFailure(unsigned short websocket_error) OVERRIDE {
70 creator_->OnConnectFailure(websocket_error);
71 }
72
73 private:
74 // A pointer to the WebSocketChannel that created us. We do not need to worry
75 // about this pointer being stale, because deleting WebSocketChannel cancels
76 // the connect process, deleting this object and preventing its callbacks from
77 // being called.
78 WebSocketChannel* const creator_;
79
80 DISALLOW_COPY_AND_ASSIGN(ConnectDelegate);
81 };
82
83 WebSocketChannel::WebSocketChannel(
84 const GURL& socket_url,
85 scoped_ptr<WebSocketEventInterface> event_interface)
86 : socket_url_(socket_url),
87 event_interface_(event_interface.Pass()),
88 send_quota_low_water_mark_(kDefaultSendQuotaLowWaterMark),
89 send_quota_high_water_mark_(kDefaultSendQuotaHighWaterMark),
90 current_send_quota_(0),
91 state_(FRESHLY_CONSTRUCTED),
92 weak_factory_(this) {}
93
94 WebSocketChannel::~WebSocketChannel() {
95 // The stream may hold a pointer to read_frame_chunks_, and so it needs to be
96 // destroyed first.
97 stream_.reset();
98 }
99
100 void WebSocketChannel::SendAddChannelRequest(
101 const std::vector<std::string>& requested_subprotocols,
102 const GURL& origin,
103 URLRequestContext* url_request_context) {
104 // Delegate to the tested version.
105 SendAddChannelRequestWithFactory(
106 requested_subprotocols,
107 origin,
108 url_request_context,
109 base::Bind(&WebSocketStream::CreateAndConnectStream));
110 }
111
112 void WebSocketChannel::SendAddChannelRequestWithFactory(
113 const std::vector<std::string>& requested_subprotocols,
114 const GURL& origin,
115 URLRequestContext* url_request_context,
116 base::Callback<scoped_ptr<WebSocketStreamRequest>(
117 const GURL&,
118 const std::vector<std::string>&,
119 const GURL&,
120 URLRequestContext*,
121 const BoundNetLog&,
122 scoped_ptr<WebSocketStream::ConnectDelegate>)> factory) {
123 DCHECK_EQ(FRESHLY_CONSTRUCTED, state_);
124 scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate(
125 new WebSocketChannel::ConnectDelegate(this));
126 stream_request_ = factory.Run(socket_url_,
127 requested_subprotocols,
128 origin,
129 url_request_context,
130 BoundNetLog(),
131 connect_delegate.Pass());
132 state_ = CONNECTING;
133 }
134
135 void WebSocketChannel::OnConnectSuccess(scoped_ptr<WebSocketStream> stream) {
136 DCHECK(stream);
137 DCHECK_EQ(CONNECTING, state_);
138 stream_ = stream.Pass();
139 event_interface_->OnAddChannelResponse(false, stream_->GetSubProtocol());
140 // TODO(ricea): Get flow control information from the WebSocketStream once we
141 // have a multiplexing WebSocketStream.
142 event_interface_->OnFlowControl(send_quota_high_water_mark_);
143 current_send_quota_ = send_quota_high_water_mark_;
144 state_ = CONNECTED;
145 // We don't need this any more.
146 stream_request_.reset();
147 ReadFrames();
148 }
149
150 void WebSocketChannel::OnConnectFailure(unsigned short web_socket_error) {
151 DCHECK_EQ(CONNECTING, state_);
152 event_interface_->OnAddChannelResponse(true, "");
153 stream_request_.reset();
154 state_ = CLOSED;
155 }
156
157 void WebSocketChannel::SendFrame(bool fin,
tyoshino (SeeGerritForStatus) 2013/06/28 08:28:39 Shorter function is good. But for this case, preco
Adam Rice 2013/06/28 13:23:30 Send() was originally also used by SendClose() but
158 WebSocketFrameHeader::OpCode op_code,
159 const std::vector<char>& data) {
160 if (data.size() > INT_MAX) {
161 NOTREACHED() << "Frame size sanity check failed";
162 return;
163 }
164 if (stream_ == NULL) {
165 LOG(DFATAL) << "Got SendFrame without a connection established; "
166 << "misbehaving renderer? fin=" << fin << " op_code=" << op_code
167 << " data.size()=" << data.size();
168 return;
169 }
170 if (state_ == SEND_CLOSED || state_ == RECV_CLOSED || state_ == CLOSED) {
171 VLOG(1) << "SendFrame called in state " << state_
172 << ". This may be a bug, or a harmless race.";
173 return;
174 }
tyoshino (SeeGerritForStatus) 2013/06/28 08:28:39 how about handling other unexpected states, too? i
Adam Rice 2013/06/28 13:23:30 Done.
175 DCHECK_EQ(CONNECTED, state_);
176 CHECK_GE(current_send_quota_, 0); // Security-critical invariant
177 typedef std::vector<char>::size_type size_type;
178 if (data.size() > static_cast<size_type>(current_send_quota_)) {
tyoshino (SeeGerritForStatus) 2013/06/28 08:28:39 just size_t. please add a comment in .h that Send
Adam Rice 2013/06/28 13:23:30 Yes. The renderer must obey our quota restrictions
179 FailChannel(SEND_INTERNAL_ERROR,
180 kWebSocketMuxErrorSendQuotaViolation,
181 "Send quota exceeded");
182 return;
183 }
184 if (!WebSocketFrameHeader::IsKnownDataOpCode(op_code)) {
185 LOG(DFATAL) << "Got SendFrame with bogus op_code " << op_code
186 << "; misbehaving renderer? fin=" << fin
187 << " data.size()=" << data.size();
188 return;
189 }
190 current_send_quota_ -= data.size();
191 // TODO(ricea): If current_send_quota_ has dropped below
192 // send_quota_low_water_mark_, we may want to consider increasing the "low
193 // water mark" and "high water mark", but only if we think we are not
194 // saturating the link to the WebSocket server.
195 // TODO(ricea): For kOpCodeText, do UTF-8 validation?
196 Send(fin, op_code, data);
197 }
198
199 void WebSocketChannel::Send(bool fin,
200 WebSocketFrameHeader::OpCode op_code,
201 const std::vector<char>& data) {
202 scoped_refptr<IOBufferWithSize> buffer(new IOBufferWithSize(data.size()));
203 std::copy(data.begin(), data.end(), buffer->data());
204 SendIOBufferWithSize(fin, op_code, buffer);
205 }
206
207 void WebSocketChannel::SendIOBufferWithSize(
208 bool fin,
209 WebSocketFrameHeader::OpCode op_code,
210 const scoped_refptr<IOBufferWithSize>& buffer) {
211 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
212 DCHECK(stream_);
213 scoped_ptr<WebSocketFrameHeader> header(new WebSocketFrameHeader(op_code));
214 header->final = fin;
215 header->masked = true;
216 header->payload_length = buffer->size();
217 scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk());
218 chunk->header = header.Pass();
219 chunk->final_chunk = true;
220 chunk->data = buffer;
221 if (currently_sending_) {
222 // Either the link to the WebSocket server is saturated, or we are simply
223 // processing a batch of messages.
224 // TODO(ricea): We need to keep some statistics to work out which situation
225 // we are in and adjust quota appropriately.
226 if (!send_next_) {
227 send_next_.reset(new SendBuffer);
228 }
229 send_next_->frames.push_back(chunk.release());
230 send_next_->total_bytes += buffer->size();
231 } else {
232 currently_sending_.reset(new SendBuffer);
233 currently_sending_->frames.push_back(chunk.release());
234 currently_sending_->total_bytes += buffer->size();
235 WriteFrames();
236 }
237 }
238
239 void WebSocketChannel::WriteFrames() {
240 // This is safe because we own the WebSocketStream and destroying it cancels
241 // all callbacks.
242 int result = stream_->WriteFrames(
243 &(currently_sending_->frames),
244 base::Bind(&WebSocketChannel::OnWriteDone, base::Unretained(this)));
245 if (result != ERR_IO_PENDING) {
246 OnWriteDone(result);
247 }
248 }
249
250 void WebSocketChannel::OnWriteDone(int result) {
251 DCHECK(state_ != FRESHLY_CONSTRUCTED && state_ != CONNECTING);
252 DCHECK_NE(ERR_IO_PENDING, result);
253 DCHECK(currently_sending_);
254 switch (result) {
255 case OK:
256 if (send_next_) {
257 currently_sending_ = send_next_.Pass();
258 WriteFrames();
259 } else {
260 currently_sending_.reset();
261 if (current_send_quota_ < send_quota_low_water_mark_) {
262 // TODO(ricea): Increase low_water_mark and high_water_mark if
263 // throughput is high, reduce them if throughput is low. Low water
264 // mark needs to be >= the bandwidth delay product *of the IPC
265 // channel*. Because factors like context-switch time, thread wake-up
266 // time, and bus speed come into play it is complex and probably needs
267 // to be determined empirically.
268 DCHECK_LE(send_quota_low_water_mark_, send_quota_high_water_mark_);
269 // TODO(ricea): Truncate quota by the quota specified by the remote
270 // server, if the protocol in use supports quota.
271 int fresh_quota = send_quota_high_water_mark_ - current_send_quota_;
272 event_interface_->OnFlowControl(fresh_quota);
273 current_send_quota_ += fresh_quota;
274 }
275 }
276 break;
277
278 // If a recoverable error condition existed, it would go here.
279
280 default:
281 DCHECK_LT(result, 0)
282 << "WriteFrames() should only return OK or ERR_ codes";
283 stream_->Close();
284 state_ = CLOSED;
285 event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
286 "Abnormal Closure");
287 break;
288 }
289 }
290
291 void WebSocketChannel::ReadFrames() {
292 // This use if base::Unretained is safe because we own the WebSocketStream,
293 // and any pending reads will be cancelled when it is destroyed.
294 int result = stream_->ReadFrames(
295 &read_frame_chunks_,
296 base::Bind(&WebSocketChannel::OnReadDone, base::Unretained(this)));
297 if (result != ERR_IO_PENDING) {
298 OnReadDone(result);
299 }
300 }
301
302 void WebSocketChannel::OnReadDone(int result) {
303 DCHECK(state_ != FRESHLY_CONSTRUCTED && state_ != CONNECTING);
304 DCHECK_NE(ERR_IO_PENDING, result);
305 switch (result) {
306 case OK:
307 // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection
308 // with no data read, not an empty response.
309 DCHECK(!read_frame_chunks_.empty())
310 << "ReadFrames() returned OK, but nothing was read.";
311 for (size_t i = 0; i < read_frame_chunks_.size(); ++i) {
312 scoped_ptr<WebSocketFrameChunk> chunk(read_frame_chunks_[i]);
313 read_frame_chunks_[i] = NULL;
314 ProcessFrameChunk(chunk.Pass());
315 }
316 read_frame_chunks_.clear();
317 // We need to always keep a call to ReadFrames pending.
318 ReadFrames();
319 return;
320
321 case ERR_CONNECTION_CLOSED: {
322 State old_state = state_;
323 state_ = CLOSED;
324 if (old_state != RECV_CLOSED && old_state != CLOSED) {
325 // We need to inform the render process of the unexpected closure.
326 event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
327 "Abnormal Closure");
328 }
329 return;
330 }
331
332 default: {
333 DCHECK_LT(result, 0)
334 << "ReadFrames() should only return OK or ERR_ codes";
335 stream_->Close();
336 State old_state = state_;
337 state_ = CLOSED;
338 if (old_state != RECV_CLOSED && old_state != CLOSED) {
339 event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
340 "Abnormal Closure");
341 }
342 return;
343 }
344 }
345 }
346
347 // TODO(ricea): This method is too long. Break it up.
348 void WebSocketChannel::ProcessFrameChunk(
349 scoped_ptr<WebSocketFrameChunk> chunk) {
350 // frame_header stores the header for this frame, either saved from a previous
351 // chunk or from this chunk if it includes a header.
352 scoped_ptr<WebSocketFrameHeader> frame_header;
353 // Borrow the value of current_frame_header_. At the end of the function we
354 // will put it back if it is still valid, or replace it with the header from
355 // the new chunk.
356 frame_header.swap(current_frame_header_);
tyoshino (SeeGerritForStatus) 2013/06/28 08:28:39 thanks for adding comment and being careful about
Adam Rice 2013/06/28 13:23:30 I removed the frame_header variable completely and
357 bool first_chunk = false;
358 if (chunk->header) {
359 first_chunk = true;
360 frame_header.swap(chunk->header);
361 if (frame_header->masked) {
362 // RFC6455 Section 5.1 "A client MUST close a connection if it detects a
363 // masked frame."
364 FailChannel(SEND_REAL_ERROR,
365 kWebSocketErrorProtocolError,
366 "Masked frame from server");
367 return;
368 }
369 }
370 if (!frame_header) {
371 DCHECK(state_ != CONNECTED) << "Unexpected header-less frame received "
372 << "(final_chunk = " << chunk->final_chunk
373 << ", data size = " << chunk->data->size()
374 << ")";
375 return;
376 }
377 scoped_refptr<IOBufferWithSize> data_buffer;
378 data_buffer.swap(chunk->data);
379 if (WebSocketFrameHeader::IsKnownControlOpCode(frame_header->opcode)) {
380 if (chunk->final_chunk) {
381 if (incomplete_control_frame_body_) {
382 VLOG(2) << "Rejoining a split control frame, opcode "
383 << frame_header->opcode;
384 scoped_refptr<IOBufferWithSize> old_data_buffer;
385 old_data_buffer.swap(incomplete_control_frame_body_);
386 scoped_refptr<IOBufferWithSize> new_data_buffer;
387 new_data_buffer.swap(data_buffer);
388 data_buffer =
389 ConcatenateIOBuffers(old_data_buffer.get(), new_data_buffer.get());
390 }
391 } else {
392 // TODO(ricea): Enforce a maximum size of 125 bytes on the control frames
393 // we accept.
394 VLOG(2) << "Encountered a split control frame, opcode "
395 << frame_header->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 byffer
401 // instead.
402 VLOG(3) << "Hit the really horrid case";
403 scoped_refptr<IOBufferWithSize> old_body;
404 old_body.swap(incomplete_control_frame_body_);
405 incomplete_control_frame_body_ =
406 ConcatenateIOBuffers(old_body.get(), data_buffer.get());
407 } else {
408 // The merely horrid case. Store the IOBufferWithSize to use when the
409 // rest of the control frame arrives.
410 incomplete_control_frame_body_.swap(data_buffer);
411 }
412 current_frame_header_.swap(frame_header);
413 return;
414 }
415 }
416
417 WebSocketFrameHeader::OpCode opcode = frame_header->opcode;
418 switch (frame_header->opcode) {
419 case WebSocketFrameHeader::kOpCodeText: // fall-thru
420 case WebSocketFrameHeader::kOpCodeBinary:
421 if (!first_chunk) {
422 opcode = WebSocketFrameHeader::kOpCodeContinuation;
423 }
424 // fall-thru
425 case WebSocketFrameHeader::kOpCodeContinuation:
426 if (state_ == RECV_CLOSED) {
427 FailChannel(SEND_REAL_ERROR,
428 kWebSocketErrorProtocolError,
429 "Data packet received after close");
430 return;
431 } else if (state_ == CONNECTED) {
432 const bool final = chunk->final_chunk && frame_header->final;
433 // TODO(ricea): Can this copy be eliminated?
434 const char* const data_begin = data_buffer->data();
435 const char* const data_end = data_begin + data_buffer->size();
436 const std::vector<char> data(data_begin, data_end);
437 // TODO(ricea): Handle the (improbable) case when ReadFrames returns far
438 // more data at once than we want to send in a single IPC (in which case
439 // we need to buffer the data and return to the event loop with a
440 // callback to send the rest in 32K chunks).
441
442 // Send the received frame to the renderer process.
443 event_interface_->OnSendFrame(final, opcode, data);
444 } else {
445 VLOG(3) << "Ignored data packet received in state " << state_;
446 }
447 break;
448
449 case WebSocketFrameHeader::kOpCodePing:
450 VLOG(1) << "Got Ping of size " << data_buffer->size();
451 if (state_ == RECV_CLOSED) {
452 FailChannel(SEND_REAL_ERROR,
453 kWebSocketErrorProtocolError,
454 "Ping received after Close");
455 return;
456 } else if (state_ == CONNECTED) {
457 SendIOBufferWithSize(
458 true, WebSocketFrameHeader::kOpCodePong, data_buffer);
459 } else {
460 VLOG(3) << "Ignored ping in state " << state_;
461 }
462 break;
463
464 case WebSocketFrameHeader::kOpCodePong:
465 VLOG(1) << "Got Pong of size " << data_buffer->size();
466 if (state_ == RECV_CLOSED) {
467 FailChannel(SEND_REAL_ERROR,
468 kWebSocketErrorProtocolError,
469 "Pong received after Close");
470 return;
471 }
472 // We do not need to do anything with pong messages.
473 break;
474
475 case WebSocketFrameHeader::kOpCodeClose: {
476 unsigned short reason = kWebSocketNormalClosure;
477 std::string reason_text;
478 ParseClose(*data_buffer, &reason, &reason_text);
479 // TODO(ricea): Find a way to safely log the message from the close
480 // message (escape control codes and so on).
481 VLOG(1) << "Got Close with reason " << reason;
482 switch (state_) {
483 case CONNECTED:
484 state_ = RECV_CLOSED;
485 SendClose(reason, reason_text);
486 event_interface_->OnDropChannel(reason, reason_text);
487 break;
488
489 case RECV_CLOSED:
490 FailChannel(SEND_REAL_ERROR,
491 kWebSocketErrorProtocolError,
492 "Close received after Close");
493 break;
494
495 case SEND_CLOSED:
496 state_ = CLOSED;
497 event_interface_->OnDropChannel(reason, reason_text);
498 break;
499
500 default:
501 LOG(DFATAL) << "Got Close in unexpected state " << state_;
502 break;
503 }
504 break;
505 }
506
507 default:
508 FailChannel(SEND_REAL_ERROR,
509 kWebSocketErrorProtocolError,
510 "Unknown opcode");
511 break;
512 }
513 if (!chunk->final_chunk) {
514 // Preserve the frame header for the next call.
515 current_frame_header_.swap(frame_header);
516 }
517 }
518
519 void WebSocketChannel::SendFlowControl(int64 quota) {
520 DCHECK_EQ(CONNECTED, state_);
521 // TODO(ricea): Add interface to WebSocketStream and implement.
522 // stream_->SendFlowControl(quota);
523 }
524
525 void WebSocketChannel::SendDropChannel(unsigned short reason,
526 const std::string& reason_text) {
527 if (state_ == SEND_CLOSED || state_ == CLOSED) {
528 VLOG(1) << "SendDropChannel called in state " << state_
529 << ". This may be a bug, or a harmless race.";
530 return;
531 }
532 DCHECK_EQ(CONNECTED, state_);
533 // TODO(ricea): Validate reason? Check that reason_text is valid UTF-8?
534 // TODO(ricea): There should probably be a timeout for the closing handshake.
535 SendClose(reason, reason_text);
536 }
537
538 void WebSocketChannel::FailChannel(ExposeError expose,
539 unsigned short code,
540 const std::string& reason) {
541 // TODO(ricea): Logging.
542 State old_state = state_;
543 if (state_ == CONNECTED) {
544 unsigned short send_code = kWebSocketErrorGoingAway;
545 std::string send_reason = "Internal Error";
546 if (expose == SEND_REAL_ERROR) {
547 send_code = code;
548 send_reason = reason;
549 }
550 SendClose(send_code, send_reason);
551 }
552 if (old_state != RECV_CLOSED && old_state != CLOSED) {
553 event_interface_->OnDropChannel(code, reason);
554 }
555 }
556
557 void WebSocketChannel::SendClose(unsigned short code,
558 const std::string& reason) {
559 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
560 uint64 payload_length = kWebSocketCloseCodeLength + reason.length();
561 std::vector<char> data(payload_length);
562 DCHECK(payload_length > 0);
563 WriteBigEndian(&data[0], code);
564 COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength,
565 they_should_both_be_two);
566 std::copy(reason.begin(),
567 reason.end(),
568 data.begin() + kWebSocketCloseCodeLength);
569 Send(true, WebSocketFrameHeader::kOpCodeClose, data);
570 state_ = state_ == CONNECTED ? SEND_CLOSED : CLOSED;
571 }
572
573 void WebSocketChannel::ParseClose(const IOBufferWithSize& buffer,
574 unsigned short* reason,
575 std::string* reason_text) {
576 const char* data = const_cast<IOBufferWithSize&>(buffer).data();
577 CHECK_GE(buffer.size(), 0); // Possibly security-critical invariant.
578 size_t size = static_cast<size_t>(buffer.size());
579 reason_text->clear();
580 if (size < kWebSocketCloseCodeLength) {
581 *reason = kWebSocketErrorNoStatusReceived;
582 if (size != 0) {
583 VLOG(1) << "Close frame with payload size " << size << " received "
584 << "(the first byte is " << std::hex << static_cast<int>(data[0])
585 << ")";
586 return;
587 }
588 return;
589 }
590 unsigned short unchecked_reason = 0;
591 ReadBigEndian(data, &unchecked_reason);
592 COMPILE_ASSERT(sizeof(unchecked_reason) == kWebSocketCloseCodeLength,
593 they_should_both_be_two_bytes);
594 if (unchecked_reason >=
595 static_cast<unsigned short>(kWebSocketNormalClosure) &&
596 unchecked_reason <=
597 static_cast<unsigned short>(kWebSocketErrorPrivateReservedMax)) {
598 *reason = unchecked_reason;
599 } else {
600 VLOG(1) << "Close frame contained reason code outside of the valid range: "
601 << unchecked_reason;
602 *reason = kWebSocketErrorProtocolError;
603 }
604 std::string text(data + kWebSocketCloseCodeLength, data + size);
605 // TODO(ricea): Is this check strict enough? In particular, check the
606 // "Security Considerations" from RFC3629.
607 if (IsStringUTF8(text)) {
608 using std::swap;
609 swap(*reason_text, text);
610 }
611 }
612
613 } // 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