1 //////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2004-2021 musikcube team
4 //
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions are met:
9 //
10 //    * Redistributions of source code must retain the above copyright notice,
11 //      this list of conditions and the following disclaimer.
12 //
13 //    * Redistributions in binary form must reproduce the above copyright
14 //      notice, this list of conditions and the following disclaimer in the
15 //      documentation and/or other materials provided with the distribution.
16 //
17 //    * Neither the name of the author nor the names of other contributors may
18 //      be used to endorse or promote products derived from this software
19 //      without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
32 //
33 //////////////////////////////////////////////////////////////////////////////
34 
35 #include "pch.hpp"
36 
37 #include <musikcore/net/RawWebSocketClient.h>
38 
39 using namespace musik::core;
40 using namespace musik::core::net;
41 
createSslContext()42 static inline RawWebSocketClient::SslContext createSslContext() {
43     RawWebSocketClient::SslContext ctx =
44         std::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23);
45 
46     try {
47         ctx->set_options(boost::asio::ssl::context::default_workarounds |
48             boost::asio::ssl::context::no_sslv2 |
49             boost::asio::ssl::context::no_sslv3 |
50             boost::asio::ssl::context::single_dh_use |
51             boost::asio::ssl::context::verify_none);
52     }
53     catch (std::exception& e) {
54         std::cerr << "Error in context pointer: " << e.what() << std::endl;
55     }
56     return ctx;
57 }
58 
RawWebSocketClient(boost::asio::io_service & io)59 RawWebSocketClient::RawWebSocketClient(boost::asio::io_service& io) {
60     websocketpp::lib::error_code ec;
61 
62     plainTextClient = std::make_unique<PlainTextClient>();
63     plainTextClient->clear_access_channels(websocketpp::log::alevel::all);
64     plainTextClient->init_asio(&io, ec);
65 
66     tlsClient = std::make_unique<TlsClient>();
67     tlsClient->clear_access_channels(websocketpp::log::alevel::all);
68     tlsClient->init_asio(&io, ec);
69     tlsClient->set_tls_init_handler([](Connection connection) -> SslContext {
70         return createSslContext();
71     });
72 }
73 
~RawWebSocketClient()74 RawWebSocketClient::~RawWebSocketClient() {
75 }
76 
SetMode(Mode mode)77 void RawWebSocketClient::SetMode(Mode mode) {
78     this->mode = mode;
79 }
80 
SetOpenHandler(OpenHandler openHandler)81 void RawWebSocketClient::SetOpenHandler(OpenHandler openHandler) {
82     this->plainTextClient->set_open_handler(openHandler);
83     this->tlsClient->set_open_handler(openHandler);
84 }
85 
SetFailHandler(FailHandler failHandler)86 void RawWebSocketClient::SetFailHandler(FailHandler failHandler) {
87     this->plainTextClient->set_fail_handler(failHandler);
88     this->tlsClient->set_fail_handler(failHandler);
89 }
90 
SetMessageHandler(MessageHandler messageHandler)91 void RawWebSocketClient::SetMessageHandler(MessageHandler messageHandler) {
92     this->plainTextClient->set_message_handler(messageHandler);
93     this->tlsClient->set_message_handler(messageHandler);
94 }
95 
SetCloseHandler(CloseHandler closeHandler)96 void RawWebSocketClient::SetCloseHandler(CloseHandler closeHandler) {
97     this->plainTextClient->set_close_handler(closeHandler);
98     this->tlsClient->set_close_handler(closeHandler);
99 }
100 
SetSendMessageErrorHandler(SendMessageErrorHandler errorHandler)101 void RawWebSocketClient::SetSendMessageErrorHandler(SendMessageErrorHandler errorHandler) {
102     this->sendMessageErrorHandler = errorHandler;
103 }
104 
Send(Connection connection,const std::string & message)105 void RawWebSocketClient::Send(Connection connection, const std::string& message) {
106     std::error_code ec;
107     if (mode == Mode::PlainText) {
108         this->plainTextClient->send(connection, message, websocketpp::frame::opcode::text, ec);
109     }
110     else if (mode == Mode::TLS) {
111         this->tlsClient->send(connection, message, websocketpp::frame::opcode::text, ec);
112     }
113     if (ec && sendMessageErrorHandler) {
114         sendMessageErrorHandler(ec);
115     }
116 }
117 
SetPongTimeout(long timeoutMs)118 void RawWebSocketClient::SetPongTimeout(long timeoutMs) {
119     this->plainTextClient->set_pong_timeout(timeoutMs);
120     this->tlsClient->set_pong_timeout(timeoutMs);
121 }
122 
Connect(const std::string & uri)123 void RawWebSocketClient::Connect(const std::string& uri) {
124     websocketpp::lib::error_code ec;
125     if (mode == Mode::PlainText) {
126         PlainTextClient::connection_ptr connection = plainTextClient->get_connection(uri, ec);
127         if (!ec) {
128             plainTextClient->connect(connection);
129         }
130     }
131     else if (mode == Mode::TLS) {
132         TlsClient::connection_ptr connection = tlsClient->get_connection(uri, ec);
133         if (!ec) {
134             tlsClient->connect(connection);
135         }
136     }
137 }
138 
Run()139 void RawWebSocketClient::Run() {
140     if (mode == Mode::PlainText) {
141         plainTextClient->run();
142     }
143     else if (mode == Mode::TLS) {
144         tlsClient->run();
145     }
146 }