Fix various copy instead of const ref reported by cppcheck (#5615)
[oweals/minetest.git] / src / httpfetch.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "socket.h" // for select()
21 #include "porting.h" // for sleep_ms(), get_sysinfo(), secure_rand_fill_buf()
22 #include "httpfetch.h"
23 #include <iostream>
24 #include <sstream>
25 #include <list>
26 #include <map>
27 #include <errno.h>
28 #include "threading/event.h"
29 #include "config.h"
30 #include "exceptions.h"
31 #include "debug.h"
32 #include "log.h"
33 #include "util/container.h"
34 #include "util/thread.h"
35 #include "version.h"
36 #include "settings.h"
37 #include "noise.h"
38
39 Mutex g_httpfetch_mutex;
40 std::map<unsigned long, std::queue<HTTPFetchResult> > g_httpfetch_results;
41 PcgRandom g_callerid_randomness;
42
43 HTTPFetchRequest::HTTPFetchRequest()
44 {
45         url = "";
46         caller = HTTPFETCH_DISCARD;
47         request_id = 0;
48         timeout = g_settings->getS32("curl_timeout");
49         connect_timeout = timeout;
50         multipart = false;
51
52         useragent = std::string(PROJECT_NAME_C "/") + g_version_hash + " (" + porting::get_sysinfo() + ")";
53 }
54
55
56 static void httpfetch_deliver_result(const HTTPFetchResult &fetch_result)
57 {
58         unsigned long caller = fetch_result.caller;
59         if (caller != HTTPFETCH_DISCARD) {
60                 MutexAutoLock lock(g_httpfetch_mutex);
61                 g_httpfetch_results[caller].push(fetch_result);
62         }
63 }
64
65 static void httpfetch_request_clear(unsigned long caller);
66
67 unsigned long httpfetch_caller_alloc()
68 {
69         MutexAutoLock lock(g_httpfetch_mutex);
70
71         // Check each caller ID except HTTPFETCH_DISCARD
72         const unsigned long discard = HTTPFETCH_DISCARD;
73         for (unsigned long caller = discard + 1; caller != discard; ++caller) {
74                 std::map<unsigned long, std::queue<HTTPFetchResult> >::iterator
75                         it = g_httpfetch_results.find(caller);
76                 if (it == g_httpfetch_results.end()) {
77                         verbosestream << "httpfetch_caller_alloc: allocating "
78                                         << caller << std::endl;
79                         // Access element to create it
80                         g_httpfetch_results[caller];
81                         return caller;
82                 }
83         }
84
85         FATAL_ERROR("httpfetch_caller_alloc: ran out of caller IDs");
86         return discard;
87 }
88
89 unsigned long httpfetch_caller_alloc_secure()
90 {
91         MutexAutoLock lock(g_httpfetch_mutex);
92
93         // Generate random caller IDs and make sure they're not
94         // already used or equal to HTTPFETCH_DISCARD
95         // Give up after 100 tries to prevent infinite loop
96         u8 tries = 100;
97         unsigned long caller;
98
99         do {
100                 caller = (((u64) g_callerid_randomness.next()) << 32) |
101                                 g_callerid_randomness.next();
102
103                 if (--tries < 1) {
104                         FATAL_ERROR("httpfetch_caller_alloc_secure: ran out of caller IDs");
105                         return HTTPFETCH_DISCARD;
106                 }
107         } while (g_httpfetch_results.find(caller) != g_httpfetch_results.end());
108
109         verbosestream << "httpfetch_caller_alloc_secure: allocating "
110                 << caller << std::endl;
111
112         // Access element to create it
113         g_httpfetch_results[caller];
114         return caller;
115 }
116
117 void httpfetch_caller_free(unsigned long caller)
118 {
119         verbosestream<<"httpfetch_caller_free: freeing "
120                         <<caller<<std::endl;
121
122         httpfetch_request_clear(caller);
123         if (caller != HTTPFETCH_DISCARD) {
124                 MutexAutoLock lock(g_httpfetch_mutex);
125                 g_httpfetch_results.erase(caller);
126         }
127 }
128
129 bool httpfetch_async_get(unsigned long caller, HTTPFetchResult &fetch_result)
130 {
131         MutexAutoLock lock(g_httpfetch_mutex);
132
133         // Check that caller exists
134         std::map<unsigned long, std::queue<HTTPFetchResult> >::iterator
135                 it = g_httpfetch_results.find(caller);
136         if (it == g_httpfetch_results.end())
137                 return false;
138
139         // Check that result queue is nonempty
140         std::queue<HTTPFetchResult> &caller_results = it->second;
141         if (caller_results.empty())
142                 return false;
143
144         // Pop first result
145         fetch_result = caller_results.front();
146         caller_results.pop();
147         return true;
148 }
149
150 #if USE_CURL
151 #include <curl/curl.h>
152
153 /*
154         USE_CURL is on: use cURL based httpfetch implementation
155 */
156
157 static size_t httpfetch_writefunction(
158                 char *ptr, size_t size, size_t nmemb, void *userdata)
159 {
160         std::ostringstream *stream = (std::ostringstream*)userdata;
161         size_t count = size * nmemb;
162         stream->write(ptr, count);
163         return count;
164 }
165
166 static size_t httpfetch_discardfunction(
167                 char *ptr, size_t size, size_t nmemb, void *userdata)
168 {
169         return size * nmemb;
170 }
171
172 class CurlHandlePool
173 {
174         std::list<CURL*> handles;
175
176 public:
177         CurlHandlePool() {}
178         ~CurlHandlePool()
179         {
180                 for (std::list<CURL*>::iterator it = handles.begin();
181                                 it != handles.end(); ++it) {
182                         curl_easy_cleanup(*it);
183                 }
184         }
185         CURL * alloc()
186         {
187                 CURL *curl;
188                 if (handles.empty()) {
189                         curl = curl_easy_init();
190                         if (curl == NULL) {
191                                 errorstream<<"curl_easy_init returned NULL"<<std::endl;
192                         }
193                 }
194                 else {
195                         curl = handles.front();
196                         handles.pop_front();
197                 }
198                 return curl;
199         }
200         void free(CURL *handle)
201         {
202                 if (handle)
203                         handles.push_back(handle);
204         }
205 };
206
207 class HTTPFetchOngoing
208 {
209 public:
210         HTTPFetchOngoing(const HTTPFetchRequest &request, CurlHandlePool *pool);
211         ~HTTPFetchOngoing();
212
213         CURLcode start(CURLM *multi);
214         const HTTPFetchResult * complete(CURLcode res);
215
216         const HTTPFetchRequest &getRequest()    const { return request; };
217         const CURL             *getEasyHandle() const { return curl; };
218
219 private:
220         CurlHandlePool *pool;
221         CURL *curl;
222         CURLM *multi;
223         HTTPFetchRequest request;
224         HTTPFetchResult result;
225         std::ostringstream oss;
226         struct curl_slist *http_header;
227         curl_httppost *post;
228 };
229
230
231 HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_,
232                 CurlHandlePool *pool_):
233         pool(pool_),
234         curl(NULL),
235         multi(NULL),
236         request(request_),
237         result(request_),
238         oss(std::ios::binary),
239         http_header(NULL),
240         post(NULL)
241 {
242         curl = pool->alloc();
243         if (curl == NULL) {
244                 return;
245         }
246
247         // Set static cURL options
248         curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
249         curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
250         curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
251         curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 1);
252
253         std::string bind_address = g_settings->get("bind_address");
254         if (!bind_address.empty()) {
255                 curl_easy_setopt(curl, CURLOPT_INTERFACE, bind_address.c_str());
256         }
257
258 #if LIBCURL_VERSION_NUM >= 0x071304
259         // Restrict protocols so that curl vulnerabilities in
260         // other protocols don't affect us.
261         // These settings were introduced in curl 7.19.4.
262         long protocols =
263                 CURLPROTO_HTTP |
264                 CURLPROTO_HTTPS |
265                 CURLPROTO_FTP |
266                 CURLPROTO_FTPS;
267         curl_easy_setopt(curl, CURLOPT_PROTOCOLS, protocols);
268         curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, protocols);
269 #endif
270
271         // Set cURL options based on HTTPFetchRequest
272         curl_easy_setopt(curl, CURLOPT_URL,
273                         request.url.c_str());
274         curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS,
275                         request.timeout);
276         curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS,
277                         request.connect_timeout);
278
279         if (request.useragent != "")
280                 curl_easy_setopt(curl, CURLOPT_USERAGENT, request.useragent.c_str());
281
282         // Set up a write callback that writes to the
283         // ostringstream ongoing->oss, unless the data
284         // is to be discarded
285         if (request.caller == HTTPFETCH_DISCARD) {
286                 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
287                                 httpfetch_discardfunction);
288                 curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
289         } else {
290                 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
291                                 httpfetch_writefunction);
292                 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &oss);
293         }
294
295         // Set POST (or GET) data
296         if (request.post_fields.empty() && request.post_data.empty()) {
297                 curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
298         } else if (request.multipart) {
299                 curl_httppost *last = NULL;
300                 for (StringMap::iterator it = request.post_fields.begin();
301                                 it != request.post_fields.end(); ++it) {
302                         curl_formadd(&post, &last,
303                                         CURLFORM_NAMELENGTH, it->first.size(),
304                                         CURLFORM_PTRNAME, it->first.c_str(),
305                                         CURLFORM_CONTENTSLENGTH, it->second.size(),
306                                         CURLFORM_PTRCONTENTS, it->second.c_str(),
307                                         CURLFORM_END);
308                 }
309                 curl_easy_setopt(curl, CURLOPT_HTTPPOST, post);
310                 // request.post_fields must now *never* be
311                 // modified until CURLOPT_HTTPPOST is cleared
312         } else if (request.post_data.empty()) {
313                 curl_easy_setopt(curl, CURLOPT_POST, 1);
314                 std::string str;
315                 for (StringMap::iterator it = request.post_fields.begin();
316                                 it != request.post_fields.end(); ++it) {
317                         if (str != "")
318                                 str += "&";
319                         str += urlencode(it->first);
320                         str += "=";
321                         str += urlencode(it->second);
322                 }
323                 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
324                                 str.size());
325                 curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS,
326                                 str.c_str());
327         } else {
328                 curl_easy_setopt(curl, CURLOPT_POST, 1);
329                 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
330                                 request.post_data.size());
331                 curl_easy_setopt(curl, CURLOPT_POSTFIELDS,
332                                 request.post_data.c_str());
333                 // request.post_data must now *never* be
334                 // modified until CURLOPT_POSTFIELDS is cleared
335         }
336         // Set additional HTTP headers
337         for (std::vector<std::string>::iterator it = request.extra_headers.begin();
338                         it != request.extra_headers.end(); ++it) {
339                 http_header = curl_slist_append(http_header, it->c_str());
340         }
341         curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_header);
342
343         if (!g_settings->getBool("curl_verify_cert")) {
344                 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
345         }
346 }
347
348 CURLcode HTTPFetchOngoing::start(CURLM *multi_)
349 {
350         if (!curl)
351                 return CURLE_FAILED_INIT;
352
353         if (!multi_) {
354                 // Easy interface (sync)
355                 return curl_easy_perform(curl);
356         }
357
358         // Multi interface (async)
359         CURLMcode mres = curl_multi_add_handle(multi_, curl);
360         if (mres != CURLM_OK) {
361                 errorstream << "curl_multi_add_handle"
362                         << " returned error code " << mres
363                         << std::endl;
364                 return CURLE_FAILED_INIT;
365         }
366         multi = multi_; // store for curl_multi_remove_handle
367         return CURLE_OK;
368 }
369
370 const HTTPFetchResult * HTTPFetchOngoing::complete(CURLcode res)
371 {
372         result.succeeded = (res == CURLE_OK);
373         result.timeout = (res == CURLE_OPERATION_TIMEDOUT);
374         result.data = oss.str();
375
376         // Get HTTP/FTP response code
377         result.response_code = 0;
378         if (curl && (curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE,
379                                 &result.response_code) != CURLE_OK)) {
380                 // We failed to get a return code, make sure it is still 0
381                 result.response_code = 0;
382         }
383
384         if (res != CURLE_OK) {
385                 errorstream << request.url << " not found ("
386                         << curl_easy_strerror(res) << ")"
387                         << " (response code " << result.response_code << ")"
388                         << std::endl;
389         }
390
391         return &result;
392 }
393
394 HTTPFetchOngoing::~HTTPFetchOngoing()
395 {
396         if (multi) {
397                 CURLMcode mres = curl_multi_remove_handle(multi, curl);
398                 if (mres != CURLM_OK) {
399                         errorstream << "curl_multi_remove_handle"
400                                 << " returned error code " << mres
401                                 << std::endl;
402                 }
403         }
404
405         // Set safe options for the reusable cURL handle
406         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
407                         httpfetch_discardfunction);
408         curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
409         curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL);
410         if (http_header) {
411                 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL);
412                 curl_slist_free_all(http_header);
413         }
414         if (post) {
415                 curl_easy_setopt(curl, CURLOPT_HTTPPOST, NULL);
416                 curl_formfree(post);
417         }
418
419         // Store the cURL handle for reuse
420         pool->free(curl);
421 }
422
423
424 class CurlFetchThread : public Thread
425 {
426 protected:
427         enum RequestType {
428                 RT_FETCH,
429                 RT_CLEAR,
430                 RT_WAKEUP,
431         };
432
433         struct Request {
434                 RequestType type;
435                 HTTPFetchRequest fetch_request;
436                 Event *event;
437         };
438
439         CURLM *m_multi;
440         MutexedQueue<Request> m_requests;
441         size_t m_parallel_limit;
442
443         // Variables exclusively used within thread
444         std::vector<HTTPFetchOngoing*> m_all_ongoing;
445         std::list<HTTPFetchRequest> m_queued_fetches;
446
447 public:
448         CurlFetchThread(int parallel_limit) :
449                 Thread("CurlFetch")
450         {
451                 if (parallel_limit >= 1)
452                         m_parallel_limit = parallel_limit;
453                 else
454                         m_parallel_limit = 1;
455         }
456
457         void requestFetch(const HTTPFetchRequest &fetch_request)
458         {
459                 Request req;
460                 req.type = RT_FETCH;
461                 req.fetch_request = fetch_request;
462                 req.event = NULL;
463                 m_requests.push_back(req);
464         }
465
466         void requestClear(unsigned long caller, Event *event)
467         {
468                 Request req;
469                 req.type = RT_CLEAR;
470                 req.fetch_request.caller = caller;
471                 req.event = event;
472                 m_requests.push_back(req);
473         }
474
475         void requestWakeUp()
476         {
477                 Request req;
478                 req.type = RT_WAKEUP;
479                 req.event = NULL;
480                 m_requests.push_back(req);
481         }
482
483 protected:
484         // Handle a request from some other thread
485         // E.g. new fetch; clear fetches for one caller; wake up
486         void processRequest(const Request &req)
487         {
488                 if (req.type == RT_FETCH) {
489                         // New fetch, queue until there are less
490                         // than m_parallel_limit ongoing fetches
491                         m_queued_fetches.push_back(req.fetch_request);
492
493                         // see processQueued() for what happens next
494
495                 }
496                 else if (req.type == RT_CLEAR) {
497                         unsigned long caller = req.fetch_request.caller;
498
499                         // Abort all ongoing fetches for the caller
500                         for (std::vector<HTTPFetchOngoing*>::iterator
501                                         it = m_all_ongoing.begin();
502                                         it != m_all_ongoing.end();) {
503                                 if ((*it)->getRequest().caller == caller) {
504                                         delete (*it);
505                                         it = m_all_ongoing.erase(it);
506                                 } else {
507                                         ++it;
508                                 }
509                         }
510
511                         // Also abort all queued fetches for the caller
512                         for (std::list<HTTPFetchRequest>::iterator
513                                         it = m_queued_fetches.begin();
514                                         it != m_queued_fetches.end();) {
515                                 if ((*it).caller == caller)
516                                         it = m_queued_fetches.erase(it);
517                                 else
518                                         ++it;
519                         }
520                 }
521                 else if (req.type == RT_WAKEUP) {
522                         // Wakeup: Nothing to do, thread is awake at this point
523                 }
524
525                 if (req.event != NULL)
526                         req.event->signal();
527         }
528
529         // Start new ongoing fetches if m_parallel_limit allows
530         void processQueued(CurlHandlePool *pool)
531         {
532                 while (m_all_ongoing.size() < m_parallel_limit &&
533                                 !m_queued_fetches.empty()) {
534                         HTTPFetchRequest request = m_queued_fetches.front();
535                         m_queued_fetches.pop_front();
536
537                         // Create ongoing fetch data and make a cURL handle
538                         // Set cURL options based on HTTPFetchRequest
539                         HTTPFetchOngoing *ongoing =
540                                 new HTTPFetchOngoing(request, pool);
541
542                         // Initiate the connection (curl_multi_add_handle)
543                         CURLcode res = ongoing->start(m_multi);
544                         if (res == CURLE_OK) {
545                                 m_all_ongoing.push_back(ongoing);
546                         }
547                         else {
548                                 httpfetch_deliver_result(*ongoing->complete(res));
549                                 delete ongoing;
550                         }
551                 }
552         }
553
554         // Process CURLMsg (indicates completion of a fetch)
555         void processCurlMessage(CURLMsg *msg)
556         {
557                 // Determine which ongoing fetch the message pertains to
558                 size_t i = 0;
559                 bool found = false;
560                 for (i = 0; i < m_all_ongoing.size(); ++i) {
561                         if (m_all_ongoing[i]->getEasyHandle() == msg->easy_handle) {
562                                 found = true;
563                                 break;
564                         }
565                 }
566                 if (msg->msg == CURLMSG_DONE && found) {
567                         // m_all_ongoing[i] succeeded or failed.
568                         HTTPFetchOngoing *ongoing = m_all_ongoing[i];
569                         httpfetch_deliver_result(*ongoing->complete(msg->data.result));
570                         delete ongoing;
571                         m_all_ongoing.erase(m_all_ongoing.begin() + i);
572                 }
573         }
574
575         // Wait for a request from another thread, or timeout elapses
576         void waitForRequest(long timeout)
577         {
578                 if (m_queued_fetches.empty()) {
579                         try {
580                                 Request req = m_requests.pop_front(timeout);
581                                 processRequest(req);
582                         }
583                         catch (ItemNotFoundException &e) {}
584                 }
585         }
586
587         // Wait until some IO happens, or timeout elapses
588         void waitForIO(long timeout)
589         {
590                 fd_set read_fd_set;
591                 fd_set write_fd_set;
592                 fd_set exc_fd_set;
593                 int max_fd;
594                 long select_timeout = -1;
595                 struct timeval select_tv;
596                 CURLMcode mres;
597
598                 FD_ZERO(&read_fd_set);
599                 FD_ZERO(&write_fd_set);
600                 FD_ZERO(&exc_fd_set);
601
602                 mres = curl_multi_fdset(m_multi, &read_fd_set,
603                                 &write_fd_set, &exc_fd_set, &max_fd);
604                 if (mres != CURLM_OK) {
605                         errorstream<<"curl_multi_fdset"
606                                 <<" returned error code "<<mres
607                                 <<std::endl;
608                         select_timeout = 0;
609                 }
610
611                 mres = curl_multi_timeout(m_multi, &select_timeout);
612                 if (mres != CURLM_OK) {
613                         errorstream<<"curl_multi_timeout"
614                                 <<" returned error code "<<mres
615                                 <<std::endl;
616                         select_timeout = 0;
617                 }
618
619                 // Limit timeout so new requests get through
620                 if (select_timeout < 0 || select_timeout > timeout)
621                         select_timeout = timeout;
622
623                 if (select_timeout > 0) {
624                         // in Winsock it is forbidden to pass three empty
625                         // fd_sets to select(), so in that case use sleep_ms
626                         if (max_fd != -1) {
627                                 select_tv.tv_sec = select_timeout / 1000;
628                                 select_tv.tv_usec = (select_timeout % 1000) * 1000;
629                                 int retval = select(max_fd + 1, &read_fd_set,
630                                                 &write_fd_set, &exc_fd_set,
631                                                 &select_tv);
632                                 if (retval == -1) {
633                                         #ifdef _WIN32
634                                         errorstream<<"select returned error code "
635                                                 <<WSAGetLastError()<<std::endl;
636                                         #else
637                                         errorstream<<"select returned error code "
638                                                 <<errno<<std::endl;
639                                         #endif
640                                 }
641                         }
642                         else {
643                                 sleep_ms(select_timeout);
644                         }
645                 }
646         }
647
648         void *run()
649         {
650                 DSTACK(FUNCTION_NAME);
651
652                 CurlHandlePool pool;
653
654                 m_multi = curl_multi_init();
655                 if (m_multi == NULL) {
656                         errorstream<<"curl_multi_init returned NULL\n";
657                         return NULL;
658                 }
659
660                 FATAL_ERROR_IF(!m_all_ongoing.empty(), "Expected empty");
661
662                 while (!stopRequested()) {
663                         BEGIN_DEBUG_EXCEPTION_HANDLER
664
665                         /*
666                                 Handle new async requests
667                         */
668
669                         while (!m_requests.empty()) {
670                                 Request req = m_requests.pop_frontNoEx();
671                                 processRequest(req);
672                         }
673                         processQueued(&pool);
674
675                         /*
676                                 Handle ongoing async requests
677                         */
678
679                         int still_ongoing = 0;
680                         while (curl_multi_perform(m_multi, &still_ongoing) ==
681                                         CURLM_CALL_MULTI_PERFORM)
682                                 /* noop */;
683
684                         /*
685                                 Handle completed async requests
686                         */
687                         if (still_ongoing < (int) m_all_ongoing.size()) {
688                                 CURLMsg *msg;
689                                 int msgs_in_queue;
690                                 msg = curl_multi_info_read(m_multi, &msgs_in_queue);
691                                 while (msg != NULL) {
692                                         processCurlMessage(msg);
693                                         msg = curl_multi_info_read(m_multi, &msgs_in_queue);
694                                 }
695                         }
696
697                         /*
698                                 If there are ongoing requests, wait for data
699                                 (with a timeout of 100ms so that new requests
700                                 can be processed).
701
702                                 If no ongoing requests, wait for a new request.
703                                 (Possibly an empty request that signals
704                                 that the thread should be stopped.)
705                         */
706                         if (m_all_ongoing.empty())
707                                 waitForRequest(100000000);
708                         else
709                                 waitForIO(100);
710
711                         END_DEBUG_EXCEPTION_HANDLER
712                 }
713
714                 // Call curl_multi_remove_handle and cleanup easy handles
715                 for (size_t i = 0; i < m_all_ongoing.size(); ++i) {
716                         delete m_all_ongoing[i];
717                 }
718                 m_all_ongoing.clear();
719
720                 m_queued_fetches.clear();
721
722                 CURLMcode mres = curl_multi_cleanup(m_multi);
723                 if (mres != CURLM_OK) {
724                         errorstream<<"curl_multi_cleanup"
725                                 <<" returned error code "<<mres
726                                 <<std::endl;
727                 }
728
729                 return NULL;
730         }
731 };
732
733 CurlFetchThread *g_httpfetch_thread = NULL;
734
735 void httpfetch_init(int parallel_limit)
736 {
737         verbosestream<<"httpfetch_init: parallel_limit="<<parallel_limit
738                         <<std::endl;
739
740         CURLcode res = curl_global_init(CURL_GLOBAL_DEFAULT);
741         FATAL_ERROR_IF(res != CURLE_OK, "CURL init failed");
742
743         g_httpfetch_thread = new CurlFetchThread(parallel_limit);
744
745         // Initialize g_callerid_randomness for httpfetch_caller_alloc_secure
746         u64 randbuf[2];
747         porting::secure_rand_fill_buf(randbuf, sizeof(u64) * 2);
748         g_callerid_randomness = PcgRandom(randbuf[0], randbuf[1]);
749 }
750
751 void httpfetch_cleanup()
752 {
753         verbosestream<<"httpfetch_cleanup: cleaning up"<<std::endl;
754
755         g_httpfetch_thread->stop();
756         g_httpfetch_thread->requestWakeUp();
757         g_httpfetch_thread->wait();
758         delete g_httpfetch_thread;
759
760         curl_global_cleanup();
761 }
762
763 void httpfetch_async(const HTTPFetchRequest &fetch_request)
764 {
765         g_httpfetch_thread->requestFetch(fetch_request);
766         if (!g_httpfetch_thread->isRunning())
767                 g_httpfetch_thread->start();
768 }
769
770 static void httpfetch_request_clear(unsigned long caller)
771 {
772         if (g_httpfetch_thread->isRunning()) {
773                 Event event;
774                 g_httpfetch_thread->requestClear(caller, &event);
775                 event.wait();
776         } else {
777                 g_httpfetch_thread->requestClear(caller, NULL);
778         }
779 }
780
781 void httpfetch_sync(const HTTPFetchRequest &fetch_request,
782                 HTTPFetchResult &fetch_result)
783 {
784         // Create ongoing fetch data and make a cURL handle
785         // Set cURL options based on HTTPFetchRequest
786         CurlHandlePool pool;
787         HTTPFetchOngoing ongoing(fetch_request, &pool);
788         // Do the fetch (curl_easy_perform)
789         CURLcode res = ongoing.start(NULL);
790         // Update fetch result
791         fetch_result = *ongoing.complete(res);
792 }
793
794 #else  // USE_CURL
795
796 /*
797         USE_CURL is off:
798
799         Dummy httpfetch implementation that always returns an error.
800 */
801
802 void httpfetch_init(int parallel_limit)
803 {
804 }
805
806 void httpfetch_cleanup()
807 {
808 }
809
810 void httpfetch_async(const HTTPFetchRequest &fetch_request)
811 {
812         errorstream << "httpfetch_async: unable to fetch " << fetch_request.url
813                         << " because USE_CURL=0" << std::endl;
814
815         HTTPFetchResult fetch_result(fetch_request); // sets succeeded = false etc.
816         httpfetch_deliver_result(fetch_result);
817 }
818
819 static void httpfetch_request_clear(unsigned long caller)
820 {
821 }
822
823 void httpfetch_sync(const HTTPFetchRequest &fetch_request,
824                 HTTPFetchResult &fetch_result)
825 {
826         errorstream << "httpfetch_sync: unable to fetch " << fetch_request.url
827                         << " because USE_CURL=0" << std::endl;
828
829         fetch_result = HTTPFetchResult(fetch_request); // sets succeeded = false etc.
830 }
831
832 #endif  // USE_CURL