Commit 3d2b8079 authored by Pavel Vainerman's avatar Pavel Vainerman

(http-resolver): inital commit

parent 3de8c3a4
......@@ -25,7 +25,8 @@
<BinDir name="./"/>
<LogDir name="./"/>
<DocDir name="./"/>
<LockDir name="./"/>
<LockDir name="/tmp/"/>
<HttpResolver name="HttpResolver"/>
<Services>
<LocalTimeService AskLifeTimeSEC="10" MaxCountTimers="100" name="TimeService"/>
<LocalInfoServer dbrepeat="1" name="InfoServer">
......
......@@ -590,6 +590,7 @@ AC_CONFIG_FILES([Makefile
extensions/tests/MQPerfTest/Makefile
extensions/LogDB/Makefile
extensions/LogDB/tests/Makefile
extensions/HttpResolver/Makefile
testsuite/Makefile
wrappers/Makefile
wrappers/python/lib/Makefile
......
/*
* Copyright (c) 2020 Pavel Vainerman.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, version 2.1.
*
* 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
* Lesser General Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// --------------------------------------------------------------------------
/*! \file
* \author Pavel Vainerman
*/
// --------------------------------------------------------------------------
#include <sstream>
#include <iomanip>
#include <unistd.h>
#include "unisetstd.h"
#include <Poco/Net/NetException.h>
#include "ujson.h"
#include "HttpResolver.h"
#include "Configuration.h"
#include "Exceptions.h"
#include "Debug.h"
#include "UniXML.h"
#include "HttpResolverSugar.h"
// --------------------------------------------------------------------------
using namespace uniset;
using namespace std;
// --------------------------------------------------------------------------
HttpResolver::HttpResolver( const string& name, int argc, const char* const* argv, const string& prefix ):
myname(name)
{
rlog = make_shared<DebugStream>();
auto logLevels = uniset::getArgParam("--" + prefix + "log-add-levels", argc, argv, "");
if( !logLevels.empty() )
rlog->addLevel( Debug::value(logLevels) );
std::string config = uniset::getArgParam("--confile", argc, argv, "configure.xml");
if( config.empty() )
throw SystemError("Unknown config file");
std::shared_ptr<UniXML> xml;
cout << myname << "(init): init from " << config << endl;
xml = make_shared<UniXML>();
try
{
xml->open(config);
}
catch( std::exception& ex )
{
throw ex;
}
xmlNode* cnode = xml->findNode(xml->getFirstNode(), "HttpResolver", name);
if( !cnode )
{
ostringstream err;
err << name << "(init): Not found confnode <HttpResolver name='" << name << "'...>";
rcrit << err.str() << endl;
throw uniset::SystemError(err.str());
}
UniXML::iterator it(cnode);
UniXML::iterator dirIt = xml->findNode(xml->getFirstNode(), "LockDir");
if( !dirIt )
{
ostringstream err;
err << name << "(init): Not found confnode <LockDir name='..'/>";
rcrit << err.str() << endl;
throw uniset::SystemError(err.str());
}
iorfile = make_shared<IORFile>(dirIt.getProp("name"));
rinfo << myname << "(init): iot directory: " << dirIt.getProp("name") << endl;
httpHost = uniset::getArgParam("--" + prefix + "httpserver-host", argc, argv, "localhost");
httpPort = uniset::getArgInt("--" + prefix + "httpserver-port", argc, argv, "8008");
httpCORS_allow = uniset::getArgParam("--" + prefix + "httpserver-cors-allow", argc, argv, httpCORS_allow);
httpReplyAddr = uniset::getArgParam("--" + prefix + "httpserver-reply-addr", argc, argv, "");
rinfo << myname << "(init): http server parameters " << httpHost << ":" << httpPort << endl;
Poco::Net::SocketAddress sa(httpHost, httpPort);
try
{
Poco::Net::HTTPServerParams* httpParams = new Poco::Net::HTTPServerParams;
int maxQ = uniset::getArgPInt("--" + prefix + "httpserver-max-queued", argc, argv, it.getProp("httpMaxQueued"), 100);
int maxT = uniset::getArgPInt("--" + prefix + "httpserver-max-threads", argc, argv, it.getProp("httpMaxThreads"), 3);
httpParams->setMaxQueued(maxQ);
httpParams->setMaxThreads(maxT);
httpserv = std::make_shared<Poco::Net::HTTPServer>(new HttpResolverRequestHandlerFactory(this), Poco::Net::ServerSocket(sa), httpParams );
}
catch( std::exception& ex )
{
std::stringstream err;
err << myname << "(init): " << httpHost << ":" << httpPort << " ERROR: " << ex.what();
throw uniset::SystemError(err.str());
}
}
//--------------------------------------------------------------------------------------------
HttpResolver::~HttpResolver()
{
if( httpserv )
httpserv->stop();
}
//--------------------------------------------------------------------------------------------
std::shared_ptr<HttpResolver> HttpResolver::init_resolver( int argc, const char* const* argv, const std::string& prefix )
{
string name = uniset::getArgParam("--" + prefix + "name", argc, argv, "HttpResolver");
if( name.empty() )
{
cerr << "(HttpResolver): Unknown name. Use --" << prefix << "name" << endl;
return nullptr;
}
return make_shared<HttpResolver>(name, argc, argv, prefix);
}
// -----------------------------------------------------------------------------
void HttpResolver::help_print()
{
cout << "Default: prefix='logdb'" << endl;
cout << "--prefix-single-confile conf.xml - Отдельный конфигурационный файл (не требующий структуры uniset)" << endl;
cout << "--prefix-name name - Имя. Для поиска настроечной секции в configure.xml" << endl;
cout << "http: " << endl;
cout << "--prefix-httpserver-host ip - IP на котором слушает http сервер. По умолчанию: localhost" << endl;
cout << "--prefix-httpserver-port num - Порт на котором принимать запросы. По умолчанию: 8080" << endl;
cout << "--prefix-httpserver-max-queued num - Размер очереди запросов к http серверу. По умолчанию: 100" << endl;
cout << "--prefix-httpserver-max-threads num - Разрешённое количество потоков для http-сервера. По умолчанию: 3" << endl;
cout << "--prefix-httpserver-cors-allow addr - (CORS): Access-Control-Allow-Origin. Default: *" << endl;
cout << "--prefix-httpserver-reply-addr host[:port] - Адрес отдаваемый клиенту для подключения. По умолчанию адрес узла где запущен logdb" << endl;
}
// -----------------------------------------------------------------------------
void HttpResolver::run()
{
if( httpserv )
httpserv->start();
pause();
}
// -----------------------------------------------------------------------------
class HttpResolverRequestHandler:
public Poco::Net::HTTPRequestHandler
{
public:
HttpResolverRequestHandler( HttpResolver* r ): resolver(r) {}
virtual void handleRequest( Poco::Net::HTTPServerRequest& request,
Poco::Net::HTTPServerResponse& response ) override
{
resolver->handleRequest(request, response);
}
private:
HttpResolver* resolver;
};
// -----------------------------------------------------------------------------
Poco::Net::HTTPRequestHandler* HttpResolver::HttpResolverRequestHandlerFactory::createRequestHandler( const Poco::Net::HTTPServerRequest& req )
{
return new HttpResolverRequestHandler(resolver);
}
// -----------------------------------------------------------------------------
void HttpResolver::handleRequest( Poco::Net::HTTPServerRequest& req, Poco::Net::HTTPServerResponse& resp )
{
using Poco::Net::HTTPResponse;
std::ostream& out = resp.send();
resp.setContentType("text/json");
resp.set("Access-Control-Allow-Methods", "GET");
resp.set("Access-Control-Allow-Request-Method", "*");
resp.set("Access-Control-Allow-Origin", httpCORS_allow /* req.get("Origin") */);
try
{
// В этой версии API поддерживается только GET
if( req.getMethod() != "GET" )
{
auto jdata = respError(resp, HTTPResponse::HTTP_BAD_REQUEST, "method must be 'GET'");
jdata->stringify(out);
out.flush();
return;
}
Poco::URI uri(req.getURI());
rlog3 << req.getHost() << ": query: " << uri.getQuery() << endl;
std::vector<std::string> seg;
uri.getPathSegments(seg);
// example: http://host:port/api/version/resolve/[json|text]
if( seg.size() < 4
|| seg[0] != "api"
|| seg[1] != HTTP_RESOLVER_API_VERSION
|| seg[2].empty()
|| seg[2] != "resolve")
{
ostringstream err;
err << "Bad request structure. Must be /api/" << HTTP_RESOLVER_API_VERSION << "/resolve/[json|text|help]?name";
auto jdata = respError(resp, HTTPResponse::HTTP_BAD_REQUEST, err.str());
jdata->stringify(out);
out.flush();
return;
}
auto qp = uri.getQueryParameters();
resp.setStatus(HTTPResponse::HTTP_OK);
string cmd = seg[3];
if( cmd == "help" )
{
using uniset::json::help::item;
uniset::json::help::object myhelp("help");
myhelp.emplace(item("help", "this help"));
myhelp.emplace(item("resolve?name", "resolve name"));
// myhelp.emplace(item("apidocs", "https://github.com/Etersoft/uniset2"));
item l("resolve?name", "resolve name");
l.param("format=json|text", "response format");
myhelp.add(l);
myhelp.get()->stringify(out);
}
else if( cmd == "json" )
{
auto json = httpJsonResolve(uri.getQuery(), qp);
json->stringify(out);
}
else if( cmd == "text" )
{
resp.setContentType("text/plain");
auto txt = httpTextResolve(uri.getQuery(), qp);
out << txt;
}
}
catch( std::exception& ex )
{
auto jdata = respError(resp, HTTPResponse::HTTP_INTERNAL_SERVER_ERROR, ex.what());
jdata->stringify(out);
}
out.flush();
}
// -----------------------------------------------------------------------------
Poco::JSON::Object::Ptr HttpResolver::respError( Poco::Net::HTTPServerResponse& resp,
Poco::Net::HTTPResponse::HTTPStatus estatus,
const string& message )
{
resp.setStatus(estatus);
resp.setContentType("text/json");
Poco::JSON::Object::Ptr jdata = new Poco::JSON::Object();
jdata->set("error", resp.getReasonForStatus(resp.getStatus()));
jdata->set("ecode", (int)resp.getStatus());
jdata->set("message", message);
return jdata;
}
// -----------------------------------------------------------------------------
Poco::JSON::Object::Ptr HttpResolver::httpJsonResolve( const std::string& query, const Poco::URI::QueryParameters& p )
{
auto iorstr = httpTextResolve(query, p);
Poco::JSON::Object::Ptr jdata = new Poco::JSON::Object();
jdata->set("ior", iorstr);
return jdata;
}
// -----------------------------------------------------------------------------
std::string HttpResolver::httpTextResolve( const std::string& query, const Poco::URI::QueryParameters& p )
{
if( query.empty() )
{
uwarn << myname << "undefined parameters for resolve" << endl;
return "";
}
if( uniset::is_digit(query) )
{
uinfo << myname << " resolve " << query << endl;
return iorfile->getIOR( uniset::uni_atoi(query) );
}
uwarn << myname << "unknown parameter type '" << query << "'" << endl;
return "";
}
// -----------------------------------------------------------------------------
/*
* Copyright (c) 2020 Pavel Vainerman.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, version 2.1.
*
* 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
* Lesser General Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// --------------------------------------------------------------------------
/*! \file
* \author Pavel Vainerman
*/
// --------------------------------------------------------------------------
#ifndef HttpResolver_H_
#define HttpResolver_H_
// --------------------------------------------------------------------------
#include <Poco/JSON/Object.h>
#include "UniSetTypes.h"
#include "DebugStream.h"
#include "UHttpRequestHandler.h"
#include "UHttpServer.h"
#include "IORFile.h"
// -------------------------------------------------------------------------
const std::string HTTP_RESOLVER_API_VERSION = "v01";
// -------------------------------------------------------------------------
namespace uniset
{
//------------------------------------------------------------------------------------------
/*!
\page page_HttpResolver Http-cервис для получения CORBA-ссылок на объекты (HttpResolver)
- \ref sec_HttpResolver_Comm
\section sec_HttpResolver_Comm Общее описание работы HttpResolver
HttpResolver это сервис, который отдаёт CORBA-ссылки (в виде строки)
на объекты запущенные на данном узле в режиме LocalIOR. Т.е. когда ссылки
публикуются в файлах.
*/
class HttpResolver:
public Poco::Net::HTTPRequestHandler
{
public:
HttpResolver( const std::string& name, int argc, const char* const* argv, const std::string& prefix );
virtual ~HttpResolver();
/*! глобальная функция для инициализации объекта */
static std::shared_ptr<HttpResolver> init_resolver( int argc, const char* const* argv, const std::string& prefix = "httpresolver-" );
/*! глобальная функция для вывода help-а */
static void help_print();
inline std::shared_ptr<DebugStream> log()
{
return rlog;
}
virtual void handleRequest( Poco::Net::HTTPServerRequest& req, Poco::Net::HTTPServerResponse& resp ) override;
void run();
protected:
Poco::JSON::Object::Ptr respError( Poco::Net::HTTPServerResponse& resp, Poco::Net::HTTPResponse::HTTPStatus s, const std::string& message );
Poco::JSON::Object::Ptr httpGetRequest( const std::string& cmd, const Poco::URI::QueryParameters& p );
Poco::JSON::Object::Ptr httpJsonResolve( const std::string& query, const Poco::URI::QueryParameters& p );
std::string httpTextResolve( const std::string& query, const Poco::URI::QueryParameters& p );
std::shared_ptr<Poco::Net::HTTPServer> httpserv;
std::string httpHost = { "" };
int httpPort = { 0 };
std::string httpCORS_allow = { "*" };
std::string httpReplyAddr = { "" };
std::shared_ptr<DebugStream> rlog;
std::string myname;
std::shared_ptr<IORFile> iorfile;
class HttpResolverRequestHandlerFactory:
public Poco::Net::HTTPRequestHandlerFactory
{
public:
HttpResolverRequestHandlerFactory( HttpResolver* r ): resolver(r) {}
virtual ~HttpResolverRequestHandlerFactory() {}
virtual Poco::Net::HTTPRequestHandler* createRequestHandler( const Poco::Net::HTTPServerRequest& req ) override;
private:
HttpResolver* resolver;
};
private:
};
// ----------------------------------------------------------------------------------
} // end of namespace uniset
//------------------------------------------------------------------------------------------
#endif
#ifndef HttpResolverSugar_H_
#define HttpResolverSugar_H_
// "синтаксический сахар"..для логов
#ifndef rinfo
#define rinfo if( rlog->debugging(Debug::INFO) ) rlog->info()
#endif
#ifndef rwarn
#define rwarn if( rlog->debugging(Debug::WARN) ) rlog->warn()
#endif
#ifndef rcrit
#define rcrit if( rlog->debugging(Debug::CRIT) ) rlog->crit()
#endif
#ifndef rlog1
#define rlog1 if( rlog->debugging(Debug::LEVEL1) ) rlog->level1()
#endif
#ifndef rlog2
#define rlog2 if( rlog->debugging(Debug::LEVEL2) ) rlog->level2()
#endif
#ifndef rlog3
#define rlog3 if( rlog->debugging(Debug::LEVEL3) ) rlog->level3()
#endif
#ifndef rlog4
#define rlog4 if( rlog->debugging(Debug::LEVEL4) ) rlog->level4()
#endif
#ifndef rlog5
#define rlog5 if( rlog->debugging(Debug::LEVEL5) ) rlog->level5()
#endif
#ifndef rlog6
#define rlog6 if( rlog->debugging(Debug::LEVEL6) ) rlog->level6()
#endif
#ifndef rlog7
#define rlog7 if( rlog->debugging(Debug::LEVEL7) ) rlog->level7()
#endif
#ifndef rlog8
#define rlog8 if( rlog->debugging(Debug::LEVEL8) ) rlog->level8()
#endif
#ifndef rlog9
#define rlog9 if( rlog->debugging(Debug::LEVEL9) ) rlog->level9()
#endif
#ifndef rlogany
#define rlogany rlog->any()
#endif
#endif
bin_PROGRAMS = @PACKAGE@-httpresolver
@PACKAGE@_httpresolver_LDADD = $(top_builddir)/lib/libUniSet2.la
@PACKAGE@_httpresolver_SOURCES = HttpResolver.cc main.cc
include $(top_builddir)/include.mk
#include "Configuration.h"
#include "HttpResolver.h"
// --------------------------------------------------------------------------
using namespace uniset;
using namespace std;
// --------------------------------------------------------------------------
int main(int argc, char** argv)
{
// std::ios::sync_with_stdio(false);
try
{
if( argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) )
{
cout << "--confile filename - configuration file. Default: configure.xml" << endl;
HttpResolver::help_print();
return 0;
}
auto resolver = HttpResolver::init_resolver(argc, argv);
if( !resolver )
return 1;
resolver->run();
return 0;
}
catch( const std::exception& ex )
{
cerr << "(HttpResolver::main): " << ex.what() << endl;
}
catch(...)
{
cerr << "(HttpResolver::main): catch ..." << endl;
}
return 1;
}
......@@ -7,7 +7,7 @@ SUBDIRS = lib include SharedMemory SharedMemory/tests IOControl IOControl/tests
ModbusMaster ModbusSlave SMViewer UniNetwork UNetUDP UNetUDP/tests \
DBServer-MySQL DBServer-SQLite DBServer-PostgreSQL MQTTPublisher \
RRDServer tests ModbusMaster/tests ModbusSlave/tests LogDB LogDB/tests \
Backend-OpenTSDB
Backend-OpenTSDB HttpResolver
pkgconfigdir = $(libdir)/pkgconfig
......
......@@ -33,11 +33,17 @@ namespace uniset
{
public:
static std::string getIOR( const ObjectId id );
static void setIOR( const ObjectId id, const std::string& sior );
static void unlinkIOR( const ObjectId id );
IORFile( const std::string& iordir );
~IORFile();
static std::string getFileName( const ObjectId id );
std::string getIOR( const ObjectId id );
void setIOR( const ObjectId id, const std::string& sior );
void unlinkIOR( const ObjectId id );
std::string getFileName( const ObjectId id );
private:
std::string iordir;
};
// -----------------------------------------------------------------------------------------
} // end of namespace
......
......@@ -68,7 +68,6 @@ ostream& uniset::Configuration::help(ostream& os)
print_help(os, 25, "--uniport num", "использовать заданный порт (переопределяет 'defaultport', заданный в конф. файле в разделе <nodes>)\n");
print_help(os, 25, "--localIOR {1,0}", "использовать локальные файлы для получения IOR (т.е. не использовать omniNames). Переопределяет параметр в конфигурационном файле.\n");
print_help(os, 25, "--transientIOR {1,0}", "использовать генерируемые IOR(не постоянные). Переопределяет параметр в конфигурационном файле. Default=1\n");
return os << "\nПример использования:\t myUniSetProgram "
<< "--ulog-add-levels level1,info,system,warn --ulog-logfile myprogrpam.log\n\n";
}
......@@ -195,8 +194,6 @@ namespace uniset
for( int i = 0; i < argc; i++ )
_argv[i] = uniset::uni_strdup(argv[i]);
iorfile = make_shared<IORFile>();
// инициализировать надо после argc,argv
if( fileConfName.empty() )
setConfFileName();
......@@ -298,6 +295,8 @@ namespace uniset
initParameters();
initRepSections();
iorfile = make_shared<IORFile>(getLockDir());
// localIOR
int lior = getArgInt("--localIOR");
......@@ -306,7 +305,6 @@ namespace uniset
// transientIOR
int tior = getArgInt("--transientIOR");
if( tior )
transientIOR = tior;
......
......@@ -19,13 +19,23 @@
#include <unistd.h>
#include "IORFile.h"
#include "Exceptions.h"
#include "Configuration.h"
#include "ORepHelpers.h"
// -----------------------------------------------------------------------------------------
using namespace uniset;
using namespace std;
// -----------------------------------------------------------------------------------------
IORFile::IORFile( const std::string& dir ):
iordir(dir)
{
}
// -----------------------------------------------------------------------------------------
IORFile::~IORFile()
{
}
// -----------------------------------------------------------------------------------------
string IORFile::getIOR( const ObjectId id )
{
string fname( getFileName(id) );
......@@ -42,10 +52,7 @@ void IORFile::setIOR( const ObjectId id, const string& sior )
ofstream ior_file(fname.c_str(), ios::out | ios::trunc);
if( !ior_file )
{
ucrit << "(IORFile): не смог открыть файл " + fname << endl;
throw ORepFailed("(IORFile): не смог создать ior-файл " + fname);
}
ior_file << sior << endl;
ior_file.close();
......@@ -60,7 +67,7 @@ void IORFile::unlinkIOR( const ObjectId id )
string IORFile::getFileName( const ObjectId id )
{
ostringstream fname;
fname << uniset_conf()->getLockDir() << id;
fname << iordir << id;
return fname.str();
}
// -----------------------------------------------------------------------------------------
......@@ -13,10 +13,9 @@ TEST_CASE("IORFile", "[iorfile][basic]" )
ObjectId testID = 1;
const std::string iorstr("testIORstring");
IORFile ior;
IORFile ior(uniset_conf()->getLockDir());
ior.setIOR(testID, iorstr);
REQUIRE( ior.getIOR(testID) == iorstr );
CHECK( file_exist(ior.getFileName(testID)) );
ior.unlinkIOR(testID);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment