#include "noise.h"
std::mutex g_httpfetch_mutex;
-std::map<unsigned long, std::queue<HTTPFetchResult>> g_httpfetch_results;
+std::map<unsigned long, std::queue<HTTPFetchResult> > g_httpfetch_results;
PcgRandom g_callerid_randomness;
HTTPFetchRequest::HTTPFetchRequest() :
- timeout(g_settings->getS32("curl_timeout")), connect_timeout(timeout),
- useragent(std::string(PROJECT_NAME_C "/") + g_version_hash + " (" +
- porting::get_sysinfo() + ")")
+ timeout(g_settings->getS32("curl_timeout")),
+ connect_timeout(timeout),
+ useragent(std::string(PROJECT_NAME_C "/") + g_version_hash + " (" + porting::get_sysinfo() + ")")
{
}
+
static void httpfetch_deliver_result(const HTTPFetchResult &fetch_result)
{
unsigned long caller = fetch_result.caller;
// 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);
+ std::map<unsigned long, std::queue<HTTPFetchResult> >::iterator
+ it = g_httpfetch_results.find(caller);
if (it == g_httpfetch_results.end()) {
- verbosestream << "httpfetch_caller_alloc: allocating " << caller
- << std::endl;
+ verbosestream << "httpfetch_caller_alloc: allocating "
+ << caller << std::endl;
// Access element to create it
g_httpfetch_results[caller];
return caller;
unsigned long caller;
do {
- caller = (((u64)g_callerid_randomness.next()) << 32) |
- g_callerid_randomness.next();
+ caller = (((u64) g_callerid_randomness.next()) << 32) |
+ g_callerid_randomness.next();
if (--tries < 1) {
- FATAL_ERROR("httpfetch_caller_alloc_secure: ran out of caller "
- "IDs");
+ FATAL_ERROR("httpfetch_caller_alloc_secure: ran out of caller IDs");
return HTTPFETCH_DISCARD;
}
} while (g_httpfetch_results.find(caller) != g_httpfetch_results.end());
- verbosestream << "httpfetch_caller_alloc_secure: allocating " << caller
- << std::endl;
+ verbosestream << "httpfetch_caller_alloc_secure: allocating "
+ << caller << std::endl;
// Access element to create it
g_httpfetch_results[caller];
void httpfetch_caller_free(unsigned long caller)
{
- verbosestream << "httpfetch_caller_free: freeing " << caller << std::endl;
+ verbosestream<<"httpfetch_caller_free: freeing "
+ <<caller<<std::endl;
httpfetch_request_clear(caller);
if (caller != HTTPFETCH_DISCARD) {
MutexAutoLock lock(g_httpfetch_mutex);
// Check that caller exists
- std::map<unsigned long, std::queue<HTTPFetchResult>>::iterator it =
- g_httpfetch_results.find(caller);
+ std::map<unsigned long, std::queue<HTTPFetchResult> >::iterator
+ it = g_httpfetch_results.find(caller);
if (it == g_httpfetch_results.end())
return false;
static size_t httpfetch_writefunction(
char *ptr, size_t size, size_t nmemb, void *userdata)
{
- std::ostringstream *stream = (std::ostringstream *)userdata;
+ std::ostringstream *stream = (std::ostringstream*)userdata;
size_t count = size * nmemb;
stream->write(ptr, count);
return count;
class CurlHandlePool
{
- std::list<CURL *> handles;
+ std::list<CURL*> handles;
public:
CurlHandlePool() = default;
~CurlHandlePool()
{
- for (std::list<CURL *>::iterator it = handles.begin();
+ for (std::list<CURL*>::iterator it = handles.begin();
it != handles.end(); ++it) {
curl_easy_cleanup(*it);
}
}
- CURL *alloc()
+ CURL * alloc()
{
CURL *curl;
if (handles.empty()) {
curl = curl_easy_init();
if (curl == NULL) {
- errorstream << "curl_easy_init returned NULL"
- << std::endl;
+ errorstream<<"curl_easy_init returned NULL"<<std::endl;
}
- } else {
+ }
+ else {
curl = handles.front();
handles.pop_front();
}
~HTTPFetchOngoing();
CURLcode start(CURLM *multi);
- const HTTPFetchResult *complete(CURLcode res);
+ const HTTPFetchResult * complete(CURLcode res);
- const HTTPFetchRequest &getRequest() const { return request; };
- const CURL *getEasyHandle() const { return curl; };
+ const HTTPFetchRequest &getRequest() const { return request; };
+ const CURL *getEasyHandle() const { return curl; };
private:
CurlHandlePool *pool;
curl_httppost *post;
};
-HTTPFetchOngoing::HTTPFetchOngoing(
- const HTTPFetchRequest &request_, CurlHandlePool *pool_) :
- pool(pool_),
- curl(NULL), multi(NULL), request(request_), result(request_),
- oss(std::ios::binary), http_header(NULL), post(NULL)
+
+HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_,
+ CurlHandlePool *pool_):
+ pool(pool_),
+ curl(NULL),
+ multi(NULL),
+ request(request_),
+ result(request_),
+ oss(std::ios::binary),
+ http_header(NULL),
+ post(NULL)
{
curl = pool->alloc();
if (curl == NULL) {
// other protocols don't affect us.
// These settings were introduced in curl 7.19.4.
long protocols =
- CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | CURLPROTO_FTPS;
+ CURLPROTO_HTTP |
+ CURLPROTO_HTTPS |
+ CURLPROTO_FTP |
+ CURLPROTO_FTPS;
curl_easy_setopt(curl, CURLOPT_PROTOCOLS, protocols);
curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, protocols);
#endif
// Set cURL options based on HTTPFetchRequest
- curl_easy_setopt(curl, CURLOPT_URL, request.url.c_str());
- curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, request.timeout);
- curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, request.connect_timeout);
+ curl_easy_setopt(curl, CURLOPT_URL,
+ request.url.c_str());
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS,
+ request.timeout);
+ curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS,
+ request.connect_timeout);
if (!request.useragent.empty())
curl_easy_setopt(curl, CURLOPT_USERAGENT, request.useragent.c_str());
// ostringstream ongoing->oss, unless the data
// is to be discarded
if (request.caller == HTTPFETCH_DISCARD) {
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, httpfetch_discardfunction);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
+ httpfetch_discardfunction);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
} else {
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, httpfetch_writefunction);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
+ httpfetch_writefunction);
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) {
- curl_formadd(&post, &last, CURLFORM_NAMELENGTH, it->first.size(),
+ 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(),
CURLFORM_CONTENTSLENGTH, it->second.size(),
CURLFORM_PTRCONTENTS, it->second.c_str(),
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) {
CURLMcode mres = curl_multi_add_handle(multi_, curl);
if (mres != CURLM_OK) {
errorstream << "curl_multi_add_handle"
- << " returned error code " << mres << std::endl;
+ << " returned error code " << mres
+ << std::endl;
return CURLE_FAILED_INIT;
}
multi = multi_; // store for curl_multi_remove_handle
return CURLE_OK;
}
-const HTTPFetchResult *HTTPFetchOngoing::complete(CURLcode res)
+const HTTPFetchResult * HTTPFetchOngoing::complete(CURLcode res)
{
result.succeeded = (res == CURLE_OK);
result.timeout = (res == CURLE_OPERATION_TIMEDOUT);
// Get HTTP/FTP response code
result.response_code = 0;
if (curl && (curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE,
- &result.response_code) != CURLE_OK)) {
+ &result.response_code) != CURLE_OK)) {
// We failed to get a return code, make sure it is still 0
result.response_code = 0;
}
if (res != CURLE_OK) {
- errorstream << request.url << " not found (" << curl_easy_strerror(res)
- << ")"
- << " (response code " << result.response_code << ")"
- << std::endl;
+ errorstream << request.url << " not found ("
+ << curl_easy_strerror(res) << ")"
+ << " (response code " << result.response_code << ")"
+ << std::endl;
}
return &result;
CURLMcode mres = curl_multi_remove_handle(multi, curl);
if (mres != CURLM_OK) {
errorstream << "curl_multi_remove_handle"
- << " returned error code " << mres << std::endl;
+ << " returned error code " << mres
+ << std::endl;
}
}
// Set safe options for the reusable cURL handle
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, httpfetch_discardfunction);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
+ httpfetch_discardfunction);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL);
if (http_header) {
pool->free(curl);
}
+
class CurlFetchThread : public Thread
{
protected:
- enum RequestType
- {
+ enum RequestType {
RT_FETCH,
RT_CLEAR,
RT_WAKEUP,
};
- struct Request
- {
+ struct Request {
RequestType type;
HTTPFetchRequest fetch_request;
Event *event;
size_t m_parallel_limit;
// Variables exclusively used within thread
- std::vector<HTTPFetchOngoing *> m_all_ongoing;
+ std::vector<HTTPFetchOngoing*> m_all_ongoing;
std::list<HTTPFetchRequest> m_queued_fetches;
public:
- CurlFetchThread(int parallel_limit) : Thread("CurlFetch")
+ CurlFetchThread(int parallel_limit) :
+ Thread("CurlFetch")
{
if (parallel_limit >= 1)
m_parallel_limit = parallel_limit;
// see processQueued() for what happens next
- } else if (req.type == RT_CLEAR) {
+ }
+ else if (req.type == RT_CLEAR) {
unsigned long caller = req.fetch_request.caller;
// Abort all ongoing fetches for the caller
- for (std::vector<HTTPFetchOngoing *>::iterator it =
- m_all_ongoing.begin();
+ for (std::vector<HTTPFetchOngoing*>::iterator
+ it = m_all_ongoing.begin();
it != m_all_ongoing.end();) {
if ((*it)->getRequest().caller == caller) {
delete (*it);
}
// Also abort all queued fetches for the caller
- for (std::list<HTTPFetchRequest>::iterator it =
- m_queued_fetches.begin();
+ for (std::list<HTTPFetchRequest>::iterator
+ it = m_queued_fetches.begin();
it != m_queued_fetches.end();) {
if ((*it).caller == caller)
it = m_queued_fetches.erase(it);
else
++it;
}
- } else if (req.type == RT_WAKEUP) {
+ }
+ else if (req.type == RT_WAKEUP) {
// Wakeup: Nothing to do, thread is awake at this point
}
// Create ongoing fetch data and make a cURL handle
// Set cURL options based on HTTPFetchRequest
- HTTPFetchOngoing *ongoing = new HTTPFetchOngoing(request, pool);
+ HTTPFetchOngoing *ongoing =
+ new HTTPFetchOngoing(request, pool);
// Initiate the connection (curl_multi_add_handle)
CURLcode res = ongoing->start(m_multi);
if (res == CURLE_OK) {
m_all_ongoing.push_back(ongoing);
- } else {
+ }
+ else {
httpfetch_deliver_result(*ongoing->complete(res));
delete ongoing;
}
try {
Request req = m_requests.pop_front(timeout);
processRequest(req);
- } catch (ItemNotFoundException &e) {
}
+ catch (ItemNotFoundException &e) {}
}
}
FD_ZERO(&write_fd_set);
FD_ZERO(&exc_fd_set);
- mres = curl_multi_fdset(m_multi, &read_fd_set, &write_fd_set, &exc_fd_set,
- &max_fd);
+ mres = curl_multi_fdset(m_multi, &read_fd_set,
+ &write_fd_set, &exc_fd_set, &max_fd);
if (mres != CURLM_OK) {
- errorstream << "curl_multi_fdset"
- << " returned error code " << mres << std::endl;
+ errorstream<<"curl_multi_fdset"
+ <<" returned error code "<<mres
+ <<std::endl;
select_timeout = 0;
}
mres = curl_multi_timeout(m_multi, &select_timeout);
if (mres != CURLM_OK) {
- errorstream << "curl_multi_timeout"
- << " returned error code " << mres << std::endl;
+ errorstream<<"curl_multi_timeout"
+ <<" returned error code "<<mres
+ <<std::endl;
select_timeout = 0;
}
select_tv.tv_sec = select_timeout / 1000;
select_tv.tv_usec = (select_timeout % 1000) * 1000;
int retval = select(max_fd + 1, &read_fd_set,
- &write_fd_set, &exc_fd_set, &select_tv);
+ &write_fd_set, &exc_fd_set,
+ &select_tv);
if (retval == -1) {
-#ifdef _WIN32
- errorstream << "select returned error code "
- << WSAGetLastError() << std::endl;
-#else
- errorstream << "select returned error code "
- << errno << std::endl;
-#endif
+ #ifdef _WIN32
+ errorstream<<"select returned error code "
+ <<WSAGetLastError()<<std::endl;
+ #else
+ errorstream<<"select returned error code "
+ <<errno<<std::endl;
+ #endif
}
- } else {
+ }
+ else {
sleep_ms(select_timeout);
}
}
m_multi = curl_multi_init();
if (m_multi == NULL) {
- errorstream << "curl_multi_init returned NULL\n";
+ errorstream<<"curl_multi_init returned NULL\n";
return NULL;
}
/*
Handle completed async requests
*/
- if (still_ongoing < (int)m_all_ongoing.size()) {
+ if (still_ongoing < (int) m_all_ongoing.size()) {
CURLMsg *msg;
int msgs_in_queue;
msg = curl_multi_info_read(m_multi, &msgs_in_queue);
while (msg != NULL) {
processCurlMessage(msg);
- msg = curl_multi_info_read(
- m_multi, &msgs_in_queue);
+ msg = curl_multi_info_read(m_multi, &msgs_in_queue);
}
}
CURLMcode mres = curl_multi_cleanup(m_multi);
if (mres != CURLM_OK) {
- errorstream << "curl_multi_cleanup"
- << " returned error code " << mres << std::endl;
+ errorstream<<"curl_multi_cleanup"
+ <<" returned error code "<<mres
+ <<std::endl;
}
return NULL;
void httpfetch_init(int parallel_limit)
{
- verbosestream << "httpfetch_init: parallel_limit=" << parallel_limit << std::endl;
+ verbosestream<<"httpfetch_init: parallel_limit="<<parallel_limit
+ <<std::endl;
CURLcode res = curl_global_init(CURL_GLOBAL_DEFAULT);
FATAL_ERROR_IF(res != CURLE_OK, "CURL init failed");
void httpfetch_cleanup()
{
- verbosestream << "httpfetch_cleanup: cleaning up" << std::endl;
+ verbosestream<<"httpfetch_cleanup: cleaning up"<<std::endl;
g_httpfetch_thread->stop();
g_httpfetch_thread->requestWakeUp();
}
}
-void httpfetch_sync(const HTTPFetchRequest &fetch_request, HTTPFetchResult &fetch_result)
+void httpfetch_sync(const HTTPFetchRequest &fetch_request,
+ HTTPFetchResult &fetch_result)
{
// Create ongoing fetch data and make a cURL handle
// Set cURL options based on HTTPFetchRequest
fetch_result = *ongoing.complete(res);
}
-#else // USE_CURL
+#else // USE_CURL
/*
USE_CURL is off:
void httpfetch_async(const HTTPFetchRequest &fetch_request)
{
errorstream << "httpfetch_async: unable to fetch " << fetch_request.url
- << " because USE_CURL=0" << std::endl;
+ << " because USE_CURL=0" << std::endl;
HTTPFetchResult fetch_result(fetch_request); // sets succeeded = false etc.
httpfetch_deliver_result(fetch_result);
{
}
-void httpfetch_sync(const HTTPFetchRequest &fetch_request, HTTPFetchResult &fetch_result)
+void httpfetch_sync(const HTTPFetchRequest &fetch_request,
+ HTTPFetchResult &fetch_result)
{
errorstream << "httpfetch_sync: unable to fetch " << fetch_request.url
- << " because USE_CURL=0" << std::endl;
+ << " because USE_CURL=0" << std::endl;
fetch_result = HTTPFetchResult(fetch_request); // sets succeeded = false etc.
}
-#endif // USE_CURL
+#endif // USE_CURL