diff --git a/libs/http_client/curl_thread_pool_p.h b/libs/http_client/curl_thread_pool_p.h index effc3674..5b4f1b7c 100644 --- a/libs/http_client/curl_thread_pool_p.h +++ b/libs/http_client/curl_thread_pool_p.h @@ -1,7 +1,6 @@ #ifndef curl_thread_pool_p_H #define curl_thread_pool_p_H -#include "piconditionvar.h" #include "piprotectedvariable.h" #include "pisemaphore.h" #include "pithread.h" diff --git a/libs/http_client/pihttpclient.cpp b/libs/http_client/pihttpclient.cpp index 29467ba8..05154374 100644 --- a/libs/http_client/pihttpclient.cpp +++ b/libs/http_client/pihttpclient.cpp @@ -102,8 +102,8 @@ void PIHTTPClient::procHeaderLine(PIString & line) { size_t PIHTTPClient::writeMemoryFunc(void * contents, size_t size, size_t nmemb, void * ptr) { size_t bytes = size * nmemb; - piCout << "writeMemoryFunc" << bytes; - auto client = reinterpret_cast(ptr); + // piCout << "writeMemoryFunc" << bytes; + auto client = reinterpret_cast(ptr); if (client->is_cancel) return CURL_WRITEFUNC_ERROR; client->buffer_out.append(contents, bytes); return bytes; @@ -112,8 +112,8 @@ size_t PIHTTPClient::writeMemoryFunc(void * contents, size_t size, size_t nmemb, size_t PIHTTPClient::readMemoryFunc(void * contents, size_t size, size_t nmemb, void * ptr) { size_t bytes = size * nmemb; - piCout << "readMemoryFunc" << bytes; - auto client = reinterpret_cast(ptr); + // piCout << "readMemoryFunc" << bytes; + auto client = reinterpret_cast(ptr); if (client->is_cancel) return CURL_READFUNC_ABORT; const auto & buffer(client->request.body()); if (buffer.isEmpty()) return 0; diff --git a/libs/http_server/microhttpd_server_p.cpp b/libs/http_server/microhttpd_server_p.cpp index d25f02b9..29a9f19f 100644 --- a/libs/http_server/microhttpd_server_p.cpp +++ b/libs/http_server/microhttpd_server_p.cpp @@ -30,13 +30,23 @@ // clang-format on -struct MicrohttpdServerConnection { - bool ready(); - int sendReply(const PIHTTP::MessageMutable & r); - int sendError(); +using namespace PIHTTP; - bool done = false; - PIHTTP::Method method = PIHTTP::Method::Unknown; + +struct BasicAuthCred { + bool exists = false; + PIString user; + PIString pass; +}; + +struct MicrohttpdServerConnection { + bool checkBasicAuth(); + bool ready(); + int sendReply(const MessageMutable & r); + BasicAuthCred getBasicAuthCred(); + + bool done = false, authorized = false; + Method method = Method::Unknown; PIString path; PIByteArray body; PIMap headers, args, post; @@ -46,12 +56,31 @@ struct MicrohttpdServerConnection { }; +bool MicrohttpdServerConnection::checkBasicAuth() { + if (headers.contains(Header::Authorization)) { + auto ba_up = getBasicAuthCred(); + bool ok = false; // server->callback_auth(ba_up.user, ba_up.pass); + if (server->callback_auth) ok = server->callback_auth(ba_up.user, ba_up.pass); + if (ok) { + authorized = true; + return true; + } + } + // piCout << "miss authorization"; + sendReply(MessageMutable::fromCode(Code::Unauthorized) + .addHeader(Header::WWWAuthenticate, "Basic realm=\"%1\", charset=\"UTF-8\""_a.arg(server->realm)) + .setBody(PIByteArray::fromAscii("Authorization required"))); + // piCout << "answer sent"; + return false; +} + + bool MicrohttpdServerConnection::ready() { if (!server) return false; if (done) return true; done = true; - PIHTTP::MessageMutable rep; - if (method == PIHTTP::Method::Get) { + MessageMutable rep; + if (method == Method::Get) { if (path == "/favicon.ico"_a) { // piCout << "send favicon" << server->favicon.size() << "bytes"; rep.setBody(server->favicon); @@ -60,13 +89,13 @@ bool MicrohttpdServerConnection::ready() { } } // piCout << "ready" << (int)method << path; - PIHTTP::MessageMutable req; + MessageMutable req; req.setMethod(method); req.setPath(path); req.setBody(body); req.headers() = headers; req.arguments() = args; - rep.setCode(PIHTTP::Code::BadRequest); + rep.setCode(Code::BadRequest); if (server->callback) rep = server->callback(req); MicrohttpdServer::addFixedHeaders(rep); sendReply(rep); @@ -75,7 +104,7 @@ bool MicrohttpdServerConnection::ready() { } -int MicrohttpdServerConnection::sendReply(const PIHTTP::MessageMutable & r) { +int MicrohttpdServerConnection::sendReply(const MessageMutable & 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(); @@ -91,8 +120,21 @@ int MicrohttpdServerConnection::sendReply(const PIHTTP::MessageMutable & r) { } -int MicrohttpdServerConnection::sendError() { - return MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, MHD_create_response_from_buffer(0, nullptr, MHD_RESPMEM_MUST_COPY)); +BasicAuthCred MicrohttpdServerConnection::getBasicAuthCred() { + BasicAuthCred ret; + char * p = nullptr; + auto u = MHD_basic_auth_get_username_password(connection, &p); + if (u) { + ret.user = PIString::fromUTF8(u); + ret.exists = true; + MHD_free(u); + } + if (p) { + ret.pass = PIString::fromUTF8(p); + ret.exists = true; + MHD_free(p); + } + return ret; } @@ -152,38 +194,38 @@ int args_iterate(void * cls, MHD_ValueKind kind, const char * key, const char * } -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) { +int answer_callback(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; - PIHTTP::Method m = PIHTTP::Method::Unknown; + Method m = Method::Unknown; if (0 == strcmp(method, "GET")) - m = PIHTTP::Method::Get; + m = Method::Get; else if (0 == strcmp(method, "POST")) - m = PIHTTP::Method::Post; + m = Method::Post; else if (0 == strcmp(method, "HEAD")) - m = PIHTTP::Method::Head; + m = Method::Head; else if (0 == strcmp(method, "PUT")) - m = PIHTTP::Method::Put; + m = Method::Put; else if (0 == strcmp(method, "DELETE")) - m = PIHTTP::Method::Delete; + m = Method::Delete; else if (0 == strcmp(method, "CONNECT")) - m = PIHTTP::Method::Connect; + m = Method::Connect; else if (0 == strcmp(method, "OPTIONS")) - m = PIHTTP::Method::Options; + m = Method::Options; else if (0 == strcmp(method, "TRACE")) - m = PIHTTP::Method::Trace; + m = Method::Trace; else if (0 == strcmp(method, "PATCH")) - m = PIHTTP::Method::Patch; + m = Method::Patch; - if (m == PIHTTP::Method::Unknown) { + if (m == Method::Unknown) { piCout << "[MicrohttpdServer]" << "Warning:" << "Unknown method!"; @@ -200,11 +242,14 @@ 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 == PIHTTP::Method::Unknown) { - return conn->sendError(); + if (m == Method::Unknown) { + return conn->sendReply(MessageMutable::fromCode(Code::MethodNotAllowed)); + } + + if (server->isBasicAuthEnabled() && !conn->authorized) { + if (!conn->checkBasicAuth()) return MHD_YES; } if (*upload_data_size) { @@ -216,7 +261,7 @@ int answer_to_connection(void * cls, *upload_data_size = 0; } else { // qDebug() << "answer ok"; - if (!conn->ready()) return conn->sendError(); + if (!conn->ready()) return conn->sendReply(MessageMutable::fromCode(Code::InternalServerError)); } return MHD_YES; } @@ -231,6 +276,7 @@ MicrohttpdServer::MicrohttpdServer() { PRIVATE->daemon = nullptr; opts[Option::ConnectionLimit] = FD_SETSIZE - 4; opts[Option::ConnectionTimeout] = 0_s; + realm = "Restricted"_a; } @@ -285,7 +331,7 @@ bool MicrohttpdServer::listen(PINetworkAddress addr) { addr.port(), nullptr, nullptr, - (MHD_AccessHandlerCallback)answer_to_connection, + (MHD_AccessHandlerCallback)answer_callback, this, MHD_OPTION_ARRAY, options.data(), @@ -307,14 +353,14 @@ void MicrohttpdServer::stop() { } -void MicrohttpdServer::addFixedHeaders(PIHTTP::MessageMutable & msg) { - if (!msg.headers().contains(PIHTTP::Header::ContentType)) { +void MicrohttpdServer::addFixedHeaders(MessageMutable & msg) { + if (!msg.headers().contains(Header::ContentType)) { if (msg.body().isNotEmpty()) { if (msg.body().startsWith(PIByteArray::fromAscii(""))) - msg.addHeader(PIHTTP::Header::ContentType, "text/html; charset=utf-8"); + msg.addHeader(Header::ContentType, "text/html; charset=utf-8"); else if (msg.body()[0] == '[' || msg.body()[0] == '{') - msg.addHeader(PIHTTP::Header::ContentType, "application/json; charset=utf-8"); + msg.addHeader(Header::ContentType, "application/json; charset=utf-8"); } } - msg.addHeader(PIHTTP::Header::AccessControlAllowOrigin, "*"); + msg.addHeader(Header::AccessControlAllowOrigin, "*"); } diff --git a/libs/main/http_common/pihttptypes.cpp b/libs/main/http_common/pihttptypes.cpp index 829e0765..2fc6560f 100644 --- a/libs/main/http_common/pihttptypes.cpp +++ b/libs/main/http_common/pihttptypes.cpp @@ -16,3 +16,47 @@ const char * PIHTTP::methodName(Method m) { }; return "UNKNOWN"; } + + +PIHTTP::MessageMutable & PIHTTP::MessageMutable::addHeader(const PIString & header, const PIString & value) { + m_headers[header] = value; + return *this; +} + + +PIHTTP::MessageMutable & PIHTTP::MessageMutable::setMethod(Method m) { + m_method = m; + return *this; +} + + +PIHTTP::MessageMutable & PIHTTP::MessageMutable::setCode(Code c) { + m_code = c; + return *this; +} + + +PIHTTP::MessageMutable & PIHTTP::MessageMutable::setPath(PIString p) { + m_path = std::move(p); + return *this; +} + + +PIHTTP::MessageMutable & PIHTTP::MessageMutable::setBody(PIByteArray b) { + m_body = std::move(b); + return *this; +} + + +PIHTTP::MessageMutable PIHTTP::MessageMutable::fromCode(Code c) { + PIHTTP::MessageMutable ret; + ret.setCode(c); + return ret; +} + + +PIHTTP::MessageMutable PIHTTP::MessageMutable::fromMethod(Method m) { + PIHTTP::MessageMutable ret; + ret.setMethod(m); + return ret; +} diff --git a/libs/main/http_common/pihttptypes.h b/libs/main/http_common/pihttptypes.h index 33157bd1..c93db8bd 100644 --- a/libs/main/http_common/pihttptypes.h +++ b/libs/main/http_common/pihttptypes.h @@ -30,16 +30,18 @@ protected: class PIP_EXPORT MessageMutable: public MessageConst { public: - void setMethod(PIHTTP::Method m) { m_method = m; } - void setCode(PIHTTP::Code c) { m_code = c; } - void setPath(PIString p) { m_path = std::move(p); } - void setBody(PIByteArray b) { m_body = std::move(b); } + MessageMutable & setMethod(PIHTTP::Method m); + MessageMutable & setCode(PIHTTP::Code c); + MessageMutable & setPath(PIString p); + MessageMutable & setBody(PIByteArray b); const PIMap & headers() const { return m_headers; } const PIMap & arguments() const { return m_arguments; } PIMap & headers() { return m_headers; } PIMap & arguments() { return m_arguments; } - void addHeader(const PIString & header, const PIString & value) { m_headers[header] = value; } + MessageMutable & addHeader(const PIString & header, const PIString & value); void removeHeader(const PIString & header) { m_headers.remove(header); } + static MessageMutable fromCode(PIHTTP::Code c); + static MessageMutable fromMethod(PIHTTP::Method m); }; diff --git a/libs/main/http_server/microhttpd_server.h b/libs/main/http_server/microhttpd_server.h index c6b4a9f3..6c944e8c 100644 --- a/libs/main/http_server/microhttpd_server.h +++ b/libs/main/http_server/microhttpd_server.h @@ -32,15 +32,25 @@ public: bool isListen() const; void stop(); + void enableBasicAuth() { setBasicAuthEnabled(true); } + void disableBasicAuth() { setBasicAuthEnabled(false); } + void setBasicAuthEnabled(bool yes) { use_basic_auth = yes; } + bool isBasicAuthEnabled() const { return use_basic_auth; } + void setBasicAuthRealm(const PIString & r) { realm = r; } + void setRequestCallback(std::function c) { callback = c; } + void setBasicAuthCallback(std::function c) { callback_auth = c; } private: static void addFixedHeaders(PIHTTP::MessageMutable & msg); PRIVATE_DECLARATION(PIP_HTTP_SERVER_EXPORT) PIByteArray favicon; + PIString realm; PIMap opts; std::function callback; + std::function callback_auth; + std::atomic_bool use_basic_auth = {false}; PIByteArray mem_key, mem_cert, key_pass; }; diff --git a/libs/main/http_server/pihttpserver.h b/libs/main/http_server/pihttpserver.h index cb0d847b..3c755023 100644 --- a/libs/main/http_server/pihttpserver.h +++ b/libs/main/http_server/pihttpserver.h @@ -17,6 +17,8 @@ public: void unregisterPath(const PIString & path, PIHTTP::Method method); void unregisterPath(const PIString & path); + // void registerBasicAuth() {} + 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(); } diff --git a/main.cpp b/main.cpp index 13ce02f2..df66637b 100644 --- a/main.cpp +++ b/main.cpp @@ -3,7 +3,7 @@ using namespace PICoutManipulators; -// PIKbdListener kbd; +PIKbdListener kbd; const char * pageTitle = "" "" "" @@ -11,14 +11,27 @@ const char * pageTitle = "" "" ""; +template +PIString stringify(const T & v) { + PIString ret; + PICout::withExternalBuffer(&ret, 0, PICoutManipulators::AddSpaces) << v; + return ret; +} int main(int argc, char * argv[]) { PIHTTPServer server; server.listen({"127.0.0.1:7777"}); + server.setBasicAuthRealm("pip"); + server.setBasicAuthEnabled(true); + server.setBasicAuthCallback([](const PIString & u, const PIString & p) -> bool { + piCout << "basic auth" << u << p; + return (u == "u" && p == "p"); + }); server.registerUnhandled([](const PIHTTP::MessageConst & msg) -> PIHTTP::MessageMutable { PIHTTP::MessageMutable ret; - piCout << "server rec:\n\tpath: %1\n\tmethod: %2\n\tbody: %3"_a.arg(msg.path()) + piCout << "server rec:\n\tpath: %1\n\tmethod: %2\n\targs: %3\n\tbody: %4\n"_a.arg(msg.path()) .arg((int)msg.method()) + .arg(stringify(msg.arguments())) .arg(PIString::fromUTF8(msg.body())); ret.setCode(PIHTTP::Code::BadRequest); ret.setBody(PIByteArray::fromAscii("hello client! 0123456789")); @@ -27,11 +40,13 @@ int main(int argc, char * argv[]) { PIHTTP::MessageMutable req; req.setBody(PIByteArray::fromAscii("hello server!")); - auto * c = PIHTTPClient::create("http://127.0.0.1:7777/api", PIHTTP::Method::Get, req); - c->onFinish([](PIHTTP::MessageConst r) { - piCout << "finish" << (int)r.code(); - piCout << "headers" << r.headers(); - piCout << "body" << PIString::fromUTF8(r.body()); + auto * c = PIHTTPClient::create("http://u:p2@127.0.0.1:7777/api", PIHTTP::Method::Get, req); + c->onFinish([](PIHTTP::MessageConst msg) { + piCout << "client rec:\n\tcode: %5\n\tpath: %1\n\tmethod: %2\n\targs: %3\n\tbody: %4\n"_a.arg(msg.path()) + .arg((int)msg.method()) + .arg(stringify(msg.arguments())) + .arg(PIString::fromUTF8(msg.body())) + .arg((int)msg.code()); }) ->onError([c](PIHTTP::MessageConst r) { piCout << "error" << (int)r.code(); @@ -42,11 +57,16 @@ int main(int argc, char * argv[]) { piCout << "msg" << c->lastError(); }) ->start(); - piMSleep(500); + + // piMSleep(500); + kbd.enableExitCapture(); + WAIT_FOR_EXIT + server.stop(); // c->abort(); - // piMSleep(500); + return 0; + // piCout << PIString::readableSize(PISystemMonitor::usedRAM()); /*PIVector vi;