Commit 09d7b43f authored by Guillaume Roguez's avatar Guillaume Roguez

add a restcpp API backend

This commit offers an alternative to the dbus client to communicate with
a headless daemon. It uses restbed, a cpp framework for RESTful services.

This commits wraps up:
- The source of the restbed implementation (in bin/restcpp)
- The integration of the target to the configure.ac
- documentation
- Add external restbed library to contrib:
  - rules.mak file, to download the version 4.0, and compile it
  - Patch for ASIO, a dependency of restbed
  - Patch for the CMakeLists.txt file, to correct the installation path

[guillaume.roguez@savoirfairelinux.com: merged with Simon Zeni patch to support restbed into contrib]
[guillaume.roguez@savoirfairelinux.com: add tuleap ticket number]
[guillaume.roguez@savoirfairelinux.com: fixed configure.ac --without-restbed behavior]

Change-Id: Id49a1a04b05aac1e803981833abe5564785fb801
Tuleap: #498
Reviewed-by: default avatarAlexandre Lision <alexandre.lision@savoirfairelinux.com>
Signed-off-by: Guillaume Roguez's avatarGuillaume Roguez <guillaume.roguez@savoirfairelinux.com>
parent 15f6d173
......@@ -47,7 +47,7 @@ Short description of content of source tree
- src/ is the core of DRing.
- bin/ contains applications main code.
- bin/dbus, the D-Bus xml interfaces, and c++ bindings
- bin/restcpp, the C++ REST API implemented with Restbed
About Savoir-faire Linux
------------------------
......@@ -133,6 +133,27 @@ interaction between daemon and client will not work; for each platform where
dbus is not available the client should implement all the methods in the
*_stub.cpp files.
How to compile with the REST API
--------------------------------
Ring offers two REST API. One written in C++, and the other written in Cython.
Up to this date, only the C++ API is available. The Cython API will soon follow.
To compile Ring-daemon with the C++ REST API, follow these two steps :
1) Compile the dependencies
cd contrib
mkdir native
cd native
../bootstrap
make
make .restbed
2) Then compile the daemon
cd ../../
./autogen.sh
./configure --without-dbus --with-restcpp
make
SIP accounts
---------------------
......
......@@ -31,3 +31,19 @@ dring_CXXFLAGS= -I$(top_srcdir)/src ${DBUSCPP_CFLAGS} \
dring_LDADD = dbus/libclient_dbus.la ${DBUSCPP_LIBS} $(top_builddir)/src/libring.la
endif
if RING_RESTCPP
SUBDIRS=restcpp
sbin_PROGRAMS = dring
dring_SOURCES = main.cpp
dring_CXXFLAGS= -g \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/dring \
-DREST_API \
-DTOP_BUILDDIR=\"$$(cd "$(top_builddir)"; pwd)\"
dring_LDADD = restcpp/libclient_rest.la $(top_builddir)/src/libring.la
endif
......@@ -4,6 +4,7 @@
* Author: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com>
* Author: Yan Morin <yan.morin@savoirfairelinux.com>
* Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com>
* Author: Simon Zeni <simon.zeni@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -25,12 +26,28 @@
#include <cstring>
#include <signal.h>
#include <getopt.h>
#include <cstdlib>
#include "dring/dring.h"
#include "logger.h"
#if REST_API
#include "restcpp/restclient.h"
#else
#include "dbus/dbusclient.h"
#endif
#include "fileutils.h"
static int ringFlags = 0;
static std::unique_ptr<DBusClient> dbusClient;
static int port = 8080;
#if REST_API
static std::unique_ptr<RestClient> restClient;
#else
static std::unique_ptr<DBusClient> dbusClient;
#endif
static void
print_title()
......@@ -52,6 +69,7 @@ print_usage()
"-c, --console \t- Log in console (instead of syslog)" << std::endl <<
"-d, --debug \t- Debug mode (more verbose)" << std::endl <<
"-p, --persistent \t- Stay alive after client quits" << std::endl <<
"--port \t- Port to use for the rest API. Default is 8080" << std::endl <<
"--auto-answer \t- Force automatic answer to incoming calls" << std::endl <<
"-h, --help \t- Print help" << std::endl;
}
......@@ -76,6 +94,7 @@ parse_args(int argc, char *argv[], bool& persistent)
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
{"auto-answer", no_argument, &autoAnswer, true},
{"port", optional_argument, NULL, 'x'},
{0, 0, 0, 0} /* Sentinel */
};
......@@ -83,7 +102,7 @@ parse_args(int argc, char *argv[], bool& persistent)
/* getopt_long stores the option index here. */
int option_index = 0;
auto c = getopt_long(argc, argv, "dcphv", long_options, &option_index);
auto c = getopt_long(argc, argv, "dcphvx:", long_options, &option_index);
// end of the options
if (c == -1)
......@@ -111,6 +130,10 @@ parse_args(int argc, char *argv[], bool& persistent)
versionFlag = true;
break;
case 'x':
port = std::atoi(optarg);
break;
default:
break;
}
......@@ -138,21 +161,6 @@ parse_args(int argc, char *argv[], bool& persistent)
return false;
}
static int
run()
{
if (dbusClient)
return dbusClient->event_loop();
return 1;
}
static void
interrupt()
{
if (dbusClient)
dbusClient->exit();
}
static void
signal_handler(int code)
{
......@@ -164,7 +172,14 @@ signal_handler(int code)
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
interrupt();
// Interrupt the process
#if REST_API
if (restClient)
restClient->exit();
#else
if (dbusClient)
dbusClient->exit();
#endif
}
int
......@@ -189,6 +204,25 @@ main(int argc, char *argv [])
if (parse_args(argc, argv, persistent))
return 0;
// TODO: Block signals for all threads but the main thread, decide how/if we should
// handle other signals
signal(SIGINT, signal_handler);
signal(SIGHUP, signal_handler);
signal(SIGTERM, signal_handler);
#if REST_API
try {
restClient.reset(new RestClient {port, ringFlags, persistent});
} catch (const std::exception& ex) {
std::cerr << "One does not simply initialize the rest client: " << ex.what() << std::endl;
return 1;
}
if (restClient)
return restClient->event_loop();
else
return 1;
#else
// initialize client/library
try {
dbusClient.reset(new DBusClient {ringFlags, persistent});
......@@ -197,11 +231,10 @@ main(int argc, char *argv [])
return 1;
}
// TODO: Block signals for all threads but the main thread, decide how/if we should
// handle other signals
signal(SIGINT, signal_handler);
signal(SIGHUP, signal_handler);
signal(SIGTERM, signal_handler);
if (dbusClient)
return dbusClient->event_loop();
else
return 1;
#endif
return run();
}
include $(top_srcdir)/globals.mak
noinst_LTLIBRARIES = libclient_rest.la
libclient_rest_la_SOURCES = \
restclient.cpp \
restclient.h \
restconfigurationmanager.cpp \
restconfigurationmanager.h \
restvideomanager.cpp \
restvideomanager.h
libclient_rest_la_CXXFLAGS = \
-std=c++14 \
-g \
-Wall \
-Wextra \
-Wno-reorder \
-Wno-unused-variable \
-Wno-unused-parameter \
-pedantic \
-I$(top_srcdir)/src
libclient_rest_la_LDFLAGS = \
-lpthread \
-lrestbed
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
*
* Author: Simon Zeni <simon.zeni@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "restclient.h"
RestClient::RestClient(int port, int flags, bool persistent) :
service_()
{
configurationManager_.reset(new RestConfigurationManager());
videoManager_.reset(new RestVideoManager());
if (initLib(flags) < 0)
throw std::runtime_error {"cannot initialize libring"};
// Fill the resources
initResources();
// Initiate the rest service
settings_ = std::make_shared<restbed::Settings>();
settings_->set_port(port);
settings_->set_default_header( "Connection", "close" );
RING_INFO("Restclient running on port [%d]", port);
// Make it run in a thread, because this is a blocking function
restbed = std::thread([this](){
service_.start(settings_);
});
}
RestClient::~RestClient()
{
RING_INFO("destroying RestClient");
exit();
}
int
RestClient::event_loop() noexcept
{
// While the client is running, the events are polled every 10 milliseconds
RING_INFO("Restclient starting to poll events");
while(!pollNoMore_)
{
DRing::pollEvents();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return 0;
}
int
RestClient::exit() noexcept
{
try {
// On exit, the client stop polling events
pollNoMore_ = true;
// The rest service is stopped
service_.stop();
// And the thread running the service is joined
restbed.join();
endLib();
} catch (const std::exception& err) {
std::cerr << "quitting: " << err.what() << std::endl;
return 1;
}
return 0;
}
int
RestClient::initLib(int flags)
{
using namespace std::placeholders;
using std::bind;
using DRing::exportable_callback;
using DRing::CallSignal;
using DRing::ConfigurationSignal;
using DRing::PresenceSignal;
using DRing::AudioSignal;
using SharedCallback = std::shared_ptr<DRing::CallbackWrapperBase>;
auto confM = configurationManager_.get();
#ifdef RING_VIDEO
using DRing::VideoSignal;
#endif
// Configuration event handlers
// This is a short example of a callbakc using a lambda. In this case, this displays the incomming messages
const std::map<std::string, SharedCallback> configEvHandlers = {
exportable_callback<ConfigurationSignal::IncomingAccountMessage>([]
(const std::string& accountID, const std::string& from, const std::map<std::string, std::string>& payloads){
RING_INFO("accountID : %s", accountID.c_str());
RING_INFO("from : %s", from.c_str());
RING_INFO("payloads");
for(auto& it : payloads)
RING_INFO("%s : %s", it.first.c_str(), it.second.c_str());
}),
};
if (!DRing::init(static_cast<DRing::InitFlag>(flags)))
return -1;
registerConfHandlers(configEvHandlers);
// Dummy callbacks are registered for the other managers
registerCallHandlers(std::map<std::string, std::shared_ptr<DRing::CallbackWrapperBase>>());
registerPresHandlers(std::map<std::string, std::shared_ptr<DRing::CallbackWrapperBase>>());
#ifdef RING_VIDEO
registerVideoHandlers(std::map<std::string, std::shared_ptr<DRing::CallbackWrapperBase>>());
#endif
if (!DRing::start())
return -1;
return 0;
}
void
RestClient::endLib() noexcept
{
DRing::fini();
}
void
RestClient::initResources()
{
// This is the function that initiates the resources.
// Each resources is defined by a route and a void function with a shared pointer to the session as argument
// In this case, here's an example of the default route. It will list all the managers availables
auto default_res = std::make_shared<restbed::Resource>();
default_res->set_path("/");
default_res->set_method_handler("GET", [](const std::shared_ptr<restbed::Session> session){
RING_INFO("[%s] GET /", session->get_origin().c_str());
std::string body = "Available routes are : \r\n/configurationManager\r\n/videoManager\r\n";
const std::multimap<std::string, std::string> headers
{
{"Content-Type", "text/html"},
{"Content-Length", std::to_string(body.length())}
};
});
// And finally, we give the resource to the service to handle it
service_.publish(default_res);
// For the sake of convenience, each manager sends a vector of their resources
for(auto& it : configurationManager_->getResources())
service_.publish(it);
for(auto& it : videoManager_->getResources())
service_.publish(it);
}
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
*
* Author: Simon Zeni <simon.zeni@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <memory>
#include <chrono>
#include <map>
#include <vector>
#include <string>
#include <iostream>
#include <thread>
#include <functional>
#include <iterator>
#include <restbed>
#include "dring/dring.h"
#include "dring/callmanager_interface.h"
#include "dring/configurationmanager_interface.h"
#include "dring/presencemanager_interface.h"
#ifdef RING_VIDEO
#include "dring/videomanager_interface.h"
#endif
#include "logger.h"
#include "restconfigurationmanager.h"
#include "restvideomanager.h"
class RestClient {
public:
RestClient(int port, int flags, bool persistent);
~RestClient();
int event_loop() noexcept;
int exit() noexcept;
private:
int initLib(int flags);
void endLib() noexcept;
void initResources();
bool pollNoMore_ = false;
std::unique_ptr<RestConfigurationManager> configurationManager_;
std::unique_ptr<RestVideoManager> videoManager_;
// Restbed attributes
restbed::Service service_;
std::shared_ptr<restbed::Settings> settings_;
std::thread restbed;
};
This diff is collapsed.
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
*
* Author: Simon Zeni <simon.zeni@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <vector>
#include <map>
#include <string>
#include <regex>
#include <restbed>
#if __GNUC__ >= 5 || (__GNUC__ >=4 && __GNUC_MINOR__ >= 6)
/* This warning option only exists for gcc 4.6.0 and greater. */
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#endif
#include "dring/dring.h"
#include "dring/callmanager_interface.h"
#include "dring/configurationmanager_interface.h"
#include "dring/presencemanager_interface.h"
#ifdef RING_VIDEO
#include "dring/videomanager_interface.h"
#endif
#include "logger.h"
#include "im/message_engine.h"
#pragma GCC diagnostic warning "-Wignored-qualifiers"
#if __GNUC__ >= 5 || (__GNUC__ >=4 && __GNUC_MINOR__ >= 6)
/* This warning option only exists for gcc 4.6.0 and greater. */
#pragma GCC diagnostic warning "-Wunused-but-set-variable"
#endif
class RestConfigurationManager
{
public:
RestConfigurationManager();
std::vector<std::shared_ptr<restbed::Resource>> getResources();
private:
// Attributes
std::vector<std::shared_ptr<restbed::Resource>> resources_;
// Methods
std::map<std::string, std::string> parsePost(const std::string& post);
void populateResources();
void defaultRoute(const std::shared_ptr<restbed::Session> session);
void getAccountDetails(const std::shared_ptr<restbed::Session> session);
void getVolatileAccountDetails(const std::shared_ptr<restbed::Session> session);
void setAccountDetails(const std::shared_ptr<restbed::Session> session);
void setAccountActive(const std::shared_ptr<restbed::Session> session);
void getAccountTemplate(const std::shared_ptr<restbed::Session> session);
void addAccount(const std::shared_ptr<restbed::Session> session);
void removeAccount(const std::shared_ptr<restbed::Session> session);
void getAccountList(const std::shared_ptr<restbed::Session> session);
void sendRegister(const std::shared_ptr<restbed::Session> session);
void registerAllAccounts(const std::shared_ptr<restbed::Session> session);
void sendTextMessage(const std::shared_ptr<restbed::Session> session);
void getMessageStatus(const std::shared_ptr<restbed::Session> session);
void getTlsDefaultSettings(const std::shared_ptr<restbed::Session> session);
void getCodecList(const std::shared_ptr<restbed::Session> session);
void getSupportedTlsMethod(const std::shared_ptr<restbed::Session> session);
void getSupportedCiphers(const std::shared_ptr<restbed::Session> session);
void getCodecDetails(const std::shared_ptr<restbed::Session> session);
void setCodecDetails(const std::shared_ptr<restbed::Session> session);
void getActiveCodecList(const std::shared_ptr<restbed::Session> session);
void setActiveCodecList(const std::string& accountID, const std::vector<unsigned>& list); // /!\ not implemented
void getAudioPluginList(const std::shared_ptr<restbed::Session> session);
void setAudioPlugin(const std::shared_ptr<restbed::Session> session);
void getAudioOutputDeviceList(const std::shared_ptr<restbed::Session> session);
void setAudioOutputDevice(const std::shared_ptr<restbed::Session> session);
void setAudioInputDevice(const std::shared_ptr<restbed::Session> session);
void setAudioRingtoneDevice(const std::shared_ptr<restbed::Session> session);
void getAudioInputDeviceList(const std::shared_ptr<restbed::Session> session);
void getCurrentAudioDevicesIndex(const std::shared_ptr<restbed::Session> session);
void getAudioInputDeviceIndex(const std::shared_ptr<restbed::Session> session);
void getAudioOutputDeviceIndex(const std::shared_ptr<restbed::Session> session);
void getCurrentAudioOutputPlugin(const std::shared_ptr<restbed::Session> session);
void getNoiseSuppressState(const std::shared_ptr<restbed::Session> session);
void setNoiseSuppressState(const std::shared_ptr<restbed::Session> session);
void isAgcEnabled(const std::shared_ptr<restbed::Session> session);
void setAgcState(const std::shared_ptr<restbed::Session> session);
void muteDtmf(const std::shared_ptr<restbed::Session> session);
void isDtmfMuted(const std::shared_ptr<restbed::Session> session);
void isCaptureMuted(const std::shared_ptr<restbed::Session> session);
void muteCapture(const std::shared_ptr<restbed::Session> session);
void isPlaybackMuted(const std::shared_ptr<restbed::Session> session);
void mutePlayback(const std::shared_ptr<restbed::Session> session);
void isRingtoneMuted(const std::shared_ptr<restbed::Session> session);
void muteRingtone(const std::shared_ptr<restbed::Session> session);
void getAudioManager(const std::shared_ptr<restbed::Session> session);
void setAudioManager(const std::shared_ptr<restbed::Session> session);
void getSupportedAudioManagers(const std::shared_ptr<restbed::Session> session);
void getRecordPath(const std::shared_ptr<restbed::Session> session);
void setRecordPath(const std::shared_ptr<restbed::Session> session);
void getIsAlwaysRecording(const std::shared_ptr<restbed::Session> session);
void setIsAlwaysRecording(const std::shared_ptr<restbed::Session> session);
void setHistoryLimit(const std::shared_ptr<restbed::Session> session);
void getHistoryLimit(const std::shared_ptr<restbed::Session> session);
void setAccountsOrder(const std::shared_ptr<restbed::Session> session);
void getHookSettings(const std::shared_ptr<restbed::Session> session);
void setHookSettings(const std::shared_ptr<restbed::Session> session);
void getCredentials(const std::shared_ptr<restbed::Session> session);
void setCredentials(const std::shared_ptr<restbed::Session> session);
void getAddrFromInterfaceName(const std::shared_ptr<restbed::Session> session);
void getAllIpInterface(const std::shared_ptr<restbed::Session> session);
void getAllIpInterfaceByName(const std::shared_ptr<restbed::Session> session);
void getShortcuts(const std::shared_ptr<restbed::Session> session);
void setShortcuts(const std::shared_ptr<restbed::Session> session);
void setVolume(const std::string& device, const double& value);
void getVolume(const std::string& device);
void validateCertificate(const std::string& accountId, const std::string& certificate);
void validateCertificatePath(const std::string& accountId, const std::string& certificatePath, const std::string& privateKey, const std::string& privateKeyPass, const std::string& caList);
void getCertificateDetails(const std::string& certificate);
void getCertificateDetailsPath(const std::string& certificatePath, const std::string& privateKey, const std::string& privateKeyPass);
void getPinnedCertificates();
void pinCertificate(const std::vector<uint8_t>& certificate, const bool& local);
void unpinCertificate(const std::string& certId);
void pinCertificatePath(const std::string& path);
void unpinCertificatePath(const std::string& path);
void pinRemoteCertificate(const std::string& accountId, const std::string& certId);
void setCertificateStatus(const std::string& account, const std::string& certId, const std::string& status);
void getCertificatesByStatus(const std::string& account, const std::string& status);
void getTrustRequests(const std::shared_ptr<restbed::Session> session);
void acceptTrustRequest(const std::string& accountId, const std::string& from);
void discardTrustRequest(const std::string& accountId, const std::string& from);
void sendTrustRequest(const std::string& accountId, const std::string& to, const std::vector<uint8_t>& payload);
void exportAccounts(const std::vector<std::string>& accountIDs, const std::string& filepath, const std::string& password);
void importAccounts(const std::string& archivePath, const std::string& password);
};
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
*
* Author: Simon Zeni <simon.zeni@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "restvideomanager.h"
#include "client/videomanager.h"
RestVideoManager::RestVideoManager() :
resources_()
{
populateResources();
}
std::vector<std::shared_ptr<restbed::Resource>>
RestVideoManager::getResources()
{
return resources_;
}
// Private
std::map<std::string, std::string>
RestVideoManager::parsePost(const std::string& post)
{
std::map<std::string, std::string> data;
auto split = [](const std::string& s, char delim){
std::vector<std::string> v;
auto i = 0;
auto pos = s.find(delim);
while (pos != std::string::npos)
{
v.push_back(s.substr(i, pos-i));
i = ++pos;
pos = s.find(delim, pos);
if (pos == std::string::npos)
v.push_back(s.substr(i, s.length()));
}
return v;
};
if(post.find_first_of('&') != std::string::npos)
{
std::vector<std::string> v = split(post, '&');
for(auto& it : v)
{
std::vector<std::string> tmp = split(it, '=');
data[tmp.front()] = tmp.back();
}
}
else
{
std::vector<std::string> tmp = split(post, '=');
data[tmp.front()] = tmp.back();
}
return data;
}
void
RestVideoManager::populateResources()
{
resources_.push_back(std::make_shared<restbed::Resource>());
resources_.back()->set_path("/videoManager");
resources_.back()->set_method_handler("GET",
std::bind(&RestVideoManager::defaultRoute, this, std::placeholders::_1));
resources_.push_back(std::make_shared<restbed::Resource>());
resources_.back()->set_path("/deviceList");
resources_.back()->set_method_handler("GET",
std::bind(&RestVideoManager::getDeviceList, this, std::placeholders::_1));
}
void
RestVideoManager::defaultRoute(const std::shared_ptr<restbed::Session> session)
{
RING_INFO("[%s] GET /videoManager", session->get_origin().c_str());
std::string body = "Available routes are : \r\n";