Compare commits

2 Commits

Author SHA1 Message Date
24112498ce new method PILog::readAllLogs 2024-11-18 22:47:02 +03:00
c9a5ddd89f PIProtectedVariable - user now can`t mistake
PIHTTPServer improvements
2024-11-18 11:11:19 +03:00
10 changed files with 227 additions and 55 deletions

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -110,6 +110,58 @@ void PILog::stop() {
}
PIStringList PILog::readAllLogs() const {
PIMap<PISystemTime, PIString> names;
auto dir = PIDir(log_dir);
auto fil = dir.entries();
for (auto fi: fil) {
if (!fi.isFile()) continue;
if (!fi.name().contains(".log.")) continue;
names[PIDateTime::current().fromString(fi.baseName(), "yyyy_MM_dd__hh_mm_ss").toSystemTime()] = dir.relative(fi.path);
}
PIStringList ret;
PIString cur_filename = dir.relative(log_file.path());
auto it = names.makeIterator();
bool was_own = false;
auto readFile = [&ret](PIFile * f) {
PIIOTextStream ts(f);
PIString line;
while (!ts.isEnd()) {
line = ts.readLine().trim();
if (line.isNotEmpty()) ret << line;
}
};
while (it.next()) {
PIFile * f = nullptr;
bool own = true;
if (it.value() == cur_filename) {
log_mutex.lock();
f = &log_file;
f->seekToBegin();
own = false;
was_own = true;
} else {
f = new PIFile(log_dir + "/" + it.value(), PIIODevice::ReadOnly);
}
readFile(f);
if (own)
delete f;
else {
f->seekToEnd();
log_mutex.unlock();
}
}
if (!was_own) {
log_mutex.lock();
log_file.seekToBegin();
readFile(&log_file);
log_file.seekToEnd();
log_mutex.unlock();
}
return ret;
}
void PILog::coutDone(int id, PIString * buffer) {
if (!buffer) return;
if (!id_by_cat.containsValue(id)) return;

View File

@@ -144,6 +144,10 @@ public:
//! \~russian Записывает все строки из очереди и останавливается. Также вызывается в деструкторе.
void stop();
//! \~english Read all previous and current log content and returns them as %PIStringList.
//! \~russian Читает все предыдущие и текущий логи и возвращает их как %PIStringList.
PIStringList readAllLogs() const;
private:
EVENT_HANDLER2(void, coutDone, int, id, PIString *, buff);
@@ -160,8 +164,8 @@ private:
void newFile();
void run() override;
PIMutex log_mutex;
PIFile log_file;
mutable PIMutex log_mutex;
mutable PIFile log_file;
PIIOTextStream log_ts;
PITimeMeasurer split_tm;
PISystemTime split_time;

View File

@@ -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<MicrohttpdServer::Reply(const MicrohttpdServer::Request &)>;
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<PIString, PIString> reply_headers;
PIMap<PIString, Endpoint> functions;
RequestFunction unhandled;
};

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
//! \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

View File

@@ -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"

View File

@@ -32,6 +32,28 @@
template<typename T>
class PIP_EXPORT PIProtectedVariable {
public:
//! \~english
//! \~russian
class PIP_EXPORT Pointer {
friend class PIProtectedVariable<T>;
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<T> & v): pv(v) {}
PIProtectedVariable<T> & 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<T> & operator=(T v) {

View File

@@ -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("<!DOCTYPE html><html><body><p>arg=%1</p></body></html>"_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("<!DOCTYPE html><html><body>API</body></html>"));
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("<!DOCTYPE html><html><body>API etry %1</body></html>"_a.arg(r.path).toUTF8());
ret.setCode(405);
return ret;
});
server.registerUnhandled([](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply {
MicrohttpdServer::Reply ret;
ret.setBody("<!DOCTYPE html><html><body>Unknown</body></html>"_a.arg(r.path).toUTF8());
ret.setCode(404);
return ret;
});
/*server.setRequestCallback([](MicrohttpdServer::Request r) -> MicrohttpdServer::Reply {
MicrohttpdServer::Reply rep;
piCout << "request" << r.path;