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/plugins/CurlInputPlugin.cxx src/input/plugins/CurlInputPlugin.hxx \
src/lib/curl/Easy.hxx \
src/lib/curl/Multi.hxx \
src/IcyMetaDataParser.cxx src/IcyMetaDataParser.hxx
endif

View File

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