OLD | NEW |
| (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 <limits.h> // for INT_MAX | |
8 | |
9 #include <algorithm> | |
10 #include <deque> | |
11 | |
12 #include "base/basictypes.h" // for size_t | |
13 #include "base/big_endian.h" | |
14 #include "base/bind.h" | |
15 #include "base/compiler_specific.h" | |
16 #include "base/memory/ref_counted.h" | |
17 #include "base/memory/weak_ptr.h" | |
18 #include "base/message_loop/message_loop.h" | |
19 #include "base/metrics/histogram.h" | |
20 #include "base/numerics/safe_conversions.h" | |
21 #include "base/stl_util.h" | |
22 #include "base/strings/stringprintf.h" | |
23 #include "base/time/time.h" | |
24 #include "net/base/io_buffer.h" | |
25 #include "net/base/net_log.h" | |
26 #include "net/http/http_request_headers.h" | |
27 #include "net/http/http_response_headers.h" | |
28 #include "net/http/http_util.h" | |
29 #include "net/websockets/websocket_errors.h" | |
30 #include "net/websockets/websocket_event_interface.h" | |
31 #include "net/websockets/websocket_frame.h" | |
32 #include "net/websockets/websocket_handshake_request_info.h" | |
33 #include "net/websockets/websocket_handshake_response_info.h" | |
34 #include "net/websockets/websocket_mux.h" | |
35 #include "net/websockets/websocket_stream.h" | |
36 #include "url/origin.h" | |
37 | |
38 namespace net { | |
39 | |
40 namespace { | |
41 | |
42 using base::StreamingUtf8Validator; | |
43 | |
44 const int kDefaultSendQuotaLowWaterMark = 1 << 16; | |
45 const int kDefaultSendQuotaHighWaterMark = 1 << 17; | |
46 const size_t kWebSocketCloseCodeLength = 2; | |
47 // Timeout for waiting for the server to acknowledge a closing handshake. | |
48 const int kClosingHandshakeTimeoutSeconds = 60; | |
49 // We wait for the server to close the underlying connection as recommended in | |
50 // https://tools.ietf.org/html/rfc6455#section-7.1.1 | |
51 // We don't use 2MSL since there're server implementations that don't follow | |
52 // the recommendation and wait for the client to close the underlying | |
53 // connection. It leads to unnecessarily long time before CloseEvent | |
54 // invocation. We want to avoid this rather than strictly following the spec | |
55 // recommendation. | |
56 const int kUnderlyingConnectionCloseTimeoutSeconds = 2; | |
57 | |
58 typedef WebSocketEventInterface::ChannelState ChannelState; | |
59 const ChannelState CHANNEL_ALIVE = WebSocketEventInterface::CHANNEL_ALIVE; | |
60 const ChannelState CHANNEL_DELETED = WebSocketEventInterface::CHANNEL_DELETED; | |
61 | |
62 // Maximum close reason length = max control frame payload - | |
63 // status code length | |
64 // = 125 - 2 | |
65 const size_t kMaximumCloseReasonLength = 125 - kWebSocketCloseCodeLength; | |
66 | |
67 // Check a close status code for strict compliance with RFC6455. This is only | |
68 // used for close codes received from a renderer that we are intending to send | |
69 // out over the network. See ParseClose() for the restrictions on incoming close | |
70 // codes. The |code| parameter is type int for convenience of implementation; | |
71 // the real type is uint16. Code 1005 is treated specially; it cannot be set | |
72 // explicitly by Javascript but the renderer uses it to indicate we should send | |
73 // a Close frame with no payload. | |
74 bool IsStrictlyValidCloseStatusCode(int code) { | |
75 static const int kInvalidRanges[] = { | |
76 // [BAD, OK) | |
77 0, 1000, // 1000 is the first valid code | |
78 1006, 1007, // 1006 MUST NOT be set. | |
79 1014, 3000, // 1014 unassigned; 1015 up to 2999 are reserved. | |
80 5000, 65536, // Codes above 5000 are invalid. | |
81 }; | |
82 const int* const kInvalidRangesEnd = | |
83 kInvalidRanges + arraysize(kInvalidRanges); | |
84 | |
85 DCHECK_GE(code, 0); | |
86 DCHECK_LT(code, 65536); | |
87 const int* upper = std::upper_bound(kInvalidRanges, kInvalidRangesEnd, code); | |
88 DCHECK_NE(kInvalidRangesEnd, upper); | |
89 DCHECK_GT(upper, kInvalidRanges); | |
90 DCHECK_GT(*upper, code); | |
91 DCHECK_LE(*(upper - 1), code); | |
92 return ((upper - kInvalidRanges) % 2) == 0; | |
93 } | |
94 | |
95 // Sets |name| to the name of the frame type for the given |opcode|. Note that | |
96 // for all of Text, Binary and Continuation opcode, this method returns | |
97 // "Data frame". | |
98 void GetFrameTypeForOpcode(WebSocketFrameHeader::OpCode opcode, | |
99 std::string* name) { | |
100 switch (opcode) { | |
101 case WebSocketFrameHeader::kOpCodeText: // fall-thru | |
102 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru | |
103 case WebSocketFrameHeader::kOpCodeContinuation: | |
104 *name = "Data frame"; | |
105 break; | |
106 | |
107 case WebSocketFrameHeader::kOpCodePing: | |
108 *name = "Ping"; | |
109 break; | |
110 | |
111 case WebSocketFrameHeader::kOpCodePong: | |
112 *name = "Pong"; | |
113 break; | |
114 | |
115 case WebSocketFrameHeader::kOpCodeClose: | |
116 *name = "Close"; | |
117 break; | |
118 | |
119 default: | |
120 *name = "Unknown frame type"; | |
121 break; | |
122 } | |
123 | |
124 return; | |
125 } | |
126 | |
127 } // namespace | |
128 | |
129 // A class to encapsulate a set of frames and information about the size of | |
130 // those frames. | |
131 class WebSocketChannel::SendBuffer { | |
132 public: | |
133 SendBuffer() : total_bytes_(0) {} | |
134 | |
135 // Add a WebSocketFrame to the buffer and increase total_bytes_. | |
136 void AddFrame(scoped_ptr<WebSocketFrame> chunk); | |
137 | |
138 // Return a pointer to the frames_ for write purposes. | |
139 ScopedVector<WebSocketFrame>* frames() { return &frames_; } | |
140 | |
141 private: | |
142 // The frames_ that will be sent in the next call to WriteFrames(). | |
143 ScopedVector<WebSocketFrame> frames_; | |
144 | |
145 // The total size of the payload data in |frames_|. This will be used to | |
146 // measure the throughput of the link. | |
147 // TODO(ricea): Measure the throughput of the link. | |
148 uint64 total_bytes_; | |
149 }; | |
150 | |
151 void WebSocketChannel::SendBuffer::AddFrame(scoped_ptr<WebSocketFrame> frame) { | |
152 total_bytes_ += frame->header.payload_length; | |
153 frames_.push_back(frame.release()); | |
154 } | |
155 | |
156 // Implementation of WebSocketStream::ConnectDelegate that simply forwards the | |
157 // calls on to the WebSocketChannel that created it. | |
158 class WebSocketChannel::ConnectDelegate | |
159 : public WebSocketStream::ConnectDelegate { | |
160 public: | |
161 explicit ConnectDelegate(WebSocketChannel* creator) : creator_(creator) {} | |
162 | |
163 void OnSuccess(scoped_ptr<WebSocketStream> stream) override { | |
164 creator_->OnConnectSuccess(stream.Pass()); | |
165 // |this| may have been deleted. | |
166 } | |
167 | |
168 void OnFailure(const std::string& message) override { | |
169 creator_->OnConnectFailure(message); | |
170 // |this| has been deleted. | |
171 } | |
172 | |
173 void OnStartOpeningHandshake( | |
174 scoped_ptr<WebSocketHandshakeRequestInfo> request) override { | |
175 creator_->OnStartOpeningHandshake(request.Pass()); | |
176 } | |
177 | |
178 void OnFinishOpeningHandshake( | |
179 scoped_ptr<WebSocketHandshakeResponseInfo> response) override { | |
180 creator_->OnFinishOpeningHandshake(response.Pass()); | |
181 } | |
182 | |
183 void OnSSLCertificateError( | |
184 scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks> | |
185 ssl_error_callbacks, | |
186 const SSLInfo& ssl_info, | |
187 bool fatal) override { | |
188 creator_->OnSSLCertificateError( | |
189 ssl_error_callbacks.Pass(), ssl_info, fatal); | |
190 } | |
191 | |
192 private: | |
193 // A pointer to the WebSocketChannel that created this object. There is no | |
194 // danger of this pointer being stale, because deleting the WebSocketChannel | |
195 // cancels the connect process, deleting this object and preventing its | |
196 // callbacks from being called. | |
197 WebSocketChannel* const creator_; | |
198 | |
199 DISALLOW_COPY_AND_ASSIGN(ConnectDelegate); | |
200 }; | |
201 | |
202 class WebSocketChannel::HandshakeNotificationSender | |
203 : public base::SupportsWeakPtr<HandshakeNotificationSender> { | |
204 public: | |
205 explicit HandshakeNotificationSender(WebSocketChannel* channel); | |
206 ~HandshakeNotificationSender(); | |
207 | |
208 static void Send(base::WeakPtr<HandshakeNotificationSender> sender); | |
209 | |
210 ChannelState SendImmediately(WebSocketEventInterface* event_interface); | |
211 | |
212 const WebSocketHandshakeRequestInfo* handshake_request_info() const { | |
213 return handshake_request_info_.get(); | |
214 } | |
215 | |
216 void set_handshake_request_info( | |
217 scoped_ptr<WebSocketHandshakeRequestInfo> request_info) { | |
218 handshake_request_info_ = request_info.Pass(); | |
219 } | |
220 | |
221 const WebSocketHandshakeResponseInfo* handshake_response_info() const { | |
222 return handshake_response_info_.get(); | |
223 } | |
224 | |
225 void set_handshake_response_info( | |
226 scoped_ptr<WebSocketHandshakeResponseInfo> response_info) { | |
227 handshake_response_info_ = response_info.Pass(); | |
228 } | |
229 | |
230 private: | |
231 WebSocketChannel* owner_; | |
232 scoped_ptr<WebSocketHandshakeRequestInfo> handshake_request_info_; | |
233 scoped_ptr<WebSocketHandshakeResponseInfo> handshake_response_info_; | |
234 }; | |
235 | |
236 WebSocketChannel::HandshakeNotificationSender::HandshakeNotificationSender( | |
237 WebSocketChannel* channel) | |
238 : owner_(channel) {} | |
239 | |
240 WebSocketChannel::HandshakeNotificationSender::~HandshakeNotificationSender() {} | |
241 | |
242 void WebSocketChannel::HandshakeNotificationSender::Send( | |
243 base::WeakPtr<HandshakeNotificationSender> sender) { | |
244 // Do nothing if |sender| is already destructed. | |
245 if (sender) { | |
246 WebSocketChannel* channel = sender->owner_; | |
247 sender->SendImmediately(channel->event_interface_.get()); | |
248 } | |
249 } | |
250 | |
251 ChannelState WebSocketChannel::HandshakeNotificationSender::SendImmediately( | |
252 WebSocketEventInterface* event_interface) { | |
253 | |
254 if (handshake_request_info_.get()) { | |
255 if (CHANNEL_DELETED == event_interface->OnStartOpeningHandshake( | |
256 handshake_request_info_.Pass())) | |
257 return CHANNEL_DELETED; | |
258 } | |
259 | |
260 if (handshake_response_info_.get()) { | |
261 if (CHANNEL_DELETED == event_interface->OnFinishOpeningHandshake( | |
262 handshake_response_info_.Pass())) | |
263 return CHANNEL_DELETED; | |
264 | |
265 // TODO(yhirano): We can release |this| to save memory because | |
266 // there will be no more opening handshake notification. | |
267 } | |
268 | |
269 return CHANNEL_ALIVE; | |
270 } | |
271 | |
272 WebSocketChannel::PendingReceivedFrame::PendingReceivedFrame( | |
273 bool final, | |
274 WebSocketFrameHeader::OpCode opcode, | |
275 const scoped_refptr<IOBuffer>& data, | |
276 uint64 offset, | |
277 uint64 size) | |
278 : final_(final), | |
279 opcode_(opcode), | |
280 data_(data), | |
281 offset_(offset), | |
282 size_(size) {} | |
283 | |
284 WebSocketChannel::PendingReceivedFrame::~PendingReceivedFrame() {} | |
285 | |
286 void WebSocketChannel::PendingReceivedFrame::ResetOpcode() { | |
287 DCHECK(WebSocketFrameHeader::IsKnownDataOpCode(opcode_)); | |
288 opcode_ = WebSocketFrameHeader::kOpCodeContinuation; | |
289 } | |
290 | |
291 void WebSocketChannel::PendingReceivedFrame::DidConsume(uint64 bytes) { | |
292 DCHECK_LE(offset_, size_); | |
293 DCHECK_LE(bytes, size_ - offset_); | |
294 offset_ += bytes; | |
295 } | |
296 | |
297 WebSocketChannel::WebSocketChannel( | |
298 scoped_ptr<WebSocketEventInterface> event_interface, | |
299 URLRequestContext* url_request_context) | |
300 : event_interface_(event_interface.Pass()), | |
301 url_request_context_(url_request_context), | |
302 send_quota_low_water_mark_(kDefaultSendQuotaLowWaterMark), | |
303 send_quota_high_water_mark_(kDefaultSendQuotaHighWaterMark), | |
304 current_send_quota_(0), | |
305 current_receive_quota_(0), | |
306 closing_handshake_timeout_(base::TimeDelta::FromSeconds( | |
307 kClosingHandshakeTimeoutSeconds)), | |
308 underlying_connection_close_timeout_(base::TimeDelta::FromSeconds( | |
309 kUnderlyingConnectionCloseTimeoutSeconds)), | |
310 has_received_close_frame_(false), | |
311 received_close_code_(0), | |
312 state_(FRESHLY_CONSTRUCTED), | |
313 notification_sender_(new HandshakeNotificationSender(this)), | |
314 sending_text_message_(false), | |
315 receiving_text_message_(false), | |
316 expecting_to_handle_continuation_(false), | |
317 initial_frame_forwarded_(false) {} | |
318 | |
319 WebSocketChannel::~WebSocketChannel() { | |
320 // The stream may hold a pointer to read_frames_, and so it needs to be | |
321 // destroyed first. | |
322 stream_.reset(); | |
323 // The timer may have a callback pointing back to us, so stop it just in case | |
324 // someone decides to run the event loop from their destructor. | |
325 close_timer_.Stop(); | |
326 } | |
327 | |
328 void WebSocketChannel::SendAddChannelRequest( | |
329 const GURL& socket_url, | |
330 const std::vector<std::string>& requested_subprotocols, | |
331 const url::Origin& origin) { | |
332 // Delegate to the tested version. | |
333 SendAddChannelRequestWithSuppliedCreator( | |
334 socket_url, | |
335 requested_subprotocols, | |
336 origin, | |
337 base::Bind(&WebSocketStream::CreateAndConnectStream)); | |
338 } | |
339 | |
340 void WebSocketChannel::SetState(State new_state) { | |
341 DCHECK_NE(state_, new_state); | |
342 | |
343 if (new_state == CONNECTED) | |
344 established_on_ = base::TimeTicks::Now(); | |
345 if (state_ == CONNECTED && !established_on_.is_null()) { | |
346 UMA_HISTOGRAM_LONG_TIMES( | |
347 "Net.WebSocket.Duration", base::TimeTicks::Now() - established_on_); | |
348 } | |
349 | |
350 state_ = new_state; | |
351 } | |
352 | |
353 bool WebSocketChannel::InClosingState() const { | |
354 // The state RECV_CLOSED is not supported here, because it is only used in one | |
355 // code path and should not leak into the code in general. | |
356 DCHECK_NE(RECV_CLOSED, state_) | |
357 << "InClosingState called with state_ == RECV_CLOSED"; | |
358 return state_ == SEND_CLOSED || state_ == CLOSE_WAIT || state_ == CLOSED; | |
359 } | |
360 | |
361 void WebSocketChannel::SendFrame(bool fin, | |
362 WebSocketFrameHeader::OpCode op_code, | |
363 const std::vector<char>& data) { | |
364 if (data.size() > INT_MAX) { | |
365 NOTREACHED() << "Frame size sanity check failed"; | |
366 return; | |
367 } | |
368 if (stream_ == NULL) { | |
369 LOG(DFATAL) << "Got SendFrame without a connection established; " | |
370 << "misbehaving renderer? fin=" << fin << " op_code=" << op_code | |
371 << " data.size()=" << data.size(); | |
372 return; | |
373 } | |
374 if (InClosingState()) { | |
375 DVLOG(1) << "SendFrame called in state " << state_ | |
376 << ". This may be a bug, or a harmless race."; | |
377 return; | |
378 } | |
379 if (state_ != CONNECTED) { | |
380 NOTREACHED() << "SendFrame() called in state " << state_; | |
381 return; | |
382 } | |
383 if (data.size() > base::checked_cast<size_t>(current_send_quota_)) { | |
384 // TODO(ricea): Kill renderer. | |
385 ignore_result( | |
386 FailChannel("Send quota exceeded", kWebSocketErrorGoingAway, "")); | |
387 // |this| has been deleted. | |
388 return; | |
389 } | |
390 if (!WebSocketFrameHeader::IsKnownDataOpCode(op_code)) { | |
391 LOG(DFATAL) << "Got SendFrame with bogus op_code " << op_code | |
392 << "; misbehaving renderer? fin=" << fin | |
393 << " data.size()=" << data.size(); | |
394 return; | |
395 } | |
396 if (op_code == WebSocketFrameHeader::kOpCodeText || | |
397 (op_code == WebSocketFrameHeader::kOpCodeContinuation && | |
398 sending_text_message_)) { | |
399 StreamingUtf8Validator::State state = | |
400 outgoing_utf8_validator_.AddBytes(vector_as_array(&data), data.size()); | |
401 if (state == StreamingUtf8Validator::INVALID || | |
402 (state == StreamingUtf8Validator::VALID_MIDPOINT && fin)) { | |
403 // TODO(ricea): Kill renderer. | |
404 ignore_result( | |
405 FailChannel("Browser sent a text frame containing invalid UTF-8", | |
406 kWebSocketErrorGoingAway, | |
407 "")); | |
408 // |this| has been deleted. | |
409 return; | |
410 } | |
411 sending_text_message_ = !fin; | |
412 DCHECK(!fin || state == StreamingUtf8Validator::VALID_ENDPOINT); | |
413 } | |
414 current_send_quota_ -= data.size(); | |
415 // TODO(ricea): If current_send_quota_ has dropped below | |
416 // send_quota_low_water_mark_, it might be good to increase the "low | |
417 // water mark" and "high water mark", but only if the link to the WebSocket | |
418 // server is not saturated. | |
419 scoped_refptr<IOBuffer> buffer(new IOBuffer(data.size())); | |
420 std::copy(data.begin(), data.end(), buffer->data()); | |
421 ignore_result(SendFrameFromIOBuffer(fin, op_code, buffer, data.size())); | |
422 // |this| may have been deleted. | |
423 } | |
424 | |
425 void WebSocketChannel::SendFlowControl(int64 quota) { | |
426 DCHECK(state_ == CONNECTING || state_ == CONNECTED || state_ == SEND_CLOSED || | |
427 state_ == CLOSE_WAIT); | |
428 // TODO(ricea): Kill the renderer if it tries to send us a negative quota | |
429 // value or > INT_MAX. | |
430 DCHECK_GE(quota, 0); | |
431 DCHECK_LE(quota, INT_MAX); | |
432 if (!pending_received_frames_.empty()) { | |
433 DCHECK_EQ(0u, current_receive_quota_); | |
434 } | |
435 while (!pending_received_frames_.empty() && quota > 0) { | |
436 PendingReceivedFrame& front = pending_received_frames_.front(); | |
437 const uint64 data_size = front.size() - front.offset(); | |
438 const uint64 bytes_to_send = | |
439 std::min(base::checked_cast<uint64>(quota), data_size); | |
440 const bool final = front.final() && data_size == bytes_to_send; | |
441 const char* data = | |
442 front.data().get() ? front.data()->data() + front.offset() : NULL; | |
443 DCHECK(!bytes_to_send || data) << "Non empty data should not be null."; | |
444 const std::vector<char> data_vector(data, data + bytes_to_send); | |
445 DVLOG(3) << "Sending frame previously split due to quota to the " | |
446 << "renderer: quota=" << quota << " data_size=" << data_size | |
447 << " bytes_to_send=" << bytes_to_send; | |
448 if (event_interface_->OnDataFrame(final, front.opcode(), data_vector) == | |
449 CHANNEL_DELETED) | |
450 return; | |
451 if (bytes_to_send < data_size) { | |
452 front.DidConsume(bytes_to_send); | |
453 front.ResetOpcode(); | |
454 return; | |
455 } | |
456 quota -= bytes_to_send; | |
457 | |
458 pending_received_frames_.pop(); | |
459 } | |
460 // If current_receive_quota_ == 0 then there is no pending ReadFrames() | |
461 // operation. | |
462 const bool start_read = | |
463 current_receive_quota_ == 0 && quota > 0 && | |
464 (state_ == CONNECTED || state_ == SEND_CLOSED || state_ == CLOSE_WAIT); | |
465 current_receive_quota_ += quota; | |
466 if (start_read) | |
467 ignore_result(ReadFrames()); | |
468 // |this| may have been deleted. | |
469 } | |
470 | |
471 void WebSocketChannel::StartClosingHandshake(uint16 code, | |
472 const std::string& reason) { | |
473 if (InClosingState()) { | |
474 // When the associated renderer process is killed while the channel is in | |
475 // CLOSING state we reach here. | |
476 DVLOG(1) << "StartClosingHandshake called in state " << state_ | |
477 << ". This may be a bug, or a harmless race."; | |
478 return; | |
479 } | |
480 if (state_ == CONNECTING) { | |
481 // Abort the in-progress handshake and drop the connection immediately. | |
482 stream_request_.reset(); | |
483 SetState(CLOSED); | |
484 DoDropChannel(false, kWebSocketErrorAbnormalClosure, ""); | |
485 return; | |
486 } | |
487 if (state_ != CONNECTED) { | |
488 NOTREACHED() << "StartClosingHandshake() called in state " << state_; | |
489 return; | |
490 } | |
491 | |
492 DCHECK(!close_timer_.IsRunning()); | |
493 // This use of base::Unretained() is safe because we stop the timer in the | |
494 // destructor. | |
495 close_timer_.Start( | |
496 FROM_HERE, | |
497 closing_handshake_timeout_, | |
498 base::Bind(&WebSocketChannel::CloseTimeout, base::Unretained(this))); | |
499 | |
500 // Javascript actually only permits 1000 and 3000-4999, but the implementation | |
501 // itself may produce different codes. The length of |reason| is also checked | |
502 // by Javascript. | |
503 if (!IsStrictlyValidCloseStatusCode(code) || | |
504 reason.size() > kMaximumCloseReasonLength) { | |
505 // "InternalServerError" is actually used for errors from any endpoint, per | |
506 // errata 3227 to RFC6455. If the renderer is sending us an invalid code or | |
507 // reason it must be malfunctioning in some way, and based on that we | |
508 // interpret this as an internal error. | |
509 if (SendClose(kWebSocketErrorInternalServerError, "") != CHANNEL_DELETED) { | |
510 DCHECK_EQ(CONNECTED, state_); | |
511 SetState(SEND_CLOSED); | |
512 } | |
513 return; | |
514 } | |
515 if (SendClose( | |
516 code, | |
517 StreamingUtf8Validator::Validate(reason) ? reason : std::string()) == | |
518 CHANNEL_DELETED) | |
519 return; | |
520 DCHECK_EQ(CONNECTED, state_); | |
521 SetState(SEND_CLOSED); | |
522 } | |
523 | |
524 void WebSocketChannel::SendAddChannelRequestForTesting( | |
525 const GURL& socket_url, | |
526 const std::vector<std::string>& requested_subprotocols, | |
527 const url::Origin& origin, | |
528 const WebSocketStreamCreator& creator) { | |
529 SendAddChannelRequestWithSuppliedCreator( | |
530 socket_url, requested_subprotocols, origin, creator); | |
531 } | |
532 | |
533 void WebSocketChannel::SetClosingHandshakeTimeoutForTesting( | |
534 base::TimeDelta delay) { | |
535 closing_handshake_timeout_ = delay; | |
536 } | |
537 | |
538 void WebSocketChannel::SetUnderlyingConnectionCloseTimeoutForTesting( | |
539 base::TimeDelta delay) { | |
540 underlying_connection_close_timeout_ = delay; | |
541 } | |
542 | |
543 void WebSocketChannel::SendAddChannelRequestWithSuppliedCreator( | |
544 const GURL& socket_url, | |
545 const std::vector<std::string>& requested_subprotocols, | |
546 const url::Origin& origin, | |
547 const WebSocketStreamCreator& creator) { | |
548 DCHECK_EQ(FRESHLY_CONSTRUCTED, state_); | |
549 if (!socket_url.SchemeIsWSOrWSS()) { | |
550 // TODO(ricea): Kill the renderer (this error should have been caught by | |
551 // Javascript). | |
552 ignore_result(event_interface_->OnAddChannelResponse(true, "", "")); | |
553 // |this| is deleted here. | |
554 return; | |
555 } | |
556 socket_url_ = socket_url; | |
557 scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate( | |
558 new ConnectDelegate(this)); | |
559 stream_request_ = creator.Run(socket_url_, | |
560 requested_subprotocols, | |
561 origin, | |
562 url_request_context_, | |
563 BoundNetLog(), | |
564 connect_delegate.Pass()); | |
565 SetState(CONNECTING); | |
566 } | |
567 | |
568 void WebSocketChannel::OnConnectSuccess(scoped_ptr<WebSocketStream> stream) { | |
569 DCHECK(stream); | |
570 DCHECK_EQ(CONNECTING, state_); | |
571 | |
572 stream_ = stream.Pass(); | |
573 | |
574 SetState(CONNECTED); | |
575 | |
576 if (event_interface_->OnAddChannelResponse( | |
577 false, stream_->GetSubProtocol(), stream_->GetExtensions()) == | |
578 CHANNEL_DELETED) | |
579 return; | |
580 | |
581 // TODO(ricea): Get flow control information from the WebSocketStream once we | |
582 // have a multiplexing WebSocketStream. | |
583 current_send_quota_ = send_quota_high_water_mark_; | |
584 if (event_interface_->OnFlowControl(send_quota_high_water_mark_) == | |
585 CHANNEL_DELETED) | |
586 return; | |
587 | |
588 // |stream_request_| is not used once the connection has succeeded. | |
589 stream_request_.reset(); | |
590 | |
591 ignore_result(ReadFrames()); | |
592 // |this| may have been deleted. | |
593 } | |
594 | |
595 void WebSocketChannel::OnConnectFailure(const std::string& message) { | |
596 DCHECK_EQ(CONNECTING, state_); | |
597 | |
598 // Copy the message before we delete its owner. | |
599 std::string message_copy = message; | |
600 | |
601 SetState(CLOSED); | |
602 stream_request_.reset(); | |
603 | |
604 if (CHANNEL_DELETED == | |
605 notification_sender_->SendImmediately(event_interface_.get())) { | |
606 // |this| has been deleted. | |
607 return; | |
608 } | |
609 ChannelState result = event_interface_->OnFailChannel(message_copy); | |
610 DCHECK_EQ(CHANNEL_DELETED, result); | |
611 // |this| has been deleted. | |
612 } | |
613 | |
614 void WebSocketChannel::OnSSLCertificateError( | |
615 scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks> ssl_error_callbacks, | |
616 const SSLInfo& ssl_info, | |
617 bool fatal) { | |
618 ignore_result(event_interface_->OnSSLCertificateError( | |
619 ssl_error_callbacks.Pass(), socket_url_, ssl_info, fatal)); | |
620 } | |
621 | |
622 void WebSocketChannel::OnStartOpeningHandshake( | |
623 scoped_ptr<WebSocketHandshakeRequestInfo> request) { | |
624 DCHECK(!notification_sender_->handshake_request_info()); | |
625 | |
626 // Because it is hard to handle an IPC error synchronously is difficult, | |
627 // we asynchronously notify the information. | |
628 notification_sender_->set_handshake_request_info(request.Pass()); | |
629 ScheduleOpeningHandshakeNotification(); | |
630 } | |
631 | |
632 void WebSocketChannel::OnFinishOpeningHandshake( | |
633 scoped_ptr<WebSocketHandshakeResponseInfo> response) { | |
634 DCHECK(!notification_sender_->handshake_response_info()); | |
635 | |
636 // Because it is hard to handle an IPC error synchronously is difficult, | |
637 // we asynchronously notify the information. | |
638 notification_sender_->set_handshake_response_info(response.Pass()); | |
639 ScheduleOpeningHandshakeNotification(); | |
640 } | |
641 | |
642 void WebSocketChannel::ScheduleOpeningHandshakeNotification() { | |
643 base::MessageLoop::current()->PostTask( | |
644 FROM_HERE, | |
645 base::Bind(HandshakeNotificationSender::Send, | |
646 notification_sender_->AsWeakPtr())); | |
647 } | |
648 | |
649 ChannelState WebSocketChannel::WriteFrames() { | |
650 int result = OK; | |
651 do { | |
652 // This use of base::Unretained is safe because this object owns the | |
653 // WebSocketStream and destroying it cancels all callbacks. | |
654 result = stream_->WriteFrames( | |
655 data_being_sent_->frames(), | |
656 base::Bind(base::IgnoreResult(&WebSocketChannel::OnWriteDone), | |
657 base::Unretained(this), | |
658 false)); | |
659 if (result != ERR_IO_PENDING) { | |
660 if (OnWriteDone(true, result) == CHANNEL_DELETED) | |
661 return CHANNEL_DELETED; | |
662 // OnWriteDone() returns CHANNEL_DELETED on error. Here |state_| is | |
663 // guaranteed to be the same as before OnWriteDone() call. | |
664 } | |
665 } while (result == OK && data_being_sent_); | |
666 return CHANNEL_ALIVE; | |
667 } | |
668 | |
669 ChannelState WebSocketChannel::OnWriteDone(bool synchronous, int result) { | |
670 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | |
671 DCHECK_NE(CONNECTING, state_); | |
672 DCHECK_NE(ERR_IO_PENDING, result); | |
673 DCHECK(data_being_sent_); | |
674 switch (result) { | |
675 case OK: | |
676 if (data_to_send_next_) { | |
677 data_being_sent_ = data_to_send_next_.Pass(); | |
678 if (!synchronous) | |
679 return WriteFrames(); | |
680 } else { | |
681 data_being_sent_.reset(); | |
682 if (current_send_quota_ < send_quota_low_water_mark_) { | |
683 // TODO(ricea): Increase low_water_mark and high_water_mark if | |
684 // throughput is high, reduce them if throughput is low. Low water | |
685 // mark needs to be >= the bandwidth delay product *of the IPC | |
686 // channel*. Because factors like context-switch time, thread wake-up | |
687 // time, and bus speed come into play it is complex and probably needs | |
688 // to be determined empirically. | |
689 DCHECK_LE(send_quota_low_water_mark_, send_quota_high_water_mark_); | |
690 // TODO(ricea): Truncate quota by the quota specified by the remote | |
691 // server, if the protocol in use supports quota. | |
692 int fresh_quota = send_quota_high_water_mark_ - current_send_quota_; | |
693 current_send_quota_ += fresh_quota; | |
694 return event_interface_->OnFlowControl(fresh_quota); | |
695 } | |
696 } | |
697 return CHANNEL_ALIVE; | |
698 | |
699 // If a recoverable error condition existed, it would go here. | |
700 | |
701 default: | |
702 DCHECK_LT(result, 0) | |
703 << "WriteFrames() should only return OK or ERR_ codes"; | |
704 | |
705 stream_->Close(); | |
706 SetState(CLOSED); | |
707 return DoDropChannel(false, kWebSocketErrorAbnormalClosure, ""); | |
708 } | |
709 } | |
710 | |
711 ChannelState WebSocketChannel::ReadFrames() { | |
712 int result = OK; | |
713 while (result == OK && current_receive_quota_ > 0) { | |
714 // This use of base::Unretained is safe because this object owns the | |
715 // WebSocketStream, and any pending reads will be cancelled when it is | |
716 // destroyed. | |
717 result = stream_->ReadFrames( | |
718 &read_frames_, | |
719 base::Bind(base::IgnoreResult(&WebSocketChannel::OnReadDone), | |
720 base::Unretained(this), | |
721 false)); | |
722 if (result != ERR_IO_PENDING) { | |
723 if (OnReadDone(true, result) == CHANNEL_DELETED) | |
724 return CHANNEL_DELETED; | |
725 } | |
726 DCHECK_NE(CLOSED, state_); | |
727 } | |
728 return CHANNEL_ALIVE; | |
729 } | |
730 | |
731 ChannelState WebSocketChannel::OnReadDone(bool synchronous, int result) { | |
732 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | |
733 DCHECK_NE(CONNECTING, state_); | |
734 DCHECK_NE(ERR_IO_PENDING, result); | |
735 switch (result) { | |
736 case OK: | |
737 // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection | |
738 // with no data read, not an empty response. | |
739 DCHECK(!read_frames_.empty()) | |
740 << "ReadFrames() returned OK, but nothing was read."; | |
741 for (size_t i = 0; i < read_frames_.size(); ++i) { | |
742 scoped_ptr<WebSocketFrame> frame(read_frames_[i]); | |
743 read_frames_[i] = NULL; | |
744 if (HandleFrame(frame.Pass()) == CHANNEL_DELETED) | |
745 return CHANNEL_DELETED; | |
746 } | |
747 read_frames_.clear(); | |
748 // There should always be a call to ReadFrames pending. | |
749 // TODO(ricea): Unless we are out of quota. | |
750 DCHECK_NE(CLOSED, state_); | |
751 if (!synchronous) | |
752 return ReadFrames(); | |
753 return CHANNEL_ALIVE; | |
754 | |
755 case ERR_WS_PROTOCOL_ERROR: | |
756 // This could be kWebSocketErrorProtocolError (specifically, non-minimal | |
757 // encoding of payload length) or kWebSocketErrorMessageTooBig, or an | |
758 // extension-specific error. | |
759 return FailChannel("Invalid frame header", | |
760 kWebSocketErrorProtocolError, | |
761 "WebSocket Protocol Error"); | |
762 | |
763 default: | |
764 DCHECK_LT(result, 0) | |
765 << "ReadFrames() should only return OK or ERR_ codes"; | |
766 | |
767 stream_->Close(); | |
768 SetState(CLOSED); | |
769 | |
770 uint16 code = kWebSocketErrorAbnormalClosure; | |
771 std::string reason = ""; | |
772 bool was_clean = false; | |
773 if (has_received_close_frame_) { | |
774 code = received_close_code_; | |
775 reason = received_close_reason_; | |
776 was_clean = (result == ERR_CONNECTION_CLOSED); | |
777 } | |
778 | |
779 return DoDropChannel(was_clean, code, reason); | |
780 } | |
781 } | |
782 | |
783 ChannelState WebSocketChannel::HandleFrame(scoped_ptr<WebSocketFrame> frame) { | |
784 if (frame->header.masked) { | |
785 // RFC6455 Section 5.1 "A client MUST close a connection if it detects a | |
786 // masked frame." | |
787 return FailChannel( | |
788 "A server must not mask any frames that it sends to the " | |
789 "client.", | |
790 kWebSocketErrorProtocolError, | |
791 "Masked frame from server"); | |
792 } | |
793 const WebSocketFrameHeader::OpCode opcode = frame->header.opcode; | |
794 DCHECK(!WebSocketFrameHeader::IsKnownControlOpCode(opcode) || | |
795 frame->header.final); | |
796 if (frame->header.reserved1 || frame->header.reserved2 || | |
797 frame->header.reserved3) { | |
798 return FailChannel(base::StringPrintf( | |
799 "One or more reserved bits are on: reserved1 = %d, " | |
800 "reserved2 = %d, reserved3 = %d", | |
801 static_cast<int>(frame->header.reserved1), | |
802 static_cast<int>(frame->header.reserved2), | |
803 static_cast<int>(frame->header.reserved3)), | |
804 kWebSocketErrorProtocolError, | |
805 "Invalid reserved bit"); | |
806 } | |
807 | |
808 // Respond to the frame appropriately to its type. | |
809 return HandleFrameByState( | |
810 opcode, frame->header.final, frame->data, frame->header.payload_length); | |
811 } | |
812 | |
813 ChannelState WebSocketChannel::HandleFrameByState( | |
814 const WebSocketFrameHeader::OpCode opcode, | |
815 bool final, | |
816 const scoped_refptr<IOBuffer>& data_buffer, | |
817 uint64 size) { | |
818 DCHECK_NE(RECV_CLOSED, state_) | |
819 << "HandleFrame() does not support being called re-entrantly from within " | |
820 "SendClose()"; | |
821 DCHECK_NE(CLOSED, state_); | |
822 if (state_ == CLOSE_WAIT) { | |
823 std::string frame_name; | |
824 GetFrameTypeForOpcode(opcode, &frame_name); | |
825 | |
826 // FailChannel() won't send another Close frame. | |
827 return FailChannel( | |
828 frame_name + " received after close", kWebSocketErrorProtocolError, ""); | |
829 } | |
830 switch (opcode) { | |
831 case WebSocketFrameHeader::kOpCodeText: // fall-thru | |
832 case WebSocketFrameHeader::kOpCodeBinary: | |
833 case WebSocketFrameHeader::kOpCodeContinuation: | |
834 return HandleDataFrame(opcode, final, data_buffer, size); | |
835 | |
836 case WebSocketFrameHeader::kOpCodePing: | |
837 DVLOG(1) << "Got Ping of size " << size; | |
838 if (state_ == CONNECTED) | |
839 return SendFrameFromIOBuffer( | |
840 true, WebSocketFrameHeader::kOpCodePong, data_buffer, size); | |
841 DVLOG(3) << "Ignored ping in state " << state_; | |
842 return CHANNEL_ALIVE; | |
843 | |
844 case WebSocketFrameHeader::kOpCodePong: | |
845 DVLOG(1) << "Got Pong of size " << size; | |
846 // There is no need to do anything with pong messages. | |
847 return CHANNEL_ALIVE; | |
848 | |
849 case WebSocketFrameHeader::kOpCodeClose: { | |
850 // TODO(ricea): If there is a message which is queued for transmission to | |
851 // the renderer, then the renderer should not receive an | |
852 // OnClosingHandshake or OnDropChannel IPC until the queued message has | |
853 // been completedly transmitted. | |
854 uint16 code = kWebSocketNormalClosure; | |
855 std::string reason; | |
856 std::string message; | |
857 if (!ParseClose(data_buffer, size, &code, &reason, &message)) { | |
858 return FailChannel(message, code, reason); | |
859 } | |
860 // TODO(ricea): Find a way to safely log the message from the close | |
861 // message (escape control codes and so on). | |
862 DVLOG(1) << "Got Close with code " << code; | |
863 switch (state_) { | |
864 case CONNECTED: | |
865 SetState(RECV_CLOSED); | |
866 | |
867 if (SendClose(code, reason) == CHANNEL_DELETED) | |
868 return CHANNEL_DELETED; | |
869 DCHECK_EQ(RECV_CLOSED, state_); | |
870 | |
871 SetState(CLOSE_WAIT); | |
872 DCHECK(!close_timer_.IsRunning()); | |
873 // This use of base::Unretained() is safe because we stop the timer | |
874 // in the destructor. | |
875 close_timer_.Start( | |
876 FROM_HERE, | |
877 underlying_connection_close_timeout_, | |
878 base::Bind( | |
879 &WebSocketChannel::CloseTimeout, base::Unretained(this))); | |
880 | |
881 if (event_interface_->OnClosingHandshake() == CHANNEL_DELETED) | |
882 return CHANNEL_DELETED; | |
883 has_received_close_frame_ = true; | |
884 received_close_code_ = code; | |
885 received_close_reason_ = reason; | |
886 break; | |
887 | |
888 case SEND_CLOSED: | |
889 SetState(CLOSE_WAIT); | |
890 DCHECK(close_timer_.IsRunning()); | |
891 close_timer_.Stop(); | |
892 // This use of base::Unretained() is safe because we stop the timer | |
893 // in the destructor. | |
894 close_timer_.Start( | |
895 FROM_HERE, | |
896 underlying_connection_close_timeout_, | |
897 base::Bind( | |
898 &WebSocketChannel::CloseTimeout, base::Unretained(this))); | |
899 | |
900 // From RFC6455 section 7.1.5: "Each endpoint | |
901 // will see the status code sent by the other end as _The WebSocket | |
902 // Connection Close Code_." | |
903 has_received_close_frame_ = true; | |
904 received_close_code_ = code; | |
905 received_close_reason_ = reason; | |
906 break; | |
907 | |
908 default: | |
909 LOG(DFATAL) << "Got Close in unexpected state " << state_; | |
910 break; | |
911 } | |
912 return CHANNEL_ALIVE; | |
913 } | |
914 | |
915 default: | |
916 return FailChannel( | |
917 base::StringPrintf("Unrecognized frame opcode: %d", opcode), | |
918 kWebSocketErrorProtocolError, | |
919 "Unknown opcode"); | |
920 } | |
921 } | |
922 | |
923 ChannelState WebSocketChannel::HandleDataFrame( | |
924 WebSocketFrameHeader::OpCode opcode, | |
925 bool final, | |
926 const scoped_refptr<IOBuffer>& data_buffer, | |
927 uint64 size) { | |
928 if (state_ != CONNECTED) { | |
929 DVLOG(3) << "Ignored data packet received in state " << state_; | |
930 return CHANNEL_ALIVE; | |
931 } | |
932 DCHECK(opcode == WebSocketFrameHeader::kOpCodeContinuation || | |
933 opcode == WebSocketFrameHeader::kOpCodeText || | |
934 opcode == WebSocketFrameHeader::kOpCodeBinary); | |
935 const bool got_continuation = | |
936 (opcode == WebSocketFrameHeader::kOpCodeContinuation); | |
937 if (got_continuation != expecting_to_handle_continuation_) { | |
938 const std::string console_log = got_continuation | |
939 ? "Received unexpected continuation frame." | |
940 : "Received start of new message but previous message is unfinished."; | |
941 const std::string reason = got_continuation | |
942 ? "Unexpected continuation" | |
943 : "Previous data frame unfinished"; | |
944 return FailChannel(console_log, kWebSocketErrorProtocolError, reason); | |
945 } | |
946 expecting_to_handle_continuation_ = !final; | |
947 WebSocketFrameHeader::OpCode opcode_to_send = opcode; | |
948 if (!initial_frame_forwarded_ && | |
949 opcode == WebSocketFrameHeader::kOpCodeContinuation) { | |
950 opcode_to_send = receiving_text_message_ | |
951 ? WebSocketFrameHeader::kOpCodeText | |
952 : WebSocketFrameHeader::kOpCodeBinary; | |
953 } | |
954 if (opcode == WebSocketFrameHeader::kOpCodeText || | |
955 (opcode == WebSocketFrameHeader::kOpCodeContinuation && | |
956 receiving_text_message_)) { | |
957 // This call is not redundant when size == 0 because it tells us what | |
958 // the current state is. | |
959 StreamingUtf8Validator::State state = incoming_utf8_validator_.AddBytes( | |
960 size ? data_buffer->data() : NULL, static_cast<size_t>(size)); | |
961 if (state == StreamingUtf8Validator::INVALID || | |
962 (state == StreamingUtf8Validator::VALID_MIDPOINT && final)) { | |
963 return FailChannel("Could not decode a text frame as UTF-8.", | |
964 kWebSocketErrorProtocolError, | |
965 "Invalid UTF-8 in text frame"); | |
966 } | |
967 receiving_text_message_ = !final; | |
968 DCHECK(!final || state == StreamingUtf8Validator::VALID_ENDPOINT); | |
969 } | |
970 if (size == 0U && !final) | |
971 return CHANNEL_ALIVE; | |
972 | |
973 initial_frame_forwarded_ = !final; | |
974 if (size > current_receive_quota_ || !pending_received_frames_.empty()) { | |
975 const bool no_quota = (current_receive_quota_ == 0); | |
976 DCHECK(no_quota || pending_received_frames_.empty()); | |
977 DVLOG(3) << "Queueing frame to renderer due to quota. quota=" | |
978 << current_receive_quota_ << " size=" << size; | |
979 WebSocketFrameHeader::OpCode opcode_to_queue = | |
980 no_quota ? opcode_to_send : WebSocketFrameHeader::kOpCodeContinuation; | |
981 pending_received_frames_.push(PendingReceivedFrame( | |
982 final, opcode_to_queue, data_buffer, current_receive_quota_, size)); | |
983 if (no_quota) | |
984 return CHANNEL_ALIVE; | |
985 size = current_receive_quota_; | |
986 final = false; | |
987 } | |
988 | |
989 // TODO(ricea): Can this copy be eliminated? | |
990 const char* const data_begin = size ? data_buffer->data() : NULL; | |
991 const char* const data_end = data_begin + size; | |
992 const std::vector<char> data(data_begin, data_end); | |
993 current_receive_quota_ -= size; | |
994 | |
995 // Sends the received frame to the renderer process. | |
996 return event_interface_->OnDataFrame(final, opcode_to_send, data); | |
997 } | |
998 | |
999 ChannelState WebSocketChannel::SendFrameFromIOBuffer( | |
1000 bool fin, | |
1001 WebSocketFrameHeader::OpCode op_code, | |
1002 const scoped_refptr<IOBuffer>& buffer, | |
1003 uint64 size) { | |
1004 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); | |
1005 DCHECK(stream_); | |
1006 | |
1007 scoped_ptr<WebSocketFrame> frame(new WebSocketFrame(op_code)); | |
1008 WebSocketFrameHeader& header = frame->header; | |
1009 header.final = fin; | |
1010 header.masked = true; | |
1011 header.payload_length = size; | |
1012 frame->data = buffer; | |
1013 | |
1014 if (data_being_sent_) { | |
1015 // Either the link to the WebSocket server is saturated, or several messages | |
1016 // are being sent in a batch. | |
1017 // TODO(ricea): Keep some statistics to work out the situation and adjust | |
1018 // quota appropriately. | |
1019 if (!data_to_send_next_) | |
1020 data_to_send_next_.reset(new SendBuffer); | |
1021 data_to_send_next_->AddFrame(frame.Pass()); | |
1022 return CHANNEL_ALIVE; | |
1023 } | |
1024 | |
1025 data_being_sent_.reset(new SendBuffer); | |
1026 data_being_sent_->AddFrame(frame.Pass()); | |
1027 return WriteFrames(); | |
1028 } | |
1029 | |
1030 ChannelState WebSocketChannel::FailChannel(const std::string& message, | |
1031 uint16 code, | |
1032 const std::string& reason) { | |
1033 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | |
1034 DCHECK_NE(CONNECTING, state_); | |
1035 DCHECK_NE(CLOSED, state_); | |
1036 | |
1037 // TODO(ricea): Logging. | |
1038 if (state_ == CONNECTED) { | |
1039 if (SendClose(code, reason) == CHANNEL_DELETED) | |
1040 return CHANNEL_DELETED; | |
1041 } | |
1042 | |
1043 // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates the browser | |
1044 // should close the connection itself without waiting for the closing | |
1045 // handshake. | |
1046 stream_->Close(); | |
1047 SetState(CLOSED); | |
1048 ChannelState result = event_interface_->OnFailChannel(message); | |
1049 DCHECK_EQ(CHANNEL_DELETED, result); | |
1050 return result; | |
1051 } | |
1052 | |
1053 ChannelState WebSocketChannel::SendClose(uint16 code, | |
1054 const std::string& reason) { | |
1055 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); | |
1056 DCHECK_LE(reason.size(), kMaximumCloseReasonLength); | |
1057 scoped_refptr<IOBuffer> body; | |
1058 uint64 size = 0; | |
1059 if (code == kWebSocketErrorNoStatusReceived) { | |
1060 // Special case: translate kWebSocketErrorNoStatusReceived into a Close | |
1061 // frame with no payload. | |
1062 DCHECK(reason.empty()); | |
1063 body = new IOBuffer(0); | |
1064 } else { | |
1065 const size_t payload_length = kWebSocketCloseCodeLength + reason.length(); | |
1066 body = new IOBuffer(payload_length); | |
1067 size = payload_length; | |
1068 base::WriteBigEndian(body->data(), code); | |
1069 static_assert(sizeof(code) == kWebSocketCloseCodeLength, | |
1070 "they should both be two"); | |
1071 std::copy( | |
1072 reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength); | |
1073 } | |
1074 if (SendFrameFromIOBuffer( | |
1075 true, WebSocketFrameHeader::kOpCodeClose, body, size) == | |
1076 CHANNEL_DELETED) | |
1077 return CHANNEL_DELETED; | |
1078 return CHANNEL_ALIVE; | |
1079 } | |
1080 | |
1081 bool WebSocketChannel::ParseClose(const scoped_refptr<IOBuffer>& buffer, | |
1082 uint64 size, | |
1083 uint16* code, | |
1084 std::string* reason, | |
1085 std::string* message) { | |
1086 reason->clear(); | |
1087 if (size < kWebSocketCloseCodeLength) { | |
1088 if (size == 0U) { | |
1089 *code = kWebSocketErrorNoStatusReceived; | |
1090 return true; | |
1091 } | |
1092 | |
1093 DVLOG(1) << "Close frame with payload size " << size << " received " | |
1094 << "(the first byte is " << std::hex | |
1095 << static_cast<int>(buffer->data()[0]) << ")"; | |
1096 *code = kWebSocketErrorProtocolError; | |
1097 *message = | |
1098 "Received a broken close frame containing an invalid size body."; | |
1099 return false; | |
1100 } | |
1101 | |
1102 const char* data = buffer->data(); | |
1103 uint16 unchecked_code = 0; | |
1104 base::ReadBigEndian(data, &unchecked_code); | |
1105 static_assert(sizeof(unchecked_code) == kWebSocketCloseCodeLength, | |
1106 "they should both be two bytes"); | |
1107 | |
1108 switch (unchecked_code) { | |
1109 case kWebSocketErrorNoStatusReceived: | |
1110 case kWebSocketErrorAbnormalClosure: | |
1111 case kWebSocketErrorTlsHandshake: | |
1112 *code = kWebSocketErrorProtocolError; | |
1113 *message = | |
1114 "Received a broken close frame containing a reserved status code."; | |
1115 return false; | |
1116 | |
1117 default: | |
1118 *code = unchecked_code; | |
1119 break; | |
1120 } | |
1121 | |
1122 std::string text(data + kWebSocketCloseCodeLength, data + size); | |
1123 if (StreamingUtf8Validator::Validate(text)) { | |
1124 reason->swap(text); | |
1125 return true; | |
1126 } | |
1127 | |
1128 *code = kWebSocketErrorProtocolError; | |
1129 *reason = "Invalid UTF-8 in Close frame"; | |
1130 *message = "Received a broken close frame containing invalid UTF-8."; | |
1131 return false; | |
1132 } | |
1133 | |
1134 ChannelState WebSocketChannel::DoDropChannel(bool was_clean, | |
1135 uint16 code, | |
1136 const std::string& reason) { | |
1137 if (CHANNEL_DELETED == | |
1138 notification_sender_->SendImmediately(event_interface_.get())) | |
1139 return CHANNEL_DELETED; | |
1140 ChannelState result = | |
1141 event_interface_->OnDropChannel(was_clean, code, reason); | |
1142 DCHECK_EQ(CHANNEL_DELETED, result); | |
1143 return result; | |
1144 } | |
1145 | |
1146 void WebSocketChannel::CloseTimeout() { | |
1147 stream_->Close(); | |
1148 SetState(CLOSED); | |
1149 DoDropChannel(false, kWebSocketErrorAbnormalClosure, ""); | |
1150 // |this| has been deleted. | |
1151 } | |
1152 | |
1153 } // namespace net | |
OLD | NEW |