add http_client library, using libcurl

take out common http entities to http_common dir
This commit is contained in:
2024-11-23 17:54:22 +03:00
parent bf9ad65ff0
commit dff4f2b3a0
17 changed files with 955 additions and 137 deletions

View File

@@ -97,7 +97,7 @@ set(PIP_UTILS_LIST)
set(PIP_TESTS_LIST) set(PIP_TESTS_LIST)
set(PIP_EXPORTS) set(PIP_EXPORTS)
set(PIP_SRC_MODULES "console;crypt;compress;usb;fftw;opencl;io_utils;client_server;cloud;lua;http_server") set(PIP_SRC_MODULES "console;crypt;compress;usb;fftw;opencl;io_utils;client_server;cloud;lua;http_client;http_server")
foreach(_m ${PIP_SRC_MODULES}) foreach(_m ${PIP_SRC_MODULES})
set(PIP_MSG_${_m} "no") set(PIP_MSG_${_m} "no")
endforeach() endforeach()
@@ -498,10 +498,8 @@ if (NOT CROSSTOOLS)
list(APPEND HDRS ${_lua_src_hdr}) list(APPEND HDRS ${_lua_src_hdr})
# libmicrohttpd # libmicrohttpd
find_library(microhttpd_LIBRARIES microhttpd HINTS "${MINGW_LIB}") pip_find_lib(microhttpd HINTS "${MINGW_LIB}")
set(microhttpd_FOUND FALSE) if (microhttpd_FOUND)
if (microhttpd_LIBRARIES)
set(microhttpd_FOUND TRUE)
set(_microhttpd_add_libs microhttpd) set(_microhttpd_add_libs microhttpd)
if(WIN32) if(WIN32)
if("${C_COMPILER}" STREQUAL "cl.exe") if("${C_COMPILER}" STREQUAL "cl.exe")
@@ -528,6 +526,12 @@ if (NOT CROSSTOOLS)
pip_module(http_server "${_microhttpd_add_libs}" "PIP HTTP server" "" "" "") pip_module(http_server "${_microhttpd_add_libs}" "PIP HTTP server" "" "" "")
endif() endif()
# libcurl
pip_find_lib(curl HINTS "${MINGW_LIB}")
if (curl_FOUND)
pip_module(http_client curl "PIP HTTP client" "" "" "")
endif()
# Test program # Test program
if(PIP_UTILS) if(PIP_UTILS)
@@ -535,9 +539,9 @@ if (NOT CROSSTOOLS)
#target_link_libraries(pip_plugin pip) #target_link_libraries(pip_plugin pip)
if (NOT DEFINED ANDROID_PLATFORM) if (NOT DEFINED ANDROID_PLATFORM)
if(microhttpd_FOUND) if(curl_FOUND)
add_executable(pip_test "main.cpp") add_executable(pip_test "main.cpp")
target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server) target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server pip_http_client)
if(sodium_FOUND) if(sodium_FOUND)
add_executable(pip_cloud_test "main_picloud_test.cpp") add_executable(pip_cloud_test "main_picloud_test.cpp")
target_link_libraries(pip_cloud_test pip_cloud) target_link_libraries(pip_cloud_test pip_cloud)
@@ -667,7 +671,7 @@ if ((NOT PIP_FREERTOS) AND (NOT CROSSTOOLS))
find_package(Doxygen) find_package(Doxygen)
if(DOXYGEN_FOUND) if(DOXYGEN_FOUND)
set(DOXY_DEFINES "${PIP_EXPORTS}") set(DOXY_DEFINES "${PIP_EXPORTS}")
foreach (_m "console" "usb" "compress" "crypt" "client_server" "cloud" "fftw" "opencl" "io_utils" "lua") foreach (_m "console" "usb" "compress" "crypt" "client_server" "cloud" "fftw" "opencl" "io_utils" "lua" "http_server" "http_client")
string(TOUPPER "${_m}" _mdef) string(TOUPPER "${_m}" _mdef)
list(APPEND DOXY_DEFINES "PIP_${_mdef}_EXPORT") list(APPEND DOXY_DEFINES "PIP_${_mdef}_EXPORT")
endforeach() endforeach()

View File

@@ -12,6 +12,7 @@ Create imported targets:
* PIP::ClientServer * PIP::ClientServer
* PIP::Cloud * PIP::Cloud
* PIP::Lua * PIP::Lua
* PIP::HTTPClient
* PIP::HTTPServer * PIP::HTTPServer
These targets include directories and depends on These targets include directories and depends on
@@ -27,7 +28,7 @@ shstk_set_find_dirs(PIP)
set(__libs "usb;crypt;console;fftw;compress;opencl;io_utils;client_server;cloud;lua;http_server") set(__libs "usb;crypt;console;fftw;compress;opencl;io_utils;client_server;cloud;lua;http_server")
if (BUILDING_PIP) if (BUILDING_PIP)
#set(_libs "pip;pip_usb;pip_console;pip_crypt;pip_fftw;pip_compress;pip_opencl;pip_io_utils;pip_cloud;pip_lua;pip_http_server") #set(_libs "pip;pip_usb;pip_console;pip_crypt;pip_fftw;pip_compress;pip_opencl;pip_io_utils;pip_cloud;pip_lua;pip_http_client;pip_http_server")
#set(_bins "pip_cmg;pip_rc;deploy_tool") #set(_bins "pip_cmg;pip_rc;deploy_tool")
#get_target_property(_path pip BINARY_DIR) #get_target_property(_path pip BINARY_DIR)
#get_target_property(_path pip LIBRARY_OUTPUT_NAME) #get_target_property(_path pip LIBRARY_OUTPUT_NAME)
@@ -97,6 +98,7 @@ set(__module_io_utils IOUtils )
set(__module_client_server ClientServer) set(__module_client_server ClientServer)
set(__module_cloud Cloud ) set(__module_cloud Cloud )
set(__module_lua Lua ) set(__module_lua Lua )
set(__module_http_client HTTPClient )
set(__module_http_server HTTPServer ) set(__module_http_server HTTPServer )
foreach (_l ${__libs}) foreach (_l ${__libs})

View File

@@ -0,0 +1,75 @@
#include "curl_thread_pool_p.h"
#include "pihttpclient.h"
#include "pitime.h"
CurlThreadPool::CurlThreadPool() {
piForTimes(10) {
auto * t = new PIThread([this]() { threadFunc(); }, true);
threads << t;
}
}
CurlThreadPool::~CurlThreadPool() {
exiting = true;
for (auto * t: threads)
t->stop();
{
auto cr = clients.getRef();
for (auto c: *cr)
c->abort();
}
sem.release(threads.size());
for (auto * t: threads) {
t->waitForFinish();
t->setDebug(false);
t->terminate();
}
piDeleteAllAndClear(threads);
{
auto cr = clients.getRef();
for (auto c: *cr)
delete c;
}
}
void CurlThreadPool::threadFunc() {
if (exiting) return;
// piCout << "threadFuncl w ...";
sem.acquire();
// piCout << "threadFuncl wdone";
if (exiting) return;
PIHTTPClient * c = nullptr;
{
auto cr = clients.getRef();
if (cr->isEmpty()) return;
c = cr->dequeue();
// piCout << "threadFuncl get c";
}
// piCout << "threadFuncl proc c";
procClient(c);
// piCout << "threadFuncl end";
}
void CurlThreadPool::procClient(PIHTTPClient * c) {
if (c->init()) c->perform();
delete c;
}
void CurlThreadPool::registerClient(PIHTTPClient * c) {
clients.getRef()->enqueue(c);
sem.release();
// piCout << "registerClient";
}
CurlThreadPool * CurlThreadPool::instance() {
static CurlThreadPool ret;
return &ret;
}

View File

@@ -0,0 +1,32 @@
#ifndef curl_thread_pool_p_H
#define curl_thread_pool_p_H
#include "piconditionvar.h"
#include "piprotectedvariable.h"
#include "pisemaphore.h"
#include "pithread.h"
class PIHTTPClient;
class CurlThreadPool {
public:
void registerClient(PIHTTPClient * c);
static CurlThreadPool * instance();
private:
NO_COPY_CLASS(CurlThreadPool)
CurlThreadPool();
~CurlThreadPool();
void threadFunc();
void procClient(PIHTTPClient * c);
PIProtectedVariable<PIQueue<PIHTTPClient *>> clients;
PISemaphore sem;
PIVector<PIThread *> threads;
std::atomic_bool exiting = {false};
};
#endif

View File

@@ -0,0 +1,210 @@
#include "pihttpclient.h"
#include "curl_thread_pool_p.h"
#include "piliterals_bytes.h"
#include "piliterals_string.h"
#include "pisystemtime.h"
#include <curl/curl.h>
int xfer_callback(void * ptr, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
return reinterpret_cast<PIHTTPClientBase *>(ptr)->__infoFunc(dltotal, dlnow, ultotal, ulnow);
}
int debug_callback(CURL * handle, curl_infotype type, char * data, size_t size, void * ptr) {
return reinterpret_cast<PIHTTPClientBase *>(ptr)->__debugFunc(type, data, size);
}
PRIVATE_DEFINITION_START(PIHTTPClient)
CURL * handle = nullptr;
PRIVATE_DEFINITION_END(PIHTTPClient)
PIHTTPClient::PIHTTPClient() {}
PIHTTPClient::~PIHTTPClient() {}
bool PIHTTPClient::init() {
if (is_cancel) return false;
PRIVATE->handle = curl_easy_init();
if (!PRIVATE->handle) return false;
curl_easy_setopt(PRIVATE->handle, CURLOPT_WRITEDATA, this);
curl_easy_setopt(PRIVATE->handle, CURLOPT_READDATA, this);
curl_easy_setopt(PRIVATE->handle, CURLOPT_XFERINFODATA, this);
curl_easy_setopt(PRIVATE->handle, CURLOPT_DEBUGDATA, this);
curl_easy_setopt(PRIVATE->handle, CURLOPT_HEADERDATA, this);
curl_easy_setopt(PRIVATE->handle, CURLOPT_WRITEFUNCTION, writeMemoryFunc);
curl_easy_setopt(PRIVATE->handle, CURLOPT_READFUNCTION, readMemoryFunc);
curl_easy_setopt(PRIVATE->handle, CURLOPT_XFERINFOFUNCTION, xfer_callback);
curl_easy_setopt(PRIVATE->handle, CURLOPT_DEBUGFUNCTION, debug_callback);
curl_easy_setopt(PRIVATE->handle, CURLOPT_HEADERFUNCTION, headerFunc);
curl_easy_setopt(PRIVATE->handle, CURLOPT_URL, url.dataUTF8());
curl_easy_setopt(PRIVATE->handle, CURLOPT_CUSTOMREQUEST, PIHTTP::methodName(request.method()));
curl_easy_setopt(PRIVATE->handle, CURLOPT_NOPROGRESS, 0L);
// curl_easy_setopt(PRIVATE->handle, CURLOPT_VERBOSE, 1L);
// curl_easy_setopt(PRIVATE->handle, CURLOPT_ERRORBUFFER, buffer_error.data());
curl_easy_setopt(PRIVATE->handle, CURLOPT_SSL_VERIFYPEER, 0L);
if (request.body().isNotEmpty()) {
curl_easy_setopt(PRIVATE->handle, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(PRIVATE->handle, CURLOPT_INFILESIZE_LARGE, static_cast<curl_off_t>(request.body().size()));
}
return true;
}
void PIHTTPClient::perform() {
if (!PRIVATE->handle) return;
if (!is_cancel) {
// piCout << "perform ...";
PITimeMeasurer tm;
CURLcode res = curl_easy_perform(PRIVATE->handle);
// piCout << "done" << res << "in" << tm.elapsed_m() << ", bytes" << buffer_out.size();
if (res == CURLE_OK) {
reply.setBody(std::move(buffer_out));
if (on_finish) on_finish(reply);
} else {
if (res == CURLE_ABORTED_BY_CALLBACK || is_cancel) {
// piCerr << "curl_easy_perform() failed:" << curl_easy_strerror(res);
if (on_abort) on_abort(reply);
} else {
last_error = curl_easy_strerror(res);
if (on_error) on_error(reply);
}
}
}
// piCout << last_error;
curl_easy_cleanup(PRIVATE->handle);
PRIVATE->handle = nullptr;
}
void PIHTTPClient::procHeaderLine(PIString & line) {
if (line.startsWith("HTTP"_a)) {
// HTTP/Версия КодСостояния Пояснение
line.cutLeft(5);
line.takeWord();
int code = line.takeWord().toInt();
// piCout << "code" << code;
reply.setCode(static_cast<PIHTTP::Code>(code));
return;
}
int ind = line.find(':');
if (ind < 0) return;
PIString hname = line.takeLeft(ind);
line.cutLeft(1).trim();
reply.addHeader(hname, 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);
if (client->is_cancel) return CURL_WRITEFUNC_ERROR;
client->buffer_out.append(contents, bytes);
return bytes;
}
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);
if (client->is_cancel) return CURL_READFUNC_ABORT;
const auto & buffer(client->request.body());
if (buffer.isEmpty()) return 0;
// piCout << bytes;
ssize_t ret = piClamp<ssize_t>(bytes, 0, buffer.size_s() - client->read_pos);
if (ret < 0) ret = 0;
if (ret > 0) memcpy(contents, buffer.data(client->read_pos), ret);
return ret;
}
size_t PIHTTPClient::headerFunc(char * contents, size_t size, size_t nmemb, void * ptr) {
size_t bytes = size * nmemb;
auto client = reinterpret_cast<PIHTTPClient *>(ptr);
if (client->is_cancel) return CURL_WRITEFUNC_ERROR;
PIString line = PIString::fromUTF8(contents, bytes).trim();
if (line.isNotEmpty()) client->procHeaderLine(line);
return bytes;
}
int PIHTTPClient::infoFunc(ssize_t dltotal, ssize_t dlnow, ssize_t ultotal, ssize_t ulnow) {
// piCout << "infoFunc" << dltotal << dlnow << ultotal << ulnow;
if (is_cancel) return 1;
return 0;
}
int PIHTTPClient::debugFunc(int type, char * data, size_t size) {
// piCout << "debugFunc" << type << PIString::fromUTF8(data, size);
return 0;
}
PIHTTPClient * PIHTTPClient::create(const PIString & url_, PIHTTP::Method method, const PIHTTP::MessageConst & req) {
PIHTTPClient * ret = new PIHTTPClient();
static_cast<PIHTTP::MessageConst &>(ret->request) = req;
ret->request.setMethod(method);
ret->url = url_;
return ret;
}
PIHTTPClient * PIHTTPClient::onFinish(std::function<void()> f) {
return onFinish([f](const PIHTTP::MessageConst &) { f(); });
}
PIHTTPClient * PIHTTPClient::onFinish(std::function<void(const PIHTTP::MessageConst &)> f) {
on_finish = f;
return this;
}
PIHTTPClient * PIHTTPClient::onError(std::function<void()> f) {
return onError([f](const PIHTTP::MessageConst &) { f(); });
}
PIHTTPClient * PIHTTPClient::onError(std::function<void(const PIHTTP::MessageConst &)> f) {
on_error = f;
return this;
}
PIHTTPClient * PIHTTPClient::onAbort(std::function<void()> f) {
return onAbort([f](const PIHTTP::MessageConst &) { f(); });
}
PIHTTPClient * PIHTTPClient::onAbort(std::function<void(const PIHTTP::MessageConst &)> f) {
on_abort = f;
return this;
}
void PIHTTPClient::start() {
CurlThreadPool::instance()->registerClient(this);
}
void PIHTTPClient::abort() {
is_cancel = true;
}
int PIHTTPClientBase::__infoFunc(ssize_t dltotal, ssize_t dlnow, ssize_t ultotal, ssize_t ulnow) {
return reinterpret_cast<PIHTTPClient *>(this)->infoFunc(dltotal, dlnow, ultotal, ulnow);
}
int PIHTTPClientBase::__debugFunc(int type, char * data, size_t size) {
return reinterpret_cast<PIHTTPClient *>(this)->debugFunc(type, data, size);
}

View File

@@ -1,4 +1,5 @@
#include "microhttpd_server.h" #include "microhttpd_server.h"
#include "piliterals_bytes.h"
#include "piliterals_string.h" #include "piliterals_string.h"
#include "piliterals_time.h" #include "piliterals_time.h"
@@ -31,11 +32,11 @@
struct MicrohttpdServerConnection { struct MicrohttpdServerConnection {
bool ready(); bool ready();
int send_reply(const MicrohttpdServer::Reply & r); int sendReply(const PIHTTP::MessageMutable & r);
int send_error(); int sendError();
bool done = false; bool done = false;
MicrohttpdServer::Method method = MicrohttpdServer::Method::Unknown; PIHTTP::Method method = PIHTTP::Method::Unknown;
PIString path; PIString path;
PIByteArray body; PIByteArray body;
PIMap<PIString, PIString> headers, args, post; PIMap<PIString, PIString> headers, args, post;
@@ -49,47 +50,48 @@ bool MicrohttpdServerConnection::ready() {
if (!server) return false; if (!server) return false;
if (done) return true; if (done) return true;
done = true; done = true;
MicrohttpdServer::Reply rep; PIHTTP::MessageMutable rep;
if (method == MicrohttpdServer::Method::Get) { if (method == PIHTTP::Method::Get) {
if (path == "/favicon.ico"_a) { if (path == "/favicon.ico"_a) {
// piCout << "send favicon" << server->favicon.size() << "bytes"; // piCout << "send favicon" << server->favicon.size() << "bytes";
rep.setBody(server->favicon); rep.setBody(server->favicon);
send_reply(rep); sendReply(rep);
return true; return true;
} }
} }
// piCout << "ready" << (int)method << path; // piCout << "ready" << (int)method << path;
MicrohttpdServer::Request req; PIHTTP::MessageMutable req;
req.method = method; req.setMethod(method);
req.path = path; req.setPath(path);
req.body = body; req.setBody(body);
req.headers = headers; req.headers() = headers;
req.args = args; req.arguments() = args;
rep.setCode(MHD_HTTP_BAD_REQUEST); rep.setCode(PIHTTP::Code::BadRequest);
if (server->callback) rep = server->callback(req); if (server->callback) rep = server->callback(req);
rep.addFixedHeaders(); MicrohttpdServer::addFixedHeaders(rep);
send_reply(rep); sendReply(rep);
// piCout << "ready ok" << (int)rep.code() << rep.body().size();
return true; return true;
} }
int MicrohttpdServerConnection::send_reply(const MicrohttpdServer::Reply & r) { int MicrohttpdServerConnection::sendReply(const PIHTTP::MessageMutable & r) {
MHD_Response * response = MHD_create_response_from_buffer(r.body.size(), (void *)r.body.data(), MHD_RESPMEM_MUST_COPY); MHD_Response * response = MHD_create_response_from_buffer(r.body().size(), (void *)r.body().data(), MHD_RESPMEM_MUST_COPY);
if (!response) { if (!response) {
// piCout << "null response" << r.body.size() << (void *)r.body.data(); // piCout << "null response" << r.body.size() << (void *)r.body.data();
return MHD_NO; return MHD_NO;
} }
auto it = r.headers.makeIterator(); auto it = r.headers().makeIterator();
while (it.next()) while (it.next())
MHD_add_response_header(response, it.key().dataAscii(), it.value().dataUTF8()); MHD_add_response_header(response, it.key().dataAscii(), it.value().dataUTF8());
// piCout << "status" << r.code; // piCout << "status" << r.code;
int ret = MHD_queue_response(connection, r.code, response); int ret = MHD_queue_response(connection, static_cast<int>(r.code()), response);
MHD_destroy_response(response); MHD_destroy_response(response);
return ret; return ret;
} }
int MicrohttpdServerConnection::send_error() { int MicrohttpdServerConnection::sendError() {
return MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, MHD_create_response_from_buffer(0, nullptr, MHD_RESPMEM_MUST_COPY)); return MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, MHD_create_response_from_buffer(0, nullptr, MHD_RESPMEM_MUST_COPY));
} }
@@ -98,9 +100,9 @@ void log_callback(void * cls, const char * fmt, va_list ap) {
MicrohttpdServer * server = (MicrohttpdServer *)cls; MicrohttpdServer * server = (MicrohttpdServer *)cls;
piCout << "log" << server; piCout << "log" << server;
if (!server) return; if (!server) return;
char buffer[1024]; char buffer[1_KiB];
memset(buffer, 0, 1024); memset(buffer, 0, 1_KiB);
std::vsnprintf(buffer, 1024, fmt, ap); std::vsnprintf(buffer, 1_KiB, fmt, ap);
piCout << buffer; piCout << buffer;
} }
@@ -158,30 +160,30 @@ int answer_to_connection(void * cls,
const char * upload_data, const char * upload_data,
size_t * upload_data_size, size_t * upload_data_size,
void ** con_cls) { void ** con_cls) {
MicrohttpdServer * server = (MicrohttpdServer *)cls; MicrohttpdServer * server = (MicrohttpdServer *)cls;
MicrohttpdServer::Method m = MicrohttpdServer::Method::Unknown; PIHTTP::Method m = PIHTTP::Method::Unknown;
if (0 == strcmp(method, "GET")) if (0 == strcmp(method, "GET"))
m = MicrohttpdServer::Method::Get; m = PIHTTP::Method::Get;
else if (0 == strcmp(method, "POST")) else if (0 == strcmp(method, "POST"))
m = MicrohttpdServer::Method::Post; m = PIHTTP::Method::Post;
else if (0 == strcmp(method, "HEAD")) else if (0 == strcmp(method, "HEAD"))
m = MicrohttpdServer::Method::Head; m = PIHTTP::Method::Head;
else if (0 == strcmp(method, "PUT")) else if (0 == strcmp(method, "PUT"))
m = MicrohttpdServer::Method::Put; m = PIHTTP::Method::Put;
else if (0 == strcmp(method, "DELETE")) else if (0 == strcmp(method, "DELETE"))
m = MicrohttpdServer::Method::Delete; m = PIHTTP::Method::Delete;
else if (0 == strcmp(method, "CONNECT")) else if (0 == strcmp(method, "CONNECT"))
m = MicrohttpdServer::Method::Connect; m = PIHTTP::Method::Connect;
else if (0 == strcmp(method, "OPTIONS")) else if (0 == strcmp(method, "OPTIONS"))
m = MicrohttpdServer::Method::Options; m = PIHTTP::Method::Options;
else if (0 == strcmp(method, "TRACE")) else if (0 == strcmp(method, "TRACE"))
m = MicrohttpdServer::Method::Trace; m = PIHTTP::Method::Trace;
else if (0 == strcmp(method, "PATCH")) else if (0 == strcmp(method, "PATCH"))
m = MicrohttpdServer::Method::Patch; m = PIHTTP::Method::Patch;
if (m == MicrohttpdServer::Method::Unknown) { if (m == PIHTTP::Method::Unknown) {
piCout << "[MicrohttpdServer]" piCout << "[MicrohttpdServer]"
<< "Warning:" << "Warning:"
<< "Unknown method!"; << "Unknown method!";
@@ -201,20 +203,20 @@ int answer_to_connection(void * cls,
return MHD_YES; return MHD_YES;
} }
if (m == MicrohttpdServer::Method::Unknown) { if (m == PIHTTP::Method::Unknown) {
return conn->send_error(); return conn->sendError();
} }
if (*upload_data_size) { if (*upload_data_size) {
if (!conn->postprocessor) { if (!conn->postprocessor) {
conn->postprocessor = MHD_create_post_processor(connection, 65536, (MHD_PostDataIterator)iterate_post, (void *)conn); conn->postprocessor = MHD_create_post_processor(connection, 64_KiB, (MHD_PostDataIterator)iterate_post, (void *)conn);
} }
conn->body.append(upload_data, *upload_data_size); conn->body.append(upload_data, *upload_data_size);
MHD_post_process(conn->postprocessor, upload_data, *upload_data_size); MHD_post_process(conn->postprocessor, upload_data, *upload_data_size);
*upload_data_size = 0; *upload_data_size = 0;
} else { } else {
// qDebug() << "answer ok"; // qDebug() << "answer ok";
if (!conn->ready()) return conn->send_error(); if (!conn->ready()) return conn->sendError();
} }
return MHD_YES; return MHD_YES;
} }
@@ -305,34 +307,14 @@ void MicrohttpdServer::stop() {
} }
void MicrohttpdServer::Reply::addHeader(const PIString & header, const PIString & value) { void MicrohttpdServer::addFixedHeaders(PIHTTP::MessageMutable & msg) {
headers[header] = value; if (!msg.headers().contains(PIHTTP::Header::ContentType)) {
} if (msg.body().isNotEmpty()) {
if (msg.body().startsWith(PIByteArray::fromAscii("<!DOCTYPE html>")))
msg.addHeader(PIHTTP::Header::ContentType, "text/html; charset=utf-8");
void MicrohttpdServer::Reply::removeHeader(const PIString & header) { else if (msg.body()[0] == '[' || msg.body()[0] == '{')
headers.remove(header); msg.addHeader(PIHTTP::Header::ContentType, "application/json; charset=utf-8");
}
void MicrohttpdServer::Reply::setBody(const PIByteArray & b) {
body = b;
}
void MicrohttpdServer::Reply::setCode(int c) {
code = c;
}
void MicrohttpdServer::Reply::addFixedHeaders() {
if (!headers.contains(MHD_HTTP_HEADER_CONTENT_TYPE)) {
if (body.isNotEmpty()) {
if (body.startsWith(PIByteArray::fromAscii("<!DOCTYPE html>")))
addHeader(MHD_HTTP_HEADER_CONTENT_TYPE, "text/html; charset=utf-8");
else if (body[0] == '[' || body[0] == '{')
addHeader(MHD_HTTP_HEADER_CONTENT_TYPE, "application/json; charset=utf-8");
} }
} }
addHeader(MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, "*"); msg.addHeader(PIHTTP::Header::AccessControlAllowOrigin, "*");
} }

View File

@@ -4,29 +4,29 @@
PIHTTPServer::PIHTTPServer() { PIHTTPServer::PIHTTPServer() {
setRequestCallback([this](const MicrohttpdServer::Request & r) -> MicrohttpdServer::Reply { setRequestCallback([this](const PIHTTP::MessageConst & r) -> PIHTTP::MessageMutable {
MicrohttpdServer::Reply rep; PIHTTP::MessageMutable reply;
rep.setCode(404); reply.setCode(PIHTTP::Code::NotFound);
auto in_path = r.path.split("/"); auto in_path = r.path().split("/");
in_path.removeAll(""); in_path.removeAll("");
auto it = functions.makeReverseIterator(); auto it = functions.makeReverseIterator();
bool found = false; bool found = false;
while (it.next()) { while (it.next()) {
if (it.value().function) { if (it.value().function) {
if (it.value().method == r.method) { if (it.value().method == r.method()) {
if (it.value().match(in_path)) { if (it.value().match(in_path)) {
rep = it.value().function(r); reply = it.value().function(r);
found = true; found = true;
break; break;
} }
} }
} }
} }
if (!found && unhandled) rep = unhandled(r); if (!found && unhandled) reply = unhandled(r);
auto hit = reply_headers.makeIterator(); auto hit = reply_headers.makeIterator();
while (hit.next()) while (hit.next())
rep.addHeader(hit.key(), hit.value()); reply.addHeader(hit.key(), hit.value());
return rep; return reply;
}); });
} }
@@ -36,8 +36,8 @@ PIHTTPServer::~PIHTTPServer() {
} }
void PIHTTPServer::registerPath(const PIString & path, Method method, RequestFunction functor) { void PIHTTPServer::registerPath(const PIString & path, PIHTTP::Method method, RequestFunction functor) {
auto & ep(functions[path + PIString::fromNumber((int)method)]); auto & ep(functions[path + PIString::fromNumber(static_cast<int>(method))]);
ep.path = path.split("/"); ep.path = path.split("/");
ep.method = method; ep.method = method;
ep.function = functor; ep.function = functor;
@@ -50,7 +50,7 @@ void PIHTTPServer::registerUnhandled(RequestFunction functor) {
} }
void PIHTTPServer::unregisterPath(const PIString & path, Method method) { void PIHTTPServer::unregisterPath(const PIString & path, PIHTTP::Method method) {
auto pl = path.split("/"); auto pl = path.split("/");
pl.removeAll(""); pl.removeAll("");
auto it = functions.makeIterator(); auto it = functions.makeIterator();

View File

@@ -2679,7 +2679,7 @@ private:
fprintf(stderr, "error with PIDeque<%s>::alloc\n", __PIP_TYPENAME__(T)); fprintf(stderr, "error with PIDeque<%s>::alloc\n", __PIP_TYPENAME__(T));
} }
#endif #endif
assert(p_d); assert(new_data);
pid_data = new_data; pid_data = new_data;
pid_rsize = new_rsize; pid_rsize = new_rsize;
} }

View File

@@ -2556,7 +2556,7 @@ private:
fprintf(stderr, "error with PIVector<%s>::alloc\n", __PIP_TYPENAME__(T)); fprintf(stderr, "error with PIVector<%s>::alloc\n", __PIP_TYPENAME__(T));
} }
#endif #endif
assert(p_d); assert(new_data);
piv_data = new_data; piv_data = new_data;
piv_rsize = new_rsize; piv_rsize = new_rsize;
} }

View File

@@ -0,0 +1,61 @@
#ifndef pihttpclient_h
#define pihttpclient_h
#include "pihttptypes.h"
#include "pip_http_client_export.h"
class PIHTTPClientBase {
public:
int __infoFunc(ssize_t dltotal, ssize_t dlnow, ssize_t ultotal, ssize_t ulnow);
int __debugFunc(int type, char * data, size_t size);
};
class PIP_HTTP_CLIENT_EXPORT PIHTTPClient: private PIHTTPClientBase {
friend class PIHTTPClientBase;
friend class CurlThreadPool;
public:
static PIHTTPClient * create(const PIString & url, PIHTTP::Method method = PIHTTP::Method::Get, const PIHTTP::MessageConst & req = {});
PIHTTPClient * onFinish(std::function<void()> f);
PIHTTPClient * onFinish(std::function<void(const PIHTTP::MessageConst &)> f);
PIHTTPClient * onError(std::function<void()> f);
PIHTTPClient * onError(std::function<void(const PIHTTP::MessageConst &)> f);
PIHTTPClient * onAbort(std::function<void()> f);
PIHTTPClient * onAbort(std::function<void(const PIHTTP::MessageConst &)> f);
void start();
void abort();
PIString lastError() const { return last_error; }
private:
NO_COPY_CLASS(PIHTTPClient)
PIHTTPClient();
virtual ~PIHTTPClient();
PRIVATE_DECLARATION(PIP_HTTP_CLIENT_EXPORT)
bool init();
void perform();
void procHeaderLine(PIString & line);
static size_t writeMemoryFunc(void * contents, size_t size, size_t nmemb, void * ptr);
static size_t readMemoryFunc(void * contents, size_t size, size_t nmemb, void * ptr);
static size_t headerFunc(char * contents, size_t size, size_t nmemb, void * ptr);
int infoFunc(ssize_t dltotal, ssize_t dlnow, ssize_t ultotal, ssize_t ulnow);
int debugFunc(int type, char * data, size_t size);
PIString url;
PIString last_error;
PIByteArray buffer_out;
PIHTTP::MessageMutable request, reply;
std::atomic_bool is_cancel = {false};
ssize_t read_pos = 0;
std::function<void(const PIHTTP::MessageConst &)> on_finish, on_error, on_abort;
};
#endif

View File

@@ -0,0 +1,54 @@
/*
PIP - Platform Independent Primitives
Module includes
Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//! \defgroup HTTPServer HTTPServer
//! \~\brief
//! \~english HTTP client
//! \~russian HTTP сервер
//!
//! \~\details
//! \~english \section cmake_module_HTTPServer Building with CMake
//! \~russian \section cmake_module_HTTPServer Сборка с использованием CMake
//!
//! \~\code
//! find_package(PIP REQUIRED)
//! target_link_libraries([target] PIP::HTTPServer)
//! \endcode
//!
//! \~english \par Common
//! \~russian \par Общее
//!
//! \~english
//! These files provides HTTP server based on libmicrohttpd
//!
//! \~russian
//! Эти файлы обеспечивают HTTP сервер, основанный на libmicrohttpd
//!
//! \~\authors
//! \~english
//! Ivan Pelipenko peri4ko@yandex.ru;
//! \~russian
//! Иван Пелипенко peri4ko@yandex.ru;
//!
#ifndef pihttpclientmodule_H
#define pihttpclientmodule_H
#include "pihttpclient.h"
#endif

View File

@@ -0,0 +1,328 @@
#ifndef pihttpconstants_h
#define pihttpconstants_h
namespace PIHTTP {
enum class Method {
Unknown,
Get,
Head,
Post,
Put,
Delete,
Connect,
Options,
Trace,
Patch
};
enum class Code {
Unknown,
Continue = 100,
SwitchingProtocols = 101,
Processing = 102,
EarlyHints = 103,
Ok = 200,
Created = 201,
Accepted = 202,
NonAuthoritativeInformation = 203,
NoContent = 204,
ResetContent = 205,
PartialContent = 206,
MultiStatus = 207,
AlreadyReported = 208,
IMUsed = 226,
MultipleChoices = 300,
MovedPermanently = 301,
Found = 302,
SeeOther = 303,
NotModified = 304,
UseProxy = 305,
SwitchProxy = 306,
TemporaryRedirect = 307,
PermanentRedirect = 308,
BadRequest = 400,
Unauthorized = 401,
PaymentRequired = 402,
Forbidden = 403,
NotFound = 404,
MethodNotAllowed = 405,
NotAcceptable = 406,
ProxyAuthenticationRequired = 407,
RequestTimeout = 408,
Conflict = 409,
Gone = 410,
LengthRequired = 411,
PreconditionFailed = 412,
ContentTooLarge = 413,
UriTooLong = 414,
UnsupportedMediaType = 415,
RangeNotSatisfiable = 416,
ExpectationFailed = 417,
MisdirectedRequest = 421,
UnprocessableContent = 422,
Locked = 423,
FailedDependency = 424,
TooEarly = 425,
UpgradeRequired = 426,
PreconditionRequired = 428,
TooManyRequests = 429,
RequestHeaderFieldsTooLarge = 431,
RetryWith = 449,
BlockedByWindowsParentalControls = 450,
UnavailableForLegalReasons = 451,
InternalServerError = 500,
NotImplemented = 501,
BadGateway = 502,
ServiceUnavailable = 503,
GatewayTimeout = 504,
HttpVersionNotSupported = 505,
VariantAlsoNegotiates = 506,
InsufficientStorage = 507,
LoopDetected = 508,
NotExtended = 510,
BandwidthLimitExceeded = 509,
NetworkAuthenticationRequired = 511,
};
namespace Header {
constexpr static char Accept[] = "Accept";
constexpr static char AcceptCharset[] = "Accept-Charset";
constexpr static char AcceptEncoding[] = "Accept-Encoding";
constexpr static char AcceptLanguage[] = "Accept-Language";
constexpr static char AcceptRanges[] = "Accept-Ranges";
constexpr static char Age[] = "Age";
constexpr static char Allow[] = "Allow";
constexpr static char AuthenticationInfo[] = "Authentication-Info";
constexpr static char Authorization[] = "Authorization";
constexpr static char CacheControl[] = "Cache-Control";
constexpr static char Close[] = "Close";
constexpr static char Connection[] = "Connection";
constexpr static char ContentEncoding[] = "Content-Encoding";
constexpr static char ContentLanguage[] = "Content-Language";
constexpr static char ContentLength[] = "Content-Length";
constexpr static char ContentLocation[] = "Content-Location";
constexpr static char ContentRange[] = "Content-Range";
constexpr static char ContentType[] = "Content-Type";
constexpr static char Date[] = "Date";
constexpr static char ETag[] = "ETag";
constexpr static char Expect[] = "Expect";
constexpr static char Expires[] = "Expires";
constexpr static char From[] = "From";
constexpr static char Host[] = "Host";
constexpr static char IfMatch[] = "If-Match";
constexpr static char IfModifiedSince[] = "If-Modified-Since";
constexpr static char IfNoneMatch[] = "If-None-Match";
constexpr static char IfRange[] = "If-Range";
constexpr static char IfUnmodifiedSince[] = "If-Unmodified-Since";
constexpr static char LastModified[] = "Last-Modified";
constexpr static char Location[] = "Location";
constexpr static char MaxForwards[] = "Max-Forwards";
constexpr static char MimeVersion[] = "MIME-Version";
constexpr static char Pragma[] = "Pragma";
constexpr static char ProxyAuthenticate[] = "Proxy-Authenticate";
constexpr static char ProxyAuthenticationInfo[] = "Proxy-Authentication-Info";
constexpr static char ProxyAuthorization[] = "Proxy-Authorization";
constexpr static char Range[] = "Range";
constexpr static char Referer[] = "Referer";
constexpr static char RetryAfter[] = "Retry-After";
constexpr static char Server[] = "Server";
constexpr static char TE[] = "TE";
constexpr static char Trailer[] = "Trailer";
constexpr static char TransferEncoding[] = "Transfer-Encoding";
constexpr static char Upgrade[] = "Upgrade";
constexpr static char UserAgent[] = "User-Agent";
constexpr static char Vary[] = "Vary";
constexpr static char Via[] = "Via";
constexpr static char WWWAuthenticate[] = "WWW-Authenticate";
constexpr static char Asterisk[] = "*";
constexpr static char AIM[] = "A-IM";
constexpr static char AcceptAdditions[] = "Accept-Additions";
constexpr static char AcceptCH[] = "Accept-CH";
constexpr static char AcceptDatetime[] = "Accept-Datetime";
constexpr static char AcceptFeatures[] = "Accept-Features";
constexpr static char AcceptPatch[] = "Accept-Patch";
constexpr static char AcceptPost[] = "Accept-Post";
constexpr static char AcceptSignature[] = "Accept-Signature";
constexpr static char AccessControlAllowCredentials[] = "Access-Control-Allow-Credentials";
constexpr static char AccessControlAllowHeaders[] = "Access-Control-Allow-Headers";
constexpr static char AccessControlAllowMethods[] = "Access-Control-Allow-Methods";
constexpr static char AccessControlAllowOrigin[] = "Access-Control-Allow-Origin";
constexpr static char AccessControlExposeHeaders[] = "Access-Control-Expose-Headers";
constexpr static char AccessControlMaxAge[] = "Access-Control-Max-Age";
constexpr static char AccessControlRequestHeaders[] = "Access-Control-Request-Headers";
constexpr static char AccessControlRequestMethod[] = "Access-Control-Request-Method";
constexpr static char ALPN[] = "ALPN";
constexpr static char AltSvc[] = "Alt-Svc";
constexpr static char AltUsed[] = "Alt-Used";
constexpr static char Alternates[] = "Alternates";
constexpr static char ApplyToRedirectRef[] = "Apply-To-Redirect-Ref";
constexpr static char AuthenticationControl[] = "Authentication-Control";
constexpr static char CacheStatus[] = "Cache-Status";
constexpr static char CalManagedID[] = "Cal-Managed-ID";
constexpr static char CalDAVTimezones[] = "CalDAV-Timezones";
constexpr static char CapsuleProtocol[] = "Capsule-Protocol";
constexpr static char CDNCacheControl[] = "CDN-Cache-Control";
constexpr static char CDNLoop[] = "CDN-Loop";
constexpr static char CertNotAfter[] = "Cert-Not-After";
constexpr static char CertNotBefore[] = "Cert-Not-Before";
constexpr static char ClearSiteData[] = "Clear-Site-Data";
constexpr static char ClientCert[] = "Client-Cert";
constexpr static char ClientCertChain[] = "Client-Cert-Chain";
constexpr static char ContentDigest[] = "Content-Digest";
constexpr static char ContentDisposition[] = "Content-Disposition";
constexpr static char ContentID[] = "Content-ID";
constexpr static char ContentSecurityPolicy[] = "Content-Security-Policy";
constexpr static char ContentSecurityPolicyReportOnly[] = "Content-Security-Policy-Report-Only";
constexpr static char Cookie[] = "Cookie";
constexpr static char CrossOriginEmbedderPolicy[] = "Cross-Origin-Embedder-Policy";
constexpr static char CrossOriginEmbedderPolicyReportOnly[] = "Cross-Origin-Embedder-Policy-Report-Only";
constexpr static char CrossOriginOpenerPolicy[] = "Cross-Origin-Opener-Policy";
constexpr static char CrossOriginOpenerPolicyReportOnly[] = "Cross-Origin-Opener-Policy-Report-Only";
constexpr static char CrossOriginResourcePolicy[] = "Cross-Origin-Resource-Policy";
constexpr static char DASL[] = "DASL";
constexpr static char DAV[] = "DAV";
constexpr static char DeltaBase[] = "Delta-Base";
constexpr static char Depth[] = "Depth";
constexpr static char Destination[] = "Destination";
constexpr static char DifferentialID[] = "Differential-ID";
constexpr static char DPoP[] = "DPoP";
constexpr static char DPoPNonce[] = "DPoP-Nonce";
constexpr static char EarlyData[] = "Early-Data";
constexpr static char ExpectCT[] = "Expect-CT";
constexpr static char Forwarded[] = "Forwarded";
constexpr static char Hobareg[] = "Hobareg";
constexpr static char If[] = "If";
constexpr static char IfScheduleTagMatch[] = "If-Schedule-Tag-Match";
constexpr static char IM[] = "IM";
constexpr static char IncludeReferredTokenBindingID[] = "Include-Referred-Token-Binding-ID";
constexpr static char KeepAlive[] = "Keep-Alive";
constexpr static char Label[] = "Label";
constexpr static char LastEventID[] = "Last-Event-ID";
constexpr static char Link[] = "Link";
constexpr static char LockToken[] = "Lock-Token";
constexpr static char MementoDatetime[] = "Memento-Datetime";
constexpr static char Meter[] = "Meter";
constexpr static char Negotiate[] = "Negotiate";
constexpr static char NEL[] = "NEL";
constexpr static char ODataEntityid[] = "OData-EntityId";
constexpr static char ODataIsolation[] = "OData-Isolation";
constexpr static char ODataMaxversion[] = "OData-MaxVersion";
constexpr static char ODataVersion[] = "OData-Version";
constexpr static char OptionalWWWAuthenticate[] = "Optional-WWW-Authenticate";
constexpr static char OrderingType[] = "Ordering-Type";
constexpr static char Origin[] = "Origin";
constexpr static char OriginAgentCluster[] = "Origin-Agent-Cluster";
constexpr static char OSCORE[] = "OSCORE";
constexpr static char OSLCCoreVersion[] = "OSLC-Core-Version";
constexpr static char Overwrite[] = "Overwrite";
constexpr static char PingFrom[] = "Ping-From";
constexpr static char PingTo[] = "Ping-To";
constexpr static char Position[] = "Position";
constexpr static char Prefer[] = "Prefer";
constexpr static char PreferenceApplied[] = "Preference-Applied";
constexpr static char Priority[] = "Priority";
constexpr static char ProxyStatus[] = "Proxy-Status";
constexpr static char PublicKeyPins[] = "Public-Key-Pins";
constexpr static char PublicKeyPinsReportOnly[] = "Public-Key-Pins-Report-Only";
constexpr static char RedirectRef[] = "Redirect-Ref";
constexpr static char Refresh[] = "Refresh";
constexpr static char ReplayNonce[] = "Replay-Nonce";
constexpr static char ReprDigest[] = "Repr-Digest";
constexpr static char ScheduleReply[] = "Schedule-Reply";
constexpr static char ScheduleTag[] = "Schedule-Tag";
constexpr static char SecPurpose[] = "Sec-Purpose";
constexpr static char SecTokenBinding[] = "Sec-Token-Binding";
constexpr static char SecWebsocketAccept[] = "Sec-WebSocket-Accept";
constexpr static char SecWebsocketExtensions[] = "Sec-WebSocket-Extensions";
constexpr static char SecWebsocketKey[] = "Sec-WebSocket-Key";
constexpr static char SecWebsocketProtocol[] = "Sec-WebSocket-Protocol";
constexpr static char SecWebsocketVersion[] = "Sec-WebSocket-Version";
constexpr static char ServerTiming[] = "Server-Timing";
constexpr static char SetCookie[] = "Set-Cookie";
constexpr static char Signature[] = "Signature";
constexpr static char SignatureInput[] = "Signature-Input";
constexpr static char SLUG[] = "SLUG";
constexpr static char Soapaction[] = "SoapAction";
constexpr static char StatusURI[] = "Status-URI";
constexpr static char StrictTransportSecurity[] = "Strict-Transport-Security";
constexpr static char Sunset[] = "Sunset";
constexpr static char SurrogateCapability[] = "Surrogate-Capability";
constexpr static char SurrogateControl[] = "Surrogate-Control";
constexpr static char TCN[] = "TCN";
constexpr static char Timeout[] = "Timeout";
constexpr static char Topic[] = "Topic";
constexpr static char Traceparent[] = "Traceparent";
constexpr static char Tracestate[] = "Tracestate";
constexpr static char TTL[] = "TTL";
constexpr static char Urgency[] = "Urgency";
constexpr static char VariantVary[] = "Variant-Vary";
constexpr static char WantContentDigest[] = "Want-Content-Digest";
constexpr static char WantReprDigest[] = "Want-Repr-Digest";
constexpr static char XContentTypeOptions[] = "X-Content-Type-Options";
constexpr static char XFrameOptions[] = "X-Frame-Options";
constexpr static char AmpCacheTransform[] = "AMP-Cache-Transform";
constexpr static char ConfigurationContext[] = "Configuration-Context";
constexpr static char EDIINTFeatures[] = "EDIINT-Features";
constexpr static char Isolation[] = "Isolation";
constexpr static char PermissionsPolicy[] = "Permissions-Policy";
constexpr static char RepeatabilityClientID[] = "Repeatability-Client-ID";
constexpr static char RepeatabilityFirstSent[] = "Repeatability-First-Sent";
constexpr static char RepeatabilityRequestID[] = "Repeatability-Request-ID";
constexpr static char RepeatabilityResult[] = "Repeatability-Result";
constexpr static char ReportingEndpoints[] = "Reporting-Endpoints";
constexpr static char SecGPC[] = "Sec-GPC";
constexpr static char TimingAllowOrigin[] = "Timing-Allow-Origin";
constexpr static char CPEPInfo[] = "C-PEP-Info";
constexpr static char ProtocolInfo[] = "Protocol-Info";
constexpr static char ProtocolQuery[] = "Protocol-Query";
constexpr static char AccessControl[] = "Access-Control";
constexpr static char CExt[] = "C-Ext";
constexpr static char CMan[] = "C-Man";
constexpr static char COpt[] = "C-Opt";
constexpr static char CPEP[] = "C-PEP";
constexpr static char ContentBase[] = "Content-Base";
constexpr static char ContentMD5[] = "Content-MD5";
constexpr static char ContentScriptType[] = "Content-Script-Type";
constexpr static char ContentStyleType[] = "Content-Style-Type";
constexpr static char ContentVersion[] = "Content-Version";
constexpr static char Cookie2[] = "Cookie2";
constexpr static char DefaultStyle[] = "Default-Style";
constexpr static char DerivedFrom[] = "Derived-From";
constexpr static char Digest[] = "Digest";
constexpr static char Ext[] = "Ext";
constexpr static char Getprofile[] = "GetProfile";
constexpr static char HTTP2Settings[] = "HTTP2-Settings";
constexpr static char Man[] = "Man";
constexpr static char MethodCheck[] = "Method-Check";
constexpr static char MethodCheckExpires[] = "Method-Check-Expires";
constexpr static char Opt[] = "Opt";
constexpr static char P3P[] = "P3P";
constexpr static char PEP[] = "PEP";
constexpr static char PepInfo[] = "Pep-Info";
constexpr static char PICSLabel[] = "PICS-Label";
constexpr static char Profileobject[] = "ProfileObject";
constexpr static char Protocol[] = "Protocol";
constexpr static char ProtocolRequest[] = "Protocol-Request";
constexpr static char ProxyFeatures[] = "Proxy-Features";
constexpr static char ProxyInstruction[] = "Proxy-Instruction";
constexpr static char Public[] = "Public";
constexpr static char RefererRoot[] = "Referer-Root";
constexpr static char Safe[] = "Safe";
constexpr static char SecurityScheme[] = "Security-Scheme";
constexpr static char SetCookie2[] = "Set-Cookie2";
constexpr static char Setprofile[] = "SetProfile";
constexpr static char URI[] = "URI";
constexpr static char WantDigest[] = "Want-Digest";
constexpr static char Warning[] = "Warning";
}; // namespace Header
}; // namespace PIHTTP
#endif

View File

@@ -0,0 +1,18 @@
#include "pihttptypes.h"
const char * PIHTTP::methodName(Method m) {
switch (m) {
case Method::Get: return "GET";
case Method::Head: return "HEAD";
case Method::Post: return "POST";
case Method::Put: return "PUT";
case Method::Delete: return "DELETE";
case Method::Connect: return "CONNECT";
case Method::Options: return "OPTIONS";
case Method::Trace: return "TRACE";
case Method::Patch: return "PATCH";
default: break;
};
return "UNKNOWN";
}

View File

@@ -0,0 +1,52 @@
#ifndef pihttptypes_h
#define pihttptypes_h
#include "pihttpconstants.h"
#include "pip_export.h"
#include "pistring.h"
namespace PIHTTP {
class PIP_EXPORT MessageConst {
public:
PIHTTP::Method method() const { return m_method; }
PIHTTP::Code code() const { return m_code; }
const PIString & path() const { return m_path; }
const PIByteArray & body() const { return m_body; }
const PIMap<PIString, PIString> & headers() const { return m_headers; }
const PIMap<PIString, PIString> & arguments() const { return m_arguments; }
protected:
PIHTTP::Method m_method = PIHTTP::Method::Unknown;
PIHTTP::Code m_code = PIHTTP::Code::Ok;
PIString m_path;
PIByteArray m_body;
PIMap<PIString, PIString> m_headers;
PIMap<PIString, PIString> m_arguments;
};
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); }
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; }
void removeHeader(const PIString & header) { m_headers.remove(header); }
};
PIP_EXPORT const char * methodName(Method m);
}; // namespace PIHTTP
#endif

View File

@@ -1,7 +1,7 @@
#ifndef MICROHTTPD_SERVER_P_H #ifndef MICROHTTPD_SERVER_P_H
#define MICROHTTPD_SERVER_P_H #define MICROHTTPD_SERVER_P_H
#include "pibase.h" #include "pihttptypes.h"
#include "piobject.h" #include "piobject.h"
#include "pip_http_server_export.h" #include "pip_http_server_export.h"
@@ -15,18 +15,6 @@ public:
MicrohttpdServer(); MicrohttpdServer();
virtual ~MicrohttpdServer(); virtual ~MicrohttpdServer();
enum class Method {
Unknown,
Get,
Head,
Post,
Put,
Delete,
Connect,
Options,
Trace,
Patch
};
enum class Option { enum class Option {
ConnectionLimit, // uint ConnectionLimit, // uint
ConnectionTimeout, // uint, sec ConnectionTimeout, // uint, sec
@@ -36,30 +24,6 @@ public:
HTTPSKeyPassword // const char * to passwd for key.pem HTTPSKeyPassword // const char * to passwd for key.pem
}; };
struct PIP_HTTP_SERVER_EXPORT Request {
MicrohttpdServer::Method method;
PIString path;
PIByteArray body;
PIMap<PIString, PIString> headers;
PIMap<PIString, PIString> args;
};
class PIP_HTTP_SERVER_EXPORT Reply {
friend struct MicrohttpdServerConnection;
public:
void addHeader(const PIString & header, const PIString & value);
void removeHeader(const PIString & header);
void setBody(const PIByteArray & b);
void setCode(int c);
private:
void addFixedHeaders();
int code = 200;
PIByteArray body;
PIMap<PIString, PIString> headers;
};
void setOption(Option o, PIVariant v); void setOption(Option o, PIVariant v);
void setFavicon(const PIByteArray & im); void setFavicon(const PIByteArray & im);
@@ -68,14 +32,15 @@ public:
bool isListen() const; bool isListen() const;
void stop(); void stop();
void setRequestCallback(std::function<Reply(Request)> c) { callback = c; } void setRequestCallback(std::function<PIHTTP::MessageMutable(const PIHTTP::MessageConst &)> c) { callback = c; }
private: private:
static void addFixedHeaders(PIHTTP::MessageMutable & msg);
PRIVATE_DECLARATION(PIP_HTTP_SERVER_EXPORT) PRIVATE_DECLARATION(PIP_HTTP_SERVER_EXPORT)
PIByteArray favicon; PIByteArray favicon;
PIMap<Option, PIVariant> opts; PIMap<Option, PIVariant> opts;
std::function<Reply(Request)> callback; std::function<PIHTTP::MessageMutable(const PIHTTP::MessageConst &)> callback;
PIByteArray mem_key, mem_cert, key_pass; PIByteArray mem_key, mem_cert, key_pass;
}; };

View File

@@ -10,11 +10,11 @@ public:
PIHTTPServer(); PIHTTPServer();
virtual ~PIHTTPServer(); virtual ~PIHTTPServer();
using RequestFunction = std::function<MicrohttpdServer::Reply(const MicrohttpdServer::Request &)>; using RequestFunction = std::function<PIHTTP::MessageMutable(const PIHTTP::MessageConst &)>;
void registerPath(const PIString & path, MicrohttpdServer::Method method, RequestFunction functor); void registerPath(const PIString & path, PIHTTP::Method method, RequestFunction functor);
void registerUnhandled(RequestFunction functor); void registerUnhandled(RequestFunction functor);
void unregisterPath(const PIString & path, MicrohttpdServer::Method method); void unregisterPath(const PIString & path, PIHTTP::Method method);
void unregisterPath(const PIString & path); void unregisterPath(const PIString & path);
void addReplyHeader(const PIString & name, const PIString & value) { reply_headers[name] = value; } void addReplyHeader(const PIString & name, const PIString & value) { reply_headers[name] = value; }
@@ -25,7 +25,7 @@ private:
struct Endpoint { struct Endpoint {
bool match(const PIStringList & in_path) const; bool match(const PIStringList & in_path) const;
PIStringList path; PIStringList path;
MicrohttpdServer::Method method = MicrohttpdServer::Method::Unknown; PIHTTP::Method method = PIHTTP::Method::Unknown;
RequestFunction function; RequestFunction function;
}; };
PIMap<PIString, PIString> reply_headers; PIMap<PIString, PIString> reply_headers;

View File

@@ -1,8 +1,9 @@
#include "pihttpclient.h"
#include "pip.h" #include "pip.h"
using namespace PICoutManipulators; using namespace PICoutManipulators;
PIKbdListener kbd; // PIKbdListener kbd;
const char * pageTitle = "<!DOCTYPE html>" const char * pageTitle = "<!DOCTYPE html>"
"<html>" "<html>"
"<body>" "<body>"
@@ -12,16 +13,50 @@ const char * pageTitle = "<!DOCTYPE html>"
int main(int argc, char * argv[]) { int main(int argc, char * argv[]) {
PIHTTPServer server;
server.listen({"127.0.0.1:7777"});
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())
.arg((int)msg.method())
.arg(PIString::fromUTF8(msg.body()));
ret.setCode(PIHTTP::Code::BadRequest);
ret.setBody(PIByteArray::fromAscii("hello client! 0123456789"));
return ret;
});
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());
})
->onError([c](PIHTTP::MessageConst r) {
piCout << "error" << (int)r.code();
piCout << "msg" << c->lastError();
})
->onAbort([c](PIHTTP::MessageConst r) {
piCout << "abort" << (int)r.code();
piCout << "msg" << c->lastError();
})
->start();
piMSleep(500);
server.stop();
// c->abort();
// piMSleep(500);
return 0;
// piCout << PIString::readableSize(PISystemMonitor::usedRAM()); // piCout << PIString::readableSize(PISystemMonitor::usedRAM());
PIVector<int> vi; /*PIVector<int> vi;
piForTimes(10) { piForTimes(10) {
piSleep(2.); piSleep(2.);
vi.enlarge(1000000); vi.enlarge(1000000);
piCout << "now" << vi.size() << vi.capacity(); piCout << "now" << vi.size() << vi.capacity();
} }
piSleep(5.); piSleep(5.);*/
/*kbd.enableExitCapture(); /*kbd.enableExitCapture();
PIHTTPServer server; PIHTTPServer server;