| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/websockets/websocket_channel.h" | 5 #include "net/websockets/websocket_channel.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/basictypes.h" // for size_t | 9 #include "base/basictypes.h" // for size_t |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/compiler_specific.h" |
| 11 #include "base/safe_numerics.h" | 12 #include "base/safe_numerics.h" |
| 12 #include "base/strings/string_util.h" | 13 #include "base/strings/string_util.h" |
| 13 #include "net/base/big_endian.h" | 14 #include "net/base/big_endian.h" |
| 14 #include "net/base/io_buffer.h" | 15 #include "net/base/io_buffer.h" |
| 15 #include "net/base/net_log.h" | 16 #include "net/base/net_log.h" |
| 16 #include "net/websockets/websocket_errors.h" | 17 #include "net/websockets/websocket_errors.h" |
| 17 #include "net/websockets/websocket_event_interface.h" | 18 #include "net/websockets/websocket_event_interface.h" |
| 18 #include "net/websockets/websocket_frame.h" | 19 #include "net/websockets/websocket_frame.h" |
| 19 #include "net/websockets/websocket_mux.h" | 20 #include "net/websockets/websocket_mux.h" |
| 20 #include "net/websockets/websocket_stream.h" | 21 #include "net/websockets/websocket_stream.h" |
| 21 | 22 |
| 22 namespace net { | 23 namespace net { |
| 23 | 24 |
| 24 namespace { | 25 namespace { |
| 25 | 26 |
| 26 const int kDefaultSendQuotaLowWaterMark = 1 << 16; | 27 const int kDefaultSendQuotaLowWaterMark = 1 << 16; |
| 27 const int kDefaultSendQuotaHighWaterMark = 1 << 17; | 28 const int kDefaultSendQuotaHighWaterMark = 1 << 17; |
| 28 const size_t kWebSocketCloseCodeLength = 2; | 29 const size_t kWebSocketCloseCodeLength = 2; |
| 30 typedef WebSocketEventInterface::ChannelState ChannelState; |
| 31 const ChannelState CHANNEL_ALIVE = WebSocketEventInterface::CHANNEL_ALIVE; |
| 32 const ChannelState CHANNEL_DELETED = WebSocketEventInterface::CHANNEL_DELETED; |
| 29 | 33 |
| 30 } // namespace | 34 } // namespace |
| 31 | 35 |
| 32 // A class to encapsulate a set of frames and information about the size of | 36 // A class to encapsulate a set of frames and information about the size of |
| 33 // those frames. | 37 // those frames. |
| 34 class WebSocketChannel::SendBuffer { | 38 class WebSocketChannel::SendBuffer { |
| 35 public: | 39 public: |
| 36 SendBuffer() : total_bytes_(0) {} | 40 SendBuffer() : total_bytes_(0) {} |
| 37 | 41 |
| 38 // Add a WebSocketFrame to the buffer and increase total_bytes_. | 42 // Add a WebSocketFrame to the buffer and increase total_bytes_. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 58 | 62 |
| 59 // Implementation of WebSocketStream::ConnectDelegate that simply forwards the | 63 // Implementation of WebSocketStream::ConnectDelegate that simply forwards the |
| 60 // calls on to the WebSocketChannel that created it. | 64 // calls on to the WebSocketChannel that created it. |
| 61 class WebSocketChannel::ConnectDelegate | 65 class WebSocketChannel::ConnectDelegate |
| 62 : public WebSocketStream::ConnectDelegate { | 66 : public WebSocketStream::ConnectDelegate { |
| 63 public: | 67 public: |
| 64 explicit ConnectDelegate(WebSocketChannel* creator) : creator_(creator) {} | 68 explicit ConnectDelegate(WebSocketChannel* creator) : creator_(creator) {} |
| 65 | 69 |
| 66 virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE { | 70 virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE { |
| 67 creator_->OnConnectSuccess(stream.Pass()); | 71 creator_->OnConnectSuccess(stream.Pass()); |
| 72 // |this| may have been deleted. |
| 68 } | 73 } |
| 69 | 74 |
| 70 virtual void OnFailure(uint16 websocket_error) OVERRIDE { | 75 virtual void OnFailure(uint16 websocket_error) OVERRIDE { |
| 71 creator_->OnConnectFailure(websocket_error); | 76 creator_->OnConnectFailure(websocket_error); |
| 77 // |this| has been deleted. |
| 72 } | 78 } |
| 73 | 79 |
| 74 private: | 80 private: |
| 75 // A pointer to the WebSocketChannel that created this object. There is no | 81 // A pointer to the WebSocketChannel that created this object. There is no |
| 76 // danger of this pointer being stale, because deleting the WebSocketChannel | 82 // danger of this pointer being stale, because deleting the WebSocketChannel |
| 77 // cancels the connect process, deleting this object and preventing its | 83 // cancels the connect process, deleting this object and preventing its |
| 78 // callbacks from being called. | 84 // callbacks from being called. |
| 79 WebSocketChannel* const creator_; | 85 WebSocketChannel* const creator_; |
| 80 | 86 |
| 81 DISALLOW_COPY_AND_ASSIGN(ConnectDelegate); | 87 DISALLOW_COPY_AND_ASSIGN(ConnectDelegate); |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 134 if (InClosingState()) { | 140 if (InClosingState()) { |
| 135 VLOG(1) << "SendFrame called in state " << state_ | 141 VLOG(1) << "SendFrame called in state " << state_ |
| 136 << ". This may be a bug, or a harmless race."; | 142 << ". This may be a bug, or a harmless race."; |
| 137 return; | 143 return; |
| 138 } | 144 } |
| 139 if (state_ != CONNECTED) { | 145 if (state_ != CONNECTED) { |
| 140 NOTREACHED() << "SendFrame() called in state " << state_; | 146 NOTREACHED() << "SendFrame() called in state " << state_; |
| 141 return; | 147 return; |
| 142 } | 148 } |
| 143 if (data.size() > base::checked_numeric_cast<size_t>(current_send_quota_)) { | 149 if (data.size() > base::checked_numeric_cast<size_t>(current_send_quota_)) { |
| 144 FailChannel(SEND_GOING_AWAY, | 150 ChannelState unused ALLOW_UNUSED = |
| 145 kWebSocketMuxErrorSendQuotaViolation, | 151 FailChannel(SEND_GOING_AWAY, |
| 146 "Send quota exceeded"); | 152 kWebSocketMuxErrorSendQuotaViolation, |
| 153 "Send quota exceeded"); |
| 154 // |this| is deleted here. |
| 147 return; | 155 return; |
| 148 } | 156 } |
| 149 if (!WebSocketFrameHeader::IsKnownDataOpCode(op_code)) { | 157 if (!WebSocketFrameHeader::IsKnownDataOpCode(op_code)) { |
| 150 LOG(DFATAL) << "Got SendFrame with bogus op_code " << op_code | 158 LOG(DFATAL) << "Got SendFrame with bogus op_code " << op_code |
| 151 << "; misbehaving renderer? fin=" << fin | 159 << "; misbehaving renderer? fin=" << fin |
| 152 << " data.size()=" << data.size(); | 160 << " data.size()=" << data.size(); |
| 153 return; | 161 return; |
| 154 } | 162 } |
| 155 current_send_quota_ -= data.size(); | 163 current_send_quota_ -= data.size(); |
| 156 // TODO(ricea): If current_send_quota_ has dropped below | 164 // TODO(ricea): If current_send_quota_ has dropped below |
| 157 // send_quota_low_water_mark_, it might be good to increase the "low | 165 // send_quota_low_water_mark_, it might be good to increase the "low |
| 158 // water mark" and "high water mark", but only if the link to the WebSocket | 166 // water mark" and "high water mark", but only if the link to the WebSocket |
| 159 // server is not saturated. | 167 // server is not saturated. |
| 160 // TODO(ricea): For kOpCodeText, do UTF-8 validation? | 168 // TODO(ricea): For kOpCodeText, do UTF-8 validation? |
| 161 scoped_refptr<IOBuffer> buffer(new IOBuffer(data.size())); | 169 scoped_refptr<IOBuffer> buffer(new IOBuffer(data.size())); |
| 162 std::copy(data.begin(), data.end(), buffer->data()); | 170 std::copy(data.begin(), data.end(), buffer->data()); |
| 163 SendIOBuffer(fin, op_code, buffer, data.size()); | 171 ChannelState unused ALLOW_UNUSED = |
| 172 SendIOBuffer(fin, op_code, buffer, data.size()); |
| 173 // |this| may have been deleted. |
| 164 } | 174 } |
| 165 | 175 |
| 166 void WebSocketChannel::SendFlowControl(int64 quota) { | 176 void WebSocketChannel::SendFlowControl(int64 quota) { |
| 167 DCHECK_EQ(CONNECTED, state_); | 177 DCHECK_EQ(CONNECTED, state_); |
| 168 // TODO(ricea): Add interface to WebSocketStream and implement. | 178 // TODO(ricea): Add interface to WebSocketStream and implement. |
| 169 // stream_->SendFlowControl(quota); | 179 // stream_->SendFlowControl(quota); |
| 170 } | 180 } |
| 171 | 181 |
| 172 void WebSocketChannel::StartClosingHandshake(uint16 code, | 182 void WebSocketChannel::StartClosingHandshake(uint16 code, |
| 173 const std::string& reason) { | 183 const std::string& reason) { |
| 174 if (InClosingState()) { | 184 if (InClosingState()) { |
| 175 VLOG(1) << "StartClosingHandshake called in state " << state_ | 185 VLOG(1) << "StartClosingHandshake called in state " << state_ |
| 176 << ". This may be a bug, or a harmless race."; | 186 << ". This may be a bug, or a harmless race."; |
| 177 return; | 187 return; |
| 178 } | 188 } |
| 179 if (state_ != CONNECTED) { | 189 if (state_ != CONNECTED) { |
| 180 NOTREACHED() << "StartClosingHandshake() called in state " << state_; | 190 NOTREACHED() << "StartClosingHandshake() called in state " << state_; |
| 181 return; | 191 return; |
| 182 } | 192 } |
| 183 // TODO(ricea): Validate |code|? Check that |reason| is valid UTF-8? | 193 // TODO(ricea): Validate |code| |
| 184 // TODO(ricea): There should be a timeout for the closing handshake. | 194 // TODO(ricea): There should be a timeout for the closing handshake. |
| 185 SendClose(code, reason); // Sets state_ to SEND_CLOSED | 195 ChannelState unused ALLOW_UNUSED = SendClose( |
| 196 code, IsStringUTF8(reason) ? reason : std::string()); // Sets state_ to |
| 197 // SEND_CLOSED |
| 198 // If |unused| is CHANNEL_DELETED, then |this| has been deleted. |
| 186 } | 199 } |
| 187 | 200 |
| 188 void WebSocketChannel::SendAddChannelRequestForTesting( | 201 void WebSocketChannel::SendAddChannelRequestForTesting( |
| 189 const GURL& socket_url, | 202 const GURL& socket_url, |
| 190 const std::vector<std::string>& requested_subprotocols, | 203 const std::vector<std::string>& requested_subprotocols, |
| 191 const GURL& origin, | 204 const GURL& origin, |
| 192 const WebSocketStreamFactory& factory) { | 205 const WebSocketStreamFactory& factory) { |
| 193 SendAddChannelRequestWithFactory(socket_url, | 206 SendAddChannelRequestWithFactory( |
| 194 requested_subprotocols, | 207 socket_url, requested_subprotocols, origin, factory); |
| 195 origin, | |
| 196 factory); | |
| 197 } | 208 } |
| 198 | 209 |
| 199 void WebSocketChannel::SendAddChannelRequestWithFactory( | 210 void WebSocketChannel::SendAddChannelRequestWithFactory( |
| 200 const GURL& socket_url, | 211 const GURL& socket_url, |
| 201 const std::vector<std::string>& requested_subprotocols, | 212 const std::vector<std::string>& requested_subprotocols, |
| 202 const GURL& origin, | 213 const GURL& origin, |
| 203 const WebSocketStreamFactory& factory) { | 214 const WebSocketStreamFactory& factory) { |
| 204 DCHECK_EQ(FRESHLY_CONSTRUCTED, state_); | 215 DCHECK_EQ(FRESHLY_CONSTRUCTED, state_); |
| 205 socket_url_ = socket_url; | 216 socket_url_ = socket_url; |
| 206 scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate( | 217 scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate( |
| 207 new ConnectDelegate(this)); | 218 new ConnectDelegate(this)); |
| 208 stream_request_ = factory.Run(socket_url_, | 219 stream_request_ = factory.Run(socket_url_, |
| 209 requested_subprotocols, | 220 requested_subprotocols, |
| 210 origin, | 221 origin, |
| 211 url_request_context_, | 222 url_request_context_, |
| 212 BoundNetLog(), | 223 BoundNetLog(), |
| 213 connect_delegate.Pass()); | 224 connect_delegate.Pass()); |
| 214 state_ = CONNECTING; | 225 state_ = CONNECTING; |
| 215 } | 226 } |
| 216 | 227 |
| 217 void WebSocketChannel::OnConnectSuccess(scoped_ptr<WebSocketStream> stream) { | 228 void WebSocketChannel::OnConnectSuccess(scoped_ptr<WebSocketStream> stream) { |
| 218 DCHECK(stream); | 229 DCHECK(stream); |
| 219 DCHECK_EQ(CONNECTING, state_); | 230 DCHECK_EQ(CONNECTING, state_); |
| 220 stream_ = stream.Pass(); | 231 stream_ = stream.Pass(); |
| 221 state_ = CONNECTED; | 232 state_ = CONNECTED; |
| 222 event_interface_->OnAddChannelResponse(false, stream_->GetSubProtocol()); | 233 if (event_interface_->OnAddChannelResponse( |
| 234 false, stream_->GetSubProtocol()) == CHANNEL_DELETED) |
| 235 return; |
| 223 | 236 |
| 224 // TODO(ricea): Get flow control information from the WebSocketStream once we | 237 // TODO(ricea): Get flow control information from the WebSocketStream once we |
| 225 // have a multiplexing WebSocketStream. | 238 // have a multiplexing WebSocketStream. |
| 226 current_send_quota_ = send_quota_high_water_mark_; | 239 current_send_quota_ = send_quota_high_water_mark_; |
| 227 event_interface_->OnFlowControl(send_quota_high_water_mark_); | 240 if (event_interface_->OnFlowControl(send_quota_high_water_mark_) == |
| 241 CHANNEL_DELETED) |
| 242 return; |
| 228 | 243 |
| 229 // |stream_request_| is not used once the connection has succeeded. | 244 // |stream_request_| is not used once the connection has succeeded. |
| 230 stream_request_.reset(); | 245 stream_request_.reset(); |
| 231 ReadFrames(); | 246 ChannelState unused ALLOW_UNUSED = ReadFrames(); |
| 247 // |this| may have been deleted. |
| 232 } | 248 } |
| 233 | 249 |
| 234 void WebSocketChannel::OnConnectFailure(uint16 websocket_error) { | 250 void WebSocketChannel::OnConnectFailure(uint16 websocket_error) { |
| 235 DCHECK_EQ(CONNECTING, state_); | 251 DCHECK_EQ(CONNECTING, state_); |
| 236 state_ = CLOSED; | 252 state_ = CLOSED; |
| 237 stream_request_.reset(); | 253 stream_request_.reset(); |
| 238 event_interface_->OnAddChannelResponse(true, ""); | 254 ChannelState unused ALLOW_UNUSED = |
| 255 event_interface_->OnAddChannelResponse(true, ""); |
| 256 // |this| has been deleted. |
| 239 } | 257 } |
| 240 | 258 |
| 241 void WebSocketChannel::WriteFrames() { | 259 ChannelState WebSocketChannel::WriteFrames() { |
| 242 int result = OK; | 260 int result = OK; |
| 243 do { | 261 do { |
| 244 // This use of base::Unretained is safe because this object owns the | 262 // This use of base::Unretained is safe because this object owns the |
| 245 // WebSocketStream and destroying it cancels all callbacks. | 263 // WebSocketStream and destroying it cancels all callbacks. |
| 246 result = stream_->WriteFrames( | 264 result = stream_->WriteFrames( |
| 247 data_being_sent_->frames(), | 265 data_being_sent_->frames(), |
| 248 base::Bind( | 266 base::Bind(base::IgnoreResult(&WebSocketChannel::OnWriteDone), |
| 249 &WebSocketChannel::OnWriteDone, base::Unretained(this), false)); | 267 base::Unretained(this), |
| 268 false)); |
| 250 if (result != ERR_IO_PENDING) { | 269 if (result != ERR_IO_PENDING) { |
| 251 OnWriteDone(true, result); | 270 if (OnWriteDone(true, result) == CHANNEL_DELETED) |
| 271 return CHANNEL_DELETED; |
| 252 } | 272 } |
| 253 } while (result == OK && data_being_sent_); | 273 } while (result == OK && data_being_sent_); |
| 274 return CHANNEL_ALIVE; |
| 254 } | 275 } |
| 255 | 276 |
| 256 void WebSocketChannel::OnWriteDone(bool synchronous, int result) { | 277 ChannelState WebSocketChannel::OnWriteDone(bool synchronous, int result) { |
| 257 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | 278 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); |
| 258 DCHECK_NE(CONNECTING, state_); | 279 DCHECK_NE(CONNECTING, state_); |
| 259 DCHECK_NE(ERR_IO_PENDING, result); | 280 DCHECK_NE(ERR_IO_PENDING, result); |
| 260 DCHECK(data_being_sent_); | 281 DCHECK(data_being_sent_); |
| 261 switch (result) { | 282 switch (result) { |
| 262 case OK: | 283 case OK: |
| 263 if (data_to_send_next_) { | 284 if (data_to_send_next_) { |
| 264 data_being_sent_ = data_to_send_next_.Pass(); | 285 data_being_sent_ = data_to_send_next_.Pass(); |
| 265 if (!synchronous) { | 286 if (!synchronous) { |
| 266 WriteFrames(); | 287 if (WriteFrames() == CHANNEL_DELETED) |
| 288 return CHANNEL_DELETED; |
| 267 } | 289 } |
| 268 } else { | 290 } else { |
| 269 data_being_sent_.reset(); | 291 data_being_sent_.reset(); |
| 270 if (current_send_quota_ < send_quota_low_water_mark_) { | 292 if (current_send_quota_ < send_quota_low_water_mark_) { |
| 271 // TODO(ricea): Increase low_water_mark and high_water_mark if | 293 // TODO(ricea): Increase low_water_mark and high_water_mark if |
| 272 // throughput is high, reduce them if throughput is low. Low water | 294 // throughput is high, reduce them if throughput is low. Low water |
| 273 // mark needs to be >= the bandwidth delay product *of the IPC | 295 // mark needs to be >= the bandwidth delay product *of the IPC |
| 274 // channel*. Because factors like context-switch time, thread wake-up | 296 // channel*. Because factors like context-switch time, thread wake-up |
| 275 // time, and bus speed come into play it is complex and probably needs | 297 // time, and bus speed come into play it is complex and probably needs |
| 276 // to be determined empirically. | 298 // to be determined empirically. |
| 277 DCHECK_LE(send_quota_low_water_mark_, send_quota_high_water_mark_); | 299 DCHECK_LE(send_quota_low_water_mark_, send_quota_high_water_mark_); |
| 278 // TODO(ricea): Truncate quota by the quota specified by the remote | 300 // TODO(ricea): Truncate quota by the quota specified by the remote |
| 279 // server, if the protocol in use supports quota. | 301 // server, if the protocol in use supports quota. |
| 280 int fresh_quota = send_quota_high_water_mark_ - current_send_quota_; | 302 int fresh_quota = send_quota_high_water_mark_ - current_send_quota_; |
| 281 current_send_quota_ += fresh_quota; | 303 current_send_quota_ += fresh_quota; |
| 282 event_interface_->OnFlowControl(fresh_quota); | 304 if (event_interface_->OnFlowControl(fresh_quota) == CHANNEL_DELETED) |
| 305 return CHANNEL_DELETED; |
| 283 } | 306 } |
| 284 } | 307 } |
| 285 return; | 308 return CHANNEL_ALIVE; |
| 286 | 309 |
| 287 // If a recoverable error condition existed, it would go here. | 310 // If a recoverable error condition existed, it would go here. |
| 288 | 311 |
| 289 default: | 312 default: |
| 290 DCHECK_LT(result, 0) | 313 DCHECK_LT(result, 0) |
| 291 << "WriteFrames() should only return OK or ERR_ codes"; | 314 << "WriteFrames() should only return OK or ERR_ codes"; |
| 292 stream_->Close(); | 315 stream_->Close(); |
| 293 if (state_ != CLOSED) { | 316 DCHECK_NE(CLOSED, state_); |
| 294 state_ = CLOSED; | 317 state_ = CLOSED; |
| 295 event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure, | 318 return event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure, |
| 296 "Abnormal Closure"); | 319 "Abnormal Closure"); |
| 297 } | |
| 298 return; | |
| 299 } | 320 } |
| 300 } | 321 } |
| 301 | 322 |
| 302 void WebSocketChannel::ReadFrames() { | 323 ChannelState WebSocketChannel::ReadFrames() { |
| 303 int result = OK; | 324 int result = OK; |
| 304 do { | 325 do { |
| 305 // This use of base::Unretained is safe because this object owns the | 326 // This use of base::Unretained is safe because this object owns the |
| 306 // WebSocketStream, and any pending reads will be cancelled when it is | 327 // WebSocketStream, and any pending reads will be cancelled when it is |
| 307 // destroyed. | 328 // destroyed. |
| 308 result = stream_->ReadFrames( | 329 result = stream_->ReadFrames( |
| 309 &read_frames_, | 330 &read_frames_, |
| 310 base::Bind( | 331 base::Bind(base::IgnoreResult(&WebSocketChannel::OnReadDone), |
| 311 &WebSocketChannel::OnReadDone, base::Unretained(this), false)); | 332 base::Unretained(this), |
| 333 false)); |
| 312 if (result != ERR_IO_PENDING) { | 334 if (result != ERR_IO_PENDING) { |
| 313 OnReadDone(true, result); | 335 if (OnReadDone(true, result) == CHANNEL_DELETED) |
| 336 return CHANNEL_DELETED; |
| 314 } | 337 } |
| 315 } while (result == OK && state_ != CLOSED); | 338 DCHECK_NE(CLOSED, state_); |
| 339 } while (result == OK); |
| 340 return CHANNEL_ALIVE; |
| 316 } | 341 } |
| 317 | 342 |
| 318 void WebSocketChannel::OnReadDone(bool synchronous, int result) { | 343 ChannelState WebSocketChannel::OnReadDone(bool synchronous, int result) { |
| 319 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | 344 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); |
| 320 DCHECK_NE(CONNECTING, state_); | 345 DCHECK_NE(CONNECTING, state_); |
| 321 DCHECK_NE(ERR_IO_PENDING, result); | 346 DCHECK_NE(ERR_IO_PENDING, result); |
| 322 switch (result) { | 347 switch (result) { |
| 323 case OK: | 348 case OK: |
| 324 // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection | 349 // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection |
| 325 // with no data read, not an empty response. | 350 // with no data read, not an empty response. |
| 326 DCHECK(!read_frames_.empty()) | 351 DCHECK(!read_frames_.empty()) |
| 327 << "ReadFrames() returned OK, but nothing was read."; | 352 << "ReadFrames() returned OK, but nothing was read."; |
| 328 for (size_t i = 0; i < read_frames_.size(); ++i) { | 353 for (size_t i = 0; i < read_frames_.size(); ++i) { |
| 329 scoped_ptr<WebSocketFrame> frame(read_frames_[i]); | 354 scoped_ptr<WebSocketFrame> frame(read_frames_[i]); |
| 330 read_frames_[i] = NULL; | 355 read_frames_[i] = NULL; |
| 331 ProcessFrame(frame.Pass()); | 356 if (ProcessFrame(frame.Pass()) == CHANNEL_DELETED) |
| 357 return CHANNEL_DELETED; |
| 332 } | 358 } |
| 333 read_frames_.clear(); | 359 read_frames_.clear(); |
| 334 // There should always be a call to ReadFrames pending. | 360 // There should always be a call to ReadFrames pending. |
| 335 // TODO(ricea): Unless we are out of quota. | 361 // TODO(ricea): Unless we are out of quota. |
| 336 if (!synchronous && state_ != CLOSED) { | 362 DCHECK_NE(CLOSED, state_); |
| 337 ReadFrames(); | 363 if (!synchronous) { |
| 364 if (ReadFrames() == CHANNEL_DELETED) |
| 365 return CHANNEL_DELETED; |
| 338 } | 366 } |
| 339 return; | 367 return CHANNEL_ALIVE; |
| 340 | 368 |
| 341 case ERR_WS_PROTOCOL_ERROR: | 369 case ERR_WS_PROTOCOL_ERROR: |
| 342 FailChannel(SEND_REAL_ERROR, | 370 return FailChannel(SEND_REAL_ERROR, |
| 343 kWebSocketErrorProtocolError, | 371 kWebSocketErrorProtocolError, |
| 344 "WebSocket Protocol Error"); | 372 "WebSocket Protocol Error"); |
| 345 return; | |
| 346 | 373 |
| 347 default: | 374 default: |
| 348 DCHECK_LT(result, 0) | 375 DCHECK_LT(result, 0) |
| 349 << "ReadFrames() should only return OK or ERR_ codes"; | 376 << "ReadFrames() should only return OK or ERR_ codes"; |
| 350 stream_->Close(); | 377 stream_->Close(); |
| 351 if (state_ != CLOSED) { | 378 DCHECK_NE(CLOSED, state_); |
| 352 state_ = CLOSED; | 379 state_ = CLOSED; |
| 353 uint16 code = kWebSocketErrorAbnormalClosure; | 380 uint16 code = kWebSocketErrorAbnormalClosure; |
| 354 std::string reason = "Abnormal Closure"; | 381 std::string reason = "Abnormal Closure"; |
| 355 if (closing_code_ != 0) { | 382 if (closing_code_ != 0) { |
| 356 code = closing_code_; | 383 code = closing_code_; |
| 357 reason = closing_reason_; | 384 reason = closing_reason_; |
| 358 } | |
| 359 event_interface_->OnDropChannel(code, reason); | |
| 360 } | 385 } |
| 361 return; | 386 return event_interface_->OnDropChannel(code, reason); |
| 362 } | 387 } |
| 363 } | 388 } |
| 364 | 389 |
| 365 void WebSocketChannel::ProcessFrame(scoped_ptr<WebSocketFrame> frame) { | 390 ChannelState WebSocketChannel::ProcessFrame(scoped_ptr<WebSocketFrame> frame) { |
| 366 if (frame->header.masked) { | 391 if (frame->header.masked) { |
| 367 // RFC6455 Section 5.1 "A client MUST close a connection if it detects a | 392 // RFC6455 Section 5.1 "A client MUST close a connection if it detects a |
| 368 // masked frame." | 393 // masked frame." |
| 369 FailChannel(SEND_REAL_ERROR, | 394 return FailChannel(SEND_REAL_ERROR, |
| 370 kWebSocketErrorProtocolError, | 395 kWebSocketErrorProtocolError, |
| 371 "Masked frame from server"); | 396 "Masked frame from server"); |
| 372 return; | |
| 373 } | 397 } |
| 374 const WebSocketFrameHeader::OpCode opcode = frame->header.opcode; | 398 const WebSocketFrameHeader::OpCode opcode = frame->header.opcode; |
| 375 if (WebSocketFrameHeader::IsKnownControlOpCode(opcode) && | 399 if (WebSocketFrameHeader::IsKnownControlOpCode(opcode) && |
| 376 !frame->header.final) { | 400 !frame->header.final) { |
| 377 FailChannel(SEND_REAL_ERROR, | 401 return FailChannel(SEND_REAL_ERROR, |
| 378 kWebSocketErrorProtocolError, | 402 kWebSocketErrorProtocolError, |
| 379 "Control message with FIN bit unset received"); | 403 "Control message with FIN bit unset received"); |
| 380 return; | |
| 381 } | 404 } |
| 382 | 405 |
| 383 // Respond to the frame appropriately to its type. | 406 // Respond to the frame appropriately to its type. |
| 384 HandleFrame( | 407 return HandleFrame( |
| 385 opcode, frame->header.final, frame->data, frame->header.payload_length); | 408 opcode, frame->header.final, frame->data, frame->header.payload_length); |
| 386 } | 409 } |
| 387 | 410 |
| 388 void WebSocketChannel::HandleFrame(const WebSocketFrameHeader::OpCode opcode, | 411 ChannelState WebSocketChannel::HandleFrame( |
| 389 bool final, | 412 const WebSocketFrameHeader::OpCode opcode, |
| 390 const scoped_refptr<IOBuffer>& data_buffer, | 413 bool final, |
| 391 size_t size) { | 414 const scoped_refptr<IOBuffer>& data_buffer, |
| 415 size_t size) { |
| 392 DCHECK_NE(RECV_CLOSED, state_) | 416 DCHECK_NE(RECV_CLOSED, state_) |
| 393 << "HandleFrame() does not support being called re-entrantly from within " | 417 << "HandleFrame() does not support being called re-entrantly from within " |
| 394 "SendClose()"; | 418 "SendClose()"; |
| 395 if (state_ == CLOSED || state_ == CLOSE_WAIT) { | 419 DCHECK_NE(CLOSED, state_); |
| 396 DVLOG_IF(1, state_ == CLOSED) << "A frame was received while in the CLOSED " | 420 if (state_ == CLOSE_WAIT) { |
| 397 "state. This is possible after a channel " | |
| 398 "failed, but should be very rare."; | |
| 399 std::string frame_name; | 421 std::string frame_name; |
| 400 switch (opcode) { | 422 switch (opcode) { |
| 401 case WebSocketFrameHeader::kOpCodeText: // fall-thru | 423 case WebSocketFrameHeader::kOpCodeText: // fall-thru |
| 402 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru | 424 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru |
| 403 case WebSocketFrameHeader::kOpCodeContinuation: | 425 case WebSocketFrameHeader::kOpCodeContinuation: |
| 404 frame_name = "Data frame"; | 426 frame_name = "Data frame"; |
| 405 break; | 427 break; |
| 406 | 428 |
| 407 case WebSocketFrameHeader::kOpCodePing: | 429 case WebSocketFrameHeader::kOpCodePing: |
| 408 frame_name = "Ping"; | 430 frame_name = "Ping"; |
| 409 break; | 431 break; |
| 410 | 432 |
| 411 case WebSocketFrameHeader::kOpCodePong: | 433 case WebSocketFrameHeader::kOpCodePong: |
| 412 frame_name = "Pong"; | 434 frame_name = "Pong"; |
| 413 break; | 435 break; |
| 414 | 436 |
| 415 case WebSocketFrameHeader::kOpCodeClose: | 437 case WebSocketFrameHeader::kOpCodeClose: |
| 416 frame_name = "Close"; | 438 frame_name = "Close"; |
| 417 break; | 439 break; |
| 418 | 440 |
| 419 default: | 441 default: |
| 420 frame_name = "Unknown frame type"; | 442 frame_name = "Unknown frame type"; |
| 421 break; | 443 break; |
| 422 } | 444 } |
| 423 // SEND_REAL_ERROR makes no difference here, as FailChannel() won't send | 445 // SEND_REAL_ERROR makes no difference here, as FailChannel() won't send |
| 424 // another Close frame. | 446 // another Close frame. |
| 425 FailChannel(SEND_REAL_ERROR, | 447 return FailChannel(SEND_REAL_ERROR, |
| 426 kWebSocketErrorProtocolError, | 448 kWebSocketErrorProtocolError, |
| 427 frame_name + " received after close"); | 449 frame_name + " received after close"); |
| 428 return; | |
| 429 } | 450 } |
| 430 switch (opcode) { | 451 switch (opcode) { |
| 431 case WebSocketFrameHeader::kOpCodeText: // fall-thru | 452 case WebSocketFrameHeader::kOpCodeText: // fall-thru |
| 432 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru | 453 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru |
| 433 case WebSocketFrameHeader::kOpCodeContinuation: | 454 case WebSocketFrameHeader::kOpCodeContinuation: |
| 434 if (state_ == CONNECTED) { | 455 if (state_ == CONNECTED) { |
| 435 // TODO(ricea): Need to fail the connection if UTF-8 is invalid | 456 // TODO(ricea): Need to fail the connection if UTF-8 is invalid |
| 436 // post-reassembly. Requires a streaming UTF-8 validator. | 457 // post-reassembly. Requires a streaming UTF-8 validator. |
| 437 // TODO(ricea): Can this copy be eliminated? | 458 // TODO(ricea): Can this copy be eliminated? |
| 438 const char* const data_begin = data_buffer->data(); | 459 const char* const data_begin = data_buffer->data(); |
| 439 const char* const data_end = data_begin + size; | 460 const char* const data_end = data_begin + size; |
| 440 const std::vector<char> data(data_begin, data_end); | 461 const std::vector<char> data(data_begin, data_end); |
| 441 // TODO(ricea): Handle the case when ReadFrames returns far | 462 // TODO(ricea): Handle the case when ReadFrames returns far |
| 442 // more data at once than should be sent in a single IPC. This needs to | 463 // more data at once than should be sent in a single IPC. This needs to |
| 443 // be handled carefully, as an overloaded IO thread is one possible | 464 // be handled carefully, as an overloaded IO thread is one possible |
| 444 // cause of receiving very large chunks. | 465 // cause of receiving very large chunks. |
| 445 | 466 |
| 446 // Sends the received frame to the renderer process. | 467 // Sends the received frame to the renderer process. |
| 447 event_interface_->OnDataFrame(final, opcode, data); | 468 if (event_interface_->OnDataFrame(final, opcode, data) == |
| 469 CHANNEL_DELETED) |
| 470 return CHANNEL_DELETED; |
| 448 } else { | 471 } else { |
| 449 VLOG(3) << "Ignored data packet received in state " << state_; | 472 VLOG(3) << "Ignored data packet received in state " << state_; |
| 450 } | 473 } |
| 451 return; | 474 return CHANNEL_ALIVE; |
| 452 | 475 |
| 453 case WebSocketFrameHeader::kOpCodePing: | 476 case WebSocketFrameHeader::kOpCodePing: |
| 454 VLOG(1) << "Got Ping of size " << size; | 477 VLOG(1) << "Got Ping of size " << size; |
| 455 if (state_ == CONNECTED) { | 478 if (state_ == CONNECTED) { |
| 456 SendIOBuffer( | 479 if (SendIOBuffer( |
| 457 true, WebSocketFrameHeader::kOpCodePong, data_buffer, size); | 480 true, WebSocketFrameHeader::kOpCodePong, data_buffer, size) == |
| 481 CHANNEL_DELETED) |
| 482 return CHANNEL_DELETED; |
| 458 } else { | 483 } else { |
| 459 VLOG(3) << "Ignored ping in state " << state_; | 484 VLOG(3) << "Ignored ping in state " << state_; |
| 460 } | 485 } |
| 461 return; | 486 return CHANNEL_ALIVE; |
| 462 | 487 |
| 463 case WebSocketFrameHeader::kOpCodePong: | 488 case WebSocketFrameHeader::kOpCodePong: |
| 464 VLOG(1) << "Got Pong of size " << size; | 489 VLOG(1) << "Got Pong of size " << size; |
| 465 // There is no need to do anything with pong messages. | 490 // There is no need to do anything with pong messages. |
| 466 return; | 491 return CHANNEL_ALIVE; |
| 467 | 492 |
| 468 case WebSocketFrameHeader::kOpCodeClose: { | 493 case WebSocketFrameHeader::kOpCodeClose: { |
| 469 uint16 code = kWebSocketNormalClosure; | 494 uint16 code = kWebSocketNormalClosure; |
| 470 std::string reason; | 495 std::string reason; |
| 471 ParseClose(data_buffer, size, &code, &reason); | 496 ParseClose(data_buffer, size, &code, &reason); |
| 472 // TODO(ricea): Find a way to safely log the message from the close | 497 // TODO(ricea): Find a way to safely log the message from the close |
| 473 // message (escape control codes and so on). | 498 // message (escape control codes and so on). |
| 474 VLOG(1) << "Got Close with code " << code; | 499 VLOG(1) << "Got Close with code " << code; |
| 475 switch (state_) { | 500 switch (state_) { |
| 476 case CONNECTED: | 501 case CONNECTED: |
| 477 state_ = RECV_CLOSED; | 502 state_ = RECV_CLOSED; |
| 478 SendClose(code, reason); // Sets state_ to CLOSE_WAIT | 503 if (SendClose(code, reason) // Sets state_ to CLOSE_WAIT |
| 479 event_interface_->OnClosingHandshake(); | 504 == CHANNEL_DELETED) |
| 505 return CHANNEL_DELETED; |
| 506 if (event_interface_->OnClosingHandshake() == CHANNEL_DELETED) |
| 507 return CHANNEL_DELETED; |
| 480 closing_code_ = code; | 508 closing_code_ = code; |
| 481 closing_reason_ = reason; | 509 closing_reason_ = reason; |
| 482 break; | 510 break; |
| 483 | 511 |
| 484 case SEND_CLOSED: | 512 case SEND_CLOSED: |
| 485 state_ = CLOSE_WAIT; | 513 state_ = CLOSE_WAIT; |
| 486 // From RFC6455 section 7.1.5: "Each endpoint | 514 // From RFC6455 section 7.1.5: "Each endpoint |
| 487 // will see the status code sent by the other end as _The WebSocket | 515 // will see the status code sent by the other end as _The WebSocket |
| 488 // Connection Close Code_." | 516 // Connection Close Code_." |
| 489 closing_code_ = code; | 517 closing_code_ = code; |
| 490 closing_reason_ = reason; | 518 closing_reason_ = reason; |
| 491 break; | 519 break; |
| 492 | 520 |
| 493 default: | 521 default: |
| 494 LOG(DFATAL) << "Got Close in unexpected state " << state_; | 522 LOG(DFATAL) << "Got Close in unexpected state " << state_; |
| 495 break; | 523 break; |
| 496 } | 524 } |
| 497 return; | 525 return CHANNEL_ALIVE; |
| 498 } | 526 } |
| 499 | 527 |
| 500 default: | 528 default: |
| 501 FailChannel( | 529 return FailChannel( |
| 502 SEND_REAL_ERROR, kWebSocketErrorProtocolError, "Unknown opcode"); | 530 SEND_REAL_ERROR, kWebSocketErrorProtocolError, "Unknown opcode"); |
| 503 return; | |
| 504 } | 531 } |
| 505 } | 532 } |
| 506 | 533 |
| 507 void WebSocketChannel::SendIOBuffer(bool fin, | 534 ChannelState WebSocketChannel::SendIOBuffer( |
| 508 WebSocketFrameHeader::OpCode op_code, | 535 bool fin, |
| 509 const scoped_refptr<IOBuffer>& buffer, | 536 WebSocketFrameHeader::OpCode op_code, |
| 510 size_t size) { | 537 const scoped_refptr<IOBuffer>& buffer, |
| 538 size_t size) { |
| 511 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); | 539 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); |
| 512 DCHECK(stream_); | 540 DCHECK(stream_); |
| 513 scoped_ptr<WebSocketFrame> frame(new WebSocketFrame(op_code)); | 541 scoped_ptr<WebSocketFrame> frame(new WebSocketFrame(op_code)); |
| 514 WebSocketFrameHeader& header = frame->header; | 542 WebSocketFrameHeader& header = frame->header; |
| 515 header.final = fin; | 543 header.final = fin; |
| 516 header.masked = true; | 544 header.masked = true; |
| 517 header.payload_length = size; | 545 header.payload_length = size; |
| 518 frame->data = buffer; | 546 frame->data = buffer; |
| 519 if (data_being_sent_) { | 547 if (data_being_sent_) { |
| 520 // Either the link to the WebSocket server is saturated, or several messages | 548 // Either the link to the WebSocket server is saturated, or several messages |
| 521 // are being sent in a batch. | 549 // are being sent in a batch. |
| 522 // TODO(ricea): Keep some statistics to work out the situation and adjust | 550 // TODO(ricea): Keep some statistics to work out the situation and adjust |
| 523 // quota appropriately. | 551 // quota appropriately. |
| 524 if (!data_to_send_next_) | 552 if (!data_to_send_next_) |
| 525 data_to_send_next_.reset(new SendBuffer); | 553 data_to_send_next_.reset(new SendBuffer); |
| 526 data_to_send_next_->AddFrame(frame.Pass()); | 554 data_to_send_next_->AddFrame(frame.Pass()); |
| 527 } else { | 555 } else { |
| 528 data_being_sent_.reset(new SendBuffer); | 556 data_being_sent_.reset(new SendBuffer); |
| 529 data_being_sent_->AddFrame(frame.Pass()); | 557 data_being_sent_->AddFrame(frame.Pass()); |
| 530 WriteFrames(); | 558 if (WriteFrames() == CHANNEL_DELETED) |
| 559 return CHANNEL_DELETED; |
| 531 } | 560 } |
| 561 return CHANNEL_ALIVE; |
| 532 } | 562 } |
| 533 | 563 |
| 534 void WebSocketChannel::FailChannel(ExposeError expose, | 564 ChannelState WebSocketChannel::FailChannel(ExposeError expose, |
| 535 uint16 code, | 565 uint16 code, |
| 536 const std::string& reason) { | 566 const std::string& reason) { |
| 537 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | 567 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); |
| 538 DCHECK_NE(CONNECTING, state_); | 568 DCHECK_NE(CONNECTING, state_); |
| 569 DCHECK_NE(CLOSED, state_); |
| 539 // TODO(ricea): Logging. | 570 // TODO(ricea): Logging. |
| 540 State old_state = state_; | |
| 541 if (state_ == CONNECTED) { | 571 if (state_ == CONNECTED) { |
| 542 uint16 send_code = kWebSocketErrorGoingAway; | 572 uint16 send_code = kWebSocketErrorGoingAway; |
| 543 std::string send_reason = "Internal Error"; | 573 std::string send_reason = "Internal Error"; |
| 544 if (expose == SEND_REAL_ERROR) { | 574 if (expose == SEND_REAL_ERROR) { |
| 545 send_code = code; | 575 send_code = code; |
| 546 send_reason = reason; | 576 send_reason = reason; |
| 547 } | 577 } |
| 548 SendClose(send_code, send_reason); // Sets state_ to SEND_CLOSED | 578 if (SendClose(send_code, send_reason) // Sets state_ to SEND_CLOSED |
| 579 == CHANNEL_DELETED) |
| 580 return CHANNEL_DELETED; |
| 549 } | 581 } |
| 550 // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates the browser | 582 // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates the browser |
| 551 // should close the connection itself without waiting for the closing | 583 // should close the connection itself without waiting for the closing |
| 552 // handshake. | 584 // handshake. |
| 553 stream_->Close(); | 585 stream_->Close(); |
| 554 state_ = CLOSED; | 586 state_ = CLOSED; |
| 555 | 587 |
| 556 if (old_state != CLOSED) { | 588 return event_interface_->OnDropChannel(code, reason); |
| 557 event_interface_->OnDropChannel(code, reason); | |
| 558 } | |
| 559 } | 589 } |
| 560 | 590 |
| 561 void WebSocketChannel::SendClose(uint16 code, const std::string& reason) { | 591 ChannelState WebSocketChannel::SendClose(uint16 code, |
| 592 const std::string& reason) { |
| 562 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); | 593 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); |
| 563 // TODO(ricea): Ensure reason.length() <= 123 | 594 // TODO(ricea): Ensure reason.length() <= 123 |
| 564 scoped_refptr<IOBuffer> body; | 595 scoped_refptr<IOBuffer> body; |
| 565 size_t size = 0; | 596 size_t size = 0; |
| 566 if (code == kWebSocketErrorNoStatusReceived) { | 597 if (code == kWebSocketErrorNoStatusReceived) { |
| 567 // Special case: translate kWebSocketErrorNoStatusReceived into a Close | 598 // Special case: translate kWebSocketErrorNoStatusReceived into a Close |
| 568 // frame with no payload. | 599 // frame with no payload. |
| 569 body = new IOBuffer(0); | 600 body = new IOBuffer(0); |
| 570 } else { | 601 } else { |
| 571 const size_t payload_length = kWebSocketCloseCodeLength + reason.length(); | 602 const size_t payload_length = kWebSocketCloseCodeLength + reason.length(); |
| 572 body = new IOBuffer(payload_length); | 603 body = new IOBuffer(payload_length); |
| 573 size = payload_length; | 604 size = payload_length; |
| 574 WriteBigEndian(body->data(), code); | 605 WriteBigEndian(body->data(), code); |
| 575 COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength, | 606 COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength, |
| 576 they_should_both_be_two); | 607 they_should_both_be_two); |
| 577 std::copy( | 608 std::copy( |
| 578 reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength); | 609 reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength); |
| 579 } | 610 } |
| 580 SendIOBuffer(true, WebSocketFrameHeader::kOpCodeClose, body, size); | 611 if (SendIOBuffer(true, WebSocketFrameHeader::kOpCodeClose, body, size) == |
| 612 CHANNEL_DELETED) |
| 613 return CHANNEL_DELETED; |
| 581 state_ = (state_ == CONNECTED) ? SEND_CLOSED : CLOSE_WAIT; | 614 state_ = (state_ == CONNECTED) ? SEND_CLOSED : CLOSE_WAIT; |
| 615 return CHANNEL_ALIVE; |
| 582 } | 616 } |
| 583 | 617 |
| 584 void WebSocketChannel::ParseClose(const scoped_refptr<IOBuffer>& buffer, | 618 void WebSocketChannel::ParseClose(const scoped_refptr<IOBuffer>& buffer, |
| 585 size_t size, | 619 size_t size, |
| 586 uint16* code, | 620 uint16* code, |
| 587 std::string* reason) { | 621 std::string* reason) { |
| 588 const char* data = buffer->data(); | 622 const char* data = buffer->data(); |
| 589 reason->clear(); | 623 reason->clear(); |
| 590 if (size < kWebSocketCloseCodeLength) { | 624 if (size < kWebSocketCloseCodeLength) { |
| 591 *code = kWebSocketErrorNoStatusReceived; | 625 *code = kWebSocketErrorNoStatusReceived; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 604 if (unchecked_code >= static_cast<uint16>(kWebSocketNormalClosure) && | 638 if (unchecked_code >= static_cast<uint16>(kWebSocketNormalClosure) && |
| 605 unchecked_code <= | 639 unchecked_code <= |
| 606 static_cast<uint16>(kWebSocketErrorPrivateReservedMax)) { | 640 static_cast<uint16>(kWebSocketErrorPrivateReservedMax)) { |
| 607 *code = unchecked_code; | 641 *code = unchecked_code; |
| 608 } else { | 642 } else { |
| 609 VLOG(1) << "Close frame contained code outside of the valid range: " | 643 VLOG(1) << "Close frame contained code outside of the valid range: " |
| 610 << unchecked_code; | 644 << unchecked_code; |
| 611 *code = kWebSocketErrorAbnormalClosure; | 645 *code = kWebSocketErrorAbnormalClosure; |
| 612 } | 646 } |
| 613 std::string text(data + kWebSocketCloseCodeLength, data + size); | 647 std::string text(data + kWebSocketCloseCodeLength, data + size); |
| 614 // TODO(ricea): Is this check strict enough? In particular, check the | 648 // IsStringUTF8() blocks surrogate pairs and non-characters, so it is strictly |
| 615 // "Security Considerations" from RFC3629. | 649 // stronger than required by RFC3629. |
| 616 if (IsStringUTF8(text)) { | 650 if (IsStringUTF8(text)) { |
| 617 reason->swap(text); | 651 reason->swap(text); |
| 618 } | 652 } |
| 619 } | 653 } |
| 620 | 654 |
| 621 } // namespace net | 655 } // namespace net |
| OLD | NEW |