X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fhttpfetch.cpp;h=16f0791c994c601f3be8b0f547cd7f63f12ccf88;hb=ec9f1575121e3b064b919bca7efddfa8b0fc4e65;hp=6b67e0e1323ee62c540f2b0131eb33f6d99faa18;hpb=87ad4d8e7f25210cd28d9f2b372aa00aa3dab929;p=minetest.git diff --git a/src/httpfetch.cpp b/src/httpfetch.cpp index 6b67e0e13..16f0791c9 100644 --- a/src/httpfetch.cpp +++ b/src/httpfetch.cpp @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include -#include +#include #include #include #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 > g_httpfetch_results; -PcgRandom g_callerid_randomness; +static std::mutex g_httpfetch_mutex; +static std::unordered_map> + 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 >::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 " < >::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,7 +241,6 @@ 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, 3); curl_easy_setopt(curl, CURLOPT_ENCODING, "gzip"); @@ -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::iterator @@ -746,10 +766,12 @@ void httpfetch_cleanup() { verbosestream<<"httpfetch_cleanup: cleaning up"<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) { }