detach PIHTTPServer::Endpoint to base struct PIHTTP::ServerEndpoint (private header), no functionality changes in HTTP server

Ready to implement this private base struct to MQTT client
This commit is contained in:
2026-05-26 21:31:58 +03:00
parent a2e54c3694
commit a2093e24a6
4 changed files with 76 additions and 300 deletions
+23 -135
View File
@@ -1,14 +1,25 @@
#include "pihttpserver.h"
#include "piliterals_string.h"
#include "piserverendpoint_p.h"
struct Endpoint: public PIHTTP::ServerEndpoint {
PIHTTP::Method method = PIHTTP::Method::Unknown;
PIHTTPServer::RequestFunction function;
};
PRIVATE_DEFINITION_START(PIHTTPServer)
PIMap<uint, PIVector<Endpoint>> endpoints;
PRIVATE_DEFINITION_END(PIHTTPServer)
PIHTTPServer::PIHTTPServer() {
setRequestCallback([this](const PIHTTP::MessageConst & r) -> PIHTTP::MessageMutable {
PIHTTP::MessageMutable reply;
reply.setCode(PIHTTP::Code::NotFound);
auto in_path = splitPath(r.path());
auto it = endpoints.makeReverseIterator();
auto in_path = PIHTTP::ServerEndpoint::splitPath(r.path());
auto it = PRIVATE->endpoints.makeReverseIterator();
bool found = false;
while (it.next()) {
for (const auto & ep: it.value()) {
@@ -41,159 +52,36 @@ PIHTTPServer::~PIHTTPServer() {
}
bool PIHTTPServer::registerPath(const PIString & path, PIHTTP::Method method, RequestFunction functor) {
bool PIHTTPServer::registerPath(const PIString & path, PIHTTP::Method method, PIHTTP::RequestFunction functor) {
Endpoint ep;
if (!ep.create(path)) return false;
ep.method = method;
ep.function = std::move(functor);
endpoints[ep.priority] << ep;
PRIVATE->endpoints[ep.priority] << ep;
return true;
}
void PIHTTPServer::registerUnhandled(RequestFunction functor) {
void PIHTTPServer::registerUnhandled(PIHTTP::RequestFunction functor) {
unhandled = functor;
}
void PIHTTPServer::unregisterPath(const PIString & path, PIHTTP::Method method) {
auto pl = splitPath(path);
auto it = endpoints.makeIterator();
auto pl = PIHTTP::ServerEndpoint::splitPath(path);
auto it = PRIVATE->endpoints.makeIterator();
while (it.next()) {
it.value().removeWhere([&pl, method](const Endpoint & ep) { return ep.path == pl && ep.method == method; });
}
endpoints.removeWhere([](uint, const PIVector<Endpoint> & epl) { return epl.isEmpty(); });
PRIVATE->endpoints.removeWhere([](uint, const PIVector<Endpoint> & epl) { return epl.isEmpty(); });
}
void PIHTTPServer::unregisterPath(const PIString & path) {
auto pl = splitPath(path);
auto it = endpoints.makeIterator();
auto pl = PIHTTP::ServerEndpoint::splitPath(path);
auto it = PRIVATE->endpoints.makeIterator();
while (it.next()) {
it.value().removeWhere([&pl](const Endpoint & ep) { return ep.path == pl; });
}
endpoints.removeWhere([](uint, const PIVector<Endpoint> & epl) { return epl.isEmpty(); });
}
PIStringList PIHTTPServer::splitPath(const PIString & path) {
auto ret = path.split("/");
ret.removeAll({});
return ret;
}
PIHTTPServer::PathElement::PathElement(const PIString & reg) {
source = reg;
if (reg == "*"_a) {
type = Type::AnyOne;
} else if (reg == "**"_a) {
type = Type::AnyMany;
} else if (reg.contains('*')) {
type = Type::AnyPart;
parts = reg.split('*');
} else if (reg.contains('{')) {
type = Type::Arguments;
int ind = 0, eind = 0, pind = 0;
for (;;) {
ind = reg.find('{', ind);
if (ind < 0) break;
eind = reg.find('}', ind + 1);
if (eind < 0) break;
arguments.insert(arguments.size_s(), reg.mid(ind + 1, eind - ind - 1));
if (ind == 0)
parts << PIString();
else {
if (ind > pind)
parts << reg.mid(pind, ind - pind);
else if (parts.isNotEmpty()) {
piCout << "[PIHTTPServer] Warning: sequential arguments, ignoring this path!";
type = Type::Invalid;
return;
}
}
ind = pind = eind + 1;
}
if (eind < reg.size_s() - 1) parts << reg.mid(eind + 1);
}
}
bool PIHTTPServer::PathElement::match(const PIString & in, PIMap<PIString, PIString> & ext_args) const {
// piCout << "match" << source << "with" << in;
if (type == Type::AnyOne) return true;
if (type == Type::AnyPart) {
int ind = 0;
for (const auto & m: parts) {
ind = in.find(m, ind);
if (ind < 0) return false;
}
return true;
}
if (type == Type::Arguments) {
int ind = 0, eind = 0;
for (int i = 0; i < parts.size_s(); ++i) {
const auto & m(parts[i]);
if (m.isNotEmpty()) {
ind = in.find(m, eind);
if (ind < 0) return false;
}
if (i > 0) {
ext_args[arguments.value(i - 1)] = in.mid(eind, ind - eind);
}
eind = ind + m.size_s();
}
if (parts.size() == arguments.size()) {
ext_args[arguments.value(arguments.size_s() - 1)] = in.mid(eind);
}
return true;
}
return source == in;
}
uint PIHTTPServer::PathElement::priority() const {
switch (type) {
case Type::Fixed: return 0x10000; break;
case Type::Arguments: return 0x1000; break;
case Type::AnyPart: return 0x100; break;
case Type::AnyOne: return 0x10; break;
case Type::AnyMany: return 0x1; break;
default: break;
}
return 0;
}
bool PIHTTPServer::Endpoint::create(const PIString & p) {
path = splitPath(p);
prepared_path.clear();
priority = 0;
for (const auto & i: path) {
PathElement pe(i);
prepared_path << pe;
path_types |= pe.type;
priority += pe.priority();
}
return !path_types[PathElement::Type::Invalid];
}
bool PIHTTPServer::Endpoint::match(const PIStringList & in_path, PIMap<PIString, PIString> & ext_args) const {
if (path_types[PathElement::Type::AnyMany]) {
int any_ind = path.indexOf("**"_a);
for (int i = 0; i < any_ind; ++i) {
if (!prepared_path[i].match(in_path[i], ext_args)) return false;
}
int si = prepared_path.size_s() - 1, ii = in_path.size_s() - 1;
for (; si > any_ind && ii >= 0; --si, --ii) {
if (!prepared_path[si].match(in_path[ii], ext_args)) return false;
}
} else {
if (in_path.size() != prepared_path.size()) return false;
for (int i = 0; i < prepared_path.size_s(); ++i) {
if (!prepared_path[i].match(in_path[i], ext_args)) return false;
}
}
return true;
PRIVATE->endpoints.removeWhere([](uint, const PIVector<Endpoint> & epl) { return epl.isEmpty(); });
}
+5
View File
@@ -210,6 +210,11 @@ public:
PIP_EXPORT const char * methodName(Method m);
//! \~english Request handler used by registered routes and fallback processing.
//! \~russian Обработчик запроса, используемый зарегистрированными маршрутами и fallback-обработкой.
using RequestFunction = std::function<PIHTTP::MessageMutable(const PIHTTP::MessageConst &)>;
}; // namespace PIHTTP
+1 -35
View File
@@ -102,43 +102,9 @@ public:
void clearReplyHeaders() { reply_headers.clear(); }
private:
struct PathElement {
enum class Type {
Invalid = 0x01,
Fixed = 0x02,
Arguments = 0x04,
AnyOne = 0x08,
AnyPart = 0x10,
AnyMany = 0x20
};
Type type = Type::Fixed;
PIString source;
PIStringList parts;
PIMap<int, PIString> arguments;
PathElement(const PIString & reg = {});
bool match(const PIString & in, PIMap<PIString, PIString> & ext_args) const;
uint priority() const;
};
struct Endpoint {
PIStringList path;
PIHTTP::Method method = PIHTTP::Method::Unknown;
RequestFunction function;
PIFlags<PathElement::Type> path_types;
PIVector<PathElement> prepared_path;
uint priority = 0;
bool create(const PIString & p);
bool match(const PIStringList & in_path, PIMap<PIString, PIString> & ext_args) const;
};
static PIStringList splitPath(const PIString & path);
PRIVATE_DECLARATION(PIP_HTTP_SERVER_EXPORT)
PIMap<PIString, PIString> reply_headers;
PIMap<uint, PIVector<Endpoint>> endpoints;
RequestFunction unhandled;
};
+47 -130
View File
@@ -10,18 +10,57 @@
using namespace PICoutManipulators;
using namespace PIHTTP;
using namespace PIUnits::Class;
int rcnt = 0, scnt = 0;
inline PIByteArray SMBusTypeInfo_genHash(PIString n) {
PICrypt c;
return piSerialize(c.shorthash(n.removeAll(" "), PIString("SMBusDataHashKey").toByteArray()));
}
PIKbdListener kbd;
MessageMutable createMessage(Code c, const char * path, const MessageConst & msg) {
piCout << "path" << path << "args" << msg.pathArguments();
return MessageMutable().setCode(c);
};
int main(int argc, char * argv[]) {
piCout << "start ...";
PIHTTPServer server;
server.registerUnhandled([](const MessageConst & msg) { return createMessage(Code::BadRequest, "unhadled", msg); });
server.registerPath("api/v1/status", Method::Get, [](const MessageConst & msg) {
return createMessage(Code::Accepted, "api/v1/status", msg);
});
server.registerPath("api/v1/plugins", Method::Get, [](const MessageConst & msg) {
return createMessage(Code::Accepted, "api/v1/plugins", msg);
});
server.registerPath("api/v1/task-status", Method::Get, [](const MessageConst & msg) {
return createMessage(Code::Accepted, "api/v1/task-status", msg);
});
server.registerPath("api/v1/task/{taskID}/status", Method::Get, [](const MessageConst & msg) {
return createMessage(Code::Accepted, "api/v1/task/{taskID}/status", msg);
});
server.registerPath("api/v1/bort/list", Method::Get, [](const MessageConst & msg) {
return createMessage(Code::Accepted, "api/v1/bort/list", msg);
});
server.registerPath("api/v1/all", Method::Get, [](const MessageConst & msg) {
return createMessage(Code::Accepted, "api/v1/all", msg);
});
server.registerPath("api/v1/all/bort{A}/f", Method::Get, [](const MessageConst & msg) {
return createMessage(Code::Accepted, "api/v1/all/*/f", msg);
});
server.registerPath("api/v1/all2/**", Method::Get, [](const MessageConst & msg) {
return createMessage(Code::Accepted, "api/v1/all2/**", msg);
});
server.listenAll(12345);
kbd.enableExitCapture('Q');
WAIT_FOR_EXIT
piCout << "exiting ...";
server.stop();
return 0;
PISystemMonitor mon;
mon.startOnSelf();
PISystemMonitor::totalRAM();
2_s .sleep();
return 0;
PIMQTT::Client cl;
cl.setConnectTimeout(2_s);
CONNECTL(&cl, connected, [&cl] {
@@ -36,7 +75,7 @@ int main(int argc, char * argv[]) {
cl.connect("localhost", "PIP");
});
CONNECTL(&cl, received, [](const PIMQTT::MessageConst & message) {
piCout << "received" << message.topic() << message.payload().size();
piCout << "received" << message.topic() << message.pathArguments() << message.payload().size();
});
cl.connect("localhost", "PIP");
@@ -45,126 +84,4 @@ int main(int argc, char * argv[]) {
WAIT_FOR_EXIT
piCout << "exiting ...";
return 0;
PICrypt _crypt;
// auto ba = PIFile::readAll("logo.png");
PIString str = "hello!"_a;
PIByteArray ba = str.toAscii();
PIByteArray key = PIString("SMBusDataHashKey").toByteArray();
const int times = 1000000;
PITimeMeasurer tm;
PISystemTime el;
tm.reset();
piForTimes(times) {
PIDigest::calculateWithKey(ba, key, PIDigest::Type::SipHash_2_4_128);
}
el = tm.elapsed();
piCout << "PIDigest" << el.toString();
tm.reset();
piForTimes(times) {
_crypt.shorthash(str, key);
}
el = tm.elapsed();
piCout << " sodium" << el.toString();
tm.reset();
piForTimes(times) {
PIDigest::calculateWithKey(ba, key, PIDigest::Type::BLAKE2b_128);
}
el = tm.elapsed();
piCout << " blake" << el.toString();
return 0;
PIEthernet *eth_r, *eth_s;
eth_r = PIIODevice::createFromFullPath("eth://udp: 192.168.1.25 :10000")->cast<PIEthernet>();
eth_s = PIIODevice::createFromFullPath("eth://udp: : : 192.168.1.25:10000")->cast<PIEthernet>();
eth_r->setReadBufferSize(1_MiB);
CONNECTL(eth_r, threadedReadEvent, [](const uchar * readed, ssize_t size) {
// piCout << "rec";
piMSleep(1);
++rcnt;
});
eth_r->startThreadedRead();
PIByteArray _ba(1400);
for (int i = 0; i < 100; ++i) {
eth_s->write(_ba);
++scnt;
}
0.2_s .sleep();
piCout << "snd" << scnt;
piCout << "rec" << rcnt;
piDeleteSafety(eth_r);
piDeleteSafety(eth_s);
return 0;
PITranslator::loadLang("ru");
/*auto ucl = PIUnits::allClasses();
for (auto c: ucl) {
piCout << (c->className() + ":");
for (auto t: c->allTypes()) {
piCout << " " << c->name(t) << "->" << c->unit(t);
}
}*/
// PIUnits::Value(1);
// piCout << PIUnits::name(PIUnits::Class::Information::Bit);
// piCout << PIUnits::name(PIUnits::Class::Information::Byte);
// piCout << PIUnits::name(PIUnits::Class::Information::_LastType);
// piCout << PIUnits::name((int)PIUnits::Class::Angle::Degree);
// piCout << PIUnits::unit(PIUnits::Class::Information::Bit);
// piCout << PIUnits::unit(PIUnits::Class::Information::Byte);
// piCout << PIUnits::unit(PIUnits::Class::Information::_LastType);
// piCout << PIUnits::unit((int)PIUnits::Class::Angle::Degree);
// for (int i = -10; i < 10; ++i)
// piCout << PIUnits::Value(pow10(i * 0.99), PIUnits::Class::Distance::Meter).toString();
auto v = PIUnits::Value(M_PI, Angle::Radian);
piCout << v << "=" << v.converted(Angle::Degree);
v = PIUnits::Value(45, Angle::Degree);
piCout << v << "=" << v.converted(Angle::Radian);
piCout << PIUnits::Value(5E-5, Time::Second);
piCout << PIUnits::Value(3E-3, Time::Second);
piCout << PIUnits::Value(0.8, Time::Second);
piCout << PIUnits::Value(1.2, Time::Second);
piCout << PIUnits::Value(1001, Time::Second);
piCout << PIUnits::Value(1000001, Time::Second);
piCout << PIUnits::Value(1_KB, Information::Byte);
piCout << PIUnits::Value(1_MB, Information::Byte);
piCout << PIUnits::Value(1_MiB, Information::Byte);
piCout << PIUnits::Value(1_MB, Information::Byte).converted(Information::Bit);
piCout << PIUnits::Value(1_MiB, Information::Byte).converted(Information::Bit);
piCout << PIUnits::Value(0., Temperature::Celsius).converted(Temperature::Kelvin);
piCout << PIUnits::Value(0., Temperature::Celsius).converted(Temperature::Fahrenheit);
piCout << PIUnits::Value(100., Temperature::Celsius).converted(Temperature::Fahrenheit);
piCout << PIUnits::Value(1., Pressure::Atmosphere).converted(Pressure::Pascal);
piCout << PIUnits::Value(1., Pressure::Atmosphere).converted(Pressure::MillimetreOfMercury);
piCout << PIUnits::Value(766., Pressure::MillimetreOfMercury).converted(Pressure::Atmosphere);
piCout << PIUnits::Value(5E-5, Time::Second).converted(Time::Hertz);
piCout << PIUnits::Value(3E-3, Time::Second).converted(Time::Hertz);
piCout << PIUnits::Value(0.8, Time::Second).converted(Time::Hertz);
piCout << PIUnits::Value(1.2, Time::Second).converted(Time::Hertz);
piCout << PIUnits::Value(1001, Time::Second).converted(Time::Hertz);
piCout << PIUnits::Value(1000001, Time::Second).converted(Time::Hertz);
// piCout << PIUnits::Value(0.2, Time::Second).converted(Time::Hertz);
// piCout << PIUnits::Value(5E-5, Time::Second).converted(Time::Hertz);
return 0;
}