summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMax Kellermann <max@musicpd.org>2018-01-21 12:17:18 +0100
committerMax Kellermann <max@musicpd.org>2018-01-21 12:30:46 +0100
commit570c6765b07f39dd744fc101f44db4f5862e76f4 (patch)
tree4652340eb7dcfe3159dfe7fedd04f457356e1fb9 /src
parent12a2285ba52efef375a8335f232c1fedd4453489 (diff)
input/tidal: parse and report userMessage from error responses
Diffstat (limited to 'src')
-rw-r--r--src/input/plugins/TidalErrorParser.cxx101
-rw-r--r--src/input/plugins/TidalErrorParser.hxx74
-rw-r--r--src/input/plugins/TidalLoginRequest.cxx17
-rw-r--r--src/input/plugins/TidalLoginRequest.hxx4
-rw-r--r--src/input/plugins/TidalTrackRequest.cxx17
-rw-r--r--src/input/plugins/TidalTrackRequest.hxx4
6 files changed, 213 insertions, 4 deletions
diff --git a/src/input/plugins/TidalErrorParser.cxx b/src/input/plugins/TidalErrorParser.cxx
new file mode 100644
index 000000000..71d523660
--- /dev/null
+++ b/src/input/plugins/TidalErrorParser.cxx
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2003-2018 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "TidalErrorParser.hxx"
+#include "lib/yajl/Callbacks.hxx"
+#include "util/ConstBuffer.hxx"
+#include "util/RuntimeError.hxx"
+
+using Wrapper = Yajl::CallbacksWrapper<TidalErrorParser>;
+static constexpr yajl_callbacks tidal_error_parser_callbacks = {
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ Wrapper::String,
+ nullptr,
+ Wrapper::MapKey,
+ Wrapper::EndMap,
+ nullptr,
+ nullptr,
+};
+
+TidalErrorParser::TidalErrorParser(unsigned _status,
+ const std::multimap<std::string, std::string> &headers)
+ :status(_status),
+ parser(&tidal_error_parser_callbacks, nullptr, this)
+{
+ auto i = headers.find("content-type");
+ if (i == headers.end() || i->second.find("/json") == i->second.npos)
+ throw FormatRuntimeError("Status %u from Tidal", status);
+}
+
+void
+TidalErrorParser::OnData(ConstBuffer<void> data)
+{
+ parser.Parse((const unsigned char *)data.data, data.size);
+}
+
+void
+TidalErrorParser::OnEnd()
+{
+ parser.CompleteParse();
+
+ if (!message.empty())
+ throw FormatRuntimeError("Error from Tidal: %s",
+ message.c_str());
+ else
+ throw FormatRuntimeError("Status %u from Tidal", status);
+}
+
+inline bool
+TidalErrorParser::String(StringView value) noexcept
+{
+ switch (state) {
+ case State::NONE:
+ break;
+
+ case State::USER_MESSAGE:
+ message.assign(value.data, value.size);
+ break;
+ }
+
+ return true;
+}
+
+inline bool
+TidalErrorParser::MapKey(StringView value) noexcept
+{
+ if (value.Equals("userMessage"))
+ state = State::USER_MESSAGE;
+ else
+ state = State::NONE;
+
+ return true;
+}
+
+inline bool
+TidalErrorParser::EndMap() noexcept
+{
+ state = State::NONE;
+
+ return true;
+}
diff --git a/src/input/plugins/TidalErrorParser.hxx b/src/input/plugins/TidalErrorParser.hxx
new file mode 100644
index 000000000..0ccf2fe8a
--- /dev/null
+++ b/src/input/plugins/TidalErrorParser.hxx
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2003-2018 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TIDAL_ERROR_PARSER_HXX
+#define TIDAL_ERROR_PARSER_HXX
+
+#include "check.h"
+#include "lib/yajl/Handle.hxx"
+
+#include <exception>
+#include <string>
+#include <map>
+
+template<typename T> struct ConstBuffer;
+struct StringView;
+
+/**
+ * Parse an error JSON response.
+ */
+class TidalErrorParser {
+ const unsigned status;
+
+ Yajl::Handle parser;
+
+ enum class State {
+ NONE,
+ USER_MESSAGE,
+ } state = State::NONE;
+
+ std::string message;
+
+public:
+ /**
+ * May throw if there is a formal error in the response
+ * headers.
+ */
+ TidalErrorParser(unsigned status,
+ const std::multimap<std::string, std::string> &headers);
+
+ /**
+ * Feed response body data into the JSON parser.
+ */
+ void OnData(ConstBuffer<void> data);
+
+ /**
+ * Throw an exception describing the error condition. Call
+ * this at the end of the response body.
+ */
+ void OnEnd();
+
+public:
+ /* yajl callbacks */
+ bool String(StringView value) noexcept;
+ bool MapKey(StringView value) noexcept;
+ bool EndMap() noexcept;
+};
+
+#endif
diff --git a/src/input/plugins/TidalLoginRequest.cxx b/src/input/plugins/TidalLoginRequest.cxx
index 71e4ce7e6..e378ef9ad 100644
--- a/src/input/plugins/TidalLoginRequest.cxx
+++ b/src/input/plugins/TidalLoginRequest.cxx
@@ -19,6 +19,7 @@
#include "config.h"
#include "TidalLoginRequest.hxx"
+#include "TidalErrorParser.hxx"
#include "lib/curl/Form.hxx"
#include "lib/yajl/Callbacks.hxx"
#include "util/RuntimeError.hxx"
@@ -69,8 +70,10 @@ void
TidalLoginRequest::OnHeaders(unsigned status,
std::multimap<std::string, std::string> &&headers)
{
- if (status != 200)
- throw FormatRuntimeError("Status %u from Tidal", status);
+ if (status != 200) {
+ error_parser = std::make_unique<TidalErrorParser>(status, headers);
+ return;
+ }
auto i = headers.find("content-type");
if (i == headers.end() || i->second.find("/json") == i->second.npos)
@@ -82,12 +85,22 @@ TidalLoginRequest::OnHeaders(unsigned status,
void
TidalLoginRequest::OnData(ConstBuffer<void> data)
{
+ if (error_parser) {
+ error_parser->OnData(data);
+ return;
+ }
+
parser.Parse((const unsigned char *)data.data, data.size);
}
void
TidalLoginRequest::OnEnd()
{
+ if (error_parser) {
+ error_parser->OnEnd();
+ return;
+ }
+
parser.CompleteParse();
if (session.empty())
diff --git a/src/input/plugins/TidalLoginRequest.hxx b/src/input/plugins/TidalLoginRequest.hxx
index d4b4820e5..e5d17d6df 100644
--- a/src/input/plugins/TidalLoginRequest.hxx
+++ b/src/input/plugins/TidalLoginRequest.hxx
@@ -27,9 +27,11 @@
#include "lib/yajl/Handle.hxx"
#include <exception>
+#include <memory>
#include <string>
class CurlRequest;
+class TidalErrorParser;
/**
* Callback class for #TidalLoginRequest.
@@ -52,6 +54,8 @@ class TidalLoginRequest final : CurlResponseHandler {
CurlRequest request;
+ std::unique_ptr<TidalErrorParser> error_parser;
+
Yajl::Handle parser;
enum class State {
diff --git a/src/input/plugins/TidalTrackRequest.cxx b/src/input/plugins/TidalTrackRequest.cxx
index c0b8d540d..e8f27ae7a 100644
--- a/src/input/plugins/TidalTrackRequest.cxx
+++ b/src/input/plugins/TidalTrackRequest.cxx
@@ -19,6 +19,7 @@
#include "config.h"
#include "TidalTrackRequest.hxx"
+#include "TidalErrorParser.hxx"
#include "lib/yajl/Callbacks.hxx"
#include "util/RuntimeError.hxx"
@@ -71,8 +72,10 @@ void
TidalTrackRequest::OnHeaders(unsigned status,
std::multimap<std::string, std::string> &&headers)
{
- if (status != 200)
- throw FormatRuntimeError("Status %u from Tidal", status);
+ if (status != 200) {
+ error_parser = std::make_unique<TidalErrorParser>(status, headers);
+ return;
+ }
auto i = headers.find("content-type");
if (i == headers.end() || i->second.find("/json") == i->second.npos)
@@ -84,12 +87,22 @@ TidalTrackRequest::OnHeaders(unsigned status,
void
TidalTrackRequest::OnData(ConstBuffer<void> data)
{
+ if (error_parser) {
+ error_parser->OnData(data);
+ return;
+ }
+
parser.Parse((const unsigned char *)data.data, data.size);
}
void
TidalTrackRequest::OnEnd()
{
+ if (error_parser) {
+ error_parser->OnEnd();
+ return;
+ }
+
parser.CompleteParse();
if (url.empty())
diff --git a/src/input/plugins/TidalTrackRequest.hxx b/src/input/plugins/TidalTrackRequest.hxx
index eb580c624..829cd053e 100644
--- a/src/input/plugins/TidalTrackRequest.hxx
+++ b/src/input/plugins/TidalTrackRequest.hxx
@@ -27,9 +27,11 @@
#include "lib/yajl/Handle.hxx"
#include <exception>
+#include <memory>
#include <string>
class CurlRequest;
+class TidalErrorParser;
/**
* Callback class for #TidalTrackRequest.
@@ -54,6 +56,8 @@ class TidalTrackRequest final : CurlResponseHandler {
CurlRequest request;
+ std::unique_ptr<TidalErrorParser> error_parser;
+
Yajl::Handle parser;
enum class State {