PIHTTPServer basic auth works
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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<PIHTTPClient *>(ptr);
|
||||
// piCout << "writeMemoryFunc" << bytes;
|
||||
auto client = reinterpret_cast<PIHTTPClient *>(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<PIHTTPClient *>(ptr);
|
||||
// piCout << "readMemoryFunc" << bytes;
|
||||
auto client = reinterpret_cast<PIHTTPClient *>(ptr);
|
||||
if (client->is_cancel) return CURL_READFUNC_ABORT;
|
||||
const auto & buffer(client->request.body());
|
||||
if (buffer.isEmpty()) return 0;
|
||||
|
||||
@@ -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,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("<!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, "*");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
Reference in New Issue
Block a user