Throw when content-type is not application/json

This commit is contained in:
King_DuckZ 2020-09-12 01:48:10 +01:00
parent 6bafd923f3
commit 2c8e557bb4
5 changed files with 84 additions and 27 deletions

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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