reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
KurentoConnection.cxx
Go to the documentation of this file.
1
2#include "cajun/json/writer.h"
3
4#include "rutil/Logger.hxx"
5#include "rutil/Subsystem.hxx"
6
9#include "Object.hxx"
10
11using namespace kurento;
12using namespace resip;
13
14#define RESIPROCATE_SUBSYSTEM kurento::KurentoSubsystem::KURENTOCLIENT
15
16//#define PING_MSG "{\"id\":\"1\",\"method\":\"ping\",\"params\":{\"interval\":240000},\"jsonrpc\":\"2.0\"}"
17
21
22KurentoConnection::KurentoConnection(KurentoConnectionObserver& observer, std::string uri, kurento::client& wSClient, std::chrono::milliseconds timeout, std::chrono::milliseconds retryInterval, bool waitForResponse)
23 : mObserver(observer),
24 mUri(uri),
25 mWSClient(wSClient),
26 mTimeout(timeout), // FIXME - we don't use mTimeout yet
27 mRetryInterval(retryInterval),
28 mWaitForResponse(waitForResponse)
29{
30
31}
32
37
38void
40{
41 InfoLog(<<"trying to open connection to Kurento");
42 websocketpp::lib::error_code ec;
43 client::connection_ptr con = mWSClient.get_connection(mUri, ec);
44 if (ec) {
45 ErrLog(<< "could not create connection because: " << ec.message());
46 resip_assert(0); // FIXME
47 }
48
49 mHandle = con->get_handle();
50
51 con->set_open_handler(websocketpp::lib::bind(
53 this,
54 &mWSClient,
55 websocketpp::lib::placeholders::_1
56 ));
57 con->set_fail_handler(websocketpp::lib::bind(
59 this,
60 &mWSClient,
61 websocketpp::lib::placeholders::_1
62 ));
63 con->set_close_handler(websocketpp::lib::bind(
65 this,
66 &mWSClient,
67 websocketpp::lib::placeholders::_1
68 ));
69 con->set_message_handler(websocketpp::lib::bind(
71 this,
72 &mWSClient,
73 websocketpp::lib::placeholders::_1,
74 websocketpp::lib::placeholders::_2
75 ));
76
77 mWSClient.connect(con);
78}
79
80void
81KurentoConnection::onOpen(client* wSClient, websocketpp::connection_hdl h)
82{
83 InfoLog(<<"onOpen");
84 mHandle = h;
85 mObserver.onConnected();
86 processSendQueue();
87}
88
89void
90KurentoConnection::onFail(client* wSClient, websocketpp::connection_hdl h)
91{
92 ErrLog(<<"connection attempt failed, will retry after " << mRetryInterval.count() << " ms");
93 wSClient->set_timer(mRetryInterval.count(), [this](websocketpp::lib::error_code ec){onRetryRequired();});
94}
95
96void
97KurentoConnection::onClose(client* wSClient, websocketpp::connection_hdl h)
98{
99 InfoLog(<<"onClose");
100 if(!mRunning)
101 {
102 return;
103 }
104 ErrLog(<<"connection closed unexpectedly, will try to reopen it");
105 wSClient->set_timer(mRetryInterval.count(), [this](websocketpp::lib::error_code ec){onRetryRequired();});
106}
107
108void
109KurentoConnection::onMessage(client* wSClient, websocketpp::connection_hdl h, client::message_ptr msg)
110{
111 if (msg->get_opcode() != websocketpp::frame::opcode::text)
112 {
113 ErrLog(<<"received unknown message type, ignoring it");
114 return;
115 }
116
117 const std::string& m = msg->get_payload();
118 DebugLog(<<"received a message: " << m.c_str());
119
120 json::Object message;
121 std::stringstream stream;
122 stream << m;
123 try
124 {
125 json::Reader::Read(message, stream);
126 }
127 catch (json::Reader::ParseException& e)
128 {
129 // lines/offsets are zero-indexed, so bump them up by one for human presentation
130 DebugLog(<<"Caught json::ParseException: " << e.what() << ", Line/offset: " << e.m_locTokenBegin.m_nLine + 1
131 << '/' << e.m_locTokenBegin.m_nLineOffset + 1);
132 //m_messages.push_back("<< " + msg->get_payload());
133 return;
134 }
135
136 if(mSessionId.length() == 0)
137 {
138 if(message.Find(JSON_RPC_RESULT) != message.End())
139 {
140 json::Object& result = message[JSON_RPC_RESULT];
141 if(result.Find(JSON_RPC_SESSION_ID) != result.End())
142 {
143 const json::String& sessionId = result[JSON_RPC_SESSION_ID];
144 mSessionId = sessionId.Value();
145 DebugLog(<<"registered new sessionId: " << mSessionId);
146 }
147 }
148 }
149
150 if(message.Find(JSON_RPC_ID) != message.End())
151 {
152 const json::String& idValue = message[JSON_RPC_ID];
153 std::string id = idValue.Value();
154 DebugLog(<<"has id = '" << id << "', response");
155
156 const KurentoResponseHandlerMap::const_iterator it = mResponseHandlers.find(id);
157
158 if(it != mResponseHandlers.end())
159 {
160 onResponse(id, it->second, message);
161 }
162 else
163 {
164 WarningLog(<<"unrecognised id = '" << id << "'");
165 }
166
167 }
168 else
169 {
170 DebugLog(<<"has no id, checking if it is a notification");
171 if(message.Find(JSON_RPC_METHOD) != message.End() && message[json::String(JSON_RPC_METHOD)] == json::String("onEvent"))
172 {
173 const json::String& eventName = message[JSON_RPC_PARAMS][JSON_RPC_VALUE][JSON_RPC_TYPE];
174 const std::string& _eventName = eventName.Value();
175 DebugLog(<<"received an event: " << _eventName);
176 // the handler is probably in a different thread
177 onEvent(_eventName, message);
178 }
179 else
180 {
181 DebugLog(<<"don't know how to handle the message: " << m);
182 }
183 }
184
185 // FIXME
186}
187
188void
189KurentoConnection::onResponse(const std::string& id, std::shared_ptr<KurentoResponseHandler> krh, const json::Object& message)
190{
191 DebugLog(<<"id: " << id);
192 unsigned long _id = std::stol(id);
193 mResponseHandlers.erase(id);
194 krh->processResponse(id, krh, message);
195 if(_id > mLastResponse)
196 {
197 mLastResponse = _id;
198 }
199 else
200 {
201 WarningLog(<<"mLastResponse = " << mLastResponse << " but received response for earlier request: " << _id);
202 }
203 mResponseReceivedCount++;
204 DebugLog(<< "mRequestSentCount = " << mRequestSentCount
205 << " mResponseReceivedCount = " << mResponseReceivedCount);
206 processSendQueue();
207}
208
209void
210KurentoConnection::onEvent(const std::string& eventName, const json::Object& message)
211{
212 DebugLog(<<"event: " << eventName);
213
214 const json::Object& values = message[JSON_RPC_PARAMS][JSON_RPC_VALUE];
215
216 const json::String& objectId = values["object"];
217 const std::string& _objectId = objectId.Value();
218
219 if(mObjects.find(_objectId) != mObjects.end())
220 {
221 std::shared_ptr<Object> object = mObjects[_objectId];
222 DebugLog(<<"event is being routed to Object " << _objectId);
223 object->onEvent(eventName, message);
224 }
225 else
226 {
227 WarningLog(<<"event is for unknown Object " << _objectId);
228 }
229
230 // FIXME - does every event have an Object?
231}
232
233void
234KurentoConnection::sendMessage(const std::string& msg)
235{
236 mSendQueue.push_back(msg);
237 DebugLog(<< "mRequestSentCount = " << mRequestSentCount
238 << " mResponseReceivedCount = " << mResponseReceivedCount);
239 processSendQueue();
240}
241
242void
243KurentoConnection::processSendQueue()
244{
245 while(!mSendQueue.empty())
246 {
247 if(mWaitForResponse && mRequestSentCount > mResponseReceivedCount)
248 {
249 DebugLog(<<"new request to send but still waiting for response for a previous request");
250 return;
251 }
252 if(mWSClient.get_con_from_hdl(mHandle)->get_state() == websocketpp::session::state::value::open)
253 {
254 std::string& msg = mSendQueue.front();
255 websocketpp::lib::error_code ec;
256 mWSClient.send(mHandle, msg.c_str(), websocketpp::frame::opcode::text, ec);
257 if(ec)
258 {
259 ErrLog(<<"failed to send a message to Kurento"); // FIXME
260 return;
261 }
262 else
263 {
264 StackLog(<<"message sent to Kurento, removing from queue: " << msg);
265 mSendQueue.pop_front();
266 mRequestSentCount++;
267 }
268 }
269 else
270 {
271 ErrLog(<<"connection to Kurento is not in the open state, message has been queued"); // FIXME
272 return;
273 }
274 }
275}
276
277std::string
278KurentoConnection::sendRequest(std::shared_ptr<KurentoResponseHandler> krh, const std::string& method, const json::Object& params)
279{
280 const std::string id = std::to_string(mNextId++);
281 StackLog(<<"generated ID " << id);
282 json::Object request;
283 json::Object _params = params;
284 if(mSessionId.length() > 0)
285 {
286 _params[JSON_RPC_SESSION_ID] = json::String(mSessionId);
287 }
288
289 request[JSON_RPC_ID] = json::String(id);
290 request[JSON_RPC_METHOD] = json::String(method);
291 request[JSON_RPC_PARAMS] = _params;
292 request[JSON_RPC_PROTO] = json::String(JSON_RPC_PROTO_VERSION);
293
294 std::stringstream stream;
295 json::Writer::Write(request, stream);
296
297 mResponseHandlers[id] = krh;
298 StackLog(<<"added ResponseHandler for id '" << id << "', handler count: " << mResponseHandlers.size());
299
300 sendMessage(stream.str());
301
302 return id;
303}
304
305void
306KurentoConnection::registerObject(std::shared_ptr<KurentoResponseHandler> object)
307{
308 std::shared_ptr<Object> _object = std::static_pointer_cast<Object>(object);
309 mObjects[_object->getId()] = _object;
310}
311
312// FIXME - we don't call this method from anywhere
313void
314KurentoConnection::unregisterObject(const std::string& objectId)
315{
316 mObjects.erase(objectId);
317}
318
319/* ====================================================================
320
321 Copyright (c) 2021, Software Freedom Institute https://softwarefreedom.institute
322 Copyright (c) 2021, Daniel Pocock https://danielpocock.com
323 All rights reserved.
324
325 Redistribution and use in source and binary forms, with or without
326 modification, are permitted provided that the following conditions are
327 met:
328
329 1. Redistributions of source code must retain the above copyright
330 notice, this list of conditions and the following disclaimer.
331
332 2. Redistributions in binary form must reproduce the above copyright
333 notice, this list of conditions and the following disclaimer in the
334 documentation and/or other materials provided with the distribution.
335
336 3. Neither the name of Plantronics nor the names of its contributors
337 may be used to endorse or promote products derived from this
338 software without specific prior written permission.
339
340 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
341 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
342 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
343 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
344 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
345 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
346 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
347 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
348 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
349 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
350 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
351
352 ==================================================================== */
#define JSON_RPC_PARAMS
#define JSON_RPC_RESULT
#define JSON_RPC_METHOD
#define JSON_RPC_VALUE
#define JSON_RPC_PROTO_VERSION
#define JSON_RPC_ID
#define JSON_RPC_SESSION_ID
#define JSON_RPC_PROTO
#define JSON_RPC_TYPE
#define DebugLog(args_)
Definition Logger.hxx:128
#define StackLog(args_)
Definition Logger.hxx:125
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
void registerObject(std::shared_ptr< KurentoResponseHandler > object)
void onMessage(client *wSClient, websocketpp::connection_hdl, client::message_ptr msg)
void unregisterObject(const std::string &objectId)
KurentoConnection(KurentoConnectionObserver &observer, std::string uri, client &wSClient, std::chrono::milliseconds timeout, std::chrono::milliseconds retryInterval, bool waitForResponse=true)
void onOpen(client *wSClient, websocketpp::connection_hdl h)
void onFail(client *wSClient, websocketpp::connection_hdl h)
std::string sendRequest(std::shared_ptr< KurentoResponseHandler > krh, const std::string &method, const json::Object &params)
void onClose(client *wSClient, websocketpp::connection_hdl h)
This class is used in the logging subsystem to identify logging messages generated from the Kurento c...
websocketpp::client< websocketpp::config::asio_client > client
dcm! – add UnusedChecking(_enum) below;