diff --git a/libs/http_server/microhttpd_server_p.cpp b/libs/http_server/microhttpd_server_p.cpp index 13d5c0ea..1bc270ea 100644 --- a/libs/http_server/microhttpd_server_p.cpp +++ b/libs/http_server/microhttpd_server_p.cpp @@ -1,5 +1,4 @@ -#include "microhttpd_server_p.h" - +#include "microhttpd_server.h" #include "piliterals_string.h" #include "piliterals_time.h" @@ -126,7 +125,10 @@ void request_completed(void * cls, MHD_Connection * connection, void ** con_cls, MicrohttpdServerConnection *& conn((MicrohttpdServerConnection *&)(*con_cls)); // piCout << "request_completed" << conn << conn->headers << conn->post << '"' << conn->body << '"'; if (!conn) return; - if (conn->method == MicrohttpdServer::Method::Post && conn->postprocessor) MHD_destroy_post_processor(conn->postprocessor); + if (conn->postprocessor) { + MHD_destroy_post_processor(conn->postprocessor); + conn->postprocessor = nullptr; + } conn->ready(); piDeleteSafety(conn); } @@ -186,7 +188,7 @@ int answer_to_connection(void * cls, return MHD_NO; } - // piCout << "answer" << url << method << server; + piCout << "answer" << url << method << (int)m << server; MicrohttpdServerConnection *& conn((MicrohttpdServerConnection *&)(*con_cls)); if (!conn) { conn = new MicrohttpdServerConnection(); @@ -196,35 +198,25 @@ int answer_to_connection(void * cls, conn->method = m; MHD_get_connection_values(connection, MHD_HEADER_KIND, (MHD_KeyValueIterator)header_iterate, *con_cls); MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, (MHD_KeyValueIterator)args_iterate, *con_cls); + return MHD_YES; + } - if (m == MicrohttpdServer::Method::Post) { - // qDebug() << "new POST" << *upload_data_size; + if (m == MicrohttpdServer::Method::Unknown) { + return conn->send_error(); + } + + if (*upload_data_size) { + if (!conn->postprocessor) { conn->postprocessor = MHD_create_post_processor(connection, 65536, (MHD_PostDataIterator)iterate_post, (void *)conn); } - return MHD_YES; - } - - switch (m) { - case MicrohttpdServer::Method::Get: + conn->body.append(upload_data, *upload_data_size); + MHD_post_process(conn->postprocessor, upload_data, *upload_data_size); + *upload_data_size = 0; + } else { + // qDebug() << "answer ok"; if (!conn->ready()) return conn->send_error(); - return MHD_YES; - case MicrohttpdServer::Method::Post: - // qDebug() << "add POST" << *upload_data_size << PIString::fromUtf8(upload_data, *upload_data_size); - if (*upload_data_size) { - conn->body.append(upload_data, *upload_data_size); - if (conn->postprocessor) MHD_post_process(conn->postprocessor, upload_data, *upload_data_size); - *upload_data_size = 0; - return MHD_YES; - } else { - // qDebug() << "answer ok"; - if (!conn->ready()) return conn->send_error(); - return MHD_YES; - } - break; - default: break; } - - return conn->send_error(); + return MHD_YES; } diff --git a/libs/http_server/pihttpserver.cpp b/libs/http_server/pihttpserver.cpp index 3419ed42..5c49e64f 100644 --- a/libs/http_server/pihttpserver.cpp +++ b/libs/http_server/pihttpserver.cpp @@ -9,15 +9,20 @@ PIHTTPServer::PIHTTPServer() { rep.setCode(404); auto in_path = r.path.split("/"); in_path.removeAll(""); - auto it = functions.makeReverseIterator(); + auto it = functions.makeReverseIterator(); + bool found = false; while (it.next()) { if (it.value().function) { - if (it.value().match(in_path)) { - rep = it.value().function(r); - break; + if (it.value().method == r.method) { + if (it.value().match(in_path)) { + rep = it.value().function(r); + found = true; + break; + } } } } + if (!found && unhandled) rep = unhandled(r); auto hit = reply_headers.makeIterator(); while (hit.next()) rep.addHeader(hit.key(), hit.value()); @@ -31,14 +36,50 @@ PIHTTPServer::~PIHTTPServer() { } -void PIHTTPServer::registerPath(const PIString & path, RequestFunction functor) { - auto & ep(functions[path]); +void PIHTTPServer::registerPath(const PIString & path, Method method, RequestFunction functor) { + auto & ep(functions[path + PIString::fromNumber((int)method)]); ep.path = path.split("/"); + ep.method = method; ep.function = functor; ep.path.removeAll(""); } +void PIHTTPServer::registerUnhandled(RequestFunction functor) { + unhandled = functor; +} + + +void PIHTTPServer::unregisterPath(const PIString & path, Method method) { + auto pl = path.split("/"); + pl.removeAll(""); + auto it = functions.makeIterator(); + while (it.next()) { + if (it.value().method == method) { + if (it.value().path == pl) { + functions.remove(it.key()); + break; + } + } + } +} + + +void PIHTTPServer::unregisterPath(const PIString & path) { + auto pl = path.split("/"); + pl.removeAll(""); + auto it = functions.makeIterator(); + PIStringList keys; + while (it.next()) { + if (it.value().path == pl) { + keys << it.key(); + } + } + for (const auto & k: keys) + functions.remove(k); +} + + bool PIHTTPServer::Endpoint::match(const PIStringList & in_path) const { if (in_path.size() != path.size()) return false; for (int i = 0; i < path.size_s(); ++i) { diff --git a/libs/main/http_server/microhttpd_server_p.h b/libs/main/http_server/microhttpd_server.h similarity index 100% rename from libs/main/http_server/microhttpd_server_p.h rename to libs/main/http_server/microhttpd_server.h diff --git a/libs/main/http_server/pihttpserver.h b/libs/main/http_server/pihttpserver.h index e8b3d44f..39ef9947 100644 --- a/libs/main/http_server/pihttpserver.h +++ b/libs/main/http_server/pihttpserver.h @@ -1,8 +1,7 @@ #ifndef PIHTTPSERVER_H #define PIHTTPSERVER_H -#include "microhttpd_server_p.h" -#include "pip_http_server_export.h" +#include "microhttpd_server.h" class PIP_HTTP_SERVER_EXPORT PIHTTPServer: public MicrohttpdServer { PIOBJECT_SUBCLASS(PIHTTPServer, MicrohttpdServer) @@ -13,7 +12,10 @@ public: using RequestFunction = std::function; - void registerPath(const PIString & path, RequestFunction functor); + void registerPath(const PIString & path, MicrohttpdServer::Method method, RequestFunction functor); + void registerUnhandled(RequestFunction functor); + void unregisterPath(const PIString & path, MicrohttpdServer::Method method); + void unregisterPath(const PIString & path); void addReplyHeader(const PIString & name, const PIString & value) { reply_headers[name] = value; } void removeReplyHeader(const PIString & name) { reply_headers.remove(name); } @@ -23,10 +25,12 @@ private: struct Endpoint { bool match(const PIStringList & in_path) const; PIStringList path; + MicrohttpdServer::Method method = MicrohttpdServer::Method::Unknown; RequestFunction function; }; PIMap reply_headers; PIMap functions; + RequestFunction unhandled; }; diff --git a/libs/main/http_server/pihttpservermodule.h b/libs/main/http_server/pihttpservermodule.h new file mode 100644 index 00000000..049774b9 --- /dev/null +++ b/libs/main/http_server/pihttpservermodule.h @@ -0,0 +1,54 @@ +/* + PIP - Platform Independent Primitives + Module includes + Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru + + 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, 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 Lesser General 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 . +*/ +//! \defgroup HTTPServer HTTPServer +//! \~\brief +//! \~english HTTP server +//! \~russian HTTP сервер +//! +//! \~\details +//! \~english \section cmake_module_HTTPServer Building with CMake +//! \~russian \section cmake_module_HTTPServer Сборка с использованием CMake +//! +//! \~\code +//! find_package(PIP REQUIRED) +//! target_link_libraries([target] PIP::HTTPServer) +//! \endcode +//! +//! \~english \par Common +//! \~russian \par Общее +//! +//! \~english +//! These files provides HTTP server based on libmicrohttpd +//! +//! \~russian +//! Эти файлы обеспечивают HTTP сервер, основанный на libmicrohttpd +//! +//! \~\authors +//! \~english +//! Ivan Pelipenko peri4ko@yandex.ru; +//! \~russian +//! Иван Пелипенко peri4ko@yandex.ru; +//! + +#ifndef pihttpservermodule_H +#define pihttpservermodule_H + +#include "pihttpserver.h" + +#endif diff --git a/libs/main/pip.h b/libs/main/pip.h index 297f31d6..066530f0 100644 --- a/libs/main/pip.h +++ b/libs/main/pip.h @@ -27,6 +27,7 @@ #include "picoremodule.h" #include "picryptmodule.h" #include "pigeomodule.h" +#include "pihttpservermodule.h" #include "piiodevicesmodule.h" #include "piioutilsmodule.h" #include "piliterals.h" diff --git a/libs/main/thread/piprotectedvariable.h b/libs/main/thread/piprotectedvariable.h index b1646764..606cccfc 100644 --- a/libs/main/thread/piprotectedvariable.h +++ b/libs/main/thread/piprotectedvariable.h @@ -32,6 +32,28 @@ template class PIP_EXPORT PIProtectedVariable { public: + //! \~english + //! \~russian + class PIP_EXPORT Pointer { + friend class PIProtectedVariable; + + public: + Pointer(const Pointer & v): pv(v.pv), counter(v.counter + 1) {} + ~Pointer() { + if (counter == 0) pv.mutex.unlock(); + } + + T * operator->() { return &pv.var; } + T & operator*() { return pv.var; } + + private: + Pointer() = delete; + Pointer(PIProtectedVariable & v): pv(v) {} + + PIProtectedVariable & pv; + int counter = 0; + }; + //! \~english Sets value to \"v\" //! \~russian Устанавливает значение как \"v\" void set(T v) { @@ -39,6 +61,13 @@ public: var = std::move(v); } + //! \~english Lock mutex and returns reference wrapper of value. Unlock on variable destructor. + //! \~russian Блокирует мьютекс и возвращает класс-обертку на значение. Разблокирует в деструкторе переменной. + Pointer getRef() { + mutex.lock(); + return Pointer(*this); + } + //! \~english Returns copy of value //! \~russian Возвращает копию значения T get() const { @@ -46,17 +75,6 @@ public: return var; } - //! \~english Lock mutex and returns reference of value - //! \~russian Блокирует мьютекс и возвращает ссылку на значение - T & lock() { - mutex.lock(); - return var; - } - - //! \~english Unlock mutex - //! \~russian Разблокирует мьютекс - void unlock() { mutex.unlock(); } - //! \~english Sets value to \"v\" //! \~russian Устанавливает значение как \"v\" PIProtectedVariable & operator=(T v) { diff --git a/main.cpp b/main.cpp index df656680..3acfe5ac 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,3 @@ -#include "pihttpserver.h" #include "pip.h" using namespace PICoutManipulators; @@ -22,31 +21,38 @@ int main(int argc, char * argv[]) { server.listen({"127.0.0.1", 7777}); // server.listen({"192.168.1.10", 7778}); - server.registerPath("/", [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { + server.registerPath("/", MicrohttpdServer::Method::Get, [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { MicrohttpdServer::Reply ret; ret.setBody(PIByteArray::fromAscii(pageTitle)); return ret; }); - server.registerPath("/html", [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { + server.registerPath("/html", MicrohttpdServer::Method::Get, [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { MicrohttpdServer::Reply ret; ret.setBody("

arg=%1

"_a.arg(r.args.value("a0")).toUTF8()); return ret; }); - server.registerPath("/api", [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { + server.registerPath("/api", MicrohttpdServer::Method::Put, [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { MicrohttpdServer::Reply ret; ret.setBody(PIByteArray::fromAscii("API")); return ret; }); - server.registerPath("/api/*", [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { + server.registerPath("/api/*", MicrohttpdServer::Method::Post, [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { MicrohttpdServer::Reply ret; ret.setBody("API etry %1"_a.arg(r.path).toUTF8()); ret.setCode(405); return ret; }); + server.registerUnhandled([](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { + MicrohttpdServer::Reply ret; + ret.setBody("Unknown"_a.arg(r.path).toUTF8()); + ret.setCode(404); + return ret; + }); + /*server.setRequestCallback([](MicrohttpdServer::Request r) -> MicrohttpdServer::Reply { MicrohttpdServer::Reply rep; piCout << "request" << r.path;