#include "Negotiate.hpp" #include namespace molo::example::fw { namespace { std::string trim(std::string s) { while (!s.empty() && (s.front() == ' ' || s.front() == '\t')) s.erase(s.begin()); while (!s.empty() && (s.back() == ' ' || s.back() == '\t')) s.pop_back(); return s; } } // namespace std::string negotiate(const std::string& accept, const std::vector& available) { if (available.empty()) throw HttpError(500, "no_offers", "no offers"); if (accept.empty()) return available.front(); struct Range { std::string type; double q = 1.0; }; std::vector ranges; std::size_t start = 0; while (start < accept.size()) { std::size_t comma = accept.find(',', start); if (comma == std::string::npos) comma = accept.size(); std::string part = trim(accept.substr(start, comma - start)); std::size_t semi = part.find(';'); Range r{trim(part.substr(0, semi)), 1.0}; if (semi != std::string::npos) { std::string params = part.substr(semi + 1); std::size_t q = params.find("q="); if (q != std::string::npos) { try { r.q = std::stod(params.substr(q + 2)); } catch (...) { r.q = 1.0; } } } ranges.push_back(r); start = comma + 1; } std::string best; double best_q = -1.0; for (const auto& a : available) { for (const auto& r : ranges) { bool hit = r.type == "*/*" || r.type == a; if (!hit) { std::size_t slash = a.find('/'); std::string wild = slash == std::string::npos ? "*/*" : a.substr(0, slash) + "/*"; hit = r.type == wild; } if (hit && r.q > best_q) { best_q = r.q; best = a; } } } if (best.empty() || best_q <= 0.0) throw HttpError(406, "not_acceptable", "no acceptable format"); return best; } } // namespace molo::example::fw