Throw when content-type is not application/json
This commit is contained in:
parent
6bafd923f3
commit
2c8e557bb4
5 changed files with 84 additions and 27 deletions
|
@ -18,6 +18,7 @@
|
|||
#include "private/api_nap.hpp"
|
||||
#include "private/v1_endpoints.hpp"
|
||||
#include "private/dateconv.hpp"
|
||||
#include "private/mime_split.hpp"
|
||||
#include "duckhandy/int_conv.hpp"
|
||||
#include "api_nap_exception.hpp"
|
||||
#include <optional>
|
||||
|
@ -98,6 +99,14 @@ void fill_creator_list (const sjd::element& elem, std::vector<Creator>& out) {
|
|||
}
|
||||
}
|
||||
|
||||
std::string_view header_entry (const nap::HttpResponse& resp, std::string_view name) {
|
||||
for (const auto& head : resp.header_list) {
|
||||
if (equal(head.first, name))
|
||||
return head.second;
|
||||
}
|
||||
return {""};
|
||||
}
|
||||
|
||||
} //unnamed namespace
|
||||
|
||||
ApiNap::ApiNap (
|
||||
|
@ -265,6 +274,14 @@ ApiOutput<T> ApiNap::fetch_and_parse (const char* endpoint, F&& data_fill, bool
|
|||
if (200 != resp.code)
|
||||
throw ServerError(resp);
|
||||
|
||||
{
|
||||
bool ok;
|
||||
int parsed_char;
|
||||
auto mime = tawashi::string_to_mime(header_entry(resp, "content-type"), ok, parsed_char);
|
||||
if (ok and (not equal(mime.type, "application") or not equal(mime.subtype, "json")))
|
||||
throw ContentTypeError(mime.type, mime.subtype);
|
||||
}
|
||||
|
||||
T dataret;
|
||||
{
|
||||
std::unique_lock lock(m_json_mutex);
|
||||
|
|
|
@ -18,8 +18,21 @@
|
|||
#include "api_nap_exception.hpp"
|
||||
#include "nap/http_response.hpp"
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
namespace oro {
|
||||
namespace {
|
||||
std::size_t append_to_char_arary (std::string_view in, char* out, std::size_t offs, std::size_t capacity) {
|
||||
assert(capacity >= offs + 1);
|
||||
|
||||
const std::size_t out_size = std::min(in.size(), capacity - offs - 1);
|
||||
std::copy(in.begin(), in.begin() + out_size, out + offs);
|
||||
out[out_size] = '\0';
|
||||
|
||||
return offs + out_size;
|
||||
}
|
||||
} //unnamed namespace
|
||||
|
||||
ServerError::ServerError (const nap::HttpResponse& resp) :
|
||||
std::runtime_error(
|
||||
|
@ -35,4 +48,20 @@ int ServerError::error_code() const noexcept {
|
|||
return m_err_code;
|
||||
}
|
||||
|
||||
ContentTypeError::ContentTypeError (std::string_view type, std::string_view subtype) :
|
||||
std::runtime_error(
|
||||
std::string("Unexpected content-type received: \"") +
|
||||
std::string(type) + "/" + std::string(subtype) + "\""
|
||||
)
|
||||
{
|
||||
std::size_t offs = 0;
|
||||
offs = append_to_char_arary(type, m_content_type.data(), offs, m_content_type.size());
|
||||
offs = append_to_char_arary("/", m_content_type.data(), offs, m_content_type.size());
|
||||
offs = append_to_char_arary(subtype, m_content_type.data(), offs, m_content_type.size());
|
||||
}
|
||||
|
||||
std::string_view ContentTypeError::content_type() const noexcept {
|
||||
return {m_content_type.data()};
|
||||
}
|
||||
|
||||
} //namespace oro
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
|
||||
namespace nap {
|
||||
struct HttpResponse;
|
||||
|
@ -34,4 +36,13 @@ private:
|
|||
int m_err_code;
|
||||
};
|
||||
|
||||
class ContentTypeError : public std::runtime_error {
|
||||
public:
|
||||
explicit ContentTypeError (std::string_view type, std::string_view subtype);
|
||||
std::string_view content_type() const noexcept;
|
||||
|
||||
private:
|
||||
std::array<char, 32> m_content_type;
|
||||
};
|
||||
|
||||
} //namespace oro
|
||||
|
|
|
@ -81,8 +81,8 @@
|
|||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
tawashi::SplitMime,
|
||||
(boost::string_view, type)
|
||||
(boost::string_view, subtype)
|
||||
(std::string_view, type)
|
||||
(std::string_view, subtype)
|
||||
(tawashi::MimeParametersMapType, parameters)
|
||||
);
|
||||
|
||||
|
@ -90,26 +90,26 @@ namespace tawashi {
|
|||
namespace {
|
||||
template <typename Iterator, typename Skipper>
|
||||
struct MimeGrammar : boost::spirit::qi::grammar<Iterator, tawashi::SplitMime(), Skipper> {
|
||||
explicit MimeGrammar (const std::string* parString);
|
||||
explicit MimeGrammar (std::string_view parString);
|
||||
|
||||
boost::spirit::qi::rule<Iterator, SplitMime(), Skipper> content_type;
|
||||
boost::spirit::qi::rule<Iterator, SplitMime(), Skipper> media_type;
|
||||
boost::spirit::qi::rule<Iterator, boost::string_view(), Skipper> type;
|
||||
boost::spirit::qi::rule<Iterator, boost::string_view(), Skipper> subtype;
|
||||
boost::spirit::qi::rule<Iterator, std::string_view(), Skipper> type;
|
||||
boost::spirit::qi::rule<Iterator, std::string_view(), Skipper> subtype;
|
||||
boost::spirit::qi::rule<Iterator, MimeParametersMapType::value_type(), Skipper> parameter;
|
||||
boost::spirit::qi::rule<Iterator, boost::string_view(), Skipper> attribute;
|
||||
boost::spirit::qi::rule<Iterator, boost::string_view(), Skipper> value;
|
||||
boost::spirit::qi::rule<Iterator, boost::string_view(), Skipper> quoted_string;
|
||||
boost::spirit::qi::rule<Iterator, boost::string_view(), Skipper> token;
|
||||
const std::string* m_master_string;
|
||||
boost::spirit::qi::rule<Iterator, std::string_view(), Skipper> attribute;
|
||||
boost::spirit::qi::rule<Iterator, std::string_view(), Skipper> value;
|
||||
boost::spirit::qi::rule<Iterator, std::string_view(), Skipper> quoted_string;
|
||||
boost::spirit::qi::rule<Iterator, std::string_view(), Skipper> token;
|
||||
std::string_view m_master_string;
|
||||
Iterator m_begin;
|
||||
};
|
||||
|
||||
template <typename Iterator, typename Skipper>
|
||||
MimeGrammar<Iterator, Skipper>::MimeGrammar (const std::string* parString) :
|
||||
MimeGrammar<Iterator, Skipper>::MimeGrammar (std::string_view parString) :
|
||||
MimeGrammar::base_type(content_type),
|
||||
m_master_string(parString),
|
||||
m_begin(m_master_string->cbegin())
|
||||
m_begin(m_master_string.cbegin())
|
||||
{
|
||||
namespace px = boost::phoenix;
|
||||
using boost::spirit::ascii::space;
|
||||
|
@ -119,7 +119,7 @@ namespace tawashi {
|
|||
using boost::spirit::qi::raw;
|
||||
using boost::spirit::qi::_val;
|
||||
using boost::spirit::qi::lexeme;
|
||||
using boost::string_view;
|
||||
using std::string_view;
|
||||
using boost::spirit::_1;
|
||||
|
||||
content_type = -media_type;
|
||||
|
@ -132,7 +132,7 @@ namespace tawashi {
|
|||
|
||||
token = raw[+(alnum | char_("_.-"))][
|
||||
_val = px::bind(
|
||||
&string_view::substr, px::construct<string_view>(px::ref(*m_master_string)),
|
||||
&string_view::substr, px::construct<string_view>(px::ref(m_master_string)),
|
||||
px::begin(_1) - px::ref(m_begin),
|
||||
px::size(_1)
|
||||
)
|
||||
|
@ -144,7 +144,7 @@ namespace tawashi {
|
|||
'"'
|
||||
]
|
||||
][_val = px::bind(
|
||||
&string_view::substr, px::construct<string_view>(px::ref(*m_master_string)),
|
||||
&string_view::substr, px::construct<string_view>(px::ref(m_master_string)),
|
||||
px::begin(_1) + 1 - px::ref(m_begin),
|
||||
px::size(_1) - 2
|
||||
)];
|
||||
|
@ -161,34 +161,34 @@ namespace tawashi {
|
|||
};
|
||||
} //unnamed namespace
|
||||
|
||||
SplitMime string_to_mime (const std::string* parMime, bool& parParseOk, int& parParsedCharCount) {
|
||||
SplitMime string_to_mime (std::string_view parMime, bool& parParseOk, int& parParsedCharCount) {
|
||||
using boost::spirit::qi::blank;
|
||||
using boost::spirit::qi::blank_type;
|
||||
|
||||
MimeGrammar<std::string::const_iterator, blank_type> gramm(parMime);
|
||||
MimeGrammar<std::string_view::const_iterator, blank_type> gramm(parMime);
|
||||
SplitMime result;
|
||||
|
||||
parParseOk = false;
|
||||
parParsedCharCount = 0;
|
||||
|
||||
std::string::const_iterator start_it = parMime->cbegin();
|
||||
std::string_view::const_iterator start_it = parMime.cbegin();
|
||||
const bool parse_ok = boost::spirit::qi::phrase_parse(
|
||||
start_it,
|
||||
parMime->cend(),
|
||||
parMime.cend(),
|
||||
gramm,
|
||||
blank,
|
||||
result
|
||||
);
|
||||
|
||||
parParseOk = parse_ok and (parMime->cend() == start_it);
|
||||
parParsedCharCount = std::distance(parMime->cbegin(), start_it);
|
||||
parParseOk = parse_ok and (parMime.cend() == start_it);
|
||||
parParsedCharCount = std::distance(parMime.cbegin(), start_it);
|
||||
assert(parParsedCharCount >= 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string mime_to_string (const SplitMime& parMime, bool& parWriteOk) {
|
||||
namespace px = boost::phoenix;
|
||||
using boost::string_view;
|
||||
using std::string_view;
|
||||
using boost::spirit::karma::generate;
|
||||
using boost::spirit::karma::char_;
|
||||
using boost::spirit::karma::string;
|
||||
|
|
|
@ -17,19 +17,19 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <boost/utility/string_view.hpp>
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace tawashi {
|
||||
typedef boost::container::flat_map<boost::string_view, boost::string_view> MimeParametersMapType;
|
||||
typedef boost::container::flat_map<std::string_view, std::string_view> MimeParametersMapType;
|
||||
|
||||
struct SplitMime {
|
||||
boost::string_view type;
|
||||
boost::string_view subtype;
|
||||
std::string_view type;
|
||||
std::string_view subtype;
|
||||
MimeParametersMapType parameters;
|
||||
};
|
||||
|
||||
SplitMime string_to_mime (const std::string* parMime, bool& parParseOk, int& parParsedCharCount);
|
||||
SplitMime string_to_mime (std::string_view parMime, bool& parParseOk, int& parParsedCharCount);
|
||||
std::string mime_to_string (const SplitMime& parMime, bool& parWriteOk);
|
||||
} //namespace tawashi
|
||||
|
|
Loading…
Reference in a new issue