#include "pihttpserver.h" #include "piliterals_string.h" 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(); bool found = false; while (it.next()) { for (const auto & ep: it.value()) { if (ep.function && ep.method == r.method()) { PIMap ext_args; if (ep.match(in_path, ext_args)) { auto & r_mut(static_cast(const_cast(r))); r_mut.pathArguments() = ext_args; r_mut.arguments() = r_mut.pathArguments(); r_mut.arguments() << r_mut.queryArguments(); reply = ep.function(r); found = true; break; } } } if (found) break; } if (!found && unhandled) reply = unhandled(r); auto hit = reply_headers.makeIterator(); while (hit.next()) reply.addHeader(hit.key(), hit.value()); return reply; }); } PIHTTPServer::~PIHTTPServer() { stop(); } bool PIHTTPServer::registerPath(const PIString & path, PIHTTP::Method method, RequestFunction functor) { Endpoint ep; if (!ep.create(path)) return false; ep.method = method; ep.function = functor; endpoints[ep.priority] << ep; return true; } void PIHTTPServer::registerUnhandled(RequestFunction functor) { unhandled = functor; } void PIHTTPServer::unregisterPath(const PIString & path, PIHTTP::Method method) { auto pl = splitPath(path); auto it = 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 & epl) { return epl.isEmpty(); }); } void PIHTTPServer::unregisterPath(const PIString & path) { auto pl = splitPath(path); auto it = endpoints.makeIterator(); while (it.next()) { it.value().removeWhere([&pl](const Endpoint & ep) { return ep.path == pl; }); } endpoints.removeWhere([](uint, const PIVector & 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 & 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 & 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; }