Official websites use .gov
A .gov website belongs to an official government organization in the United States.

Secure .gov websites use HTTPS
A lock ( ) or https:// means you’ve safely connected to the .gov website. Share sensitive information only on official, secure websites.

Isis Developer Reference
restincurl.h
Go to the documentation of this file.
1#pragma once
2
3/*
4 MIT License
5
6 Copyright (c) 2018 Jarle Aase
7
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14
15 The above copyright notice and this permission notice shall be included in all
16 copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25
26 On Github: https://github.com/jgaa/RESTinCurl
27*/
28
30
31#include <algorithm>
32#include <atomic>
33#include <deque>
34#include <exception>
35#include <functional>
36#include <iostream>
37#include <iterator>
38#include <map>
39#include <memory>
40#include <mutex>
41#include <string>
42#include <thread>
43#include <vector>
44
45#include <assert.h>
46#include <curl/curl.h>
47#include <curl/easy.h>
48#include <fcntl.h>
49#include <string.h>
50#include <sys/select.h>
51#include <sys/time.h>
52#include <sys/types.h>
53#include <sys/stat.h>
54#include <unistd.h>
55
56#ifdef RESTINCURL_WITH_OPENSSL_THREADS
57# include <openssl/crypto.h>
58#endif
59
72#ifndef RESTINCURL_MAX_CONNECTIONS
73# define RESTINCURL_MAX_CONNECTIONS 32L
74#endif
75
86
87#ifndef RESTINCURL_ENABLE_ASYNC
88# define RESTINCURL_ENABLE_ASYNC 1
89#endif
90
101#ifndef RESTINCURL_IDLE_TIMEOUT_SEC
102# define RESTINCURL_IDLE_TIMEOUT_SEC 60
103#endif
104
113#ifndef RESTINCURL_LOG_VERBOSE_ENABLE
114# define RESTINCURL_LOG_VERBOSE_ENABLE 0
115#endif
116
117#if defined(_LOGFAULT_H) && !defined (RESTINCURL_LOG) && !defined (RESTINCURL_LOG_TRACE)
118# define RESTINCURL_LOG(msg) LFLOG_DEBUG << "restincurl: " << msg
119# if RESTINCURL_LOG_VERBOSE_ENABLE
120# define RESTINCURL_LOG_TRACE(msg) LFLOG_IFALL_TRACE("restincurl: " << msg)
121# endif // RESTINCURL_LOG_VERBOSE_ENABLE
122#endif
123
124#if defined(RESTINCURL_USE_SYSLOG) || defined(RESTINCURL_USE_ANDROID_NDK_LOG)
125# ifdef RESTINCURL_USE_SYSLOG
126# include <syslog.h>
127# endif
128# ifdef RESTINCURL_USE_ANDROID_NDK_LOG
129# include <android/log.h>
130# endif
131# include <sstream>
132# define RESTINCURL_LOG(msg) ::restincurl::Log(restincurl::LogLevel::DEBUG).Line() << msg
133#endif
134
149#ifndef RESTINCURL_ENABLE_DEFAULT_LOGGER
150# define RESTINCURL_ENABLE_DEFAULT_LOGGER 0
151#endif
152
166#ifndef RESTINCURL_LOG
167# if RESTINCURL_ENABLE_DEFAULT_LOGGER
168# define RESTINCURL_LOG(msg) std::clog << msg << std::endl
169# else
170# define RESTINCURL_LOG(msg)
171# endif
172#endif
173
189#ifndef RESTINCURL_LOG_TRACE
190# if RESTINCURL_LOG_VERBOSE_ENABLE
191# define RESTINCURL_LOG_TRACE(msg) RESTINCURL_LOG(msg)
192# else
193# define RESTINCURL_LOG_TRACE(msg)
194# endif
195#endif
196
197namespace restincurl {
198
199#if defined(RESTINCURL_USE_SYSLOG) || defined(RESTINCURL_USE_ANDROID_NDK_LOG)
200 enum class LogLevel { DEBUG };
201
202 class Log {
203 public:
204 Log(const LogLevel level) : level_{level} {}
205 ~Log() {
206# ifdef RESTINCURL_USE_SYSLOG
207 static const std::array<int, 1> syslog_priority = { LOG_DEBUG };
208 static std::once_flag syslog_opened;
209 std::call_once(syslog_opened, [] {
210 openlog(nullptr, 0, LOG_USER);
211 });
212# endif
213# ifdef RESTINCURL_USE_ANDROID_NDK_LOG
214 static const std::array<int, 1> android_priority = { ANDROID_LOG_DEBUG };
215# endif
216 const auto msg = out_.str();
217
218# ifdef RESTINCURL_USE_SYSLOG
219 syslog(syslog_priority.at(static_cast<int>(level_)), "%s", msg.c_str());
220# endif
221# ifdef RESTINCURL_USE_ANDROID_NDK_LOG
222 __android_log_write(android_priority.at(static_cast<int>(level_)),
223 "restincurl", msg.c_str());
224# endif
225 }
226
227 std::ostringstream& Line() { return out_; }
228
229private:
230 const LogLevel level_;
231 std::ostringstream out_;
232 };
233#endif
234
235
236
237 using lock_t = std::lock_guard<std::mutex>;
238
241 struct Result {
242 Result() = default;
243 Result(const CURLcode& code) {
244 curl_code = code;
245 msg = curl_easy_strerror(code);
246 }
247
249 bool isOk() const noexcept {
250 if (curl_code == CURLE_OK) {
251 if ((http_response_code >= 200) && (http_response_code < 300)) {
252 return true;
253 }
254 }
255
256 return false;
257 }
258
263 CURLcode curl_code = {};
264
267
269 std::string msg;
270
275 std::string body;
276 };
277
279
286 using completion_fn_t = std::function<void (const Result& result)>;
287
289 class Exception : public std::runtime_error {
290 public:
291 Exception(const std::string& msg) : runtime_error(msg) {}
292 };
293
295 class SystemException : public Exception {
296 public:
297 SystemException(const std::string& msg, const int e) : Exception(msg + " " + strerror(e)), err_{e} {}
298
299 int getErrorCode() const noexcept { return err_; }
300
301 private:
302 const int err_;
303 };
304
306 class CurlException : public Exception {
307 public:
308 CurlException(const std::string msg, const CURLcode err)
309 : Exception(msg + '(' + std::to_string(err) + "): " + curl_easy_strerror(err))
310 , err_{err}
311 {}
312
313 CurlException(const std::string msg, const CURLMcode err)
314 : Exception(msg + '(' + std::to_string(err) + "): " + curl_multi_strerror(err))
315 , err_{err}
316 {}
317
318 int getErrorCode() const noexcept { return err_; }
319
320 private:
321 const int err_;
322 };
323
325 public:
326 using ptr_t = std::unique_ptr<EasyHandle>;
327 using handle_t = decltype(curl_easy_init());
328
330 RESTINCURL_LOG("EasyHandle created: " << handle_);
331 }
332
334 Close();
335 }
336
337 void Close() {
338 if (handle_) {
339 RESTINCURL_LOG("Cleaning easy-handle " << handle_);
340 curl_easy_cleanup(handle_);
341 handle_ = nullptr;
342 }
343 }
344
345 operator handle_t () const noexcept { return handle_; }
346
347 private:
348 handle_t handle_ = curl_easy_init();
349 };
350
355 class Options {
356 public:
357 Options(EasyHandle& eh) : eh_{eh} {}
358
364 template <typename T>
365 Options& Set(const CURLoption& opt, const T& value) {
366 const auto ret = curl_easy_setopt(eh_, opt, value);
367 if (ret) {
368 throw CurlException(
369 std::string("Setting option ") + std::to_string(opt), ret);
370 }
371 return *this;
372 }
373
379 Options& Set(const CURLoption& opt, const std::string& value) {
380 return Set(opt, value.c_str());
381 }
382
383 private:
384 EasyHandle& eh_;
385 };
386
393 virtual ~DataHandlerBase() = default;
394 };
395
404 template <typename T>
406 InDataHandler(T& data) : data_{data} {
407 RESTINCURL_LOG_TRACE("InDataHandler address: " << this);
408 }
409
410 static size_t write_callback(char *ptr, size_t size, size_t nitems, void *userdata) {
411 assert(userdata);
412 auto self = reinterpret_cast<InDataHandler *>(userdata);
413 const auto bytes = size * nitems;
414 if (bytes > 0) {
415 std::copy(ptr, ptr + bytes, std::back_inserter(self->data_));
416 }
417 return bytes;
418 }
419
421 };
422
431 template <typename T>
433 OutDataHandler() = default;
434 OutDataHandler(const T& v) : data_{v} {}
435 OutDataHandler(T&& v) : data_{std::move(v)} {}
436
437 static size_t read_callback(char *bufptr, size_t size, size_t nitems, void *userdata) {
438 assert(userdata);
439 OutDataHandler *self = reinterpret_cast<OutDataHandler *>(userdata);
440 const auto bytes = size * nitems;
441 auto out_bytes = std::min<size_t>(bytes, (self->data_.size() - self->sendt_bytes_));
442 std::copy(self->data_.cbegin() + self->sendt_bytes_,
443 self->data_.cbegin() + (self->sendt_bytes_ + out_bytes),
444 bufptr);
445 self->sendt_bytes_ += out_bytes;
446
447 RESTINCURL_LOG_TRACE("Sent " << out_bytes << " of total " << self->data_.size() << " bytes.");
448 return out_bytes;
449 }
450
452 size_t sendt_bytes_ = 0;
453 };
454
455 class Request {
456 public:
457 using ptr_t = std::unique_ptr<Request>;
458
460 : eh_{std::make_unique<EasyHandle>()}
461 {
462 }
463
465 : eh_{std::move(eh)}
466 {
467 }
468
470 if (headers_) {
471 curl_slist_free_all(headers_);
472 }
473 }
474
475 void Prepare(const RequestType rq, completion_fn_t completion) {
476 request_type_ = rq;
477 SetRequestType();
478 completion_ = std::move(completion);
479 }
480
481 void OpenSourceFile(const std::string& path) {
482 assert(!fp_);
483 auto fp = fopen(path.c_str(), "rb");
484 if (!fp) {
485 const auto e = errno;
486 throw SystemException{std::string{"Unable to open file "} + path, e};
487 }
488
489 fp_= std::shared_ptr<FILE>(fp, std::fclose);
490 }
491
492 FILE *GetSourceFp() {
493 assert(fp_);
494 return fp_.get();
495 }
496
497 // Synchronous execution.
498 void Execute() {
499 const auto result = curl_easy_perform(*eh_);
500 CallCompletion(result);
501 }
502
503 void Complete(CURLcode cc, const CURLMSG& /*msg*/) {
504 CallCompletion(cc);
505 }
506
507 EasyHandle& GetEasyHandle() noexcept { assert(eh_); return *eh_; }
508 RequestType GetRequestType() noexcept { return request_type_; }
509
510 void SetDefaultInHandler(std::unique_ptr<DataHandlerBase> ptr) {
511 default_in_handler_ = std::move(ptr);
512 }
513
514 void SetDefaultOutHandler(std::unique_ptr<DataHandlerBase> ptr) {
515 default_out_handler_ = std::move(ptr);
516 }
517
518 using headers_t = curl_slist *;
520 return headers_;
521 }
522
523 std::string& getDefaultInBuffer() {
524 return default_data_buffer_;
525 }
526
527 void InitMime() {
528 if (!mime_) {
529 mime_ = curl_mime_init(*eh_);
530 }
531 }
532
533 void AddFileAsMimeData(const std::string& path,
534 const std::string& name,
535 const std::string& remoteName,
536 const std::string& mimeType) {
537
538 InitMime();
539 assert(mime_);
540 auto * part = curl_mime_addpart(mime_);
541 curl_mime_filedata(part, path.c_str());
542 curl_mime_name(part, name.empty() ? "file" :name.c_str());
543
544 if (!remoteName.empty()) {
545 curl_mime_filename(part, remoteName.c_str());
546 }
547
548 if (!mimeType.empty()) {
549 curl_mime_type(part, mimeType.c_str());
550 }
551 }
552
553 private:
554 void CallCompletion(CURLcode cc) {
555 Result result(cc);
556
557 curl_easy_getinfo (*eh_, CURLINFO_RESPONSE_CODE,
558 &result.http_response_code);
559 RESTINCURL_LOG("Complete: http code: " << result.http_response_code);
560 if (completion_) {
561 if (!default_data_buffer_.empty()) {
562 result.body = std::move(default_data_buffer_);
563 }
564 completion_(result);
565 }
566 }
567
568 void SetRequestType() {
569 switch(request_type_) {
570 case RequestType::GET:
571 curl_easy_setopt(*eh_, CURLOPT_HTTPGET, 1L);
572 break;
573 case RequestType::PUT:
574 headers_ = curl_slist_append(headers_, "Transfer-Encoding: chunked");
575 curl_easy_setopt(*eh_, CURLOPT_UPLOAD, 1L);
576 break;
578 headers_ = curl_slist_append(headers_, "Transfer-Encoding: chunked");
579 curl_easy_setopt(*eh_, CURLOPT_UPLOAD, 0L);
580 curl_easy_setopt(*eh_, CURLOPT_POST, 1L);
581 break;
583 curl_easy_setopt(*eh_, CURLOPT_NOBODY, 1L);
584 break;
586 curl_easy_setopt(*eh_, CURLOPT_CUSTOMREQUEST, "OPTIONS");
587 break;
589 headers_ = curl_slist_append(headers_, "Transfer-Encoding: chunked");
590 curl_easy_setopt(*eh_, CURLOPT_CUSTOMREQUEST, "PATCH");
591 break;
593 curl_easy_setopt(*eh_, CURLOPT_CUSTOMREQUEST, "DELETE");
594 break;
596 InitMime();
597 curl_easy_setopt(*eh_, CURLOPT_MIMEPOST, mime_);
598 break;
599 default:
600 throw Exception("Unsupported request type" + std::to_string(static_cast<int>(request_type_)));
601 }
602 }
603
605 RequestType request_type_ = RequestType::INVALID;
606 completion_fn_t completion_;
607 std::unique_ptr<DataHandlerBase> default_out_handler_;
608 std::unique_ptr<DataHandlerBase> default_in_handler_;
609 headers_t headers_ = nullptr;
610 std::string default_data_buffer_;
611 std::shared_ptr<FILE> fp_;
612 curl_mime *mime_ = {};
613 };
614
615#if RESTINCURL_ENABLE_ASYNC
616
617 class Signaler {
618 enum FdUsage { FD_READ = 0, FD_WRITE = 1};
619
620 public:
621 using pipefd_t = std::array<int, 2>;
622
624 auto status = pipe(pipefd_.data());
625 if (status) {
626 throw SystemException("pipe", status);
627 }
628 for(auto fd : pipefd_) {
629 int flags = 0;
630 if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
631 flags = 0;
632 fcntl(fd, F_SETFL, flags | O_NONBLOCK);
633 }
634 }
635
637 for(auto fd : pipefd_) {
638 close(fd);
639 }
640 }
641
642 void Signal() {
643 char byte = {};
644 RESTINCURL_LOG_TRACE("Signal: Signaling!");
645 if (write(pipefd_[FD_WRITE], &byte, 1) != 1) {
646 throw SystemException("write pipe", errno);
647 }
648 }
649
650 int GetReadFd() { return pipefd_[FD_READ]; }
651
653 bool rval = false;
654 char byte = {};
655 while(read(pipefd_[FD_READ], &byte, 1) > 0) {
656 RESTINCURL_LOG_TRACE("Signal: Was signalled");
657 rval = true;
658 }
659
660 return rval;
661 }
662
663 private:
664 pipefd_t pipefd_;
665 };
666
689 {
690 public:
691
692#ifdef RESTINCURL_WITH_OPENSSL_THREADS
693 static void opensslLockCb(int mode, int type, char *, int) {
694 if(mode & CRYPTO_LOCK) {
695 lock();
696 }
697 else {
698 unlock();
699 }
700 }
701
702 static unsigned long getThreadId(void) {
703 return reinterpret_cast<unsigned long>(::this_thread::get_id());
704 }
705
707 static void tlsLockInit() {
708 CRYPTO_set_id_callback((unsigned long (*)())getThreadId);
709 CRYPTO_set_locking_callback((void (*)())opensslLockCb);
710 }
711
713 static void tlsLockKill() {
714 CRYPTO_set_locking_callback(NULL);
715 }
716
717#else
719 static void tlsLOckInit() {
720 ; // Do nothing
721 }
722
724 static void tlsLockKill() {
725 ; // Do nothing
726 }
727#endif
728
729
730
731
732 private:
733 static void lock() {
734 mutex_.lock();
735 }
736
737 static void unlock() {
738 mutex_.unlock();
739 }
740
741 static std::mutex mutex_;
742 };
743
744 class Worker {
745 class WorkerThread {
746 public:
747 WorkerThread(std::function<void ()> && fn)
748 : thread_{std::move(fn)} {}
749
750 ~WorkerThread() {
751 if (thread_.get_id() == std::this_thread::get_id()) {
752 // Allow the thread to finish exiting the lambda
753 thread_.detach();
754 }
755 }
756
757 void Join() const {
758 std::call_once(joined_, [this] {
759 const_cast<WorkerThread *>(this)->thread_.join();
760 });
761 }
762
763 bool Joinable() const {
764 return thread_.joinable();
765 }
766
767 operator std::thread& () { return thread_; }
768
769 private:
770 std::thread thread_;
771 mutable std::once_flag joined_;
772 };
773
774 public:
775 Worker() = default;
776
778 if (thread_ && thread_->Joinable()) {
779 Close();
780 Join();
781 }
782 }
783
785 // Must only be called when the mutex_ is acquired!
786 assert(!mutex_.try_lock());
787 if (abort_ || done_) {
788 return;
789 }
790 if (!thread_) {
791 thread_ = std::make_shared<WorkerThread>([&] {
792 try {
793 RESTINCURL_LOG("Starting thread " << std::this_thread::get_id());
794 Init();
795 Run();
796 Clean();
797 } catch (const std::exception& ex) {
798 RESTINCURL_LOG("Worker: " << ex.what());
799 }
800 RESTINCURL_LOG("Exiting thread " << std::this_thread::get_id());
801
802 lock_t lock(mutex_);
803 thread_.reset();
804 });
805 }
806 assert(!abort_);
807 assert(!done_);
808 }
809
810 static std::unique_ptr<Worker> Create() {
811 return std::make_unique<Worker>();
812 }
813
815 RESTINCURL_LOG_TRACE("Queuing request ");
816 lock_t lock(mutex_);
818 queue_.push_back(std::move(req));
819 Signal();
820 }
821
822 void Join() const {
823 decltype(thread_) thd;
824
825 {
826 lock_t lock(mutex_);
827 if (thread_ && thread_->Joinable()) {
828 thd = thread_;
829 }
830 }
831
832 if (thd) {
833 thd->Join();
834 }
835 }
836
837 // Let the current transfers complete, then quit
839 {
840 lock_t lock(mutex_);
841 close_pending_ = true;
842 }
843 Signal();
844 }
845
846 // Shut down now. Abort all transfers
847 void Close() {
848 {
849 lock_t lock(mutex_);
850 abort_ = true;
851 }
852 Signal();
853 }
854
855 // Check if the run loop has finished.
856 bool IsDone() const {
857 lock_t lock(mutex_);
858 return done_;
859 }
860
861 bool HaveThread() const noexcept {
862 lock_t lock(mutex_);
863 if (thread_) {
864 return true;
865 }
866 return false;
867 }
868
869 size_t GetNumActiveRequests() const {
870 lock_t lock(mutex_);
871 return ongoing_.size();
872 }
873
874 private:
875 void Signal() {
876 signal_.Signal();
877 }
878
879 void Dequeue() {
880 decltype(queue_) tmp;
881
882 {
883 lock_t lock(mutex_);
884 if ((queue_.size() + ongoing_.size()) <= RESTINCURL_MAX_CONNECTIONS) {
885 tmp = std::move(queue_);
886 pending_entries_in_queue_ = false;
887 } else {
888 auto remains = std::min<size_t>(RESTINCURL_MAX_CONNECTIONS - ongoing_.size(), queue_.size());
889 if (remains) {
890 auto it = queue_.begin();
891 RESTINCURL_LOG_TRACE("Adding only " << remains << " of " << queue_.size()
892 << " requests from queue: << RESTINCURL_MAX_CONNECTIONS=" << RESTINCURL_MAX_CONNECTIONS);
893 while(remains--) {
894 assert(it != queue_.end());
895 tmp.push_back(std::move(*it));
896 ++it;
897 }
898 queue_.erase(queue_.begin(), it);
899 } else {
900 assert(ongoing_.size() == RESTINCURL_MAX_CONNECTIONS);
901 RESTINCURL_LOG_TRACE("Adding no entries from queue: RESTINCURL_MAX_CONNECTIONS="
903 }
904 pending_entries_in_queue_ = true;
905 }
906 }
907
908 for(auto& req: tmp) {
909 assert(req);
910 const auto& eh = req->GetEasyHandle();
911 RESTINCURL_LOG_TRACE("Adding request: " << eh);
912 ongoing_[eh] = std::move(req);
913 const auto mc = curl_multi_add_handle(handle_, eh);
914 if (mc != CURLM_OK) {
915 throw CurlException("curl_multi_add_handle", mc);
916 }
917 }
918 }
919
920 void Init() {
921 if ((handle_ = curl_multi_init()) == nullptr) {
922 throw std::runtime_error("curl_multi_init() failed");
923 }
924
925 curl_multi_setopt(handle_, CURLMOPT_MAXCONNECTS, RESTINCURL_MAX_CONNECTIONS);
926 }
927
928 void Clean() {
929 if (handle_) {
930 RESTINCURL_LOG_TRACE("Calling curl_multi_cleanup: " << handle_);
931 curl_multi_cleanup(handle_);
932 handle_ = nullptr;
933 }
934 }
935
936 bool EvaluateState(const bool transfersRunning, const bool doDequeue) const noexcept {
937 lock_t lock(mutex_);
938
939 RESTINCURL_LOG_TRACE("Run loop: transfers_running=" << transfersRunning
940 << ", do_dequeue=" << doDequeue
941 << ", close_pending_=" << close_pending_);
942
943 return !abort_ && (transfersRunning || !close_pending_);
944 }
945
946 auto GetNextTimeout() const noexcept {
947 return std::chrono::steady_clock::now()
948 + std::chrono::seconds(RESTINCURL_IDLE_TIMEOUT_SEC);
949 }
950
951 void Run() {
952 int transfers_running = -1;
953 fd_set fdread = {};
954 fd_set fdwrite = {};
955 fd_set fdexcep = {};
956 bool do_dequeue = true;
957 auto timeout = GetNextTimeout();
958
959 while (EvaluateState(transfers_running, do_dequeue)) {
960
961 if (do_dequeue) {
962 Dequeue();
963 do_dequeue = false;
964 }
965
966 /* timeout or readable/writable sockets */
967 const bool initial_ideling = transfers_running == -1;
968 curl_multi_perform(handle_, &transfers_running);
969 if ((transfers_running == 0) && initial_ideling) {
970 transfers_running = -1; // Let's ignore close_pending_ until we have seen a request
971 }
972
973 // Shut down the thread if we have been idling too long
974 if (transfers_running <= 0) {
975 if (timeout < std::chrono::steady_clock::now()) {
976 RESTINCURL_LOG("Idle timeout. Will shut down the worker-thread.");
977 break;
978 }
979 } else {
980 timeout = GetNextTimeout();
981 }
982
983 int numLeft = {};
984 while (auto m = curl_multi_info_read(handle_, &numLeft)) {
985 assert(m);
986 auto it = ongoing_.find(m->easy_handle);
987 if (it != ongoing_.end()) {
988 RESTINCURL_LOG("Finishing request with easy-handle: "
989 << (EasyHandle::handle_t)it->second->GetEasyHandle()
990 << "; with result: " << m->data.result << " expl: '" << curl_easy_strerror(m->data.result)
991 << "'; with msg: " << m->msg);
992
993 try {
994 it->second->Complete(m->data.result, m->msg);
995 } catch(const std::exception& ex) {
996 RESTINCURL_LOG("Complete threw: " << ex.what());
997 }
998 if (m->msg == CURLMSG_DONE) {
999 curl_multi_remove_handle(handle_, m->easy_handle);
1000 }
1001 it->second->GetEasyHandle().Close();
1002 ongoing_.erase(it);
1003 } else {
1004 RESTINCURL_LOG("Failed to find easy_handle in ongoing!");
1005 assert(false);
1006 }
1007 }
1008
1009 {
1010 lock_t lock(mutex_);
1011 // Avoid using select() as a timer when we need to exit anyway
1012 if (abort_ || (!transfers_running && close_pending_)) {
1013 break;
1014 }
1015 }
1016
1017 auto next_timeout = std::chrono::duration_cast<std::chrono::milliseconds>(
1018 timeout - std::chrono::steady_clock::now());
1019 long sleep_duration = std::max<long>(1, next_timeout.count());
1020 /* extract sleep_duration value */
1021
1022
1023 FD_ZERO(&fdread);
1024 FD_ZERO(&fdwrite);
1025 FD_ZERO(&fdexcep);
1026
1027 int maxfd = -1;
1028 if (transfers_running > 0) {
1029 curl_multi_timeout(handle_, &sleep_duration);
1030 if (sleep_duration < 0) {
1031 sleep_duration = 1000;
1032 }
1033
1034 /* get file descriptors from the transfers */
1035 const auto mc = curl_multi_fdset(handle_, &fdread, &fdwrite, &fdexcep, &maxfd);
1036 RESTINCURL_LOG_TRACE("maxfd: " << maxfd);
1037 if (mc != CURLM_OK) {
1038 throw CurlException("curl_multi_fdset", mc);
1039 }
1040
1041 if (maxfd == -1) {
1042 // Curl want's us to revisit soon
1043 sleep_duration = 50;
1044 }
1045 } // active transfers
1046
1047 struct timeval tv = {};
1048 tv.tv_sec = sleep_duration / 1000;
1049 tv.tv_usec = (sleep_duration % 1000) * 1000;
1050
1051 const auto signalfd = signal_.GetReadFd();
1052
1053 RESTINCURL_LOG_TRACE("Calling select() with timeout of "
1054 << sleep_duration
1055 << " ms. Next timeout in " << next_timeout.count() << " ms. "
1056 << transfers_running << " active transfers.");
1057
1058 FD_SET(signalfd, &fdread);
1059 maxfd = std::max(signalfd, maxfd) + 1;
1060
1061 const auto rval = select(maxfd, &fdread, &fdwrite, &fdexcep, &tv);
1062 RESTINCURL_LOG_TRACE("select(" << maxfd << ") returned: " << rval);
1063
1064 if (rval > 0) {
1065 if (FD_ISSET(signalfd, &fdread)) {
1066 RESTINCURL_LOG_TRACE("FD_ISSET was true: ");
1067 do_dequeue = signal_.WasSignalled();
1068 }
1069
1070 }
1071 if (pending_entries_in_queue_) {
1072 do_dequeue = true;
1073 }
1074 } // loop
1075
1076
1077 lock_t lock(mutex_);
1078 if (close_pending_ || abort_) {
1079 done_ = true;
1080 }
1081 }
1082
1083 bool close_pending_ {false};
1084 bool abort_ {false};
1085 bool done_ {false};
1086 bool pending_entries_in_queue_ = false;
1087 decltype(curl_multi_init()) handle_ = {};
1088 mutable std::mutex mutex_;
1089 std::shared_ptr<WorkerThread> thread_;
1090 std::deque<Request::ptr_t> queue_;
1091 std::map<EasyHandle::handle_t, Request::ptr_t> ongoing_;
1092 Signaler signal_;
1093 };
1094#endif // RESTINCURL_ENABLE_ASYNC
1095
1096
1104 // noop handler for incoming data
1105 static size_t write_callback(char *ptr, size_t size, size_t nitems, void *userdata) {
1106 const auto bytes = size * nitems;
1107 return bytes;
1108 }
1109
1110 static int debug_callback(CURL *handle, curl_infotype type,
1111 char *data, size_t size,
1112 void *userp) {
1113
1114 std::string msg;
1115 switch(type) {
1116 case CURLINFO_TEXT:
1117 msg = "==> Info: ";
1118 break;
1119 case CURLINFO_HEADER_OUT:
1120 msg = "=> Send header: ";
1121 break;
1122 case CURLINFO_DATA_OUT:
1123 msg = "=> Send data: ";
1124 break;
1125 case CURLINFO_SSL_DATA_OUT:
1126 msg = "=> Send SSL data: ";
1127 break;
1128 case CURLINFO_HEADER_IN:
1129 msg = "<= Recv header: ";
1130 break;
1131 case CURLINFO_DATA_IN:
1132 msg = "<= Recv data: ";
1133 break;
1134 case CURLINFO_SSL_DATA_IN:
1135 msg = "<= Recv SSL data: ";
1136 break;
1137 case CURLINFO_END: // Don't seems to be used
1138 msg = "<= End: ";
1139 break;
1140 }
1141
1142 std::copy(data, data + size, std::back_inserter(msg));
1143 RESTINCURL_LOG(handle << " " << msg);
1144 return 0;
1145 }
1146
1147 public:
1148 using ptr_t = std::unique_ptr<RequestBuilder>;
1151 Worker& worker
1152#endif
1153 )
1154 : request_{std::make_unique<Request>()}
1155 , options_{std::make_unique<class Options>(request_->GetEasyHandle())}
1157 , worker_(worker)
1158#endif
1159 {}
1160
1163
1164 protected:
1165 RequestBuilder& Prepare(RequestType rt, const std::string& url) {
1166 assert(request_type_ == RequestType::INVALID);
1167 assert(!is_built_);
1168 request_type_ = rt;
1169 url_ = url;
1170 return *this;
1171 }
1172
1173 public:
1174 bool CanSendFile() const noexcept {
1175 return request_type_ == RequestType::POST
1176 || request_type_ == RequestType::PUT;
1177 }
1178
1179
1191
1193 RequestBuilder& Get(const std::string& url) {
1194 return Prepare(RequestType::GET, url);
1195 }
1196
1198 RequestBuilder& Head(const std::string& url) {
1199 return Prepare(RequestType::HEAD, url);
1200 }
1201
1203 RequestBuilder& Post(const std::string& url) {
1204 return Prepare(RequestType::POST, url);
1205 }
1206
1208 RequestBuilder& PostMime(const std::string& url) {
1209 return Prepare(RequestType::POST_MIME, url);
1210 }
1211
1213 RequestBuilder& Put(const std::string& url) {
1214 return Prepare(RequestType::PUT, url);
1215 }
1216
1218 RequestBuilder& Patch(const std::string& url) {
1219 return Prepare(RequestType::PATCH, url);
1220 }
1221
1223 RequestBuilder& Delete(const std::string& url) {
1224 return Prepare(RequestType::DELETE, url);
1225 }
1226
1228 RequestBuilder& Options(const std::string& url) {
1229 return Prepare(RequestType::OPTIONS, url);
1230 }
1231
1232
1237 RequestBuilder& Header(const char *value) {
1238 assert(value);
1239 assert(!is_built_);
1240 request_->GetHeaders() = curl_slist_append(request_->GetHeaders(), value);
1241 return *this;
1242 }
1243
1251 RequestBuilder& Header(const std::string& name,
1252 const std::string& value) {
1253 const auto v = name + ": " + value;
1254 return Header(v.c_str());
1255 }
1256
1259 return Header("Content-type: Application/json; charset=utf-8");
1260 }
1261
1266 RequestBuilder& WithJson(std::string body) {
1267 WithJson();
1268 return SendData(std::move(body));
1269 }
1270
1273 return Header("Accept: Application/json");
1274 }
1275
1287 template <typename T>
1288 RequestBuilder& Option(const CURLoption& opt, const T& value) {
1289 assert(!is_built_);
1290 options_->Set(opt, value);
1291 return *this;
1292 }
1293
1301 RequestBuilder& Trace(bool enable = true) {
1302 if (enable) {
1303 Option(CURLOPT_DEBUGFUNCTION, debug_callback);
1304 Option(CURLOPT_VERBOSE, 1L);
1305 } else {
1306 Option(CURLOPT_VERBOSE, 0L);
1307 }
1308
1309 return *this;
1310 }
1311
1316 RequestBuilder& RequestTimeout(const long timeout) {
1317 request_timeout_ = timeout;
1318 return *this;
1319 }
1320
1325 RequestBuilder& ConnectTimeout(const long timeout) {
1326 connect_timeout_ = timeout;
1327 return *this;
1328 }
1329
1337 RequestBuilder& SendFile(const std::string& path) {
1338 assert(!is_built_);
1339 assert(CanSendFile());
1340 if (!CanSendFile()) {
1341 throw Exception{"Invalid curl operation for a file upload"};
1342 }
1343
1344 assert(request_);
1345 request_->OpenSourceFile(path);
1346 struct stat st = {};
1347 if(fstat(fileno(request_->GetSourceFp()), &st) != 0) {
1348 const auto e = errno;
1349 throw SystemException{std::string{"Unable to stat file "} + path, e};
1350 }
1351
1352 // set where to read from (on Windows you need to use READFUNCTION too)
1353 options_->Set(CURLOPT_READDATA, request_->GetSourceFp());
1354 options_->Set(CURLOPT_INFILESIZE_LARGE, static_cast<curl_off_t>(st.st_size));
1355 have_data_out_ = true;
1356 return *this;
1357 }
1358
1370 RequestBuilder& SendFileAsMimeData(const std::string& path,
1371 const std::string& name = {},
1372 const std::string& remoteName = {},
1373 const std::string& mimeType = {}) {
1374 assert(request_);
1375 assert(request_type_ == RequestType::POST_MIME);
1376 if (request_type_ != RequestType::POST_MIME) {
1377 throw Exception{"Must use PostMime operation to add mime attachments"};
1378 }
1379 request_->AddFileAsMimeData(path, name, remoteName, mimeType);
1380 return *this;
1381 }
1382
1390 RequestBuilder& SendFileAsForm(const std::string& path) {
1391 assert(!is_built_);
1392 assert(CanSendFile());
1393 if (!CanSendFile()) {
1394 throw Exception{"Invalid curl operation for a file upload"};
1395 }
1396
1397 assert(request_);
1398 request_->OpenSourceFile(path);
1399 struct stat st = {};
1400 if(fstat(fileno(request_->GetSourceFp()), &st) != 0) {
1401 const auto e = errno;
1402 throw SystemException{std::string{"Unable to stat file "} + path, e};
1403 }
1404
1405 // set where to read from (on Windows you need to use READFUNCTION too)
1406 options_->Set(CURLOPT_READDATA, request_->GetSourceFp());
1407 options_->Set(CURLOPT_INFILESIZE_LARGE, static_cast<curl_off_t>(st.st_size));
1408 have_data_out_ = true;
1409 return *this;
1410 }
1411
1422 template <typename T>
1424 assert(!is_built_);
1425 options_->Set(CURLOPT_READFUNCTION, dh.read_callback);
1426 options_->Set(CURLOPT_READDATA, &dh);
1427 have_data_out_ = true;
1428 return *this;
1429 }
1430
1438 template <typename T>
1440 assert(!is_built_);
1441 auto handler = std::make_unique<OutDataHandler<T>>(std::move(data));
1442 auto& handler_ref = *handler;
1443 request_->SetDefaultOutHandler(std::move(handler));
1444 return SendData(handler_ref);
1445 }
1446
1457 template <typename T>
1459 assert(!is_built_);
1460 options_->Set(CURLOPT_WRITEFUNCTION, dh.write_callback);
1461 options_->Set(CURLOPT_WRITEDATA, &dh);
1462 have_data_in_ = true;
1463 return *this;
1464 }
1465
1474 template <typename T>
1476 assert(!is_built_);
1477 auto handler = std::make_unique<InDataHandler<T>>(data);
1478 auto& handler_ref = *handler;
1479 request_->SetDefaultInHandler(std::move(handler));
1480 return StoreData(handler_ref);
1481 }
1482
1490 options_->Set(CURLOPT_WRITEFUNCTION, write_callback);
1491 have_data_in_ = true;
1492 return *this;
1493 }
1494
1506 assert(!is_built_);
1507 completion_ = std::move(fn);
1508 return *this;
1509 }
1510
1523 RequestBuilder& BasicAuthentication(const std::string& name,
1524 const std::string& passwd) {
1525 assert(!is_built_);
1526
1527 if (!name.empty() && !passwd.empty()) {
1528 options_->Set(CURLOPT_USERNAME, name.c_str());
1529 options_->Set(CURLOPT_PASSWORD, passwd.c_str());
1530 }
1531
1532 return *this;
1533 }
1534
1541 RequestBuilder& SetReadHandler(size_t (*handler)(char *, size_t , size_t , void *), void *userdata) {
1542 options_->Set(CURLOPT_READFUNCTION, handler);
1543 options_->Set(CURLOPT_READDATA, userdata);
1544 have_data_out_ = true;
1545 return *this;
1546 }
1547
1554 RequestBuilder& SetWriteHandler(size_t (*handler)(char *, size_t , size_t , void *), void *userdata) {
1555 options_->Set(CURLOPT_WRITEFUNCTION,handler);
1556 options_->Set(CURLOPT_WRITEDATA, userdata);
1557 have_data_in_ = true;
1558 return *this;
1559 }
1560
1561 void Build() {
1562 if (!is_built_) {
1563 // Set up Data Handlers
1564 if (!have_data_in_) {
1565 // Use a default std::string. We expect json anyway...
1566 this->StoreData(request_->getDefaultInBuffer());
1567 }
1568
1569 if (have_data_out_) {
1570 options_->Set(CURLOPT_UPLOAD, 1L);
1571 }
1572
1573 if (request_timeout_ >= 0) {
1574 options_->Set(CURLOPT_TIMEOUT_MS, request_timeout_);
1575 }
1576
1577 if (connect_timeout_ >= 0) {
1578 options_->Set(CURLOPT_CONNECTTIMEOUT_MS, connect_timeout_);
1579 }
1580
1581 // Set headers
1582 if (request_->GetHeaders()) {
1583 options_->Set(CURLOPT_HTTPHEADER, request_->GetHeaders());
1584 }
1585
1586 // TODO: Prepare the final url (we want nice, correctly encoded request arguments)
1587 options_->Set(CURLOPT_URL, url_);
1588 RESTINCURL_LOG("Preparing connect to: " << url_);
1589
1590 // Prepare request
1591 request_->Prepare(request_type_, std::move(completion_));
1592 is_built_ = true;
1593 }
1594 }
1595
1606 Build();
1607 request_->Execute();
1608 }
1609
1610#if RESTINCURL_ENABLE_ASYNC
1611
1623 void Execute() {
1624 Build();
1625 worker_.Enqueue(std::move(request_));
1626 }
1627#endif
1628
1629 private:
1630 std::unique_ptr<Request> request_;
1631 std::unique_ptr<class Options> options_;
1632 std::string url_;
1633 RequestType request_type_ = RequestType::INVALID;
1634 bool have_data_in_ = false;
1635 bool have_data_out_ = false;
1636 bool is_built_ = false;
1637 completion_fn_t completion_;
1638 long request_timeout_ = 10000L; // 10 seconds
1639 long connect_timeout_ = 3000L; // 1 second
1640#if RESTINCURL_ENABLE_ASYNC
1641 Worker& worker_;
1642#endif
1643 };
1644
1656 class Client {
1657
1658 public:
1672 Client(const bool init = true) {
1673 if (init) {
1674 static std::once_flag flag;
1675 std::call_once(flag, [] {
1676 RESTINCURL_LOG("One time initialization of curl.");
1677 curl_global_init(CURL_GLOBAL_DEFAULT);
1678 });
1679 }
1680 }
1681
1691 virtual ~Client() {
1692#if RESTINCURL_ENABLE_ASYNC
1693 if (worker_) {
1694 try {
1695 worker_->Close();
1696 } catch (const std::exception& ex) {
1697 RESTINCURL_LOG("~Client(): " << ex.what());
1698 }
1699 }
1700#endif
1701 }
1702
1720 std::unique_ptr<RequestBuilder> Build() {
1721 return std::make_unique<RequestBuilder>(
1723 *worker_
1724#endif
1725 );
1726 }
1727
1728#if RESTINCURL_ENABLE_ASYNC
1734 worker_->CloseWhenFinished();
1735 }
1736
1746 void Close() {
1747 worker_->Close();
1748 }
1749
1757 worker_->Join();
1758 }
1759
1764 bool HaveWorker() const {
1765 return worker_->HaveThread();
1766 }
1767
1773 return worker_->GetNumActiveRequests();
1774 }
1775#endif
1776
1777 private:
1778#if RESTINCURL_ENABLE_ASYNC
1779 std::unique_ptr<Worker> worker_ = std::make_unique<Worker>();
1780#endif
1781 };
1782
1783
1784} // namespace
int errno
This is free and unencumbered software released into the public domain.
std::unique_ptr< RequestBuilder > Build()
Definition restincurl.h:1720
virtual ~Client()
Definition restincurl.h:1691
void CloseWhenFinished()
Definition restincurl.h:1733
size_t GetNumActiveRequests()
Definition restincurl.h:1772
Client(const bool init=true)
Definition restincurl.h:1672
bool HaveWorker() const
Definition restincurl.h:1764
void Close()
Definition restincurl.h:1746
void WaitForFinish()
Definition restincurl.h:1756
Definition restincurl.h:306
Definition restincurl.h:324
void Close()
Definition restincurl.h:337
EasyHandle()
Definition restincurl.h:329
std::unique_ptr< EasyHandle > ptr_t
Definition restincurl.h:326
decltype(curl_easy_init()) handle_t
Definition restincurl.h:327
~EasyHandle()
Definition restincurl.h:333
Definition restincurl.h:289
Exception(const std::string &msg)
Definition restincurl.h:291
Options & Set(const CURLoption &opt, const std::string &value)
Definition restincurl.h:379
Options & Set(const CURLoption &opt, const T &value)
Definition restincurl.h:365
Options(EasyHandle &eh)
Definition restincurl.h:357
RequestBuilder & Delete(const std::string &url)
Definition restincurl.h:1223
RequestBuilder & BasicAuthentication(const std::string &name, const std::string &passwd)
Definition restincurl.h:1523
RequestBuilder & Trace(bool enable=true)
Definition restincurl.h:1301
RequestBuilder & WithJson(std::string body)
Definition restincurl.h:1266
RequestBuilder & Patch(const std::string &url)
Definition restincurl.h:1218
bool CanSendFile() const noexcept
Definition restincurl.h:1174
RequestBuilder & IgnoreIncomingData()
Definition restincurl.h:1489
RequestBuilder & RequestTimeout(const long timeout)
Definition restincurl.h:1316
void ExecuteSynchronous()
Definition restincurl.h:1605
RequestBuilder & Post(const std::string &url)
Definition restincurl.h:1203
std::unique_ptr< RequestBuilder > ptr_t
Definition restincurl.h:1148
RequestBuilder & PostMime(const std::string &url)
Definition restincurl.h:1208
RequestBuilder & AcceptJson()
Definition restincurl.h:1272
RequestBuilder & WithJson()
Definition restincurl.h:1258
RequestBuilder & SendFile(const std::string &path)
Definition restincurl.h:1337
RequestBuilder & Put(const std::string &url)
Definition restincurl.h:1213
RequestBuilder & Get(const std::string &url)
Definition restincurl.h:1193
RequestBuilder & StoreData(T &data)
Definition restincurl.h:1475
void Build()
Definition restincurl.h:1561
RequestBuilder & SetReadHandler(size_t(*handler)(char *, size_t, size_t, void *), void *userdata)
Definition restincurl.h:1541
RequestBuilder(Worker &worker)
Definition restincurl.h:1149
RequestBuilder & Prepare(RequestType rt, const std::string &url)
Definition restincurl.h:1165
void Execute()
Definition restincurl.h:1623
RequestBuilder & SetWriteHandler(size_t(*handler)(char *, size_t, size_t, void *), void *userdata)
Definition restincurl.h:1554
RequestBuilder & StoreData(InDataHandler< T > &dh)
Definition restincurl.h:1458
RequestBuilder & SendFileAsMimeData(const std::string &path, const std::string &name={}, const std::string &remoteName={}, const std::string &mimeType={})
Definition restincurl.h:1370
RequestBuilder & ConnectTimeout(const long timeout)
Definition restincurl.h:1325
RequestBuilder & Head(const std::string &url)
Definition restincurl.h:1198
RequestBuilder & Header(const std::string &name, const std::string &value)
Definition restincurl.h:1251
RequestBuilder & WithCompletion(completion_fn_t fn)
Definition restincurl.h:1505
~RequestBuilder()
Definition restincurl.h:1161
RequestBuilder & SendData(OutDataHandler< T > &dh)
Definition restincurl.h:1423
RequestBuilder & Options(const std::string &url)
Definition restincurl.h:1228
RequestBuilder & Header(const char *value)
Definition restincurl.h:1237
RequestBuilder & SendData(T data)
Definition restincurl.h:1439
RequestBuilder & SendFileAsForm(const std::string &path)
Definition restincurl.h:1390
RequestBuilder & Option(const CURLoption &opt, const T &value)
Definition restincurl.h:1288
Definition restincurl.h:455
~Request()
Definition restincurl.h:469
void SetDefaultOutHandler(std::unique_ptr< DataHandlerBase > ptr)
Definition restincurl.h:514
Request(EasyHandle::ptr_t &&eh)
Definition restincurl.h:464
void Execute()
Definition restincurl.h:498
EasyHandle & GetEasyHandle() noexcept
Definition restincurl.h:507
void InitMime()
Definition restincurl.h:527
void SetDefaultInHandler(std::unique_ptr< DataHandlerBase > ptr)
Definition restincurl.h:510
void Prepare(const RequestType rq, completion_fn_t completion)
Definition restincurl.h:475
curl_slist * headers_t
Definition restincurl.h:518
void Complete(CURLcode cc, const CURLMSG &)
Definition restincurl.h:503
Request()
Definition restincurl.h:459
RequestType GetRequestType() noexcept
Definition restincurl.h:508
FILE * GetSourceFp()
Definition restincurl.h:492
headers_t & GetHeaders()
Definition restincurl.h:519
std::unique_ptr< Request > ptr_t
Definition restincurl.h:457
std::string & getDefaultInBuffer()
Definition restincurl.h:523
void AddFileAsMimeData(const std::string &path, const std::string &name, const std::string &remoteName, const std::string &mimeType)
Definition restincurl.h:533
void OpenSourceFile(const std::string &path)
Definition restincurl.h:481
~Signaler()
Definition restincurl.h:636
void Signal()
Definition restincurl.h:642
Signaler()
Definition restincurl.h:623
int GetReadFd()
Definition restincurl.h:650
std::array< int, 2 > pipefd_t
Definition restincurl.h:621
bool WasSignalled()
Definition restincurl.h:652
Definition restincurl.h:295
int getErrorCode() const noexcept
Definition restincurl.h:299
SystemException(const std::string &msg, const int e)
Definition restincurl.h:297
Definition restincurl.h:689
static void tlsLockKill()
Definition restincurl.h:724
static void tlsLOckInit()
Definition restincurl.h:719
Definition restincurl.h:744
void Close()
Definition restincurl.h:847
size_t GetNumActiveRequests() const
Definition restincurl.h:869
static std::unique_ptr< Worker > Create()
Definition restincurl.h:810
~Worker()
Definition restincurl.h:777
void CloseWhenFinished()
Definition restincurl.h:838
void Enqueue(Request::ptr_t req)
Definition restincurl.h:814
void Join() const
Definition restincurl.h:822
bool HaveThread() const noexcept
Definition restincurl.h:861
void PrepareThread()
Definition restincurl.h:784
bool IsDone() const
Definition restincurl.h:856
Definition restincurl.h:197
RequestType
Definition restincurl.h:278
@ OPTIONS
Definition restincurl.h:278
@ DELETE
Definition restincurl.h:278
@ PUT
Definition restincurl.h:278
@ PATCH
Definition restincurl.h:278
@ GET
Definition restincurl.h:278
@ POST
Definition restincurl.h:278
@ POST_MIME
Definition restincurl.h:278
@ INVALID
Definition restincurl.h:278
@ HEAD
Definition restincurl.h:278
std::lock_guard< std::mutex > lock_t
Definition restincurl.h:237
std::function< void(const Result &result)> completion_fn_t
Definition restincurl.h:286
Namespace for the standard library.
#define RESTINCURL_LOG(msg)
Macro to log debug messages.
Definition restincurl.h:170
#define RESTINCURL_IDLE_TIMEOUT_SEC
How long to wait for the next request before the idle worker-thread is stopped.
Definition restincurl.h:102
#define RESTINCURL_MAX_CONNECTIONS
Max concurrent connections.
Definition restincurl.h:73
#define RESTINCURL_LOG_TRACE(msg)
Macro to log debug messages.
Definition restincurl.h:193
#define RESTINCURL_ENABLE_ASYNC
Enables or disables asynchronous mode.
Definition restincurl.h:88
Definition restincurl.h:392
virtual ~DataHandlerBase()=default
Definition restincurl.h:405
static size_t write_callback(char *ptr, size_t size, size_t nitems, void *userdata)
Definition restincurl.h:410
T & data_
Definition restincurl.h:420
InDataHandler(T &data)
Definition restincurl.h:406
Definition restincurl.h:432
size_t sendt_bytes_
Definition restincurl.h:452
OutDataHandler(T &&v)
Definition restincurl.h:435
static size_t read_callback(char *bufptr, size_t size, size_t nitems, void *userdata)
Definition restincurl.h:437
T data_
Definition restincurl.h:451
OutDataHandler(const T &v)
Definition restincurl.h:434
Definition restincurl.h:241
CURLcode curl_code
Definition restincurl.h:263
long http_response_code
Definition restincurl.h:266
Result(const CURLcode &code)
Definition restincurl.h:243
std::string msg
Definition restincurl.h:269
std::string body
Definition restincurl.h:275
bool isOk() const noexcept
Definition restincurl.h:249