diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e248eaf..b214b66a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,7 +97,7 @@ set(PIP_UTILS_LIST) set(PIP_TESTS_LIST) set(PIP_EXPORTS) -set(PIP_SRC_MODULES "console;crypt;compress;usb;fftw;opencl;io_utils;client_server;cloud;lua") +set(PIP_SRC_MODULES "console;crypt;compress;usb;fftw;opencl;io_utils;client_server;cloud;lua;http_server") foreach(_m ${PIP_SRC_MODULES}) set(PIP_MSG_${_m} "no") endforeach() @@ -481,6 +481,33 @@ if (NOT CROSSTOOLS) list(APPEND HDR_DIRS "${_lua_bri_dir}/LuaBridge") list(APPEND HDRS ${_lua_src_hdr}) + # libmicrohttpd + find_library(microhttpd_LIBRARIES microhttpd HINTS "${MINGW_LIB}") + if (microhttpd_LIBRARIES) + set(_microhttpd_add_libs) + if(WIN32) + if("${C_COMPILER}" STREQUAL "cl.exe") + else() + list(APPEND _microhttpd_add_libs ws2_32) + endif() + else() + list(APPEND _microhttpd_add_libs dl) + find_library(tls_lib gnutls) + if (tls_lib) + list(APPEND _microhttpd_add_libs ${tls_lib}) + endif() + if(DEFINED ENV{QNX_HOST}) + list(APPEND _microhttpd_add_libs socket) + else() + if (NOT DEFINED ANDROID_PLATFORM) + list(APPEND _microhttpd_add_libs pthread util) + endif() + endif() + endif() + list(APPEND microhttpd_LIBRARIES "${_microhttpd_add_libs}") + pip_module(http_server "${microhttpd_LIBRARIES}" "PIP HTTP server" "" "" "") + endif() + # Test program if(PIP_UTILS) @@ -488,7 +515,7 @@ if (NOT CROSSTOOLS) #target_link_libraries(pip_plugin pip) add_executable(pip_test "main.cpp") - target_link_libraries(pip_test pip pip_io_utils pip_client_server) + target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server) if(sodium_FOUND) add_executable(pip_cloud_test "main_picloud_test.cpp") target_link_libraries(pip_cloud_test pip_cloud) diff --git a/cmake/FindPIP.cmake b/cmake/FindPIP.cmake index 1730ae9f..223c2655 100644 --- a/cmake/FindPIP.cmake +++ b/cmake/FindPIP.cmake @@ -12,6 +12,7 @@ Create imported targets: * PIP::ClientServer * PIP::Cloud * PIP::Lua + * PIP::HTTPServer These targets include directories and depends on main library @@ -23,10 +24,10 @@ include(SHSTKMacros) shstk_set_find_dirs(PIP) -set(__libs "usb;crypt;console;fftw;compress;opencl;io_utils;client_server;cloud;lua") +set(__libs "usb;crypt;console;fftw;compress;opencl;io_utils;client_server;cloud;lua;http_server") if (BUILDING_PIP) - #set(_libs "pip;pip_usb;pip_console;pip_crypt;pip_fftw;pip_compress;pip_opencl;pip_io_utils;pip_cloud;pip_lua") + #set(_libs "pip;pip_usb;pip_console;pip_crypt;pip_fftw;pip_compress;pip_opencl;pip_io_utils;pip_cloud;pip_lua;pip_http_server") #set(_bins "pip_cmg;pip_rc;deploy_tool") #get_target_property(_path pip BINARY_DIR) #get_target_property(_path pip LIBRARY_OUTPUT_NAME) @@ -96,6 +97,7 @@ set(__module_io_utils IOUtils ) set(__module_client_server ClientServer) set(__module_cloud Cloud ) set(__module_lua Lua ) +set(__module_http_server HTTPServer ) foreach (_l ${__libs}) set( __inc_${_l} "") diff --git a/libs/http_server/microhttpd_server_p.cpp b/libs/http_server/microhttpd_server_p.cpp new file mode 100644 index 00000000..fb89d1a3 --- /dev/null +++ b/libs/http_server/microhttpd_server_p.cpp @@ -0,0 +1,308 @@ +#include "microhttpd_server_p.h" + +#include "pidir.h" +#include "pifile.h" +#include "piliterals_string.h" + +#include + + +struct MicrohttpdServerConnection { + bool ready(); + int send_reply(const MicrohttpdServer::Reply & r); + int send_error(); + + bool done = false; + MicrohttpdServer::Method method = MicrohttpdServer::Method::Unknown; + PIString path; + PIByteArray body; + PIMap headers, args, post; + MHD_Connection * connection = nullptr; + MicrohttpdServer * server = nullptr; + MHD_PostProcessor * postprocessor = nullptr; +}; + + +bool MicrohttpdServerConnection::ready() { + if (!server) return false; + if (done) return true; + done = true; + MicrohttpdServer::Reply rep; + if (method == MicrohttpdServer::Method::Get) { + PIByteArray file = server->getFile(path); + if (!file.isEmpty()) { + rep.addHeader(MHD_HTTP_HEADER_CACHE_CONTROL, "public"); + rep.addHeader(MHD_HTTP_HEADER_CONTENT_TYPE, "application/octet-stream"); + rep.setBody(file); + send_reply(rep); + return true; + } + if (path == "/favicon.ico"_a) { + piCout << "send favicon" << server->favicon.size() << "bytes"; + rep.setBody(server->favicon); + send_reply(rep); + return true; + } + } + piCout << "ready" << (int)method << path; + MicrohttpdServer::Request req; + req.method = method; + req.path = path; + req.body = body; + req.headers = headers; + req.args = args; + // rep.headers = headers; + if (server->callback) rep = server->callback(req); + rep.addFixedHeaders(); + send_reply(rep); + return true; +} + + +int MicrohttpdServerConnection::send_reply(const MicrohttpdServer::Reply & r) { + MHD_Response * response = MHD_create_response_from_buffer(r.body.size(), (void *)r.body.data(), MHD_RESPMEM_MUST_COPY); + if (!response) { + piCout << "null response" << r.body.size() << (void *)r.body.data(); + return MHD_NO; + } + auto it = r.headers.makeIterator(); + while (it.next()) + MHD_add_response_header(response, it.key().dataAscii(), it.value().dataUTF8()); + piCout << "status" << r.code; + int ret = MHD_queue_response(connection, r.code, response); + MHD_destroy_response(response); + return ret; +} + + +int MicrohttpdServerConnection::send_error() { + return MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, MHD_create_response_from_buffer(0, nullptr, MHD_RESPMEM_MUST_COPY)); +} + + +int iterate_post(void * conn_cls, + MHD_ValueKind kind, + const char * key, + const char * filename, + const char * content_type, + const char * transfer_encoding, + const char * data, + uint64_t off, + size_t size) { + MicrohttpdServerConnection * conn = (MicrohttpdServerConnection *)conn_cls; + if (!conn) return MHD_NO; + conn->post[PIString::fromUTF8(key)] = PIString::fromUTF8(data); + return MHD_YES; +} + + +void request_completed(void * cls, MHD_Connection * connection, void ** con_cls, MHD_RequestTerminationCode toe) { + MicrohttpdServerConnection *& conn((MicrohttpdServerConnection *&)(*con_cls)); + // qDebug() << "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); + conn->ready(); + piDeleteSafety(conn); +} + +int header_iterate(void * cls, MHD_ValueKind kind, const char * key, const char * value) { + MicrohttpdServerConnection * conn = (MicrohttpdServerConnection *)cls; + if (!conn) return MHD_NO; + conn->headers[PIString::fromUTF8(key)] = PIString::fromUTF8(value); + return MHD_YES; +} + +int args_iterate(void * cls, MHD_ValueKind kind, const char * key, const char * value) { + MicrohttpdServerConnection * conn = (MicrohttpdServerConnection *)cls; + if (!conn) return MHD_NO; + conn->args[PIString::fromUTF8(key)] = PIString::fromUTF8(value); + return MHD_YES; +} + + +int answer_to_connection(void * cls, + MHD_Connection * connection, + const char * url, + const char * method, + const char * version, + const char * upload_data, + size_t * upload_data_size, + void ** con_cls) { + MicrohttpdServer * server = (MicrohttpdServer *)cls; + + MicrohttpdServer::Method m = MicrohttpdServer::Method::Unknown; + + if (0 == strcmp(method, "GET")) + m = MicrohttpdServer::Method::Get; + else if (0 == strcmp(method, "POST")) + m = MicrohttpdServer::Method::Post; + else if (0 == strcmp(method, "HEAD")) + m = MicrohttpdServer::Method::Head; + else if (0 == strcmp(method, "PUT")) + m = MicrohttpdServer::Method::Put; + else if (0 == strcmp(method, "DELETE")) + m = MicrohttpdServer::Method::Delete; + else if (0 == strcmp(method, "CONNECT")) + m = MicrohttpdServer::Method::Connect; + else if (0 == strcmp(method, "OPTIONS")) + m = MicrohttpdServer::Method::Options; + else if (0 == strcmp(method, "TRACE")) + m = MicrohttpdServer::Method::Trace; + else if (0 == strcmp(method, "PATCH")) + m = MicrohttpdServer::Method::Patch; + + if (m == MicrohttpdServer::Method::Unknown) { + piCout << "[MicrohttpdServer] Unknown method!"; + return MHD_NO; + } + + // piCout << "answer" << url << method << server; + MicrohttpdServerConnection *& conn((MicrohttpdServerConnection *&)(*con_cls)); + if (!conn) { + conn = new MicrohttpdServerConnection(); + conn->connection = connection; + conn->server = server; + conn->path = PIString::fromUTF8(url); + 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); + + if (m == MicrohttpdServer::Method::Post) { + // qDebug() << "new POST" << *upload_data_size; + conn->postprocessor = MHD_create_post_processor(connection, 65536, (MHD_PostDataIterator)iterate_post, (void *)conn); + } + return MHD_YES; + } + + switch (m) { + case MicrohttpdServer::Method::Get: + 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(); +} + + +PRIVATE_DEFINITION_START(MicrohttpdServer) + MHD_Daemon * daemon; +PRIVATE_DEFINITION_END(MicrohttpdServer) + + +MicrohttpdServer::MicrohttpdServer() { + PRIVATE->daemon = nullptr; +} + + +MicrohttpdServer::~MicrohttpdServer() { + stop(); +} + + +void MicrohttpdServer::setWWWRoot(const PIString & path) { + www_root = path; + if (www_root.isEmpty()) return; + www_root = PIDir(www_root).absolutePath(); +} + + +void MicrohttpdServer::setFavicon(const PIByteArray & im) { + favicon = im; +} + + +bool MicrohttpdServer::listen(PINetworkAddress addr) { + stop(); + uint flags = 0; +#if MHD_VERSION <= 0x00095100 + flags = MHD_USE_POLL_INTERNALLY; +#else + flags = MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD; +#endif + sockaddr_in sa_addr; + memset(&sa_addr, 0, sizeof(sa_addr)); + sa_addr.sin_port = htons(addr.port()); + sa_addr.sin_addr.s_addr = addr.ip(); + sa_addr.sin_family = AF_INET; + PRIVATE->daemon = MHD_start_daemon(flags, + addr.port(), + nullptr, + nullptr, + (MHD_AccessHandlerCallback)answer_to_connection, + this, + MHD_OPTION_NOTIFY_COMPLETED, + request_completed, + nullptr, + MHD_OPTION_SOCK_ADDR, + &sa_addr, + MHD_OPTION_END); + return isListen(); +} + + +bool MicrohttpdServer::isListen() const { + return PRIVATE->daemon; +} + + +PIByteArray MicrohttpdServer::getFile(PIString name) { + if (www_root.isEmpty()) return PIByteArray(); + if (name == "/") name += "index.html"; + PIString path = PIDir(www_root).relative(name); + // qDebug() << "getFile" << path; + if (path.isEmpty() || !path.startsWith(www_root)) return PIByteArray(); + return PIFile::readAll(PIDir(www_root).absolute(name)); +} + + +void MicrohttpdServer::stop() { + if (PRIVATE->daemon) { + MHD_stop_daemon(PRIVATE->daemon); + PRIVATE->daemon = nullptr; + } +} + + +void MicrohttpdServer::Reply::addHeader(const PIString & header, const PIString & value) { + headers[header] = value; +} + + +void MicrohttpdServer::Reply::removeHeader(const PIString & header) { + headers.remove(header); +} + + +void MicrohttpdServer::Reply::setBody(const PIByteArray & b) { + body = b; +} + + +void MicrohttpdServer::Reply::setCode(int c) { + code = c; +} + + +void MicrohttpdServer::Reply::addFixedHeaders() { + if (!headers.contains(MHD_HTTP_HEADER_CONTENT_TYPE)) { + if (body.startsWith(PIByteArray::fromAscii(""))) + addHeader(MHD_HTTP_HEADER_CONTENT_TYPE, "text/html; charset=utf-8"); + else + addHeader(MHD_HTTP_HEADER_CONTENT_TYPE, "application/json; charset=utf-8"); + } + addHeader(MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, "*"); +} diff --git a/libs/http_server/pihttpserver.cpp b/libs/http_server/pihttpserver.cpp new file mode 100644 index 00000000..f32a653e --- /dev/null +++ b/libs/http_server/pihttpserver.cpp @@ -0,0 +1,50 @@ +#include "pihttpserver.h" + +#include "piliterals_string.h" + + +PIHTTPServer::PIHTTPServer() { + setRequestCallback([this](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { + MicrohttpdServer::Reply rep; + rep.setCode(404); + auto in_path = r.path.split("/"); + in_path.removeAll(""); + auto it = functions.makeReverseIterator(); + while (it.next()) { + if (it.value().function) { + if (it.value().match(in_path)) { + rep.setCode(200); + rep = it.value().function(r); + break; + } + } + } + auto hit = reply_headers.makeIterator(); + while (hit.next()) + rep.addHeader(hit.key(), hit.value()); + return rep; + }); +} + + +PIHTTPServer::~PIHTTPServer() { + stop(); +} + + +void PIHTTPServer::registerPath(const PIString & path, RequestFunction functor) { + auto & ep(functions[path]); + ep.path = path.split("/"); + ep.function = functor; + ep.path.removeAll(""); +} + + +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) { + if (path[i] == "*"_a) continue; + if (path[i] != in_path[i]) return false; + } + return true; +} diff --git a/libs/main/http_server/microhttpd_server_p.h b/libs/main/http_server/microhttpd_server_p.h new file mode 100644 index 00000000..c26c31bf --- /dev/null +++ b/libs/main/http_server/microhttpd_server_p.h @@ -0,0 +1,75 @@ +#ifndef MICROHTTPD_SERVER_P_H +#define MICROHTTPD_SERVER_P_H + +#include "pibase.h" +#include "piobject.h" +#include "pip_http_server_export.h" + +struct MicrohttpdServerConnection; + +class PIP_HTTP_SERVER_EXPORT MicrohttpdServer: public PIObject { + PIOBJECT(MicrohttpdServer) + friend struct MicrohttpdServerConnection; + +public: + MicrohttpdServer(); + virtual ~MicrohttpdServer(); + + enum class Method { + Unknown, + Get, + Head, + Post, + Put, + Delete, + Connect, + Options, + Trace, + Patch + }; + + struct PIP_HTTP_SERVER_EXPORT Request { + MicrohttpdServer::Method method; + PIString path; + PIByteArray body; + PIMap headers; + PIMap args; + }; + + class PIP_HTTP_SERVER_EXPORT Reply { + friend struct MicrohttpdServerConnection; + + public: + void addHeader(const PIString & header, const PIString & value); + void removeHeader(const PIString & header); + void setBody(const PIByteArray & b); + void setCode(int c); + + private: + void addFixedHeaders(); + int code = 200; + PIByteArray body; + PIMap headers; + }; + + void setWWWRoot(const PIString & path); + void setFavicon(const PIByteArray & im); + + bool listen(PINetworkAddress addr); + bool listenAll(ushort port) { return listen({0, port}); } + bool isListen() const; + void stop(); + + void setRequestCallback(std::function c) { callback = c; } + +private: + PIByteArray getFile(PIString name); + + PRIVATE_DECLARATION(PIP_HTTP_SERVER_EXPORT) + PIString www_root; + PIByteArray favicon; + std::function callback; +}; + + +#endif diff --git a/libs/main/http_server/pihttpserver.h b/libs/main/http_server/pihttpserver.h new file mode 100644 index 00000000..e8b3d44f --- /dev/null +++ b/libs/main/http_server/pihttpserver.h @@ -0,0 +1,33 @@ +#ifndef PIHTTPSERVER_H +#define PIHTTPSERVER_H + +#include "microhttpd_server_p.h" +#include "pip_http_server_export.h" + +class PIP_HTTP_SERVER_EXPORT PIHTTPServer: public MicrohttpdServer { + PIOBJECT_SUBCLASS(PIHTTPServer, MicrohttpdServer) + +public: + PIHTTPServer(); + virtual ~PIHTTPServer(); + + using RequestFunction = std::function; + + void registerPath(const PIString & path, RequestFunction functor); + + void addReplyHeader(const PIString & name, const PIString & value) { reply_headers[name] = value; } + void removeReplyHeader(const PIString & name) { reply_headers.remove(name); } + void clearReplyHeaders() { reply_headers.clear(); } + +private: + struct Endpoint { + bool match(const PIStringList & in_path) const; + PIStringList path; + RequestFunction function; + }; + PIMap reply_headers; + PIMap functions; +}; + + +#endif diff --git a/libs/main/text/pistring.cpp b/libs/main/text/pistring.cpp index 515d36f0..6c8dbb6f 100644 --- a/libs/main/text/pistring.cpp +++ b/libs/main/text/pistring.cpp @@ -553,6 +553,17 @@ uint PIString::hash() const { } +PIByteArray PIString::toAscii() const { + if (isEmpty()) return PIByteArray(); + PIByteArray ret; + ret.resize(size()); + for (int i = 0; i < size_s(); ++i) { + ret[i] = uchar(at(i).ch); + } + return ret; +} + + PIByteArray PIString::toSystem() const { if (isEmpty()) return PIByteArray(); buildData(__syslocname__); diff --git a/libs/main/text/pistring.h b/libs/main/text/pistring.h index 27ffa208..ab573d31 100644 --- a/libs/main/text/pistring.h +++ b/libs/main/text/pistring.h @@ -1107,6 +1107,10 @@ public: //! \~russian Тоже самое, что \a toUTF8(). PIByteArray toByteArray() const { return toUTF8(); } + //! \~english Returns \a PIByteArray contains \a dataAscii() of this string without terminating null-char. + //! \~russian Возвращает \a PIByteArray содержащий \a dataAscii() строки без завершающего нулевого байта. + PIByteArray toAscii() const; + //! \~english Returns \a PIByteArray contains \a data() of this string without terminating null-char. //! \~russian Возвращает \a PIByteArray содержащий \a data() строки без завершающего нулевого байта. PIByteArray toSystem() const; diff --git a/libs/main/thread/piprotectedvariable.h b/libs/main/thread/piprotectedvariable.h index b7490f95..b1646764 100644 --- a/libs/main/thread/piprotectedvariable.h +++ b/libs/main/thread/piprotectedvariable.h @@ -32,16 +32,9 @@ template class PIP_EXPORT PIProtectedVariable { public: - //! \~english Sets value to copy of \"v\" - //! \~russian Устанавливает значение как копию \"v\" - void set(const T & v) { - PIMutexLocker _ml(mutex); - var = v; - } - - //! \~english Sets value by moving \"v\" - //! \~russian Устанавливает значение перемещением \"v\" - void set(T && v) { + //! \~english Sets value to \"v\" + //! \~russian Устанавливает значение как \"v\" + void set(T v) { PIMutexLocker _ml(mutex); var = std::move(v); } @@ -64,10 +57,10 @@ public: //! \~russian Разблокирует мьютекс void unlock() { mutex.unlock(); } - //! \~english Sets value to copy of \"v\" - //! \~russian Устанавливает значение как копию \"v\" - PIProtectedVariable & operator=(const T & v) { - set(v); + //! \~english Sets value to \"v\" + //! \~russian Устанавливает значение как \"v\" + PIProtectedVariable & operator=(T v) { + set(std::move(v)); return *this; } diff --git a/libs/main/types/pibytearray.cpp b/libs/main/types/pibytearray.cpp index 6207c9ea..f3c5359d 100644 --- a/libs/main/types/pibytearray.cpp +++ b/libs/main/types/pibytearray.cpp @@ -129,6 +129,20 @@ struct base64HelpStruct { // clang-format on +bool PIByteArray::startsWith(const PIByteArray & o) const { + if (o.isEmpty()) return false; + if (size() < o.size()) return false; + return piCompareBinary(data(), o.data(), o.size()); +} + + +bool PIByteArray::endsWith(const PIByteArray & o) const { + if (o.isEmpty()) return false; + if (size() < o.size()) return false; + return piCompareBinary(data(size() - o.size()), o.data(), o.size()); +} + + PIByteArray & PIByteArray::convertToBase64() { return *this = toBase64(); } @@ -396,6 +410,17 @@ PIByteArray PIByteArray::fromHex(PIString str) { } +PIByteArray PIByteArray::fromAscii(const char * str) { + PIByteArray ret; + int ind = 0; + while (str[ind] != 0) { + ret.append(str[ind]); + ++ind; + } + return ret; +} + + PICout operator<<(PICout s, const PIByteArray & ba) { s.space(); s.saveAndSetControls(0); diff --git a/libs/main/types/pibytearray.h b/libs/main/types/pibytearray.h index f7f434aa..9cd02076 100644 --- a/libs/main/types/pibytearray.h +++ b/libs/main/types/pibytearray.h @@ -340,6 +340,10 @@ public: //! \~\sa \a every(), \a any(), \a contains(), \a indexWhere() inline int entries(std::function test, ssize_t start = 0) const { return d.entries(test, start); } + bool startsWith(const PIByteArray & o) const; + + bool endsWith(const PIByteArray & o) const; + //! \~english Returns the first index at which a given element `e` //! can be found in the array, or `-1` if it is not present. //! \~russian Возвращает первый индекс, по которому данный элемент `e` @@ -1165,6 +1169,8 @@ public: static PIByteArray fromHex(PIString str); + static PIByteArray fromAscii(const char * str); + //! \~english Return converted from Base 64 data //! \~russian Возвращает массив из Base 64 представления static PIByteArray fromBase64(const PIByteArray & base64); diff --git a/main.cpp b/main.cpp index 338791b2..4952e4d3 100644 --- a/main.cpp +++ b/main.cpp @@ -1,220 +1,64 @@ -#include "pibytearray.h" -#include "piclientserver_client.h" -#include "piclientserver_server.h" -#include "picodeparser.h" -#include "piintrospection_server.h" -#include "piiostream.h" -#include "pijson.h" -#include "pilog.h" -#include "pimathbase.h" +#include "pihttpserver.h" #include "pip.h" -#include "piprotectedvariable.h" -#include "pitranslator_p.h" -#include "pivaluetree_conversions.h" - -#include using namespace PICoutManipulators; -/* -class MyStr: public PIString { -public: - MyStr(): PIString() {} - MyStr(const char * o): PIString(o) { piCout << "MyStr *"; } - MyStr(const MyStr & o): PIString(o) { piCout << "MyStr &"; } - // MyStr(const MyStr & o): PIString(o) { piCout << "MyStr &"; } - MyStr(MyStr && o): PIString(o) { piCout << "MyStr &&"; } - MyStr & operator=(const MyStr & o) { - *this += o; - piCout << "MyStr =&"; - return *this; - } - MyStr & operator=(MyStr && o) { - *this += o; - piCout << "MyStr =&&"; - return *this; - } -}; -*/ +PIKbdListener kbd; +const char * pageTitle = "" + "" + "" + "

Title

" + "" + ""; -class RWL { -public: - void lockWrite() { - PIMutexLocker _ml(mutex); - while (reading > 0 || writing) { - var.wait(mutex); - } - writing = true; - } - void unlockWrite() { - PIMutexLocker _ml(mutex); - writing = false; - var.notifyAll(); - } - void lockRead() { - PIMutexLocker _ml(mutex); - while (writing) { - var.wait(mutex); - } - ++reading; - } - void unlockRead() { - PIMutexLocker _ml(mutex); - --reading; - var.notifyAll(); - } - -private: - PIConditionVariable var; - int reading = 0; - bool writing = false; - PIMutex mutex; -}; - -PIMutex mutex; -PISemaphore sem(10); -PIReadWriteLock rwl; int main(int argc, char * argv[]) { - /*sem.acquire(2); - piCout << sem.tryAcquire(2); - piCout << sem.available(); - return 0;*/ - PIThread t_w0( - [] { - // PIMutexLocker _ml(mutex); - PIWriteLocker rl(rwl); - piCout << "write0 start ..."; - piMSleep(500); - piCout << "write0 end" - << "\n"; - }, - true, - 1_Hz); - PIThread t_w1( - [] { - // PIMutexLocker _ml(mutex); - PIWriteLocker rl(rwl); - piCout << "write1 start ..."; - piMSleep(500); - piCout << "write1 end" - << "\n"; - }, - true, - 1_Hz); + kbd.enableExitCapture(); - int cnt0 = 0, cnt1 = 0; - PIThread t_r0( - [&cnt0] { - // PIMutexLocker _ml(mutex); - PIReadLocker rl(rwl); - piCout << "read0 start ..."; - piMSleep(50); - // bool ok = rwl.tryLockRead(100_ms); - // if (ok) ++cnt0; - piCout << "read0 end"; - // if (ok) rwl.unlockRead(); - }, - true, - 10_Hz); - PIThread t_r1( - [&cnt1] { - // PIMutexLocker _ml(mutex); - // PIReadLocker rl(rwl); - piCout << "read1 start ..."; - piMSleep(50); - bool ok = rwl.tryLockRead(100_ms); - if (ok) ++cnt1; - piCout << "read1 end" << ok; - if (ok) rwl.unlockRead(); - }, - true, - 11_Hz); + PIHTTPServer server; - piSleep(8.); + server.setFavicon(PIFile::readAll("logo.png", false)); + server.listen({"127.0.0.1", 7777}); - t_r0.stopAndWait(); - t_r1.stopAndWait(); - t_w0.stopAndWait(); - t_w1.stopAndWait(); + server.registerPath("/", [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { + MicrohttpdServer::Reply ret; + ret.setBody(PIByteArray::fromAscii(pageTitle)); + return ret; + }); - piCout << cnt0 << cnt1; + server.registerPath("/html", [](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { + MicrohttpdServer::Reply ret; + ret.setBody("

arg=%1

"_a.arg(r.args.value("a0")).toUTF8()); + return ret; + }); - /*PICodeParser parser; - parser.parseFile("client_server.h"); - for (auto m: parser.enums) { + server.registerPath("/api", [](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 { + MicrohttpdServer::Reply ret; + ret.setBody("API etry %1"_a.arg(r.path).toUTF8()); + ret.setCode(405); + return ret; + }); + + /*server.setRequestCallback([](MicrohttpdServer::Request r) -> MicrohttpdServer::Reply { + MicrohttpdServer::Reply rep; + piCout << "request" << r.path; + piCout << " header" << r.headers; + piCout << " args" << r.args; + piCout << " body" << r.body; piCout << ""; - piCout << m.name; // << m.args << m.value; - // piCout << m.expand({"hello"}); - }*/ + rep.setBody(PIByteArray::fromAscii("[{\"value1\": true, \"value2\": \"ыекштп\"}]")); + return rep; + });*/ + piCout << "start" << server.isListen(); - return 0; - PITranslator::loadLang("ru"); - PISerial f("COM123"); - f.open(); - - /*auto test = [](PIString s, PIString v) { - piCout << " in:" << s; - piCout << "arg:" << minArgPlaceholder(s); - piCout << "out:" << arg(s, v); - piCout << ""; - }; - - test(" %", "asd"); - test("%", "asd"); - test("1", "asd"); - test(" %11", "asd"); - test("%1%2 %0f%0g", "asd"); - test("%01 ", "asd");*/ - - /*piCout << PIString::readableSize(50_KiB); - piCout << PIString::readableSize(1_GB); - PITranslator::loadLang("ru"); - piCout << PIString::readableSize(50_KiB); - piCout << PIString::readableSize(1_GB); - piCout << "test\nstring"_tr; - PITranslator::clear(); - piCout << PIString::readableSize(50_KiB); - piCout << PIString::readableSize(1_GB); - piCout << "test\nstring"_tr; - piCout << "hello!"_tr; - PITranslator::loadConfig("[]\nhello!=привет!\n[Co]\nhello!=привет CO!\n"_u8); - piCout << "hello!"_tr("Co") << "hello!"_tr;*/ - // piCout << "hello!"_trNoOp; - - - /*PISet set; - piCout << set << piSerialize(set) << piDeserialize>(piSerialize(set)); - set << 1 << 2 << 3; - piCout << set << piSerialize(set) << piDeserialize>(piSerialize(set)); - set << 1 << -2 << 50 << -100; - piCout << set << piSerialize(set) << piDeserialize>(piSerialize(set));*/ - return 0; - - std::numeric_limits::epsilon(); - using cmlp = complexf; - PIMathMatrixT<3, 3, double> v0; - PIMathMatrixT<3, 3, cmlp> v1; - v0[0][1] = 1; - v0[1][1] = 2; - v0[2][1] = 3; - v1[0][1] = cmlp(1., 0); - v1[1][1] = cmlp(2., -1); - v1[2][1] = cmlp(3., 2); - piCout << v0 << v1; - piCout << (v0 * 2.) << (v1 * cmlp(2., 2.)); - piCout << (v0 + 2.) << (v1 + cmlp(2.)); - piCout << (v0 - 2.) << (v1 - cmlp(2.)); - piCout << (v0 / 2.) << (v1 / cmlp(2., 1.)); - // piCout << (v0.length()) << (v1.length()); - // piCout << (v0.lengthSqr()) << (v1.lengthSqr()); - // piCout << (v0.manhattanLength()) << (v1.manhattanLength()); - - /*foo(); - foo(); - foo(); - foo(); - return 0;*/ + WAIT_FOR_EXIT + server.stop(); return 0; }