From 4a330a4c3371c1d11935b5e72fbaf5e4e0c21df2 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 24 Jan 2018 11:13:04 +0100 Subject: [PATCH] input/qobuz: use class DelegateCurlResponseHandler to eliminate duplicate code --- src/input/plugins/QobuzErrorParser.cxx | 12 +--- src/input/plugins/QobuzErrorParser.hxx | 20 ++---- src/input/plugins/QobuzLoginRequest.cxx | 92 ++++++++++++++----------- src/input/plugins/QobuzLoginRequest.hxx | 49 +++---------- src/input/plugins/QobuzTrackRequest.cxx | 74 ++++++++++---------- src/input/plugins/QobuzTrackRequest.hxx | 34 +++------ 6 files changed, 120 insertions(+), 161 deletions(-) diff --git a/src/input/plugins/QobuzErrorParser.cxx b/src/input/plugins/QobuzErrorParser.cxx index 7298d1477..fb7cce509 100644 --- a/src/input/plugins/QobuzErrorParser.cxx +++ b/src/input/plugins/QobuzErrorParser.cxx @@ -40,24 +40,18 @@ static constexpr yajl_callbacks qobuz_error_parser_callbacks = { QobuzErrorParser::QobuzErrorParser(unsigned _status, const std::multimap &headers) - :status(_status), - parser(&qobuz_error_parser_callbacks, nullptr, this) + :YajlResponseParser(&qobuz_error_parser_callbacks, nullptr, this), + status(_status) { auto i = headers.find("content-type"); if (i == headers.end() || i->second.find("/json") == i->second.npos) throw FormatRuntimeError("Status %u from Qobuz", status); } -void -QobuzErrorParser::OnData(ConstBuffer data) -{ - parser.Parse((const unsigned char *)data.data, data.size); -} - void QobuzErrorParser::OnEnd() { - parser.CompleteParse(); + YajlResponseParser::OnEnd(); if (!message.empty()) throw FormatRuntimeError("Error from Qobuz: %s", diff --git a/src/input/plugins/QobuzErrorParser.hxx b/src/input/plugins/QobuzErrorParser.hxx index 4c1ac3422..35e3d8e7d 100644 --- a/src/input/plugins/QobuzErrorParser.hxx +++ b/src/input/plugins/QobuzErrorParser.hxx @@ -21,9 +21,8 @@ #define QOBUZ_ERROR_PARSER_HXX #include "check.h" -#include "lib/yajl/Handle.hxx" +#include "lib/yajl/ResponseParser.hxx" -#include #include #include @@ -33,11 +32,9 @@ struct StringView; /** * Parse an error JSON response. */ -class QobuzErrorParser { +class QobuzErrorParser final : public YajlResponseParser { const unsigned status; - Yajl::Handle parser; - enum class State { NONE, MESSAGE, @@ -53,16 +50,9 @@ public: QobuzErrorParser(unsigned status, const std::multimap &headers); - /** - * Feed response body data into the JSON parser. - */ - void OnData(ConstBuffer data); - - /** - * Throw an exception describing the error condition. Call - * this at the end of the response body. - */ - void OnEnd(); +protected: + /* virtual methods from CurlResponseParser */ + void OnEnd() override; public: /* yajl callbacks */ diff --git a/src/input/plugins/QobuzLoginRequest.cxx b/src/input/plugins/QobuzLoginRequest.cxx index 48c510bb4..2be918ccb 100644 --- a/src/input/plugins/QobuzLoginRequest.cxx +++ b/src/input/plugins/QobuzLoginRequest.cxx @@ -20,11 +20,12 @@ #include "config.h" #include "QobuzLoginRequest.hxx" #include "QobuzErrorParser.hxx" +#include "QobuzSession.hxx" #include "lib/curl/Form.hxx" #include "lib/yajl/Callbacks.hxx" #include "util/RuntimeError.hxx" -using Wrapper = Yajl::CallbacksWrapper; +using Wrapper = Yajl::CallbacksWrapper; static constexpr yajl_callbacks parse_callbacks = { nullptr, nullptr, @@ -39,6 +40,43 @@ static constexpr yajl_callbacks parse_callbacks = { nullptr, }; +class QobuzLoginRequest::ResponseParser final : public YajlResponseParser { + enum class State { + NONE, + DEVICE, + DEVICE_ID, + USER_AUTH_TOKEN, + } state = State::NONE; + + unsigned map_depth = 0; + + QobuzSession session; + +public: + explicit ResponseParser() noexcept + :YajlResponseParser(&parse_callbacks, nullptr, this) {} + + QobuzSession &&GetSession(); + + /* yajl callbacks */ + bool String(StringView value) noexcept; + bool StartMap() noexcept; + bool MapKey(StringView value) noexcept; + bool EndMap() noexcept; +}; + +inline QobuzSession && +QobuzLoginRequest::ResponseParser::GetSession() +{ + if (session.user_auth_token.empty()) + throw std::runtime_error("No user_auth_token in login response"); + + if (session.device_id.empty()) + throw std::runtime_error("No device id in login response"); + + return std::move(session); +} + static std::multimap MakeLoginForm(const char *app_id, const char *username, const char *email, @@ -95,50 +133,26 @@ QobuzLoginRequest::~QobuzLoginRequest() noexcept request.StopIndirect(); } -void -QobuzLoginRequest::OnHeaders(unsigned status, - std::multimap &&headers) +std::unique_ptr +QobuzLoginRequest::MakeParser(unsigned status, + std::multimap &&headers) { - if (status != 200) { - error_parser = std::make_unique(status, headers); - return; - } + if (status != 200) + return std::make_unique(status, headers); auto i = headers.find("content-type"); if (i == headers.end() || i->second.find("/json") == i->second.npos) throw std::runtime_error("Not a JSON response from Qobuz"); - parser = {&parse_callbacks, nullptr, this}; + return std::make_unique(); } void -QobuzLoginRequest::OnData(ConstBuffer data) +QobuzLoginRequest::FinishParser(std::unique_ptr p) { - if (error_parser) { - error_parser->OnData(data); - return; - } - - parser.Parse((const unsigned char *)data.data, data.size); -} - -void -QobuzLoginRequest::OnEnd() -{ - if (error_parser) { - error_parser->OnEnd(); - return; - } - - parser.CompleteParse(); - - if (session.user_auth_token.empty()) - throw std::runtime_error("No user_auth_token in login response"); - - if (session.device_id.empty()) - throw std::runtime_error("No device id in login response"); - - handler.OnQobuzLoginSuccess(std::move(session)); + assert(dynamic_cast(p.get()) != nullptr); + auto &rp = (ResponseParser &)*p; + handler.OnQobuzLoginSuccess(rp.GetSession()); } void @@ -148,7 +162,7 @@ QobuzLoginRequest::OnError(std::exception_ptr e) noexcept } inline bool -QobuzLoginRequest::String(StringView value) noexcept +QobuzLoginRequest::ResponseParser::String(StringView value) noexcept { switch (state) { case State::NONE: @@ -168,7 +182,7 @@ QobuzLoginRequest::String(StringView value) noexcept } inline bool -QobuzLoginRequest::StartMap() noexcept +QobuzLoginRequest::ResponseParser::StartMap() noexcept { switch (state) { case State::NONE: @@ -187,7 +201,7 @@ QobuzLoginRequest::StartMap() noexcept } inline bool -QobuzLoginRequest::MapKey(StringView value) noexcept +QobuzLoginRequest::ResponseParser::MapKey(StringView value) noexcept { switch (state) { case State::NONE: @@ -217,7 +231,7 @@ QobuzLoginRequest::MapKey(StringView value) noexcept } inline bool -QobuzLoginRequest::EndMap() noexcept +QobuzLoginRequest::ResponseParser::EndMap() noexcept { switch (state) { case State::NONE: diff --git a/src/input/plugins/QobuzLoginRequest.hxx b/src/input/plugins/QobuzLoginRequest.hxx index d0150c86d..8cc933c72 100644 --- a/src/input/plugins/QobuzLoginRequest.hxx +++ b/src/input/plugins/QobuzLoginRequest.hxx @@ -21,17 +21,11 @@ #define QOBUZ_LOGIN_REQUEST_HXX #include "check.h" -#include "QobuzSession.hxx" -#include "lib/curl/Handler.hxx" +#include "lib/curl/Delegate.hxx" #include "lib/curl/Request.hxx" -#include "lib/yajl/Handle.hxx" - -#include -#include -#include class CurlRequest; -class QobuzErrorParser; +struct QobuzSession; class QobuzLoginHandler { public: @@ -39,29 +33,14 @@ public: virtual void OnQobuzLoginError(std::exception_ptr error) noexcept = 0; }; -class QobuzLoginRequest final : CurlResponseHandler { +class QobuzLoginRequest final : DelegateCurlResponseHandler { CurlRequest request; - std::unique_ptr error_parser; - - Yajl::Handle parser; - - enum class State { - NONE, - DEVICE, - DEVICE_ID, - USER_AUTH_TOKEN, - } state = State::NONE; - - unsigned map_depth = 0; - - QobuzSession session; - - std::exception_ptr error; - QobuzLoginHandler &handler; public: + class ResponseParser; + QobuzLoginRequest(CurlGlobal &curl, const char *base_url, const char *app_id, const char *username, const char *email, @@ -76,19 +55,13 @@ public: } private: - /* virtual methods from CurlResponseHandler */ - void OnHeaders(unsigned status, - std::multimap &&headers) override; - void OnData(ConstBuffer data) override; - void OnEnd() override; - void OnError(std::exception_ptr e) noexcept override; + /* virtual methods from DelegateCurlResponseHandler */ + std::unique_ptr MakeParser(unsigned status, + std::multimap &&headers) override; + void FinishParser(std::unique_ptr p) override; -public: - /* yajl callbacks */ - bool String(StringView value) noexcept; - bool StartMap() noexcept; - bool MapKey(StringView value) noexcept; - bool EndMap() noexcept; + /* virtual methods from CurlResponseHandler */ + void OnError(std::exception_ptr e) noexcept override; }; #endif diff --git a/src/input/plugins/QobuzTrackRequest.cxx b/src/input/plugins/QobuzTrackRequest.cxx index 80f9660e2..535568532 100644 --- a/src/input/plugins/QobuzTrackRequest.cxx +++ b/src/input/plugins/QobuzTrackRequest.cxx @@ -24,7 +24,7 @@ #include "lib/yajl/Callbacks.hxx" #include "util/RuntimeError.hxx" -using Wrapper = Yajl::CallbacksWrapper; +using Wrapper = Yajl::CallbacksWrapper; static constexpr yajl_callbacks parse_callbacks = { nullptr, nullptr, @@ -39,6 +39,31 @@ static constexpr yajl_callbacks parse_callbacks = { nullptr, }; +class QobuzTrackRequest::ResponseParser final : public YajlResponseParser { + enum class State { + NONE, + URL, + } state = State::NONE; + + std::string url; + +public: + explicit ResponseParser() noexcept + :YajlResponseParser(&parse_callbacks, nullptr, this) {} + + std::string &&GetUrl() { + if (url.empty()) + throw std::runtime_error("No url in track response"); + + return std::move(url); + } + + /* yajl callbacks */ + bool String(StringView value) noexcept; + bool MapKey(StringView value) noexcept; + bool EndMap() noexcept; +}; + static std::string MakeTrackUrl(QobuzClient &client, const char *track_id) { @@ -68,47 +93,26 @@ QobuzTrackRequest::~QobuzTrackRequest() noexcept request.StopIndirect(); } -void -QobuzTrackRequest::OnHeaders(unsigned status, - std::multimap &&headers) +std::unique_ptr +QobuzTrackRequest::MakeParser(unsigned status, + std::multimap &&headers) { - if (status != 200) { - error_parser = std::make_unique(status, headers); - return; - } + if (status != 200) + return std::make_unique(status, headers); auto i = headers.find("content-type"); if (i == headers.end() || i->second.find("/json") == i->second.npos) throw std::runtime_error("Not a JSON response from Qobuz"); - parser = {&parse_callbacks, nullptr, this}; + return std::make_unique(); } void -QobuzTrackRequest::OnData(ConstBuffer data) +QobuzTrackRequest::FinishParser(std::unique_ptr p) { - if (error_parser) { - error_parser->OnData(data); - return; - } - - parser.Parse((const unsigned char *)data.data, data.size); -} - -void -QobuzTrackRequest::OnEnd() -{ - if (error_parser) { - error_parser->OnEnd(); - return; - } - - parser.CompleteParse(); - - if (url.empty()) - throw std::runtime_error("No url in track response"); - - handler.OnQobuzTrackSuccess(std::move(url)); + assert(dynamic_cast(p.get()) != nullptr); + auto &rp = (ResponseParser &)*p; + handler.OnQobuzTrackSuccess(rp.GetUrl()); } void @@ -118,7 +122,7 @@ QobuzTrackRequest::OnError(std::exception_ptr e) noexcept } inline bool -QobuzTrackRequest::String(StringView value) noexcept +QobuzTrackRequest::ResponseParser::String(StringView value) noexcept { switch (state) { case State::NONE: @@ -133,7 +137,7 @@ QobuzTrackRequest::String(StringView value) noexcept } inline bool -QobuzTrackRequest::MapKey(StringView value) noexcept +QobuzTrackRequest::ResponseParser::MapKey(StringView value) noexcept { if (value.Equals("url")) state = State::URL; @@ -144,7 +148,7 @@ QobuzTrackRequest::MapKey(StringView value) noexcept } inline bool -QobuzTrackRequest::EndMap() noexcept +QobuzTrackRequest::ResponseParser::EndMap() noexcept { state = State::NONE; diff --git a/src/input/plugins/QobuzTrackRequest.hxx b/src/input/plugins/QobuzTrackRequest.hxx index 95d0ad40c..6a9c2061b 100644 --- a/src/input/plugins/QobuzTrackRequest.hxx +++ b/src/input/plugins/QobuzTrackRequest.hxx @@ -21,17 +21,11 @@ #define QOBUZ_TRACK_REQUEST_HXX #include "check.h" -#include "lib/curl/Handler.hxx" +#include "lib/curl/Delegate.hxx" #include "lib/curl/Slist.hxx" #include "lib/curl/Request.hxx" -#include "lib/yajl/Handle.hxx" - -#include -#include -#include class QobuzClient; -class QobuzErrorParser; struct QobuzSession; class QobuzTrackHandler @@ -42,27 +36,16 @@ public: virtual void OnQobuzTrackError(std::exception_ptr error) noexcept = 0; }; -class QobuzTrackRequest final : CurlResponseHandler { +class QobuzTrackRequest final : DelegateCurlResponseHandler { CurlSlist request_headers; CurlRequest request; - std::unique_ptr error_parser; - - Yajl::Handle parser; - - enum class State { - NONE, - URL, - } state = State::NONE; - - std::string url; - - std::exception_ptr error; - QobuzTrackHandler &handler; public: + class ResponseParser; + QobuzTrackRequest(QobuzClient &client, const QobuzSession &session, const char *track_id, QobuzTrackHandler &_handler) noexcept; @@ -74,11 +57,12 @@ public: } private: + /* virtual methods from DelegateCurlResponseHandler */ + std::unique_ptr MakeParser(unsigned status, + std::multimap &&headers) override; + void FinishParser(std::unique_ptr p) override; + /* virtual methods from CurlResponseHandler */ - void OnHeaders(unsigned status, - std::multimap &&headers) override; - void OnData(ConstBuffer data) override; - void OnEnd() override; void OnError(std::exception_ptr e) noexcept override; public: