mirror of
https://github.com/openappsec/openappsec.git
synced 2025-09-29 19:24:26 +03:00
First release of open-appsec source code
This commit is contained in:
4
core/rest/CMakeLists.txt
Normal file
4
core/rest/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
include_directories(${PROJECT_SOURCE_DIR}/components/include)
|
||||
add_library(rest rest_server.cc rest_conn.cc rest.cc)
|
||||
|
||||
add_subdirectory(rest_ut)
|
32
core/rest/i_rest_invoke.h
Normal file
32
core/rest/i_rest_invoke.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (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.
|
||||
|
||||
#ifndef __I_REST_INVOKE_H__
|
||||
#define __I_REST_INVOKE_H__
|
||||
|
||||
#include <string>
|
||||
#include <istream>
|
||||
|
||||
#include "maybe_res.h"
|
||||
|
||||
class I_RestInvoke
|
||||
{
|
||||
public:
|
||||
virtual Maybe<std::string> getSchema(const std::string &uri) const = 0;
|
||||
virtual Maybe<std::string> invokeRest(const std::string &uri, std::istream &in) const = 0;
|
||||
|
||||
protected:
|
||||
~I_RestInvoke() {}
|
||||
};
|
||||
|
||||
#endif // __I_REST_INVOKE_H__
|
129
core/rest/rest.cc
Normal file
129
core/rest/rest.cc
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (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 "rest.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
ostream &
|
||||
RestHelper::printIndent(ostream &os, uint indent)
|
||||
{
|
||||
for (uint i = 0; i < indent; i++) os << " ";
|
||||
return os;
|
||||
}
|
||||
|
||||
void
|
||||
RestHelper::reportError(std::string const &err)
|
||||
{
|
||||
throw JsonError(err);
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
ServerRest::performRestCall(istream &in)
|
||||
{
|
||||
try {
|
||||
try {
|
||||
cereal::JSONInputArchive in_ar(in);
|
||||
load(in_ar);
|
||||
} catch (cereal::Exception &e) {
|
||||
throw JsonError(string("JSON parsing failed: ") + e.what());
|
||||
}
|
||||
doCall();
|
||||
stringstream out;
|
||||
{
|
||||
cereal::JSONOutputArchive out_ar(out);
|
||||
save(out_ar);
|
||||
}
|
||||
return out.str();
|
||||
} catch (const JsonError &e) {
|
||||
return genError(e.getMsg());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BasicRest::performOutputingSchema(ostream &out, int level)
|
||||
{
|
||||
RestHelper::printIndent(out, level) << "{\n";
|
||||
|
||||
RestHelper::printIndent(out, level + 1) << "\"properties\": {";
|
||||
outputSchema(out, level + 2);
|
||||
out << "\n";
|
||||
RestHelper::printIndent(out, level + 1) << "},\n";
|
||||
|
||||
RestHelper::printIndent(out, level + 1) << "\"required\": [";
|
||||
outputRequired(out, level + 2);
|
||||
out << "\n";
|
||||
RestHelper::printIndent(out, level+1) << "]\n";
|
||||
|
||||
RestHelper::printIndent(out, level) << "}";
|
||||
}
|
||||
|
||||
void
|
||||
BasicRest::outputSchema(ostream &os, int level)
|
||||
{
|
||||
bool first = true;
|
||||
for (auto it : schema_func) {
|
||||
if (!first) os << ',';
|
||||
os << '\n';
|
||||
it(os, level);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BasicRest::outputRequired(ostream &os, int level)
|
||||
{
|
||||
bool first = true;
|
||||
for (auto it : required) {
|
||||
if (!first) os << ',';
|
||||
os << '\n';
|
||||
RestHelper::printIndent(os, level) << "\"" << it << '"';
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
ClientRest::genJson() const
|
||||
{
|
||||
try {
|
||||
stringstream out;
|
||||
{
|
||||
cereal::JSONOutputArchive out_ar(out);
|
||||
save(out_ar);
|
||||
}
|
||||
return out.str();
|
||||
} catch (const JsonError &e) {
|
||||
return genError(e.getMsg());
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ClientRest::loadJson(const string &json)
|
||||
{
|
||||
try
|
||||
{
|
||||
stringstream in;
|
||||
in.str(json);
|
||||
try {
|
||||
cereal::JSONInputArchive in_ar(in);
|
||||
load(in_ar);
|
||||
} catch (cereal::Exception &e) {
|
||||
throw JsonError(string("JSON parsing failed: ") + e.what());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (const JsonError &j)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
158
core/rest/rest_conn.cc
Normal file
158
core/rest/rest_conn.cc
Normal file
@@ -0,0 +1,158 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (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 "rest_conn.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
USE_DEBUG_FLAG(D_API);
|
||||
|
||||
RestConn::RestConn(int _fd, I_MainLoop *_mainloop, const I_RestInvoke *_invoke)
|
||||
:
|
||||
fd(_fd),
|
||||
mainloop(_mainloop),
|
||||
invoke(_invoke)
|
||||
{}
|
||||
|
||||
RestConn::~RestConn()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
RestConn::parseConn() const
|
||||
{
|
||||
char ch;
|
||||
|
||||
if (recv(fd, &ch, sizeof(char), MSG_PEEK) != sizeof(char)) {
|
||||
dbgDebug(D_API) << "Socket " << fd << " ended";
|
||||
stop();
|
||||
}
|
||||
|
||||
string line = readLine();
|
||||
stringstream os;
|
||||
os.str(line);
|
||||
string method;
|
||||
os >> method;
|
||||
|
||||
if (method!="POST" && method!="GET") {
|
||||
dbgWarning(D_API) << "Unsupported REST method: " << method;
|
||||
sendResponse("405 Method Not Allowed", "Method " + method + " is not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
string uri;
|
||||
os >> uri;
|
||||
string identifier = uri.substr(uri.find_last_of('/') + 1);
|
||||
|
||||
uint len = 0;
|
||||
while (true) {
|
||||
line = readLine();
|
||||
if (line.size() < 3) break;
|
||||
|
||||
os.str(line);
|
||||
string head, data;
|
||||
os >> head >> data;
|
||||
if (head == "Content-Length:" || head == "content-length:") {
|
||||
try {
|
||||
len = stoi(data, nullptr);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (method=="POST" && len==0) {
|
||||
dbgWarning(D_API) << "No length was found - could be chunked, but we still do not support that";
|
||||
sendResponse("411 Length Required", "");
|
||||
stop();
|
||||
}
|
||||
|
||||
stringstream body;
|
||||
body.str(readSize(len));
|
||||
|
||||
Maybe<string> res = (method == "POST") ? invoke->invokeRest(identifier, body) : invoke->getSchema(identifier);
|
||||
|
||||
if (res.ok()) {
|
||||
sendResponse("200 OK", res.unpack());
|
||||
} else {
|
||||
sendResponse("500 Internal Server Error", res.getErr());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RestConn::stop() const
|
||||
{
|
||||
close(fd);
|
||||
mainloop->stop();
|
||||
}
|
||||
|
||||
string
|
||||
RestConn::readLine() const
|
||||
{
|
||||
string res;
|
||||
char ch = 0;
|
||||
while (ch != '\n') {
|
||||
if (read(fd, &ch, sizeof(char)) != sizeof(char)) {
|
||||
dbgWarning(D_API) << "Failed to read from socket " << fd;
|
||||
sendResponse("598 Network read timeout error", "");
|
||||
stop();
|
||||
}
|
||||
res += ch;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
string
|
||||
RestConn::readSize(int len) const
|
||||
{
|
||||
string res;
|
||||
for (int i = 0; i < len; i++) {
|
||||
char ch;
|
||||
if (read(fd, &ch, sizeof(char)) != sizeof(char)) {
|
||||
dbgWarning(D_API) << "Failed to read from socket " << fd;
|
||||
sendResponse("598 Network read timeout error", "");
|
||||
stop();
|
||||
}
|
||||
res += ch;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
RestConn::sendResponse(const string &status, const string &body) const
|
||||
{
|
||||
stringstream stream;
|
||||
stream <<
|
||||
"HTTP/1.1 " << status << "\r\n" <<
|
||||
"Content-Type: application/json\r\n" <<
|
||||
"Content-Length: " << (body.size() + 2) << "\r\n" <<
|
||||
"\r\n" <<
|
||||
body << "\r\n";
|
||||
|
||||
|
||||
string res = stream.str();
|
||||
while (res.size() > 0 ) {
|
||||
auto written = write(fd, res.c_str(), res.size());
|
||||
if (written < 1) {
|
||||
dbgWarning(D_API) << "Failed to write to socket " << fd;
|
||||
stop();
|
||||
}
|
||||
res = res.substr(written);
|
||||
}
|
||||
}
|
40
core/rest/rest_conn.h
Normal file
40
core/rest/rest_conn.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (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.
|
||||
|
||||
#ifndef __REST_CONN_H__
|
||||
#define __REST_CONN_H__
|
||||
|
||||
#include <string>
|
||||
#include "i_mainloop.h"
|
||||
#include "i_rest_invoke.h"
|
||||
|
||||
class RestConn
|
||||
{
|
||||
public:
|
||||
RestConn(int _fd, I_MainLoop *_mainloop, const I_RestInvoke *_invoke);
|
||||
~RestConn();
|
||||
|
||||
void parseConn() const;
|
||||
|
||||
private:
|
||||
void stop() const;
|
||||
std::string readLine() const;
|
||||
std::string readSize(int len) const;
|
||||
void sendResponse(const std::string &status, const std::string &body) const;
|
||||
|
||||
int fd;
|
||||
I_MainLoop *mainloop;
|
||||
const I_RestInvoke *invoke;
|
||||
};
|
||||
|
||||
#endif // __REST_CONN_H__
|
261
core/rest/rest_server.cc
Normal file
261
core/rest/rest_server.cc
Normal file
@@ -0,0 +1,261 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (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 "rest_server.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "singleton.h"
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "rest_conn.h"
|
||||
#include "i_rest_invoke.h"
|
||||
#include "sasal.h"
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
USE_DEBUG_FLAG(D_API);
|
||||
|
||||
static const int listen_limit = 100;
|
||||
static const chrono::milliseconds bind_retry_interval_msec = chrono::milliseconds(500);
|
||||
|
||||
SASAL_START // REST Server
|
||||
|
||||
#include <iostream>
|
||||
|
||||
class RestServer::Impl
|
||||
:
|
||||
Singleton::Provide<I_RestApi>::From<RestServer>,
|
||||
I_RestInvoke
|
||||
{
|
||||
public:
|
||||
void init();
|
||||
void fini();
|
||||
|
||||
void startNewConnection() const;
|
||||
|
||||
bool bindRestServerSocket(struct sockaddr_in &addr, vector<uint16_t> port_range);
|
||||
bool addRestCall(RestAction oper, const string &uri, unique_ptr<RestInit> &&init) override;
|
||||
Maybe<std::string> getSchema(const std::string &uri) const override;
|
||||
Maybe<std::string> invokeRest(const std::string &uri, istream &in) const override;
|
||||
|
||||
private:
|
||||
void prepareConfiguration();
|
||||
Maybe<uint, Context::Error> getPortConfig(const string &config) const;
|
||||
|
||||
string changeActionToString(RestAction oper);
|
||||
|
||||
int fd = -1;
|
||||
I_MainLoop::RoutineID id;
|
||||
I_MainLoop *mainloop;
|
||||
map<string, unique_ptr<RestInit>> rest_calls;
|
||||
uint16_t port_used = 0;
|
||||
vector<uint16_t> port_range;
|
||||
};
|
||||
|
||||
bool
|
||||
RestServer::Impl::bindRestServerSocket(struct sockaddr_in &addr, vector<uint16_t> port_range)
|
||||
{
|
||||
for (uint16_t port : port_range) {
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) == 0) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Maybe<uint, Context::Error>
|
||||
RestServer::Impl::getPortConfig(const string &config) const
|
||||
{
|
||||
auto conf_value = getConfiguration<uint>("connection", config);
|
||||
if (conf_value.ok()) return *conf_value;
|
||||
return Singleton::Consume<I_Environment>::by<RestServer>()->get<uint>(config);
|
||||
}
|
||||
|
||||
void
|
||||
RestServer::Impl::prepareConfiguration()
|
||||
{
|
||||
auto primary_port = getPortConfig("Nano service API Port Primary");
|
||||
auto alternative_port = getPortConfig("Nano service API Port Alternative");
|
||||
if (primary_port.ok() && alternative_port.ok()) {
|
||||
port_range.push_back(*primary_port);
|
||||
port_range.push_back(*alternative_port);
|
||||
} else {
|
||||
auto range_start = getPortConfig("Nano service API Port Range start");
|
||||
auto range_end = getPortConfig("Nano service API Port Range end");
|
||||
dbgAssert(range_start.ok() && range_end.ok()) << "Rest port configuration was not provided";
|
||||
dbgAssert(*range_start < *range_end) << "Rest port range corrupted (lower bound higher then upper bound)";
|
||||
|
||||
port_range.resize(*range_end - *range_start);
|
||||
for (uint16_t i = 0, port = *range_start; i < port_range.size(); i++, port++) {
|
||||
port_range[i] = port;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RestServer::Impl::init()
|
||||
{
|
||||
mainloop = Singleton::Consume<I_MainLoop>::by<RestServer>();
|
||||
|
||||
auto init_connection = [this] () {
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
dbgAssert(fd >= 0) << "Failed to open a socket";
|
||||
int socket_enable = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &socket_enable, sizeof(int)) < 0) {
|
||||
dbgWarning(D_API) << "Could not set the socket options";
|
||||
}
|
||||
|
||||
struct sockaddr_in addr;
|
||||
bzero(&addr, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
|
||||
while (!bindRestServerSocket(addr, port_range)) {
|
||||
mainloop->yield(bind_retry_interval_msec);
|
||||
}
|
||||
|
||||
listen(fd, listen_limit);
|
||||
|
||||
auto is_primary = Singleton::Consume<I_Environment>::by<RestServer>()->get<bool>("Is Rest primary routine");
|
||||
id = mainloop->addFileRoutine(
|
||||
I_MainLoop::RoutineType::Offline,
|
||||
fd,
|
||||
[&] () { this->startNewConnection(); },
|
||||
"REST server listener",
|
||||
is_primary.ok() && *is_primary
|
||||
);
|
||||
|
||||
uint16_t listening_port = ntohs(addr.sin_port);
|
||||
dbgInfo(D_API) << "REST server started: " << listening_port;
|
||||
Singleton::Consume<I_Environment>::by<RestServer>()->registerValue<int>("Listening Port", listening_port);
|
||||
};
|
||||
|
||||
prepareConfiguration();
|
||||
mainloop->addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, init_connection, "REST server startup");
|
||||
}
|
||||
|
||||
void
|
||||
RestServer::Impl::fini()
|
||||
{
|
||||
dbgInfo(D_API) << "Stoping the REST server";
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
if (mainloop->doesRoutineExist(id)) mainloop->stop(id);
|
||||
port_range.clear();
|
||||
}
|
||||
|
||||
void
|
||||
RestServer::Impl::startNewConnection() const
|
||||
{
|
||||
dbgFlow(D_API) << "Starting a new connection";
|
||||
int new_socket = accept(fd, nullptr, nullptr);
|
||||
if (new_socket < 0) {
|
||||
dbgWarning(D_API) << "Failed to accept a new socket";
|
||||
return;
|
||||
}
|
||||
dbgDebug(D_API) << "Starting a new socket: " << new_socket;
|
||||
|
||||
RestConn conn(new_socket, mainloop, this);
|
||||
mainloop->addFileRoutine(
|
||||
I_MainLoop::RoutineType::Offline,
|
||||
new_socket,
|
||||
[conn] () { conn.parseConn(); },
|
||||
"REST server connection handler"
|
||||
);
|
||||
}
|
||||
|
||||
bool
|
||||
RestServer::Impl::addRestCall(RestAction oper, const string &uri, unique_ptr<RestInit> &&rest)
|
||||
{
|
||||
string full_uri = changeActionToString(oper) + uri;
|
||||
return rest_calls.emplace(make_pair(full_uri, move(rest))).second;
|
||||
}
|
||||
|
||||
Maybe<std::string>
|
||||
RestServer::Impl::getSchema(const std::string &uri) const
|
||||
{
|
||||
auto iter = rest_calls.find(uri);
|
||||
if (iter == rest_calls.end()) return genError("No matching REST call was found");
|
||||
|
||||
auto instance = iter->second->getRest();
|
||||
stringstream out;
|
||||
instance->performOutputingSchema(out);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
Maybe<std::string>
|
||||
RestServer::Impl::invokeRest(const std::string &uri, istream &in) const
|
||||
{
|
||||
auto iter = rest_calls.find(uri);
|
||||
if (iter == rest_calls.end()) return genError("No matching REST call was found");
|
||||
auto instance = iter->second->getRest();
|
||||
return instance->performRestCall(in);
|
||||
}
|
||||
|
||||
string
|
||||
RestServer::Impl::changeActionToString(RestAction oper)
|
||||
{
|
||||
switch(oper) {
|
||||
case RestAction::ADD: {
|
||||
return "add-";
|
||||
}
|
||||
case RestAction::SET: {
|
||||
return "set-";
|
||||
}
|
||||
case RestAction::SHOW: {
|
||||
return "show-";
|
||||
}
|
||||
case RestAction::DELETE: {
|
||||
return "delete-";
|
||||
}
|
||||
default: {
|
||||
dbgAssert(false) << "Unknown REST action";
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RestServer::RestServer() : Component("RestServer"), pimpl(make_unique<RestServer::Impl>()) {}
|
||||
|
||||
RestServer::~RestServer() {}
|
||||
|
||||
void
|
||||
RestServer::init()
|
||||
{
|
||||
pimpl->init();
|
||||
}
|
||||
|
||||
void
|
||||
RestServer::fini()
|
||||
{
|
||||
pimpl->fini();
|
||||
}
|
||||
|
||||
void
|
||||
RestServer::preload()
|
||||
{
|
||||
registerExpectedConfiguration<uint>("connection", "Nano service API Port Primary");
|
||||
registerExpectedConfiguration<uint>("connection", "Nano service API Port Alternative");
|
||||
registerExpectedConfiguration<uint>("connection", "Nano service API Port Range start");
|
||||
registerExpectedConfiguration<uint>("connection", "Nano service API Port Range end");
|
||||
}
|
||||
|
||||
SASAL_END
|
8
core/rest/rest_ut/CMakeLists.txt
Executable file
8
core/rest/rest_ut/CMakeLists.txt
Executable file
@@ -0,0 +1,8 @@
|
||||
link_directories(${BOOST_ROOT}/lib)
|
||||
link_directories(${ng_module_osrc_zlib_path}/lib)
|
||||
|
||||
add_unit_test(
|
||||
rest_server_ut
|
||||
"rest_schema_ut.cc;rest_must_param_ut.cc;rest_config_ut.cc"
|
||||
"singleton;rest;environment;-lz;shell_cmd;-lboost_filesystem;message;instance_awareness;messaging_buffer;-lz;debug_is;time_proxy;mainloop;agent_details;encryptor;event_is;metric;-lboost_context;-lboost_regex;-lssl;-lcrypto;connkey"
|
||||
)
|
108
core/rest/rest_ut/rest_config_ut.cc
Executable file
108
core/rest/rest_ut/rest_config_ut.cc
Executable file
@@ -0,0 +1,108 @@
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "cptest.h"
|
||||
#include "environment.h"
|
||||
#include "config.h"
|
||||
#include "config_component.h"
|
||||
#include "singleton.h"
|
||||
#include "time_proxy.h"
|
||||
#include "mainloop.h"
|
||||
#include "rest_server.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace testing;
|
||||
|
||||
USE_DEBUG_FLAG(D_API);
|
||||
|
||||
class RestConfigTest : public Test
|
||||
{
|
||||
public:
|
||||
RestConfigTest()
|
||||
{
|
||||
rest_server.preload();
|
||||
|
||||
time_proxy.init();
|
||||
mainloop_comp.init();
|
||||
|
||||
string config_json =
|
||||
"{\n"
|
||||
" \"connection\": {\n"
|
||||
" \"Nano service API Port Primary\": [\n"
|
||||
" {\n"
|
||||
" \"value\": 9777\n"
|
||||
" }\n"
|
||||
" ],\n"
|
||||
" \"Nano service API Port Alternative\": [\n"
|
||||
" {\n"
|
||||
" \"value\": 9778\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
" }\n"
|
||||
"}\n";
|
||||
|
||||
istringstream ss(config_json);
|
||||
Singleton::Consume<Config::I_Config>::from(config)->loadConfiguration(ss);
|
||||
|
||||
Debug::setUnitTestFlag(D_API, Debug::DebugLevel::NOISE);
|
||||
Debug::setNewDefaultStdout(&capture_debug);
|
||||
}
|
||||
|
||||
~RestConfigTest()
|
||||
{
|
||||
Debug::setNewDefaultStdout(&cout);
|
||||
time_proxy.fini();
|
||||
mainloop_comp.fini();
|
||||
}
|
||||
|
||||
ostringstream capture_debug;
|
||||
TimeProxyComponent time_proxy;
|
||||
MainloopComponent mainloop_comp;
|
||||
::Environment env;
|
||||
ConfigComponent config;
|
||||
RestServer rest_server;
|
||||
};
|
||||
|
||||
TEST_F(RestConfigTest, alternative_port_used)
|
||||
{
|
||||
int file_descriptor = socket(AF_INET, SOCK_STREAM, 0);
|
||||
EXPECT_NE(file_descriptor, -1);
|
||||
|
||||
auto primary_port = getConfiguration<uint>("connection", "Nano service API Port Primary");
|
||||
EXPECT_TRUE(primary_port.ok());
|
||||
|
||||
struct sockaddr_in sa;
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(primary_port.unpack());
|
||||
sa.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
int socket_enable = 1;
|
||||
EXPECT_EQ(setsockopt(file_descriptor, SOL_SOCKET, SO_REUSEADDR, &socket_enable, sizeof(int)), 0);
|
||||
EXPECT_EQ(bind(file_descriptor, reinterpret_cast<struct sockaddr *>(&sa), sizeof(struct sockaddr_in)), 0);
|
||||
EXPECT_EQ(listen(file_descriptor, 1), 0);
|
||||
|
||||
auto alternative_port = getConfiguration<uint>("connection", "Nano service API Port Alternative");
|
||||
EXPECT_TRUE(alternative_port.ok());
|
||||
|
||||
rest_server.init();
|
||||
|
||||
file_descriptor = socket(AF_INET, SOCK_STREAM, 0);
|
||||
EXPECT_NE(file_descriptor, -1);
|
||||
|
||||
auto mainloop = Singleton::Consume<I_MainLoop>::from(mainloop_comp);
|
||||
I_MainLoop::Routine stop_routine = [mainloop] () { mainloop->stopAll(); };
|
||||
mainloop->addOneTimeRoutine(
|
||||
I_MainLoop::RoutineType::RealTime,
|
||||
stop_routine,
|
||||
"RestConfigTest-alternative_port_used stop routine",
|
||||
false
|
||||
);
|
||||
mainloop->run();
|
||||
|
||||
sa.sin_port = htons(alternative_port.unpack());
|
||||
EXPECT_EQ(bind(file_descriptor, reinterpret_cast<struct sockaddr *>(&sa), sizeof(struct sockaddr_in)), -1);
|
||||
|
||||
EXPECT_THAT(capture_debug.str(), HasSubstr("REST server started: " + to_string(alternative_port.unpack())));
|
||||
|
||||
rest_server.fini();
|
||||
}
|
81
core/rest/rest_ut/rest_must_param_ut.cc
Executable file
81
core/rest/rest_ut/rest_must_param_ut.cc
Executable file
@@ -0,0 +1,81 @@
|
||||
#include "rest.h"
|
||||
#include "cptest.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
using namespace testing;
|
||||
|
||||
class MustParamClientCheck : public ClientRest
|
||||
{
|
||||
public:
|
||||
C2S_PARAM(int, output_int);
|
||||
S2C_PARAM(int, input_int);
|
||||
};
|
||||
|
||||
TEST(RestMustParam, normal_client_operation)
|
||||
{
|
||||
MustParamClientCheck rest;
|
||||
rest.output_int = 3;
|
||||
EXPECT_THAT(rest.genJson(), IsValue("{\n \"output_int\": 3\n}"));
|
||||
|
||||
EXPECT_TRUE(rest.loadJson("{ \"input_int\" : 7 }"));
|
||||
EXPECT_EQ(rest.input_int, 7);
|
||||
}
|
||||
|
||||
TEST(RestMustParam, client_missing_output_variable)
|
||||
{
|
||||
MustParamClientCheck rest;
|
||||
EXPECT_THAT(rest.genJson(), IsError("Couldn't output variable output_int"));
|
||||
}
|
||||
|
||||
TEST(RestMustParam, client_missing_input_variable)
|
||||
{
|
||||
MustParamClientCheck rest;
|
||||
rest.output_int = 3;
|
||||
EXPECT_THAT(rest.genJson(), IsValue("{\n \"output_int\": 3\n}"));
|
||||
|
||||
EXPECT_FALSE(rest.loadJson("{}"));
|
||||
}
|
||||
|
||||
class MustParamServerCheck : public ServerRest
|
||||
{
|
||||
public:
|
||||
void doCall() override { if (set_output) output_int = 9; }
|
||||
|
||||
C2S_PARAM(int, input_int);
|
||||
S2C_PARAM(int, output_int);
|
||||
bool set_output = true;
|
||||
};
|
||||
|
||||
TEST(RestMustParam, normal_server_operation)
|
||||
{
|
||||
MustParamServerCheck rest;
|
||||
|
||||
stringstream ss;
|
||||
ss << "{ \"input_int\": 5 }";
|
||||
|
||||
EXPECT_THAT(rest.performRestCall(ss), IsValue("{\n \"output_int\": 9\n}"));
|
||||
EXPECT_EQ(rest.input_int, 5);
|
||||
EXPECT_EQ(rest.output_int, 9);
|
||||
}
|
||||
|
||||
TEST(RestMustParam, server_missing_input_variable)
|
||||
{
|
||||
MustParamServerCheck rest;
|
||||
|
||||
stringstream ss;
|
||||
ss << "{}";
|
||||
|
||||
EXPECT_THAT(rest.performRestCall(ss), IsError("Couldn't get variable input_int"));
|
||||
}
|
||||
|
||||
TEST(RestMustParam, server_missing_output_variable)
|
||||
{
|
||||
MustParamServerCheck rest;
|
||||
rest.set_output = false;
|
||||
|
||||
stringstream ss;
|
||||
ss << "{ \"input_int\": 5 }";
|
||||
EXPECT_THAT(rest.performRestCall(ss), IsError("Couldn't output variable output_int"));
|
||||
}
|
524
core/rest/rest_ut/rest_schema_ut.cc
Normal file
524
core/rest/rest_ut/rest_schema_ut.cc
Normal file
@@ -0,0 +1,524 @@
|
||||
#include "rest.h"
|
||||
#include "shell_cmd.h"
|
||||
#include "rest_server.h"
|
||||
#include "cptest.h"
|
||||
#include "singleton.h"
|
||||
#include "mainloop.h"
|
||||
#include "encryptor.h"
|
||||
#include "proto_message_comp.h"
|
||||
#include "time_proxy.h"
|
||||
#include "environment.h"
|
||||
#include "config.h"
|
||||
#include "config_component.h"
|
||||
#include "agent_details.h"
|
||||
#include "messaging_buffer.h"
|
||||
#include "instance_awareness.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sstream>
|
||||
#include "customized_cereal_map.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class MustInt : public ServerRest
|
||||
{
|
||||
void doCall() override {}
|
||||
|
||||
C2S_PARAM(int, must_int);
|
||||
};
|
||||
|
||||
TEST(RestSchema, must_int)
|
||||
{
|
||||
stringstream schema;
|
||||
MustInt().performOutputingSchema(schema);
|
||||
EXPECT_EQ(
|
||||
schema.str(),
|
||||
"{\n"
|
||||
" \"properties\": {\n"
|
||||
" \"must_int\": {\n"
|
||||
" \"type\": \"integer\"\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"required\": [\n"
|
||||
" \"must_int\"\n"
|
||||
" ]\n"
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
class MustBool : public ServerRest
|
||||
{
|
||||
void doCall() override {}
|
||||
|
||||
C2S_PARAM(bool, must_bool);
|
||||
};
|
||||
|
||||
|
||||
TEST(RestSchema, must_bool)
|
||||
{
|
||||
stringstream schema;
|
||||
MustBool().performOutputingSchema(schema);
|
||||
EXPECT_EQ(
|
||||
schema.str(),
|
||||
"{\n"
|
||||
" \"properties\": {\n"
|
||||
" \"must_bool\": {\n"
|
||||
" \"type\": \"boolean\"\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"required\": [\n"
|
||||
" \"must_bool\"\n"
|
||||
" ]\n"
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
class MustString : public ServerRest
|
||||
{
|
||||
void doCall() override {}
|
||||
|
||||
C2S_PARAM(string, must_string);
|
||||
};
|
||||
|
||||
TEST(RestSchema, must_string)
|
||||
{
|
||||
stringstream schema;
|
||||
MustString().performOutputingSchema(schema);
|
||||
EXPECT_EQ(
|
||||
schema.str(),
|
||||
"{\n"
|
||||
" \"properties\": {\n"
|
||||
" \"must_string\": {\n"
|
||||
" \"type\": \"string\"\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"required\": [\n"
|
||||
" \"must_string\"\n"
|
||||
" ]\n"
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
class MustVectorInt : public ServerRest
|
||||
{
|
||||
void doCall() override {}
|
||||
|
||||
C2S_PARAM(vector<int>, must_vector);
|
||||
};
|
||||
|
||||
TEST(RestSchema, must_vector)
|
||||
{
|
||||
stringstream schema;
|
||||
MustVectorInt().performOutputingSchema(schema);
|
||||
EXPECT_EQ(
|
||||
schema.str(),
|
||||
"{\n"
|
||||
" \"properties\": {\n"
|
||||
" \"must_vector\": {\n"
|
||||
" \"type\": \"array\",\n"
|
||||
" \"items\": {\n"
|
||||
" \"type\": \"integer\"\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"required\": [\n"
|
||||
" \"must_vector\"\n"
|
||||
" ]\n"
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
class MustSetString : public ServerRest
|
||||
{
|
||||
void doCall() override {}
|
||||
|
||||
C2S_PARAM(set<string>, must_set);
|
||||
};
|
||||
|
||||
TEST(RestSchema, must_set)
|
||||
{
|
||||
stringstream schema;
|
||||
MustSetString().performOutputingSchema(schema);
|
||||
EXPECT_EQ(
|
||||
schema.str(),
|
||||
"{\n"
|
||||
" \"properties\": {\n"
|
||||
" \"must_set\": {\n"
|
||||
" \"type\": \"array\",\n"
|
||||
" \"items\": {\n"
|
||||
" \"type\": \"string\"\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"required\": [\n"
|
||||
" \"must_set\"\n"
|
||||
" ]\n"
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
class MustMapString : public ServerRest
|
||||
{
|
||||
void doCall() override {}
|
||||
|
||||
using mapStringString = map<string, string>;
|
||||
C2S_PARAM(mapStringString, must_map_string);
|
||||
};
|
||||
|
||||
class MustMapInt : public ServerRest
|
||||
{
|
||||
void doCall() override {}
|
||||
|
||||
using mapStringInt = map<string, int>;
|
||||
C2S_PARAM(mapStringInt, must_map_int);
|
||||
};
|
||||
|
||||
TEST(RestSchema, must_map)
|
||||
{
|
||||
stringstream string_map_schema;
|
||||
MustMapString().performOutputingSchema(string_map_schema);
|
||||
EXPECT_EQ(
|
||||
string_map_schema.str(),
|
||||
"{\n"
|
||||
" \"properties\": {\n"
|
||||
" \"must_map_string\": {\n"
|
||||
" \"type\": \"object\",\n"
|
||||
" \"additionalProperties\": {\n"
|
||||
" \"type\": \"string\"\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"required\": [\n"
|
||||
" \"must_map_string\"\n"
|
||||
" ]\n"
|
||||
"}"
|
||||
);
|
||||
|
||||
stringstream int_map_schema;
|
||||
MustMapInt().performOutputingSchema(int_map_schema);
|
||||
EXPECT_EQ(
|
||||
int_map_schema.str(),
|
||||
"{\n"
|
||||
" \"properties\": {\n"
|
||||
" \"must_map_int\": {\n"
|
||||
" \"type\": \"object\",\n"
|
||||
" \"additionalProperties\": {\n"
|
||||
" \"type\": \"integer\"\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"required\": [\n"
|
||||
" \"must_map_int\"\n"
|
||||
" ]\n"
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
class MustObject : public ServerRest
|
||||
{
|
||||
void doCall() override {}
|
||||
|
||||
C2S_PARAM(MustInt, must_object);
|
||||
};
|
||||
|
||||
TEST(RestSchema, must_object)
|
||||
{
|
||||
stringstream schema;
|
||||
MustObject().performOutputingSchema(schema);
|
||||
EXPECT_EQ(
|
||||
schema.str(),
|
||||
"{\n"
|
||||
" \"properties\": {\n"
|
||||
" \"must_object\": {\n"
|
||||
" \"type\": \"object\",\n"
|
||||
" {\n"
|
||||
" \"properties\": {\n"
|
||||
" \"must_int\": {\n"
|
||||
" \"type\": \"integer\"\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"required\": [\n"
|
||||
" \"must_int\"\n"
|
||||
" ]\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"required\": [\n"
|
||||
" \"must_object\"\n"
|
||||
" ]\n"
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
class OptionalInt: public ServerRest
|
||||
{
|
||||
void doCall() override {}
|
||||
|
||||
C2S_OPTIONAL_PARAM(int, optional_int);
|
||||
};
|
||||
|
||||
TEST(RestSchema, optional_int)
|
||||
{
|
||||
stringstream schema;
|
||||
OptionalInt().performOutputingSchema(schema);
|
||||
EXPECT_EQ(
|
||||
schema.str(),
|
||||
"{\n"
|
||||
" \"properties\": {\n"
|
||||
" \"optional_int\": {\n"
|
||||
" \"type\": \"integer\"\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"required\": [\n"
|
||||
" ]\n"
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
class DefaultInt : public ServerRest
|
||||
{
|
||||
void doCall() override {}
|
||||
|
||||
C2S_OPTIONAL_PARAM(int, default_int);
|
||||
};
|
||||
|
||||
TEST(RestSchema, default_int)
|
||||
{
|
||||
stringstream schema;
|
||||
DefaultInt().performOutputingSchema(schema);
|
||||
EXPECT_EQ(
|
||||
schema.str(),
|
||||
"{\n"
|
||||
" \"properties\": {\n"
|
||||
" \"default_int\": {\n"
|
||||
" \"type\": \"integer\"\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"required\": [\n"
|
||||
" ]\n"
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
class MustLabelInt : public ServerRest
|
||||
{
|
||||
void doCall() override {}
|
||||
|
||||
C2S_LABEL_PARAM(int, must_int, "must-int");
|
||||
};
|
||||
|
||||
TEST(RestSchema, must_int_label)
|
||||
{
|
||||
stringstream schema;
|
||||
MustLabelInt().performOutputingSchema(schema);
|
||||
EXPECT_EQ(
|
||||
schema.str(),
|
||||
"{\n"
|
||||
" \"properties\": {\n"
|
||||
" \"must-int\": {\n"
|
||||
" \"type\": \"integer\"\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"required\": [\n"
|
||||
" \"must-int\"\n"
|
||||
" ]\n"
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
class BothSidesInt : public ServerRest
|
||||
{
|
||||
public:
|
||||
void doCall() override { }
|
||||
|
||||
BOTH_PARAM(int, must_int);
|
||||
};
|
||||
|
||||
TEST(RestSchema, both_must_int)
|
||||
{
|
||||
stringstream schema;
|
||||
BothSidesInt().performOutputingSchema(schema);
|
||||
EXPECT_EQ(
|
||||
schema.str(),
|
||||
"{\n"
|
||||
" \"properties\": {\n"
|
||||
" \"must_int\": {\n"
|
||||
" \"type\": \"integer\"\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"required\": [\n"
|
||||
" \"must_int\"\n"
|
||||
" ]\n"
|
||||
"}"
|
||||
);
|
||||
}
|
||||
|
||||
TEST(RestSchema, copy_constructor)
|
||||
{
|
||||
BothSidesInt orig;
|
||||
orig.must_int = 99;
|
||||
BothSidesInt copy(orig);
|
||||
EXPECT_EQ(copy.must_int, 99);
|
||||
}
|
||||
|
||||
class TypedSchema : public ClientRest
|
||||
{
|
||||
public:
|
||||
S2C_PARAM(string, type);
|
||||
};
|
||||
|
||||
class ProperitiesSchema : public ClientRest
|
||||
{
|
||||
public:
|
||||
S2C_PARAM(TypedSchema, must_int);
|
||||
};
|
||||
|
||||
class GetSchema : public ClientRest
|
||||
{
|
||||
public:
|
||||
S2C_PARAM(vector<string>, required);
|
||||
S2C_PARAM(ProperitiesSchema, properties);
|
||||
};
|
||||
|
||||
TEST(RestSchema, server_schema)
|
||||
{
|
||||
AgentDetails agent_details;
|
||||
TimeProxyComponent time_proxy;
|
||||
MainloopComponent mainloop_comp;
|
||||
::Environment env;
|
||||
Encryptor encryptor;
|
||||
MessagingBuffer messaging_buffer;
|
||||
InstanceAwareness instance_awareness;
|
||||
ShellCmd cmd;
|
||||
ProtoMessageComp message;
|
||||
RestServer server;
|
||||
ConfigComponent config;
|
||||
|
||||
env.preload();
|
||||
setConfiguration(false, string("message"), string("HTTPS connection"));
|
||||
setConfiguration(uint(9777), string("connection"), string("Nano service API Port Primary"));
|
||||
setConfiguration(uint(9778), string("connection"), string("Nano service API Port Alternative"));
|
||||
Singleton::Consume<I_Environment>::from(env)->registerValue<string>("Executable Name", "a/b/");
|
||||
|
||||
messaging_buffer.init();
|
||||
message.init();
|
||||
server.init();
|
||||
cmd.init();
|
||||
time_proxy.init();
|
||||
mainloop_comp.init();
|
||||
auto api = Singleton::Consume<I_RestApi>::from(server);
|
||||
api->addRestCall<BothSidesInt>(RestAction::ADD, "int");
|
||||
|
||||
auto mainloop = Singleton::Consume<I_MainLoop>::from(mainloop_comp);
|
||||
|
||||
bool stop = false;
|
||||
I_MainLoop::Routine stop_routine = [&stop, mainloop] () {
|
||||
while (!stop) {
|
||||
mainloop->yield(true);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < 26; i++) {
|
||||
mainloop->yield(true);
|
||||
}
|
||||
|
||||
mainloop->stopAll();
|
||||
};
|
||||
mainloop->addOneTimeRoutine(
|
||||
I_MainLoop::RoutineType::RealTime,
|
||||
stop_routine,
|
||||
"RestSchema server_schema stop routine",
|
||||
true
|
||||
);
|
||||
|
||||
auto i_message = Singleton::Consume<I_Messaging>::from(message);
|
||||
I_MainLoop::Routine action = [&stop, i_message] () {
|
||||
GetSchema schema;
|
||||
Flags<MessageConnConfig> conn_flags;
|
||||
conn_flags.setFlag(MessageConnConfig::ONE_TIME_CONN);
|
||||
EXPECT_TRUE(
|
||||
i_message->sendObject(schema, I_Messaging::Method::GET, "127.0.0.1", 9777, conn_flags, "/add-int")
|
||||
);
|
||||
vector<string> expected_req = { "must_int" };
|
||||
EXPECT_EQ(schema.required.get(), expected_req);
|
||||
ProperitiesSchema properties(schema.properties.get());
|
||||
TypedSchema must_int(properties.must_int.get());
|
||||
EXPECT_EQ(must_int.type.get(), "integer");
|
||||
stop = true;
|
||||
};
|
||||
mainloop->addOneTimeRoutine(I_MainLoop::RoutineType::RealTime, action, "RestSchema server_schema action routine");
|
||||
|
||||
mainloop->run();
|
||||
server.fini();
|
||||
cmd.fini();
|
||||
time_proxy.fini();
|
||||
mainloop_comp.fini();
|
||||
}
|
||||
|
||||
TEST(RestSchema, short_connection_server)
|
||||
{
|
||||
TimeProxyComponent time_proxy;
|
||||
ProtoMessageComp message;
|
||||
AgentDetails agent_details;
|
||||
MainloopComponent mainloop_comp;
|
||||
::Environment env;
|
||||
RestServer server;
|
||||
ConfigComponent config;
|
||||
server.preload();
|
||||
env.init();
|
||||
time_proxy.init();
|
||||
|
||||
setConfiguration<uint>(uint(9777), string("connection"), string("Nano service API Port Range start"));
|
||||
setConfiguration<uint>(uint(9778), string("connection"), string("Nano service API Port Range end"));
|
||||
|
||||
Singleton::Consume<I_Environment>::from(env)->registerValue<bool>("Is Rest primary routine", true);
|
||||
server.init();
|
||||
|
||||
auto mainloop = Singleton::Consume<I_MainLoop>::from(mainloop_comp);
|
||||
|
||||
bool stop = false;
|
||||
I_MainLoop::Routine stop_routine = [&stop, mainloop] () {
|
||||
while (!stop) {
|
||||
mainloop->yield(true);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < 16; i++) {
|
||||
mainloop->yield(true);
|
||||
}
|
||||
|
||||
mainloop->stopAll();
|
||||
};
|
||||
mainloop->addOneTimeRoutine(
|
||||
I_MainLoop::RoutineType::RealTime,
|
||||
stop_routine,
|
||||
"RestSchema short_connection_server stop routine",
|
||||
true
|
||||
);
|
||||
|
||||
I_MainLoop::Routine action = [&] () {
|
||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd < 0) return;
|
||||
struct sockaddr_in sa;
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(9777);
|
||||
sa.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
mainloop->yield(true);
|
||||
if (connect(fd, (struct sockaddr*)&sa, sizeof(struct sockaddr)) < 0) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
close(fd);
|
||||
stop = true;
|
||||
};
|
||||
mainloop->addOneTimeRoutine(
|
||||
I_MainLoop::RoutineType::RealTime,
|
||||
action,
|
||||
"RestSchema short_connection_server action routine"
|
||||
);
|
||||
|
||||
mainloop->run();
|
||||
server.fini();
|
||||
time_proxy.fini();
|
||||
mainloop_comp.fini();
|
||||
}
|
Reference in New Issue
Block a user