input/curl: wrap CURLM* in new class CurlMulti

This commit is contained in:
Max Kellermann 2016-12-19 16:41:12 +01:00
parent 7063c423eb
commit fc83d38e67
3 changed files with 112 additions and 24 deletions

View File

@ -1277,6 +1277,7 @@ libinput_a_SOURCES += \
src/input/IcyInputStream.cxx src/input/IcyInputStream.hxx \ src/input/IcyInputStream.cxx src/input/IcyInputStream.hxx \
src/input/plugins/CurlInputPlugin.cxx src/input/plugins/CurlInputPlugin.hxx \ src/input/plugins/CurlInputPlugin.cxx src/input/plugins/CurlInputPlugin.hxx \
src/lib/curl/Easy.hxx \ src/lib/curl/Easy.hxx \
src/lib/curl/Multi.hxx \
src/IcyMetaDataParser.cxx src/IcyMetaDataParser.hxx src/IcyMetaDataParser.cxx src/IcyMetaDataParser.hxx
endif endif

View File

@ -20,6 +20,7 @@
#include "config.h" #include "config.h"
#include "CurlInputPlugin.hxx" #include "CurlInputPlugin.hxx"
#include "lib/curl/Easy.hxx" #include "lib/curl/Easy.hxx"
#include "lib/curl/Multi.hxx"
#include "../AsyncInputStream.hxx" #include "../AsyncInputStream.hxx"
#include "../IcyInputStream.hxx" #include "../IcyInputStream.hxx"
#include "../InputPlugin.hxx" #include "../InputPlugin.hxx"
@ -194,14 +195,10 @@ private:
* Manager for the global CURLM object. * Manager for the global CURLM object.
*/ */
class CurlGlobal final : private TimeoutMonitor { class CurlGlobal final : private TimeoutMonitor {
CURLM *const multi; CurlMulti multi;
public: public:
CurlGlobal(EventLoop &_loop, CURLM *_multi); explicit CurlGlobal(EventLoop &_loop);
~CurlGlobal() {
curl_multi_cleanup(multi);
}
void Add(CurlInputStream *c); void Add(CurlInputStream *c);
void Remove(CurlInputStream *c); void Remove(CurlInputStream *c);
@ -214,7 +211,7 @@ public:
void ReadInfo(); void ReadInfo();
void Assign(curl_socket_t fd, CurlSocket &cs) { void Assign(curl_socket_t fd, CurlSocket &cs) {
curl_multi_assign(multi, fd, &cs); curl_multi_assign(multi.Get(), fd, &cs);
} }
void SocketAction(curl_socket_t fd, int ev_bitmask); void SocketAction(curl_socket_t fd, int ev_bitmask);
@ -230,7 +227,7 @@ public:
*/ */
void ResumeSockets() { void ResumeSockets() {
int running_handles; int running_handles;
curl_multi_socket_all(multi, &running_handles); curl_multi_socket_all(multi.Get(), &running_handles);
} }
private: private:
@ -258,15 +255,14 @@ static CurlGlobal *curl_global;
static constexpr Domain curl_domain("curl"); static constexpr Domain curl_domain("curl");
static constexpr Domain curlm_domain("curlm"); static constexpr Domain curlm_domain("curlm");
CurlGlobal::CurlGlobal(EventLoop &_loop, CURLM *_mutex) CurlGlobal::CurlGlobal(EventLoop &_loop)
:TimeoutMonitor(_loop), multi(_mutex) :TimeoutMonitor(_loop)
{ {
curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, multi.SetOption(CURLMOPT_SOCKETFUNCTION, CurlSocket::SocketFunction);
CurlSocket::SocketFunction); multi.SetOption(CURLMOPT_SOCKETDATA, this);
curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, this);
curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, TimerFunction); multi.SetOption(CURLMOPT_TIMERFUNCTION, TimerFunction);
curl_multi_setopt(multi, CURLMOPT_TIMERDATA, this); multi.SetOption(CURLMOPT_TIMERDATA, this);
} }
/** /**
@ -366,7 +362,7 @@ CurlGlobal::Add(CurlInputStream *c)
assert(c != nullptr); assert(c != nullptr);
assert(c->easy); assert(c->easy);
CURLMcode mcode = curl_multi_add_handle(multi, c->easy.Get()); CURLMcode mcode = curl_multi_add_handle(multi.Get(), c->easy.Get());
if (mcode != CURLM_OK) if (mcode != CURLM_OK)
throw FormatRuntimeError("curl_multi_add_handle() failed: %s", throw FormatRuntimeError("curl_multi_add_handle() failed: %s",
curl_multi_strerror(mcode)); curl_multi_strerror(mcode));
@ -394,7 +390,7 @@ input_curl_easy_add_indirect(CurlInputStream *c)
inline void inline void
CurlGlobal::Remove(CurlInputStream *c) CurlGlobal::Remove(CurlInputStream *c)
{ {
curl_multi_remove_handle(multi, c->easy.Get()); curl_multi_remove_handle(multi.Get(), c->easy.Get());
} }
void void
@ -467,7 +463,7 @@ void
CurlGlobal::SocketAction(curl_socket_t fd, int ev_bitmask) CurlGlobal::SocketAction(curl_socket_t fd, int ev_bitmask)
{ {
int running_handles; int running_handles;
CURLMcode mcode = curl_multi_socket_action(multi, fd, ev_bitmask, CURLMcode mcode = curl_multi_socket_action(multi.Get(), fd, ev_bitmask,
&running_handles); &running_handles);
if (mcode != CURLM_OK) if (mcode != CURLM_OK)
FormatError(curlm_domain, FormatError(curlm_domain,
@ -490,7 +486,7 @@ CurlGlobal::ReadInfo()
CURLMsg *msg; CURLMsg *msg;
int msgs_in_queue; int msgs_in_queue;
while ((msg = curl_multi_info_read(multi, while ((msg = curl_multi_info_read(multi.Get(),
&msgs_in_queue)) != nullptr) { &msgs_in_queue)) != nullptr) {
if (msg->msg == CURLMSG_DONE) if (msg->msg == CURLMSG_DONE)
input_curl_handle_done(msg->easy_handle, msg->data.result); input_curl_handle_done(msg->easy_handle, msg->data.result);
@ -501,7 +497,7 @@ int
CurlGlobal::TimerFunction(gcc_unused CURLM *_global, long timeout_ms, void *userp) CurlGlobal::TimerFunction(gcc_unused CURLM *_global, long timeout_ms, void *userp)
{ {
auto &global = *(CurlGlobal *)userp; auto &global = *(CurlGlobal *)userp;
assert(_global == global.multi); assert(_global == global.multi.Get());
if (timeout_ms < 0) { if (timeout_ms < 0) {
global.Cancel(); global.Cancel();
@ -566,14 +562,14 @@ input_curl_init(const ConfigBlock &block)
verify_peer = block.GetBlockValue("verify_peer", true); verify_peer = block.GetBlockValue("verify_peer", true);
verify_host = block.GetBlockValue("verify_host", true); verify_host = block.GetBlockValue("verify_host", true);
CURLM *global = curl_multi_init(); try {
if (global == nullptr) { curl_global = new CurlGlobal(io_thread_get());
} catch (const std::runtime_error &e) {
LogError(e);
curl_slist_free_all(http_200_aliases); curl_slist_free_all(http_200_aliases);
curl_global_cleanup(); curl_global_cleanup();
throw PluginUnavailable("curl_multi_init() failed"); throw PluginUnavailable("curl_multi_init() failed");
} }
curl_global = new CurlGlobal(io_thread_get(), global);
} }
static void static void

91
src/lib/curl/Multi.hxx Normal file
View File

@ -0,0 +1,91 @@
/*
* Copyright (C) 2016 Max Kellermann <max@duempel.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CURL_MULTI_HXX
#define CURL_MULTI_HXX
#include <curl/curl.h>
#include <utility>
#include <stdexcept>
#include <cstddef>
/**
* An OO wrapper for a "CURLM*" (a libCURL "multi" handle).
*/
class CurlMulti {
CURLM *handle = nullptr;
public:
/**
* Allocate a new CURLM*.
*
* Throws std::runtime_error on error.
*/
CurlMulti()
:handle(curl_multi_init())
{
if (handle == nullptr)
throw std::runtime_error("curl_multi_init() failed");
}
/**
* Create an empty instance.
*/
CurlMulti(std::nullptr_t):handle(nullptr) {}
CurlMulti(CurlMulti &&src):handle(std::exchange(src.handle, nullptr)) {}
~CurlMulti() {
if (handle != nullptr)
curl_multi_cleanup(handle);
}
operator bool() const {
return handle != nullptr;
}
CurlMulti &operator=(CurlMulti &&src) {
std::swap(handle, src.handle);
return *this;
}
CURL *Get() {
return handle;
}
template<typename T>
void SetOption(CURLMoption option, T value) {
auto code = curl_multi_setopt(handle, option, value);
if (code != CURLM_OK)
throw std::runtime_error(curl_multi_strerror(code));
}
};
#endif