226 lines
6.8 KiB
C++
226 lines
6.8 KiB
C++
#include "pihttpclient.h"
|
|
|
|
#include "curl_thread_pool_p.h"
|
|
#include "piliterals_bytes.h"
|
|
#include "piliterals_string.h"
|
|
#include "pisystemtime.h"
|
|
|
|
#include <curl/curl.h>
|
|
|
|
#if !defined(CURL_WRITEFUNC_ERROR)
|
|
# define CURL_WRITEFUNC_ERROR 0xFFFFFFFF
|
|
#endif
|
|
|
|
|
|
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;
|
|
auto ait = request.arguments().makeIterator();
|
|
while (ait.next()) {
|
|
if (!url.contains('?'))
|
|
url.append('?');
|
|
else
|
|
url.append('&');
|
|
url.append(ait.key().toPercentageEncoding());
|
|
url.append('=');
|
|
url.append(ait.value().toPercentageEncoding());
|
|
}
|
|
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->reply.setMethod(method);
|
|
ret->url = url_.toPercentageEncoding();
|
|
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);
|
|
}
|