| // Copyright 2018 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/websocket/cobalt_web_socket_event_handler.h" |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "cobalt/websocket/web_socket_impl.h" |
| |
| namespace cobalt { |
| namespace websocket { |
| namespace { |
| typedef std::vector<std::pair<scoped_refptr<net::IOBuffer>, size_t>> |
| FrameDataVector; |
| std::size_t GetMessageLength(const FrameDataVector& frame_data) { |
| std::size_t total_length = 0; |
| for (const auto& i : frame_data) { |
| total_length += i.second; |
| } |
| return total_length; |
| } |
| std::size_t CombineFramesChunks(FrameDataVector::const_iterator begin, |
| FrameDataVector::const_iterator end, |
| char* out_destination, |
| std::size_t buffer_length) { |
| DCHECK(out_destination); |
| std::size_t bytes_written = 0; |
| std::size_t bytes_available = buffer_length; |
| for (FrameDataVector::const_iterator iterator = begin; iterator != end; |
| ++iterator) { |
| const scoped_refptr<net::IOBuffer>& data = iterator->first; |
| |
| std::size_t frame_chunk_size = iterator->second; |
| |
| if (bytes_available >= frame_chunk_size) { |
| memcpy(out_destination, data->data(), frame_chunk_size); |
| out_destination += frame_chunk_size; |
| bytes_written += frame_chunk_size; |
| bytes_available -= frame_chunk_size; |
| } |
| } |
| |
| DCHECK_EQ(bytes_written, buffer_length); |
| return bytes_written; |
| } |
| } // namespace |
| |
| void CobaltWebSocketEventHandler::OnAddChannelResponse( |
| const std::string& selected_subprotocol, const std::string& extensions) { |
| creator_->OnHandshakeComplete(selected_subprotocol); |
| } |
| void CobaltWebSocketEventHandler::OnDataFrame( |
| bool fin, WebSocketMessageType type, scoped_refptr<net::IOBuffer> buffer, |
| size_t buffer_size) { |
| if (message_type_ == net::WebSocketFrameHeader::kOpCodeControlUnused) { |
| message_type_ = type; |
| } |
| if (type != net::WebSocketFrameHeader::kOpCodeContinuation) { |
| DCHECK_EQ(message_type_, type); |
| } |
| frame_data_.push_back(std::make_pair(std::move(buffer), buffer_size)); |
| if (fin) { |
| std::size_t message_length = GetMessageLength(frame_data_); |
| scoped_refptr<net::IOBufferWithSize> buf = |
| base::WrapRefCounted(new net::IOBufferWithSize(message_length)); |
| CombineFramesChunks(frame_data_.begin(), frame_data_.end(), buf->data(), |
| message_length); |
| frame_data_.clear(); |
| |
| bool is_text_message = |
| message_type_ == net::WebSocketFrameHeader::kOpCodeText; |
| if (is_text_message && buf && (buf->size() > 0)) { |
| base::StringPiece payload_string_piece(buf->data(), buf->size()); |
| if (!base::IsStringUTF8(payload_string_piece)) { |
| WebSocketImpl::CloseInfo close_info(net::kWebSocketErrorProtocolError); |
| creator_->TrampolineClose(close_info); |
| return; |
| } |
| } |
| creator_->OnWebSocketReceivedData(is_text_message, std::move(buf)); |
| } |
| } |
| |
| void CobaltWebSocketEventHandler::OnClosingHandshake() { |
| creator_->OnClose(true, net::kWebSocketNormalClosure, |
| "Received close handshake initiation."); |
| } |
| |
| void CobaltWebSocketEventHandler::OnFailChannel(const std::string& message) { |
| DLOG(WARNING) << "WebSocket channel failed due to: " << message; |
| creator_->OnClose(true, net::kWebSocketErrorAbnormalClosure, message); |
| } |
| |
| void CobaltWebSocketEventHandler::OnDropChannel(bool was_clean, uint16_t code, |
| const std::string& reason) { |
| creator_->OnClose(was_clean, code, reason); |
| } |
| |
| void CobaltWebSocketEventHandler::OnSSLCertificateError( |
| std::unique_ptr<net::WebSocketEventInterface::SSLErrorCallbacks> |
| ssl_error_callbacks, |
| const GURL& url, const net::SSLInfo& ssl_info, bool fatal) { |
| // TODO: determine if there are circumstances we want to continue |
| // the request. |
| DLOG(WARNING) << "SSL cert failure occured, cancelling connection"; |
| ssl_error_callbacks->CancelSSLRequest(net::ERR_BAD_SSL_CLIENT_AUTH_CERT, |
| nullptr); |
| } |
| int CobaltWebSocketEventHandler::OnAuthRequired( |
| scoped_refptr<net::AuthChallengeInfo> auth_info, |
| scoped_refptr<net::HttpResponseHeaders> response_headers, |
| const net::HostPortPair& host_port_pair, |
| base::OnceCallback<void(const net::AuthCredentials*)> callback, |
| base::Optional<net::AuthCredentials>* credentials) { |
| NOTIMPLEMENTED(); |
| return net::OK; |
| } |
| |
| void CobaltWebSocketEventHandler::OnWriteDone(uint64_t bytes_written) { |
| creator_->OnWriteDone(bytes_written); |
| } |
| |
| void CobaltWebSocketEventHandler::OnFlowControl(int64_t quota) { |
| creator_->OnFlowControl(quota); |
| } |
| |
| } // namespace websocket |
| } // namespace cobalt |