]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/httpfetch.cpp
Remove obsolete eye_height related workaround
[dragonfireclient.git] / src / httpfetch.cpp
index d8504ad64750b1d90f81eaf85b548c458c7dcdfa..16f0791c994c601f3be8b0f547cd7f63f12ccf88 100644 (file)
@@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <iostream>
 #include <sstream>
 #include <list>
-#include <map>
+#include <unordered_map>
 #include <cerrno>
 #include <mutex>
 #include "network/socket.h" // for select()
@@ -37,13 +37,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h"
 #include "noise.h"
 
-std::mutex g_httpfetch_mutex;
-std::map<unsigned long, std::queue<HTTPFetchResult> > g_httpfetch_results;
-PcgRandom g_callerid_randomness;
+static std::mutex g_httpfetch_mutex;
+static std::unordered_map<u64, std::queue<HTTPFetchResult>>
+       g_httpfetch_results;
+static PcgRandom g_callerid_randomness;
 
 HTTPFetchRequest::HTTPFetchRequest() :
        timeout(g_settings->getS32("curl_timeout")),
-       connect_timeout(timeout),
+       connect_timeout(10 * 1000),
        useragent(std::string(PROJECT_NAME_C "/") + g_version_hash + " (" + porting::get_sysinfo() + ")")
 {
 }
@@ -51,24 +52,22 @@ HTTPFetchRequest::HTTPFetchRequest() :
 
 static void httpfetch_deliver_result(const HTTPFetchResult &fetch_result)
 {
-       unsigned long caller = fetch_result.caller;
+       u64 caller = fetch_result.caller;
        if (caller != HTTPFETCH_DISCARD) {
                MutexAutoLock lock(g_httpfetch_mutex);
-               g_httpfetch_results[caller].push(fetch_result);
+               g_httpfetch_results[caller].emplace(fetch_result);
        }
 }
 
-static void httpfetch_request_clear(unsigned long caller);
+static void httpfetch_request_clear(u64 caller);
 
-unsigned long httpfetch_caller_alloc()
+u64 httpfetch_caller_alloc()
 {
        MutexAutoLock lock(g_httpfetch_mutex);
 
-       // Check each caller ID except HTTPFETCH_DISCARD
-       const unsigned long discard = HTTPFETCH_DISCARD;
-       for (unsigned long caller = discard + 1; caller != discard; ++caller) {
-               std::map<unsigned long, std::queue<HTTPFetchResult> >::iterator
-                       it = g_httpfetch_results.find(caller);
+       // Check each caller ID except reserved ones
+       for (u64 caller = HTTPFETCH_CID_START; caller != 0; ++caller) {
+               auto it = g_httpfetch_results.find(caller);
                if (it == g_httpfetch_results.end()) {
                        verbosestream << "httpfetch_caller_alloc: allocating "
                                        << caller << std::endl;
@@ -79,18 +78,17 @@ unsigned long httpfetch_caller_alloc()
        }
 
        FATAL_ERROR("httpfetch_caller_alloc: ran out of caller IDs");
-       return discard;
 }
 
-unsigned long httpfetch_caller_alloc_secure()
+u64 httpfetch_caller_alloc_secure()
 {
        MutexAutoLock lock(g_httpfetch_mutex);
 
        // Generate random caller IDs and make sure they're not
-       // already used or equal to HTTPFETCH_DISCARD
+       // already used or reserved.
        // Give up after 100 tries to prevent infinite loop
-       u8 tries = 100;
-       unsigned long caller;
+       size_t tries = 100;
+       u64 caller;
 
        do {
                caller = (((u64) g_callerid_randomness.next()) << 32) |
@@ -100,7 +98,8 @@ unsigned long httpfetch_caller_alloc_secure()
                        FATAL_ERROR("httpfetch_caller_alloc_secure: ran out of caller IDs");
                        return HTTPFETCH_DISCARD;
                }
-       } while (g_httpfetch_results.find(caller) != g_httpfetch_results.end());
+       } while (caller >= HTTPFETCH_CID_START &&
+               g_httpfetch_results.find(caller) != g_httpfetch_results.end());
 
        verbosestream << "httpfetch_caller_alloc_secure: allocating "
                << caller << std::endl;
@@ -110,7 +109,7 @@ unsigned long httpfetch_caller_alloc_secure()
        return caller;
 }
 
-void httpfetch_caller_free(unsigned long caller)
+void httpfetch_caller_free(u64 caller)
 {
        verbosestream<<"httpfetch_caller_free: freeing "
                        <<caller<<std::endl;
@@ -122,13 +121,12 @@ void httpfetch_caller_free(unsigned long caller)
        }
 }
 
-bool httpfetch_async_get(unsigned long caller, HTTPFetchResult &fetch_result)
+bool httpfetch_async_get(u64 caller, HTTPFetchResult &fetch_result)
 {
        MutexAutoLock lock(g_httpfetch_mutex);
 
        // Check that caller exists
-       std::map<unsigned long, std::queue<HTTPFetchResult> >::iterator
-               it = g_httpfetch_results.find(caller);
+       auto it = g_httpfetch_results.find(caller);
        if (it == g_httpfetch_results.end())
                return false;
 
@@ -138,7 +136,7 @@ bool httpfetch_async_get(unsigned long caller, HTTPFetchResult &fetch_result)
                return false;
 
        // Pop first result
-       fetch_result = caller_results.front();
+       fetch_result = std::move(caller_results.front());
        caller_results.pop();
        return true;
 }
@@ -243,9 +241,8 @@ HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_,
 
        // Set static cURL options
        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
-       curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
-       curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 1);
+       curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 3);
        curl_easy_setopt(curl, CURLOPT_ENCODING, "gzip");
 
        std::string bind_address = g_settings->get("bind_address");
@@ -253,6 +250,10 @@ HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_,
                curl_easy_setopt(curl, CURLOPT_INTERFACE, bind_address.c_str());
        }
 
+       if (!g_settings->getBool("enable_ipv6")) {
+               curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+       }
+
 #if LIBCURL_VERSION_NUM >= 0x071304
        // Restrict protocols so that curl vulnerabilities in
        // other protocols don't affect us.
@@ -290,13 +291,11 @@ HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_,
                curl_easy_setopt(curl, CURLOPT_WRITEDATA, &oss);
        }
 
-       // Set POST (or GET) data
-       if (request.post_fields.empty() && request.post_data.empty()) {
-               curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
-       } else if (request.multipart) {
+       // Set data from fields or raw_data
+       if (request.multipart) {
                curl_httppost *last = NULL;
-               for (StringMap::iterator it = request.post_fields.begin();
-                               it != request.post_fields.end(); ++it) {
+               for (StringMap::iterator it = request.fields.begin();
+                               it != request.fields.end(); ++it) {
                        curl_formadd(&post, &last,
                                        CURLFORM_NAMELENGTH, it->first.size(),
                                        CURLFORM_PTRNAME, it->first.c_str(),
@@ -307,28 +306,42 @@ HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_,
                curl_easy_setopt(curl, CURLOPT_HTTPPOST, post);
                // request.post_fields must now *never* be
                // modified until CURLOPT_HTTPPOST is cleared
-       } else if (request.post_data.empty()) {
-               curl_easy_setopt(curl, CURLOPT_POST, 1);
-               std::string str;
-               for (auto &post_field : request.post_fields) {
-                       if (!str.empty())
-                               str += "&";
-                       str += urlencode(post_field.first);
-                       str += "=";
-                       str += urlencode(post_field.second);
-               }
-               curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
-                               str.size());
-               curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS,
-                               str.c_str());
        } else {
-               curl_easy_setopt(curl, CURLOPT_POST, 1);
-               curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
-                               request.post_data.size());
-               curl_easy_setopt(curl, CURLOPT_POSTFIELDS,
-                               request.post_data.c_str());
-               // request.post_data must now *never* be
-               // modified until CURLOPT_POSTFIELDS is cleared
+               switch (request.method) {
+               case HTTP_GET:
+                       curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
+                       break;
+               case HTTP_POST:
+                       curl_easy_setopt(curl, CURLOPT_POST, 1);
+                       break;
+               case HTTP_PUT:
+                       curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
+                       break;
+               case HTTP_DELETE:
+                       curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
+                       break;
+               }
+               if (request.method != HTTP_GET) {
+                       if (!request.raw_data.empty()) {
+                               curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
+                                               request.raw_data.size());
+                               curl_easy_setopt(curl, CURLOPT_POSTFIELDS,
+                                               request.raw_data.c_str());
+                       } else if (!request.fields.empty()) {
+                               std::string str;
+                               for (auto &field : request.fields) {
+                                       if (!str.empty())
+                                               str += "&";
+                                       str += urlencode(field.first);
+                                       str += "=";
+                                       str += urlencode(field.second);
+                               }
+                               curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE,
+                                               str.size());
+                               curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS,
+                                               str.c_str());
+                       }
+               }
        }
        // Set additional HTTP headers
        for (const std::string &extra_header : request.extra_headers) {
@@ -378,10 +391,17 @@ const HTTPFetchResult * HTTPFetchOngoing::complete(CURLcode res)
        }
 
        if (res != CURLE_OK) {
-               errorstream << request.url << " not found ("
-                       << curl_easy_strerror(res) << ")"
-                       << " (response code " << result.response_code << ")"
+               errorstream << "HTTPFetch for " << request.url << " failed ("
+                       << curl_easy_strerror(res) << ")" << std::endl;
+       } else if (result.response_code >= 400) {
+               errorstream << "HTTPFetch for " << request.url
+                       << " returned response code " << result.response_code
                        << std::endl;
+               if (result.caller == HTTPFETCH_PRINT_ERR && !result.data.empty()) {
+                       errorstream << "Response body:" << std::endl;
+                       safe_print_string(errorstream, result.data);
+                       errorstream << std::endl;
+               }
        }
 
        return &result;
@@ -459,7 +479,7 @@ class CurlFetchThread : public Thread
                m_requests.push_back(req);
        }
 
-       void requestClear(unsigned long caller, Event *event)
+       void requestClear(u64 caller, Event *event)
        {
                Request req;
                req.type = RT_CLEAR;
@@ -490,7 +510,7 @@ class CurlFetchThread : public Thread
 
                }
                else if (req.type == RT_CLEAR) {
-                       unsigned long caller = req.fetch_request.caller;
+                       u64 caller = req.fetch_request.caller;
 
                        // Abort all ongoing fetches for the caller
                        for (std::vector<HTTPFetchOngoing*>::iterator
@@ -746,10 +766,12 @@ void httpfetch_cleanup()
 {
        verbosestream<<"httpfetch_cleanup: cleaning up"<<std::endl;
 
-       g_httpfetch_thread->stop();
-       g_httpfetch_thread->requestWakeUp();
-       g_httpfetch_thread->wait();
-       delete g_httpfetch_thread;
+       if (g_httpfetch_thread) {
+               g_httpfetch_thread->stop();
+               g_httpfetch_thread->requestWakeUp();
+               g_httpfetch_thread->wait();
+               delete g_httpfetch_thread;
+       }
 
        curl_global_cleanup();
 }
@@ -761,7 +783,7 @@ void httpfetch_async(const HTTPFetchRequest &fetch_request)
                g_httpfetch_thread->start();
 }
 
-static void httpfetch_request_clear(unsigned long caller)
+static void httpfetch_request_clear(u64 caller)
 {
        if (g_httpfetch_thread->isRunning()) {
                Event event;
@@ -810,7 +832,7 @@ void httpfetch_async(const HTTPFetchRequest &fetch_request)
        httpfetch_deliver_result(fetch_result);
 }
 
-static void httpfetch_request_clear(unsigned long caller)
+static void httpfetch_request_clear(u64 caller)
 {
 }