#include "pihttpclient.h" #include "curl_thread_pool_p.h" #include "piliterals_bytes.h" #include "piliterals_string.h" #include "pisystemtime.h" #include int xfer_callback(void * ptr, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { return reinterpret_cast(ptr)->__infoFunc(dltotal, dlnow, ultotal, ulnow); } int debug_callback(CURL * handle, curl_infotype type, char * data, size_t size, void * ptr) { return reinterpret_cast(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(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(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(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(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(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(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(ret->request) = req; ret->request.setMethod(method); ret->url = url_; return ret; } PIHTTPClient * PIHTTPClient::onFinish(std::function f) { return onFinish([f](const PIHTTP::MessageConst &) { f(); }); } PIHTTPClient * PIHTTPClient::onFinish(std::function f) { on_finish = f; return this; } PIHTTPClient * PIHTTPClient::onError(std::function f) { return onError([f](const PIHTTP::MessageConst &) { f(); }); } PIHTTPClient * PIHTTPClient::onError(std::function f) { on_error = f; return this; } PIHTTPClient * PIHTTPClient::onAbort(std::function f) { return onAbort([f](const PIHTTP::MessageConst &) { f(); }); } PIHTTPClient * PIHTTPClient::onAbort(std::function 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(this)->infoFunc(dltotal, dlnow, ultotal, ulnow); } int PIHTTPClientBase::__debugFunc(int type, char * data, size_t size) { return reinterpret_cast(this)->debugFunc(type, data, size); }