diff --git a/server/Makefile b/server/Makefile index 503e3d5..ccf1da1 100644 --- a/server/Makefile +++ b/server/Makefile @@ -64,9 +64,6 @@ endif ifdef ETPAN_LIB EXTRA_LIB_PATHS:=$(EXTRA_LIB_PATHS)$(patsubst -L%,%,$(ETPAN_LIB)): endif -ifdef EVENT_LIB - EXTRA_LIB_PATHS:=$(EXTRA_LIB_PATHS)$(patsubst -L%,%,$(EVENT_LIB)): -endif ifdef GPGME_LIB EXTRA_LIB_PATHS:=$(EXTRA_LIB_PATHS)$(patsubst -L%,%,$(GPGME_LIB)): endif diff --git a/server/Makefile.conf b/server/Makefile.conf index f1426ce..e044d8a 100644 --- a/server/Makefile.conf +++ b/server/Makefile.conf @@ -60,11 +60,11 @@ WARN=placeholder TARGET_ARCH= ifeq ($(BUILD_FOR),Linux) - CXX=g++ -std=c++11 -pthread -fPIC + CXX=g++ -std=c++14 -pthread -fPIC else ifeq ($(BUILD_FOR),Darwin) # clang issues a warning when "-pthread" is used for linking. So, include it in CXXFLAGS, and not in CXX # "c++" != "clang" - CXX=c++ -std=c++11 -fPIC + CXX=c++ -std=c++14 -fPIC endif HTML_DIR ?= "html" @@ -72,7 +72,7 @@ HTML_DIR ?= "html" CPPFLAGS=-isystem $(GTEST_DIR)/include -DHTML_DIR=$(HTML_DIR) ifeq ($(BUILD_FOR),Linux) - CXXFLAGS=-fstack-protector-all -fdiagnostics-color=always + CXXFLAGS=-fstack-protector-all # -fdiagnostics-color=always ifdef WARN CXXFLAGS+= -Wall else @@ -84,7 +84,7 @@ ifeq ($(BUILD_FOR),Linux) CXXFLAGS+= -O3 -DNDEBUG endif else ifeq ($(BUILD_FOR),Darwin) - CXXFLAGS=-pthread -fstack-protector-all -fcolor-diagnostics + CXXFLAGS=-pthread -fstack-protector-all # -fcolor-diagnostics ifdef WARN CXXFLAGS+= -Wall else @@ -97,13 +97,13 @@ else ifeq ($(BUILD_FOR),Darwin) endif endif -CXXFLAGS+= -Ijson_spirit $(BOOST_INC) $(ENGINE_INC) $(ETPAN_INC) $(EVENT_INC) $(GPGME_INC) $(UUID_INC) -LDFLAGS+= $(BOOST_LIB) $(ENGINE_LIB) $(ETPAN_LIB) $(EVENT_LIB) $(GPGME_LIB) $(UUID_LIB) +CXXFLAGS+= -Ijson_spirit $(BOOST_INC) $(ENGINE_INC) $(ETPAN_INC) $(GPGME_INC) $(UUID_INC) +LDFLAGS+= $(BOOST_LIB) $(ENGINE_LIB) $(ETPAN_LIB) $(GPGME_LIB) $(UUID_LIB) ifeq ($(BUILD_FOR),Linux) - LDLIBS+= -levent -lpEpEngine -lpEpAdapter -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread -Wl,-rpath-link="$(patsubst -L%,%,$(ETPAN_LIB))" + LDLIBS+= -lpEpEngine -lpEpAdapter -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread -Wl,-rpath-link="$(patsubst -L%,%,$(ETPAN_LIB))" GTEST_DIR=/usr/src/googletest/googletest/ else ifeq ($(BUILD_FOR),Darwin) - LDLIBS+= -levent -lpEpEngine -lpEpAdapter -lboost_system-mt -lboost_filesystem-mt -lboost_program_options-mt -lboost_thread-mt + LDLIBS+= -lpEpEngine -lpEpAdapter -lboost_system-mt -lboost_filesystem-mt -lboost_program_options-mt -lboost_thread-mt endif ######### Google Test ######### diff --git a/server/ev_server.cc b/server/ev_server.cc index 7e4fbac..13aebe6 100644 --- a/server/ev_server.cc +++ b/server/ev_server.cc @@ -1,4 +1,4 @@ -#include +#include #include "ev_server.hh" #include "c_string.hh" @@ -58,13 +58,6 @@ std::string getBinaryPath() } -void connection_close_cb(evhttp_connection* conn, void* arg) -{ - JsonAdapter::getInstance().connection_close_cb(); -} - - - // these are the pEp functions that are callable by the client const FunctionMap functions = { @@ -168,7 +161,7 @@ const FunctionMap functions = { } // end of anonymous namespace -void ev_server::sendReplyString(evhttp_request* req, const char* contentType, const std::string& outputText) +void ev_server::sendReplyString(pEp::Webserver::request& req, const char* contentType, const std::string& outputText) { auto* outBuf = evhttp_request_get_output_buffer(req); if (!outBuf) @@ -192,7 +185,7 @@ void ev_server::sendReplyString(evhttp_request* req, const char* contentType, co } -void ev_server::sendFile( evhttp_request* req, const std::string& mimeType, const fs::path& fileName) +void ev_server::sendFile(pEp::Webserver::request& req, const std::string& mimeType, const fs::path& fileName) { auto* outBuf = evhttp_request_get_output_buffer(req); if (!outBuf) @@ -213,7 +206,7 @@ struct FileRequest }; // catch-all callback -void ev_server::OnOtherRequest(evhttp_request* req, void*) +pEp::Webserver::response ev_server::OnOtherRequest(boost::cmatch match, const pEp::Webserver::request& req) { static const std::map files = { @@ -223,23 +216,19 @@ void ev_server::OnOtherRequest(evhttp_request* req, void*) { "/favicon.ico" , {"image/vnd.microsoft.icon", path_to_html / "json-test.ico"} }, }; - const evhttp_uri* uri = evhttp_request_get_evhttp_uri(req); - const char* path = evhttp_uri_get_path(uri); - const char* uri_string = evhttp_request_get_uri(req); - Log() << Logger::Debug << "** Request: [" << uri_string << "] " << (path? " Path: [" + std::string(path) + "]" : "null path") << "\n"; + const sv path = req.target(); // NB: is percent-encoded! does not relevant for the supported paths above. + + Log() << Logger::Debug << "** Request: [" << request.method_string() << "] " << "Path: [" + path + "]"; try{ - if(path) + const auto q = files.find(path); + if(q != files.end()) // found in "files" map { - const auto q = files.find(path); - if(q != files.end()) // found in "files" map - { Log() << Logger::Debug << "\t found file \"" << q->second.fileName.string() << "\", type=" << q->second.mimeType << ".\n"; - sendFile( req, q->second.mimeType, q->second.fileName); - return; - } + sendFile( req, q->second.mimeType, q->second.fileName); + return; } - + const std::string reply = std::string("URI \"") + uri_string + "\" not found! " + (!path ? "NULL Path" : "Path: \"" + std::string(path) + "\""); evhttp_send_error(req, HTTP_NOTFOUND, reply.c_str()); @@ -256,7 +245,7 @@ void ev_server::OnOtherRequest(evhttp_request* req, void*) // generate a JavaScript file containing the definition of all registered callable functions, see above. -void ev_server::OnGetFunctions(evhttp_request* req, void*) +pEp::Webserver::response ev_server::OnGetFunctions(boost::cmatch match, const pEp::Webserver::request& req) { static const auto& version = server_version(); static const std::string preamble = @@ -296,7 +285,7 @@ void ev_server::OnGetFunctions(evhttp_request* req, void*) } -void ev_server::OnApiRequest(evhttp_request* req, void* obj) +pEp::Webserver::response ev_server::OnApiRequest(boost::cmatch match, const pEp::Webserver::request& req) { Logger L( Log(), "OnApiReq"); evbuffer* inbuf = evhttp_request_get_input_buffer(req); diff --git a/server/ev_server.hh b/server/ev_server.hh index b03651e..be01756 100644 --- a/server/ev_server.hh +++ b/server/ev_server.hh @@ -5,8 +5,8 @@ #include #include +#include -struct evhttp_request; class Logger; @@ -14,22 +14,22 @@ class ev_server { public: static - void sendReplyString(evhttp_request* req, const char* contentType, const std::string& outputText); + void sendReplyString(pEp::Webserver::request& req, const char* contentType, const std::string& outputText); static - void sendFile( evhttp_request* req, const std::string& mimeType, const boost::filesystem::path& fileName); + void sendFile( pEp::Webserver::request& req, const std::string& mimeType, const boost::filesystem::path& fileName); // catch-all callback. Used by demo html & JavaScript client to deliver static HTML & JS files static - void OnOtherRequest(evhttp_request* req, void*); + pEp::Webserver::response OnOtherRequest(boost::cmatch match, const pEp::Webserver::request& req); // generate a JavaScript file containing the definition of all registered callable functions, see above. static - void OnGetFunctions(evhttp_request* req, void*); + pEp::Webserver::response OnGetFunctions(boost::cmatch match, const pEp::Webserver::request& req); // handles calls to the JSON-RPC API static - void OnApiRequest(evhttp_request* req, void* obj); + pEp::Webserver::response OnApiRequest(boost::cmatch match, const pEp::Webserver::request& req); // should be set before any of the methods above is called, due to static initializers use that value, // so changing it later might be useless. diff --git a/server/json-adapter.cc b/server/json-adapter.cc index 9057dc5..f109eea 100644 --- a/server/json-adapter.cc +++ b/server/json-adapter.cc @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include @@ -43,7 +43,6 @@ namespace { std::string BaseUrl = "/ja/0.1/"; -int SrvThreadCount = 3; const std::string CreateSessionUrl = BaseUrl + "createSession"; const std::string GetAllSessionsUrl = BaseUrl + "getAllSessions"; @@ -52,15 +51,6 @@ const std::string ApiRequestUrl = BaseUrl + "callFunction"; const uint64_t Guard_0 = 123456789; const uint64_t Guard_1 = 987654321; -auto ThreadDeleter = [](std::thread* t) -{ - auto& session_registry = JsonAdapter::getSessionRegistry(); - session_registry.remove(t->get_id()); -}; - - -typedef std::unique_ptr ThreadPtr; -typedef std::vector ThreadPool; typedef std::recursive_mutex Mutex; typedef std::unique_lock Lock; @@ -83,8 +73,6 @@ JsonAdapter* JsonAdapter::singleton = nullptr; struct JsonAdapter::Internal { - std::unique_ptr eventBase = {nullptr, &event_base_free}; - std::unique_ptr evHttp = {nullptr, &evhttp_free}; std::unique_ptr session_registry{}; std::string address; std::string token; @@ -94,16 +82,14 @@ struct JsonAdapter::Internal unsigned start_port = 0; unsigned end_port = 0; unsigned port = 0; + std::unique_ptr webserver; inject_sync_event_t inject_sync_event = nullptr; unsigned request_count = 0; - evutil_socket_t sock = -1; bool running = false; bool silent = false; bool ignore_session_error = false; bool deliver_html = true; - ThreadPool threads; -// PEP_SESSION session = nullptr; explicit Internal() : Log("JAI") @@ -114,32 +100,7 @@ struct JsonAdapter::Internal ~Internal() { -// if(session) -// pEp::call_with_lock(&release, session); -// session=nullptr; - } - - static - void requestDone(evhttp_request* req, void* userdata) - { - // Hum, what is to do here? } - -/* - static - PEP_STATUS deliverRequest(const std::string& uri, evhttp_connection* connection, const js::Object& request) - { - const std::string request_s = js::write(request, js::raw_utf8); - evhttp_request* ereq = evhttp_request_new( &requestDone, nullptr ); // ownership goes to the connection in evhttp_make_request() below. - evhttp_add_header(ereq->output_headers, "Content-Length", std::to_string(request_s.length()).c_str()); - auto output_buffer = evhttp_request_get_output_buffer(ereq); - evbuffer_add(output_buffer, request_s.data(), request_s.size()); - - const int ret = evhttp_make_request(connection, ereq, EVHTTP_REQ_POST, uri.c_str() ); - - return (ret == 0) ? PEP_STATUS_OK : PEP_UNKNOWN_ERROR; - } -*/ void makeAndDeliverRequest(const char* function_name, const js::Array& params) { @@ -195,7 +156,6 @@ ServerVersion JsonAdapter::version() PEP_STATUS JsonAdapter::messageToSend(message* msg) { -// JsonAdapter* ja = static_cast(obj); js::Value v{to_json(msg)}; getInstance().i->makeAndDeliverRequest("messageToSend", js::Array{ std::move(v) } ); return PEP_STATUS_OK; @@ -204,7 +164,6 @@ PEP_STATUS JsonAdapter::messageToSend(message* msg) PEP_STATUS JsonAdapter::notifyHandshake(pEp_identity* self, pEp_identity* partner, sync_handshake_signal sig) { -// JsonAdapter* ja = static_cast(obj); js::Array param_array; param_array.emplace_back( to_json(self) ); param_array.emplace_back( to_json(partner) ); @@ -219,13 +178,7 @@ JsonAdapter::JsonAdapter() , i(new Internal{}) , guard_1(Guard_1) { - i->eventBase.reset(event_base_new()); - if (!i->eventBase) - throw std::runtime_error("Failed to create new base_event."); - - i->evHttp.reset( evhttp_new(i->eventBase.get()) ); - if (!i->evHttp) - throw std::runtime_error("Failed to create new evhttp."); + // nothing to do here. } @@ -265,99 +218,20 @@ void JsonAdapter::prepare_run(const std::string& address, unsigned start_port, u i->start_port = start_port; i->end_port = end_port; -// Log() << "ThreadFunc: thread id " << std::this_thread::get_id() << ". \n Registry: " << to_string( session_registry ); - - unsigned port_ofs = 0; -try_next_port: - auto* boundSock = evhttp_bind_socket_with_handle(i->evHttp.get(), i->address.c_str(), i->start_port + port_ofs); - if (!boundSock) + // pEp::Webserver does not support port probing, yet. + i->webserver = std::make_unique(pEp::net::ip::address::from_string(address), start_port, ""); + + i->webserver->add_url_handler(ApiRequestUrl, ev_server::OnApiRequest); + if(i->deliver_html) { - ++port_ofs; - if(i->start_port + port_ofs > i->end_port) - { - throw std::runtime_error("Failed to bind server socket: " - "No free port between " + std::to_string(i->start_port) + " and " + std::to_string(i->end_port) - ); - } - goto try_next_port; + i->webserver->add_url_handler("/pEp_functions.js", ev_server::OnGetFunctions); + i->webserver->add_generic_url_handler(ev_server::OnOtherRequest); } - if ((i->sock = evhttp_bound_socket_get_fd(boundSock)) == -1) - throw std::runtime_error("Failed to get server socket for next instance."); - - i->port = i->start_port + port_ofs; + i->port = i->start_port; i->token = create_security_token(i->address, i->port, BaseUrl); - Log() << "Bound to port " << i->port << ", sec_token=\"" << i->token << "\", sock=" << i->sock << "."; -} - - -void JsonAdapter::threadFunc() -{ - Logger L("JA:tF"); - try - { - const auto id = std::this_thread::get_id(); - L << Logger::Info << " +++ Thread starts: isRun=" << i->running << ", id=" << id << ". +++"; - PEP_SESSION session = i->session_registry->get(id); - (void)session; // not used, yet. - - std::unique_ptr eventBase(event_base_new(), &event_base_free); - if (!eventBase) - throw std::runtime_error("Failed to create new base_event."); - - std::unique_ptr evHttp(evhttp_new(eventBase.get()), &evhttp_free); - if (!evHttp) - throw std::runtime_error("Failed to create new evhttp."); - - evhttp_set_cb(evHttp.get(), ApiRequestUrl.c_str() , ev_server::OnApiRequest , this); - - if(i->deliver_html) - { - evhttp_set_cb(evHttp.get(), "/pEp_functions.js" , ev_server::OnGetFunctions , this); - evhttp_set_gencb(evHttp.get(), ev_server::OnOtherRequest, nullptr); - } - - if (i->sock == -1) // no port bound, yet - { - throw std::runtime_error("You have to call prepare_run() before run()!"); - } - else - { - L << Logger::Info << "\tnow I call evhttp_accept_socket()..."; - if (evhttp_accept_socket(evHttp.get(), i->sock) == -1) - throw std::runtime_error("Failed to accept() on server socket for new instance."); - } - - while(i->running) - { -#ifdef EVLOOP_NO_EXIT_ON_EMPTY - // for libevent 2.1: - event_base_loop(eventBase.get(), EVLOOP_NO_EXIT_ON_EMPTY); -#else - // for libevent 2.0: - event_base_loop(eventBase.get(), 0); -#endif - } - - Lock L{_mtx}; - i->eventListener.erase( std::this_thread::get_id() ); - } - catch (const std::exception& e) - { - L << Logger::Error << " +++ std::exception in ThreadFunc: " << e.what(); - initExcept = std::current_exception(); - Lock L{_mtx}; - i->eventListener.erase( std::this_thread::get_id() ); - } - catch (...) - { - L << Logger::Crit << " +++ UNKNOWN EXCEPTION in ThreadFunc +++ "; - initExcept = std::current_exception(); - Lock L{_mtx}; - i->eventListener.erase( std::this_thread::get_id() ); - } - L << Logger::Info << " +++ Thread exit? isRun=" << i->running << ", id=" << std::this_thread::get_id() << ". initExcept is " << (initExcept?"":"not ") << "set. +++"; + Log() << "Bound to port " << i->port << ", sec_token=\"" << i->token << "\"."; } @@ -371,24 +245,7 @@ try L << Logger::Debug << "Registry:\n" << i->session_registry->to_string(); i->running = true; - for(int t=0; tjoin(); - i->running = false; - std::rethrow_exception(initExcept); - } - i->threads.push_back(std::move(thread)); - } - L << Logger::Debug << "All " << SrvThreadCount << " thread(s) started:"; - for(const auto& t:i->threads) - { - L << Logger::Debug << "\tthread_id()=" << t->get_id() << "."; - } + i->webserver->run(); } catch (std::exception const &e) { @@ -415,22 +272,7 @@ void JsonAdapter::shutdown(timeval* t) check_guard(); Log() << "JS::shutdown() was called."; i->running = false; - - /**** FIXME: proper shutdown! - const int ret = event_base_loopexit(i->eventBase.get(), t); - if(ret!=0) - { - throw std::runtime_error("JsonAdapter::shutdown() failed."); - } - ****/ - Log() << "JS::shutdown(): event_base loop is finished.\n"; - Log() << "\t there are " << i->threads.size() << " threads remaining in the threadpool."; - for(const auto& t : i->threads) - { - Log() << "\t\tjoin() on id=" << t->get_id() << "...."; - t->join(); - } - i->threads.clear(); + i->webserver->shutdown(); } @@ -449,12 +291,7 @@ bool JsonAdapter::verify_security_token(const std::string& s) const void JsonAdapter::augment(json_spirit::Object& returnObject) { check_guard(); - /* - PEP_SESSION session = this->i->session; - auto errorstack = get_errorstack(session); - returnObject.emplace_back( "errorstack", to_json(errorstack) ); - clear_errorstack(session); - */ + // nothing to do anymore. }