mirror of
https://github.com/clangen/musikcube.git
synced 2024-10-02 04:52:32 +00:00
Initial version of a simple wrapper around the libcurl "simple"
interface to make it a bit nicer to use. Nothing too exotic.
This commit is contained in:
parent
c3a1a26130
commit
a4c0056a07
@ -160,6 +160,7 @@
|
||||
<ClInclude Include="debug.h" />
|
||||
<ClInclude Include="i18n\Locale.h" />
|
||||
<ClInclude Include="io\DataStreamFactory.h" />
|
||||
<ClInclude Include="io\HttpClient.h" />
|
||||
<ClInclude Include="io\LocalFileStream.h" />
|
||||
<ClInclude Include="library\IIndexer.h" />
|
||||
<ClInclude Include="library\ILibrary.h" />
|
||||
|
@ -534,5 +534,8 @@
|
||||
<ClInclude Include="library\query\local\PersistedPlayQueueQuery.h">
|
||||
<Filter>src\library\query\local</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="io\HttpClient.h">
|
||||
<Filter>src\io</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
302
src/core/io/HttpClient.h
Normal file
302
src/core/io/HttpClient.h
Normal file
@ -0,0 +1,302 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2007-2017 musikcube team
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// 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 COPYRIGHT OWNER 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.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <core/version.h>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace musik { namespace core { namespace io {
|
||||
template <typename T>
|
||||
class HttpClient {
|
||||
public:
|
||||
using HttpHeaders = std::unordered_map<std::string, std::string>;
|
||||
using Callback = std::function<void(HttpClient<T>* caller, int, CURLcode)>;
|
||||
using DecoratorCallback = std::function<void(CURL*)>;
|
||||
using HeaderCallback = std::function<void(std::string, std::string)>;
|
||||
using CanceledCallback = std::function<void(HttpClient<T>* caller)>;
|
||||
|
||||
static std::unique_ptr<HttpClient<T>> Create(T&& stream) {
|
||||
return std::unique_ptr<HttpClient<T>>(new HttpClient<T>(std::move(stream)));
|
||||
}
|
||||
|
||||
~HttpClient();
|
||||
|
||||
HttpClient<T>& Url(const std::string& url);
|
||||
HttpClient<T>& Header(const std::string& key, const std::string& value);
|
||||
HttpClient<T>& Headers(HeaderCallback headersCb);
|
||||
HttpClient<T>& Decorator(DecoratorCallback decoratorCb);
|
||||
HttpClient<T>& Canceled(CanceledCallback canceledCb);
|
||||
|
||||
const T& Stream() {
|
||||
return this->ostream;
|
||||
}
|
||||
|
||||
const HttpHeaders& ResponseHeaders() {
|
||||
return this->responseHeaders;
|
||||
}
|
||||
|
||||
const HttpHeaders& RequestHeaders() {
|
||||
return this->requestHeaders;
|
||||
}
|
||||
|
||||
HttpClient<T>& Run(Callback callback = Callback());
|
||||
void Wait();
|
||||
void Cancel();
|
||||
|
||||
private:
|
||||
HttpClient(T&& stream);
|
||||
|
||||
static size_t CurlWriteCallback(char *ptr, size_t size, size_t nmemb, void *userdata);
|
||||
static int CurlTransferCallback(void *ptr, curl_off_t downTotal, curl_off_t downNow, curl_off_t upTotal, curl_off_t upNow);
|
||||
static size_t CurlHeaderCallback(char *buffer, size_t size, size_t nitems, void *userdata);
|
||||
static std::string DefaultUserAgent();
|
||||
|
||||
std::recursive_mutex mutex;
|
||||
std::shared_ptr<std::thread> thread;
|
||||
|
||||
T ostream;
|
||||
std::string url;
|
||||
HttpHeaders requestHeaders, responseHeaders;
|
||||
HeaderCallback headersCb;
|
||||
DecoratorCallback decoratorCb;
|
||||
CanceledCallback canceledCallback;
|
||||
bool cancel;
|
||||
CURL* curl;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::string HttpClient<T>::DefaultUserAgent() {
|
||||
#ifdef WIN32
|
||||
static const std::string PLATFORM = "win32";
|
||||
#elif defined __APPLE__
|
||||
static const std::string PLATFORM = "macos";
|
||||
#else
|
||||
static const std::string PLATFORM = "linux";
|
||||
#endif
|
||||
|
||||
std::string version = boost::str(boost::format("%d.%d.%d")
|
||||
% VERSION_MAJOR % VERSION_MINOR % VERSION_PATCH);
|
||||
|
||||
return boost::str(boost::format("musikcube %s (%s)") % version % PLATFORM);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t HttpClient<T>::CurlWriteCallback(char *ptr, size_t size, size_t nmemb, void *userdata) {
|
||||
if (ptr && userdata) {
|
||||
HttpClient* context = static_cast<HttpClient*>(userdata);
|
||||
if (context->cancel) {
|
||||
return 0; /* aborts */
|
||||
}
|
||||
context->ostream.write(ptr, size * nmemb);
|
||||
}
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int HttpClient<T>::CurlTransferCallback(
|
||||
void *ptr, curl_off_t downTotal, curl_off_t downNow, curl_off_t upTotal, curl_off_t upNow)
|
||||
{
|
||||
HttpClient* context = static_cast<HttpClient*>(ptr);
|
||||
if (context->cancel) {
|
||||
return -1; /* kill the stream */
|
||||
}
|
||||
return 0; /* ok! */
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t HttpClient<T>::CurlHeaderCallback(char *buffer, size_t size, size_t nitems, void *userdata) {
|
||||
HttpClient* stream = static_cast<HttpClient*>(userdata);
|
||||
|
||||
std::string header(buffer, size * nitems);
|
||||
|
||||
boost::algorithm::replace_all(header, "\r\n", "");
|
||||
|
||||
size_t splitAt = header.find_first_of(":");
|
||||
if (splitAt != std::string::npos) {
|
||||
std::string key = boost::trim_copy(header.substr(0, splitAt));
|
||||
std::string value = boost::trim_copy(header.substr(splitAt + 1));
|
||||
stream->responseHeaders[key] = value;
|
||||
|
||||
if (stream->headersCb) {
|
||||
stream->headersCb(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
return size * nitems;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
HttpClient<T>::HttpClient(T&& stream) {
|
||||
this->curl = nullptr;
|
||||
this->cancel = false;
|
||||
std::swap(this->ostream, stream);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
HttpClient<T>::~HttpClient() {
|
||||
std::unique_lock<std::recursive_mutex> lock(this->mutex);
|
||||
|
||||
if (this->curl) {
|
||||
curl_easy_cleanup(this->curl);
|
||||
}
|
||||
|
||||
if (this->thread && this->thread->joinable()) {
|
||||
this->cancel = true;
|
||||
this->thread->join();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
HttpClient<T>& HttpClient<T>::Run(Callback callback) {
|
||||
std::unique_lock<std::recursive_mutex> lock(this->mutex);
|
||||
|
||||
if (this->thread) {
|
||||
throw std::runtime_error("already started");
|
||||
}
|
||||
|
||||
this->curl = curl_easy_init();
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_HEADER, 0);
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_AUTOREFERER, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, DefaultUserAgent().c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
|
||||
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3000);
|
||||
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 7500);
|
||||
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 500);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &CurlWriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, this);
|
||||
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, &CurlTransferCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_HEADERDATA, this);
|
||||
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, &CurlHeaderCallback);
|
||||
|
||||
if (this->decoratorCb) {
|
||||
this->decoratorCb(this->curl);
|
||||
}
|
||||
|
||||
if (this->requestHeaders.size()) {
|
||||
struct curl_slist* slist = nullptr;
|
||||
for (auto it : this->requestHeaders) {
|
||||
std::string header = it.first + ": " + it.second;
|
||||
slist = curl_slist_append(slist, header.c_str());
|
||||
}
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
|
||||
}
|
||||
|
||||
this->thread.reset(new std::thread([callback, this] {
|
||||
if (this->cancel) {
|
||||
if (this->canceledCallback) {
|
||||
this->canceledCallback(this);
|
||||
}
|
||||
}
|
||||
else {
|
||||
CURLcode curlCode = curl_easy_perform(this->curl);
|
||||
int httpStatus = 0;
|
||||
curl_easy_getinfo(this->curl, CURLINFO_RESPONSE_CODE, &httpStatus);
|
||||
|
||||
if (callback) {
|
||||
callback(this, httpStatus, curlCode);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void HttpClient<T>::Wait() {
|
||||
std::unique_lock<std::recursive_mutex> lock(this->mutex);
|
||||
|
||||
if (this->thread && this->thread->joinable()) {
|
||||
this->thread->join();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
HttpClient<T>& HttpClient<T>::Url(const std::string& url) {
|
||||
this->url = url;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
HttpClient<T>& HttpClient<T>::Header(const std::string& key, const std::string& value) {
|
||||
this->requestHeaders[key] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
HttpClient<T>& HttpClient<T>::Headers(HeaderCallback headersCb) {
|
||||
this->headersCb = headersCb;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
HttpClient<T>& HttpClient<T>::Decorator(DecoratorCallback decoratorCb) {
|
||||
this->decoratorCb = decoratorCb;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
HttpClient<T>& HttpClient<T>::Canceled(CanceledCallback canceledCb) {
|
||||
this->canceledCb = canceledCb;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void HttpClient<T>::Cancel() {
|
||||
std::unique_lock<std::recursive_mutex> lock(this->mutex);
|
||||
|
||||
if (this->thread) {
|
||||
this->cancel = true;
|
||||
if (this->thread->joinable()) {
|
||||
this->thread->join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T& Stream() {
|
||||
return this->ostream;
|
||||
}
|
||||
} } }
|
@ -43,9 +43,9 @@
|
||||
|
||||
#include <core/sdk/IPlugin.h>
|
||||
#include <core/plugin/PluginFactory.h>
|
||||
#include <core/version.h>
|
||||
|
||||
#include <app/util/Hotkeys.h>
|
||||
#include <app/util/Version.h>
|
||||
#include <app/util/Messages.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
@ -41,10 +41,10 @@
|
||||
#include <cursespp/DialogOverlay.h>
|
||||
|
||||
#include <core/runtime/Message.h>
|
||||
#include <core/version.h>
|
||||
|
||||
#include <app/util/Messages.h>
|
||||
#include <app/util/PreferenceKeys.h>
|
||||
#include <app/util/Version.h>
|
||||
|
||||
using namespace nlohmann;
|
||||
using namespace musik::cube;
|
||||
|
@ -228,7 +228,6 @@ xcopy "$(SolutionDir)src\3rdparty\bin\win32\font\*.ttf" "$(TargetDir)fonts\" /Y
|
||||
<ClInclude Include="app\util\Playback.h" />
|
||||
<ClInclude Include="app\util\PreferenceKeys.h" />
|
||||
<ClInclude Include="app\util\UpdateCheck.h" />
|
||||
<ClInclude Include="app\util\Version.h" />
|
||||
<ClInclude Include="app\window\CategoryListView.h" />
|
||||
<ClInclude Include="app\window\LogWindow.h" />
|
||||
<ClInclude Include="app\window\TrackListView.h" />
|
||||
|
@ -246,9 +246,6 @@
|
||||
<ClInclude Include="cursespp\IOverlay.h">
|
||||
<Filter>cursespp</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="app\util\Version.h">
|
||||
<Filter>app\util</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="app\overlay\VisualizerOverlay.h">
|
||||
<Filter>app\overlay</Filter>
|
||||
</ClInclude>
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include <boost/locale.hpp>
|
||||
#include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
|
||||
|
||||
#include "../musikcube/app/util/Version.h"
|
||||
#include "../core/version.h"
|
||||
|
||||
using namespace musik;
|
||||
using namespace musik::core;
|
||||
|
Loading…
Reference in New Issue
Block a user