summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMax Kellermann <max@musicpd.org>2018-01-21 20:31:59 +0100
committerMax Kellermann <max@musicpd.org>2018-01-23 16:48:43 +0100
commit1da64fb79c8aa327fd62b3eedf832efbf3d420d0 (patch)
tree848ab50d230af0d5c063ed8bb505611ddd8d12d4 /src
parent8b42fd0a2876b44c6c68f874495693b9466cb5b7 (diff)
lib/curl/Delegate: helper class for response body parsers
Eliminate duplicate code.
Diffstat (limited to 'src')
-rw-r--r--src/input/plugins/TidalErrorParser.cxx12
-rw-r--r--src/input/plugins/TidalErrorParser.hxx20
-rw-r--r--src/input/plugins/TidalLoginRequest.cxx76
-rw-r--r--src/input/plugins/TidalLoginRequest.hxx42
-rw-r--r--src/input/plugins/TidalTrackRequest.cxx74
-rw-r--r--src/input/plugins/TidalTrackRequest.hxx42
-rw-r--r--src/lib/curl/Delegate.cxx54
-rw-r--r--src/lib/curl/Delegate.hxx73
-rw-r--r--src/lib/curl/Parser.hxx42
-rw-r--r--src/lib/yajl/ResponseParser.cxx43
-rw-r--r--src/lib/yajl/ResponseParser.hxx52
11 files changed, 370 insertions, 160 deletions
diff --git a/src/input/plugins/TidalErrorParser.cxx b/src/input/plugins/TidalErrorParser.cxx
index 71d523660..149037786 100644
--- a/src/input/plugins/TidalErrorParser.cxx
+++ b/src/input/plugins/TidalErrorParser.cxx
@@ -40,8 +40,8 @@ static constexpr yajl_callbacks tidal_error_parser_callbacks = {
TidalErrorParser::TidalErrorParser(unsigned _status,
const std::multimap<std::string, std::string> &headers)
- :status(_status),
- parser(&tidal_error_parser_callbacks, nullptr, this)
+ :YajlResponseParser(&tidal_error_parser_callbacks, nullptr, this),
+ status(_status)
{
auto i = headers.find("content-type");
if (i == headers.end() || i->second.find("/json") == i->second.npos)
@@ -49,15 +49,9 @@ TidalErrorParser::TidalErrorParser(unsigned _status,
}
void
-TidalErrorParser::OnData(ConstBuffer<void> data)
-{
- parser.Parse((const unsigned char *)data.data, data.size);
-}
-
-void
TidalErrorParser::OnEnd()
{
- parser.CompleteParse();
+ YajlResponseParser::OnEnd();
if (!message.empty())
throw FormatRuntimeError("Error from Tidal: %s",
diff --git a/src/input/plugins/TidalErrorParser.hxx b/src/input/plugins/TidalErrorParser.hxx
index 0ccf2fe8a..969ec0cf9 100644
--- a/src/input/plugins/TidalErrorParser.hxx
+++ b/src/input/plugins/TidalErrorParser.hxx
@@ -21,9 +21,8 @@
#define TIDAL_ERROR_PARSER_HXX
#include "check.h"
-#include "lib/yajl/Handle.hxx"
+#include "lib/yajl/ResponseParser.hxx"
-#include <exception>
#include <string>
#include <map>
@@ -33,11 +32,9 @@ struct StringView;
/**
* Parse an error JSON response.
*/
-class TidalErrorParser {
+class TidalErrorParser final : public YajlResponseParser {
const unsigned status;
- Yajl::Handle parser;
-
enum class State {
NONE,
USER_MESSAGE,
@@ -53,16 +50,9 @@ public:
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();
+protected:
+ /* virtual methods from CurlResponseParser */
+ void OnEnd() override;
public:
/* yajl callbacks */
diff --git a/src/input/plugins/TidalLoginRequest.cxx b/src/input/plugins/TidalLoginRequest.cxx
index e378ef9ad..71c04112f 100644
--- a/src/input/plugins/TidalLoginRequest.cxx
+++ b/src/input/plugins/TidalLoginRequest.cxx
@@ -21,10 +21,12 @@
#include "TidalLoginRequest.hxx"
#include "TidalErrorParser.hxx"
#include "lib/curl/Form.hxx"
+#include "lib/yajl/Handle.hxx"
#include "lib/yajl/Callbacks.hxx"
+#include "lib/yajl/ResponseParser.hxx"
#include "util/RuntimeError.hxx"
-using Wrapper = Yajl::CallbacksWrapper<TidalLoginRequest>;
+using Wrapper = Yajl::CallbacksWrapper<TidalLoginRequest::ResponseParser>;
static constexpr yajl_callbacks parse_callbacks = {
nullptr,
nullptr,
@@ -39,6 +41,31 @@ static constexpr yajl_callbacks parse_callbacks = {
nullptr,
};
+class TidalLoginRequest::ResponseParser final : public YajlResponseParser {
+ enum class State {
+ NONE,
+ SESSION_ID,
+ } state = State::NONE;
+
+ std::string session;
+
+public:
+ explicit ResponseParser() noexcept
+ :YajlResponseParser(&parse_callbacks, nullptr, this) {}
+
+ std::string &&GetSession() {
+ if (session.empty())
+ throw std::runtime_error("No sessionId in login response");
+
+ return std::move(session);
+ }
+
+ /* yajl callbacks */
+ bool String(StringView value) noexcept;
+ bool MapKey(StringView value) noexcept;
+ bool EndMap() noexcept;
+};
+
static std::string
MakeLoginUrl(const char *base_url)
{
@@ -66,47 +93,26 @@ TidalLoginRequest::~TidalLoginRequest() noexcept
request.StopIndirect();
}
-void
-TidalLoginRequest::OnHeaders(unsigned status,
- std::multimap<std::string, std::string> &&headers)
+std::unique_ptr<CurlResponseParser>
+TidalLoginRequest::MakeParser(unsigned status,
+ std::multimap<std::string, std::string> &&headers)
{
- if (status != 200) {
- error_parser = std::make_unique<TidalErrorParser>(status, headers);
- return;
- }
+ if (status != 200)
+ return std::make_unique<TidalErrorParser>(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 Tidal");
- parser = {&parse_callbacks, nullptr, this};
+ return std::make_unique<ResponseParser>();
}
void
-TidalLoginRequest::OnData(ConstBuffer<void> data)
+TidalLoginRequest::FinishParser(std::unique_ptr<CurlResponseParser> p)
{
- 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())
- throw std::runtime_error("No sessionId in login response");
-
- handler.OnTidalLoginSuccess(std::move(session));
+ assert(dynamic_cast<ResponseParser *>(p.get()) != nullptr);
+ auto &rp = (ResponseParser &)*p;
+ handler.OnTidalLoginSuccess(rp.GetSession());
}
void
@@ -116,7 +122,7 @@ TidalLoginRequest::OnError(std::exception_ptr e) noexcept
}
inline bool
-TidalLoginRequest::String(StringView value) noexcept
+TidalLoginRequest::ResponseParser::String(StringView value) noexcept
{
switch (state) {
case State::NONE:
@@ -131,7 +137,7 @@ TidalLoginRequest::String(StringView value) noexcept
}
inline bool
-TidalLoginRequest::MapKey(StringView value) noexcept
+TidalLoginRequest::ResponseParser::MapKey(StringView value) noexcept
{
if (value.Equals("sessionId"))
state = State::SESSION_ID;
@@ -142,7 +148,7 @@ TidalLoginRequest::MapKey(StringView value) noexcept
}
inline bool
-TidalLoginRequest::EndMap() noexcept
+TidalLoginRequest::ResponseParser::EndMap() noexcept
{
state = State::NONE;
diff --git a/src/input/plugins/TidalLoginRequest.hxx b/src/input/plugins/TidalLoginRequest.hxx
index e5d17d6df..51b4fde53 100644
--- a/src/input/plugins/TidalLoginRequest.hxx
+++ b/src/input/plugins/TidalLoginRequest.hxx
@@ -21,17 +21,9 @@
#define TIDAL_LOGIN_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 <exception>
-#include <memory>
-#include <string>
-
-class CurlRequest;
-class TidalErrorParser;
/**
* Callback class for #TidalLoginRequest.
@@ -49,27 +41,16 @@ public:
*
* After construction, call Start() to initiate the request.
*/
-class TidalLoginRequest final : CurlResponseHandler {
+class TidalLoginRequest final : DelegateCurlResponseHandler {
CurlSlist request_headers;
CurlRequest request;
- std::unique_ptr<TidalErrorParser> error_parser;
-
- Yajl::Handle parser;
-
- enum class State {
- NONE,
- SESSION_ID,
- } state = State::NONE;
-
- std::string session;
-
- std::exception_ptr error;
-
TidalLoginHandler &handler;
public:
+ class ResponseParser;
+
TidalLoginRequest(CurlGlobal &curl,
const char *base_url, const char *token,
const char *username, const char *password,
@@ -82,18 +63,13 @@ public:
}
private:
+ /* virtual methods from DelegateCurlResponseHandler */
+ std::unique_ptr<CurlResponseParser> MakeParser(unsigned status,
+ std::multimap<std::string, std::string> &&headers) override;
+ void FinishParser(std::unique_ptr<CurlResponseParser> p) override;
+
/* virtual methods from CurlResponseHandler */
- void OnHeaders(unsigned status,
- std::multimap<std::string, std::string> &&headers) override;
- void OnData(ConstBuffer<void> data) override;
- void OnEnd() override;
void OnError(std::exception_ptr e) noexcept override;
-
-public:
- /* yajl callbacks */
- bool String(StringView value) noexcept;
- bool MapKey(StringView value) noexcept;
- bool EndMap() noexcept;
};
#endif
diff --git a/src/input/plugins/TidalTrackRequest.cxx b/src/input/plugins/TidalTrackRequest.cxx
index e8f27ae7a..34ce48a3c 100644
--- a/src/input/plugins/TidalTrackRequest.cxx
+++ b/src/input/plugins/TidalTrackRequest.cxx
@@ -23,7 +23,7 @@
#include "lib/yajl/Callbacks.hxx"
#include "util/RuntimeError.hxx"
-using Wrapper = Yajl::CallbacksWrapper<TidalTrackRequest>;
+using Wrapper = Yajl::CallbacksWrapper<TidalTrackRequest::ResponseParser>;
static constexpr yajl_callbacks parse_callbacks = {
nullptr,
nullptr,
@@ -38,6 +38,31 @@ static constexpr yajl_callbacks parse_callbacks = {
nullptr,
};
+class TidalTrackRequest::ResponseParser final : public YajlResponseParser {
+ enum class State {
+ NONE,
+ URLS,
+ } 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(const char *base_url, const char *track_id)
{
@@ -68,47 +93,26 @@ TidalTrackRequest::~TidalTrackRequest() noexcept
request.StopIndirect();
}
-void
-TidalTrackRequest::OnHeaders(unsigned status,
- std::multimap<std::string, std::string> &&headers)
+std::unique_ptr<CurlResponseParser>
+TidalTrackRequest::MakeParser(unsigned status,
+ std::multimap<std::string, std::string> &&headers)
{
- if (status != 200) {
- error_parser = std::make_unique<TidalErrorParser>(status, headers);
- return;
- }
+ if (status != 200)
+ return std::make_unique<TidalErrorParser>(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 Tidal");
- parser = {&parse_callbacks, nullptr, this};
+ return std::make_unique<ResponseParser>();
}
void
-TidalTrackRequest::OnData(ConstBuffer<void> data)
+TidalTrackRequest::FinishParser(std::unique_ptr<CurlResponseParser> p)
{
- 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())
- throw std::runtime_error("No url in track response");
-
- handler.OnTidalTrackSuccess(std::move(url));
+ assert(dynamic_cast<ResponseParser *>(p.get()) != nullptr);
+ auto &rp = (ResponseParser &)*p;
+ handler.OnTidalTrackSuccess(rp.GetUrl());
}
void
@@ -118,7 +122,7 @@ TidalTrackRequest::OnError(std::exception_ptr e) noexcept
}
inline bool
-TidalTrackRequest::String(StringView value) noexcept
+TidalTrackRequest::ResponseParser::String(StringView value) noexcept
{
switch (state) {
case State::NONE:
@@ -134,7 +138,7 @@ TidalTrackRequest::String(StringView value) noexcept
}
inline bool
-TidalTrackRequest::MapKey(StringView value) noexcept
+TidalTrackRequest::ResponseParser::MapKey(StringView value) noexcept
{
if (value.Equals("urls"))
state = State::URLS;
@@ -145,7 +149,7 @@ TidalTrackRequest::MapKey(StringView value) noexcept
}
inline bool
-TidalTrackRequest::EndMap() noexcept
+TidalTrackRequest::ResponseParser::EndMap() noexcept
{
state = State::NONE;
diff --git a/src/input/plugins/TidalTrackRequest.hxx b/src/input/plugins/TidalTrackRequest.hxx
index 829cd053e..f51877c8a 100644
--- a/src/input/plugins/TidalTrackRequest.hxx
+++ b/src/input/plugins/TidalTrackRequest.hxx
@@ -21,17 +21,9 @@
#define TIDAL_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 <exception>
-#include <memory>
-#include <string>
-
-class CurlRequest;
-class TidalErrorParser;
/**
* Callback class for #TidalTrackRequest.
@@ -51,27 +43,16 @@ public:
*
* After construction, call Start() to initiate the request.
*/
-class TidalTrackRequest final : CurlResponseHandler {
+class TidalTrackRequest final : DelegateCurlResponseHandler {
CurlSlist request_headers;
CurlRequest request;
- std::unique_ptr<TidalErrorParser> error_parser;
-
- Yajl::Handle parser;
-
- enum class State {
- NONE,
- URLS,
- } state = State::NONE;
-
- std::string url;
-
- std::exception_ptr error;
-
TidalTrackHandler &handler;
public:
+ class ResponseParser;
+
TidalTrackRequest(CurlGlobal &curl,
const char *base_url, const char *token,
const char *session,
@@ -85,18 +66,13 @@ public:
}
private:
+ /* virtual methods from DelegateCurlResponseHandler */
+ std::unique_ptr<CurlResponseParser> MakeParser(unsigned status,
+ std::multimap<std::string, std::string> &&headers) override;
+ void FinishParser(std::unique_ptr<CurlResponseParser> p) override;
+
/* virtual methods from CurlResponseHandler */
- void OnHeaders(unsigned status,
- std::multimap<std::string, std::string> &&headers) override;
- void OnData(ConstBuffer<void> data) override;
- void OnEnd() override;
void OnError(std::exception_ptr e) noexcept override;
-
-public:
- /* yajl callbacks */
- bool String(StringView value) noexcept;
- bool MapKey(StringView value) noexcept;
- bool EndMap() noexcept;
};
#endif
diff --git a/src/lib/curl/Delegate.cxx b/src/lib/curl/Delegate.cxx
new file mode 100644
index 000000000..a2511d2d5
--- /dev/null
+++ b/src/lib/curl/Delegate.cxx
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008-2018 Max Kellermann <max.kellermann@gmail.com>
+ *
+ * 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.
+ */
+
+#include "Delegate.hxx"
+#include "Parser.hxx"
+
+#include <assert.h>
+
+void
+DelegateCurlResponseHandler::OnHeaders(unsigned status,
+ std::multimap<std::string, std::string> &&headers)
+{
+ parser = MakeParser(status, std::move(headers));
+ assert(parser);
+}
+
+void
+DelegateCurlResponseHandler::OnData(ConstBuffer<void> data)
+{
+ parser->OnData(data);
+}
+
+void
+DelegateCurlResponseHandler::OnEnd()
+{
+ parser->OnEnd();
+ FinishParser(std::move(parser));
+}
diff --git a/src/lib/curl/Delegate.hxx b/src/lib/curl/Delegate.hxx
new file mode 100644
index 000000000..fe6a65f35
--- /dev/null
+++ b/src/lib/curl/Delegate.hxx
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2008-2018 Max Kellermann <max.kellermann@gmail.com>
+ *
+ * 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_DELEGATE_HXX
+#define CURL_DELEGATE_HXX
+
+#include "Handler.hxx"
+
+#include <memory>
+
+class CurlResponseParser;
+
+/**
+ * A #CurlResponseHandler implementation which delegates response body
+ * parsing to another object which is created dynamically. This is
+ * useful when a different parser needs to be used under certain
+ * runtime conditions (e.g. depending on the status or content type).
+ */
+class DelegateCurlResponseHandler : public CurlResponseHandler {
+ std::unique_ptr<CurlResponseParser> parser;
+
+protected:
+ /**
+ * HTTP response headers have been received, and we now need a
+ * parser. This method constructs one and returns it (or
+ * throws an exception which will be passed to
+ * CurlResponseParser::OnError()).
+ */
+ virtual std::unique_ptr<CurlResponseParser> MakeParser(unsigned status,
+ std::multimap<std::string, std::string> &&headers) = 0;
+
+ /**
+ * The parser has finished parsing the response body. This
+ * method can be used to evaluate the result. Exceptions
+ * thrown by this method will be passed to
+ * CurlResponseParser::OnError().
+ */
+ virtual void FinishParser(std::unique_ptr<CurlResponseParser> p) = 0;
+
+public:
+ void OnHeaders(unsigned status,
+ std::multimap<std::string, std::string> &&headers) final;
+ void OnData(ConstBuffer<void> data) final;
+ void OnEnd() final;
+};
+
+#endif
diff --git a/src/lib/curl/Parser.hxx b/src/lib/curl/Parser.hxx
new file mode 100644
index 000000000..709b4937f
--- /dev/null
+++ b/src/lib/curl/Parser.hxx
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008-2018 Max Kellermann <max.kellermann@gmail.com>
+ *
+ * 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_PARSER_HXX
+#define CURL_PARSER_HXX
+
+template<typename T> struct ConstBuffer;
+
+class CurlResponseParser {
+public:
+ virtual ~CurlResponseParser() = default;
+ virtual void OnData(ConstBuffer<void> data) = 0;
+ virtual void OnEnd() = 0;
+};
+
+#endif
diff --git a/src/lib/yajl/ResponseParser.cxx b/src/lib/yajl/ResponseParser.cxx
new file mode 100644
index 000000000..f8c0b3727
--- /dev/null
+++ b/src/lib/yajl/ResponseParser.cxx
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008-2018 Max Kellermann <max.kellermann@gmail.com>
+ *
+ * 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.
+ */
+
+#include "ResponseParser.hxx"
+#include "util/ConstBuffer.hxx"
+
+void
+YajlResponseParser::OnData(ConstBuffer<void> data)
+{
+ handle.Parse((const unsigned char *)data.data, data.size);
+}
+
+void
+YajlResponseParser::OnEnd()
+{
+ handle.CompleteParse();
+}
diff --git a/src/lib/yajl/ResponseParser.hxx b/src/lib/yajl/ResponseParser.hxx
new file mode 100644
index 000000000..be08dd3d5
--- /dev/null
+++ b/src/lib/yajl/ResponseParser.hxx
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2008-2018 Max Kellermann <max.kellermann@gmail.com>
+ *
+ * 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 YAJL_RESPONSE_PARSER_HXX
+#define YAJL_RESPONSE_PARSER_HXX
+
+#include "Handle.hxx"
+#include "lib/curl/Parser.hxx"
+
+/**
+ * A helper class which parses the response body as JSON.
+ */
+class YajlResponseParser : public CurlResponseParser {
+ Yajl::Handle handle;
+
+public:
+ template<typename... Args>
+ YajlResponseParser(Args... args) noexcept
+ :handle(std::forward<Args>(args)...) {}
+
+ /* virtual methods fro CurlResponseParser */
+ void OnData(ConstBuffer<void> data) final;
+ void OnEnd() override;
+};
+
+#endif