PIHTTPServer basic auth works

This commit is contained in:
2024-11-24 23:23:08 +03:00
parent c2c9822169
commit 1acaf24df9
8 changed files with 184 additions and 61 deletions

View File

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

View File

@@ -102,7 +102,7 @@ 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;
// piCout << "writeMemoryFunc" << bytes;
auto client = reinterpret_cast<PIHTTPClient *>(ptr);
if (client->is_cancel) return CURL_WRITEFUNC_ERROR;
client->buffer_out.append(contents, bytes);
@@ -112,7 +112,7 @@ 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;
// piCout << "readMemoryFunc" << bytes;
auto client = reinterpret_cast<PIHTTPClient *>(ptr);
if (client->is_cancel) return CURL_READFUNC_ABORT;
const auto & buffer(client->request.body());

View File

@@ -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<PIString, PIString> 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,7 +194,7 @@ int args_iterate(void * cls, MHD_ValueKind kind, const char * key, const char *
}
int answer_to_connection(void * cls,
int answer_callback(void * cls,
MHD_Connection * connection,
const char * url,
const char * method,
@@ -162,28 +204,28 @@ int answer_to_connection(void * cls,
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("<!DOCTYPE html>")))
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, "*");
}

View File

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

View File

@@ -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<PIString, PIString> & headers() const { return m_headers; }
const PIMap<PIString, PIString> & arguments() const { return m_arguments; }
PIMap<PIString, PIString> & headers() { return m_headers; }
PIMap<PIString, PIString> & 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);
};

View File

@@ -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<PIHTTP::MessageMutable(const PIHTTP::MessageConst &)> c) { callback = c; }
void setBasicAuthCallback(std::function<bool(const PIString &, const PIString &)> c) { callback_auth = c; }
private:
static void addFixedHeaders(PIHTTP::MessageMutable & msg);
PRIVATE_DECLARATION(PIP_HTTP_SERVER_EXPORT)
PIByteArray favicon;
PIString realm;
PIMap<Option, PIVariant> opts;
std::function<PIHTTP::MessageMutable(const PIHTTP::MessageConst &)> callback;
std::function<bool(const PIString &, const PIString &)> callback_auth;
std::atomic_bool use_basic_auth = {false};
PIByteArray mem_key, mem_cert, key_pass;
};

View File

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

View File

@@ -3,7 +3,7 @@
using namespace PICoutManipulators;
// PIKbdListener kbd;
PIKbdListener kbd;
const char * pageTitle = "<!DOCTYPE html>"
"<html>"
"<body>"
@@ -11,14 +11,27 @@ const char * pageTitle = "<!DOCTYPE html>"
"</body>"
"</html>";
template<typename T>
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<int> vi;