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/api_nap.hpp"
#include "private/v1_endpoints.hpp" #include "private/v1_endpoints.hpp"
#include "private/dateconv.hpp" #include "private/dateconv.hpp"
#include "private/mime_split.hpp"
#include "duckhandy/int_conv.hpp" #include "duckhandy/int_conv.hpp"
#include "api_nap_exception.hpp" #include "api_nap_exception.hpp"
#include <optional> #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 } //unnamed namespace
ApiNap::ApiNap ( ApiNap::ApiNap (
@ -265,6 +274,14 @@ ApiOutput<T> ApiNap::fetch_and_parse (const char* endpoint, F&& data_fill, bool
if (200 != resp.code) if (200 != resp.code)
throw ServerError(resp); 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; T dataret;
{ {
std::unique_lock lock(m_json_mutex); std::unique_lock lock(m_json_mutex);

View file

@ -18,8 +18,21 @@
#include "api_nap_exception.hpp" #include "api_nap_exception.hpp"
#include "nap/http_response.hpp" #include "nap/http_response.hpp"
#include <string> #include <string>
#include <algorithm>
#include <cassert>
namespace oro { 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) : ServerError::ServerError (const nap::HttpResponse& resp) :
std::runtime_error( std::runtime_error(
@ -35,4 +48,20 @@ int ServerError::error_code() const noexcept {
return m_err_code; 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 } //namespace oro

View file

@ -18,6 +18,8 @@
#pragma once #pragma once
#include <stdexcept> #include <stdexcept>
#include <array>
#include <string_view>
namespace nap { namespace nap {
struct HttpResponse; struct HttpResponse;
@ -34,4 +36,13 @@ private:
int m_err_code; 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 } //namespace oro

View file

@ -81,8 +81,8 @@
BOOST_FUSION_ADAPT_STRUCT( BOOST_FUSION_ADAPT_STRUCT(
tawashi::SplitMime, tawashi::SplitMime,
(boost::string_view, type) (std::string_view, type)
(boost::string_view, subtype) (std::string_view, subtype)
(tawashi::MimeParametersMapType, parameters) (tawashi::MimeParametersMapType, parameters)
); );
@ -90,26 +90,26 @@ namespace tawashi {
namespace { namespace {
template <typename Iterator, typename Skipper> template <typename Iterator, typename Skipper>
struct MimeGrammar : boost::spirit::qi::grammar<Iterator, tawashi::SplitMime(), 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> content_type;
boost::spirit::qi::rule<Iterator, SplitMime(), Skipper> media_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, std::string_view(), Skipper> type;
boost::spirit::qi::rule<Iterator, boost::string_view(), Skipper> subtype; 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, MimeParametersMapType::value_type(), Skipper> parameter;
boost::spirit::qi::rule<Iterator, boost::string_view(), Skipper> attribute; boost::spirit::qi::rule<Iterator, std::string_view(), Skipper> attribute;
boost::spirit::qi::rule<Iterator, boost::string_view(), Skipper> value; boost::spirit::qi::rule<Iterator, std::string_view(), Skipper> value;
boost::spirit::qi::rule<Iterator, boost::string_view(), Skipper> quoted_string; boost::spirit::qi::rule<Iterator, std::string_view(), Skipper> quoted_string;
boost::spirit::qi::rule<Iterator, boost::string_view(), Skipper> token; boost::spirit::qi::rule<Iterator, std::string_view(), Skipper> token;
const std::string* m_master_string; std::string_view m_master_string;
Iterator m_begin; Iterator m_begin;
}; };
template <typename Iterator, typename Skipper> 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), MimeGrammar::base_type(content_type),
m_master_string(parString), m_master_string(parString),
m_begin(m_master_string->cbegin()) m_begin(m_master_string.cbegin())
{ {
namespace px = boost::phoenix; namespace px = boost::phoenix;
using boost::spirit::ascii::space; using boost::spirit::ascii::space;
@ -119,7 +119,7 @@ namespace tawashi {
using boost::spirit::qi::raw; using boost::spirit::qi::raw;
using boost::spirit::qi::_val; using boost::spirit::qi::_val;
using boost::spirit::qi::lexeme; using boost::spirit::qi::lexeme;
using boost::string_view; using std::string_view;
using boost::spirit::_1; using boost::spirit::_1;
content_type = -media_type; content_type = -media_type;
@ -132,7 +132,7 @@ namespace tawashi {
token = raw[+(alnum | char_("_.-"))][ token = raw[+(alnum | char_("_.-"))][
_val = px::bind( _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::begin(_1) - px::ref(m_begin),
px::size(_1) px::size(_1)
) )
@ -144,7 +144,7 @@ namespace tawashi {
'"' '"'
] ]
][_val = px::bind( ][_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::begin(_1) + 1 - px::ref(m_begin),
px::size(_1) - 2 px::size(_1) - 2
)]; )];
@ -161,34 +161,34 @@ namespace tawashi {
}; };
} //unnamed namespace } //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;
using boost::spirit::qi::blank_type; 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; SplitMime result;
parParseOk = false; parParseOk = false;
parParsedCharCount = 0; 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( const bool parse_ok = boost::spirit::qi::phrase_parse(
start_it, start_it,
parMime->cend(), parMime.cend(),
gramm, gramm,
blank, blank,
result result
); );
parParseOk = parse_ok and (parMime->cend() == start_it); parParseOk = parse_ok and (parMime.cend() == start_it);
parParsedCharCount = std::distance(parMime->cbegin(), start_it); parParsedCharCount = std::distance(parMime.cbegin(), start_it);
assert(parParsedCharCount >= 0); assert(parParsedCharCount >= 0);
return result; return result;
} }
std::string mime_to_string (const SplitMime& parMime, bool& parWriteOk) { std::string mime_to_string (const SplitMime& parMime, bool& parWriteOk) {
namespace px = boost::phoenix; namespace px = boost::phoenix;
using boost::string_view; using std::string_view;
using boost::spirit::karma::generate; using boost::spirit::karma::generate;
using boost::spirit::karma::char_; using boost::spirit::karma::char_;
using boost::spirit::karma::string; using boost::spirit::karma::string;

View file

@ -17,19 +17,19 @@
#pragma once #pragma once
#include <boost/utility/string_view.hpp>
#include <boost/container/flat_map.hpp> #include <boost/container/flat_map.hpp>
#include <string> #include <string>
#include <string_view>
namespace tawashi { 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 { struct SplitMime {
boost::string_view type; std::string_view type;
boost::string_view subtype; std::string_view subtype;
MimeParametersMapType parameters; 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); std::string mime_to_string (const SplitMime& parMime, bool& parWriteOk);
} //namespace tawashi } //namespace tawashi