#pragma once #include #include "crow/http_request.h" #include "crow/http_response.h" namespace crow { // Any middleware requires following 3 members: // struct context; // storing data for the middleware; can be read from another middleware or handlers // before_handle // called before handling the request. // if res.end() is called, the operation is halted. // (still call after_handle of this middleware) // 2 signatures: // void before_handle(request& req, response& res, context& ctx) // if you only need to access this middlewares context. // template // void before_handle(request& req, response& res, context& ctx, AllContext& all_ctx) // you can access another middlewares' context by calling `all_ctx.template get()' // ctx == all_ctx.template get() // after_handle // called after handling the request. // void after_handle(request& req, response& res, context& ctx) // template // void after_handle(request& req, response& res, context& ctx, AllContext& all_ctx) struct CookieParser { struct context { std::unordered_map jar; std::unordered_map cookies_to_add; std::string get_cookie(const std::string& key) { if (jar.count(key)) return jar[key]; return {}; } void set_cookie(const std::string& key, const std::string& value) { cookies_to_add.emplace(key, value); } }; void before_handle(request& req, response& res, context& ctx) { int count = req.headers.count("Cookie"); if (!count) return; if (count > 1) { res.code = 400; res.end(); return; } std::string cookies = req.get_header_value("Cookie"); size_t pos = 0; while(pos < cookies.size()) { size_t pos_equal = cookies.find('=', pos); if (pos_equal == cookies.npos) break; std::string name = cookies.substr(pos, pos_equal-pos); boost::trim(name); pos = pos_equal+1; while(pos < cookies.size() && cookies[pos] == ' ') pos++; if (pos == cookies.size()) break; std::string value; if (cookies[pos] == '"') { int dquote_meet_count = 0; pos ++; size_t pos_dquote = pos-1; do { pos_dquote = cookies.find('"', pos_dquote+1); dquote_meet_count ++; } while(pos_dquote < cookies.size() && cookies[pos_dquote-1] == '\\'); if (pos_dquote == cookies.npos) break; if (dquote_meet_count == 1) value = cookies.substr(pos, pos_dquote - pos); else { value.clear(); value.reserve(pos_dquote-pos); for(size_t p = pos; p < pos_dquote; p++) { // FIXME minimal escaping if (cookies[p] == '\\' && p + 1 < pos_dquote) { p++; if (cookies[p] == '\\' || cookies[p] == '"') value += cookies[p]; else { value += '\\'; value += cookies[p]; } } else value += cookies[p]; } } ctx.jar.emplace(std::move(name), std::move(value)); pos = cookies.find(";", pos_dquote+1); if (pos == cookies.npos) break; pos++; while(pos < cookies.size() && cookies[pos] == ' ') pos++; if (pos == cookies.size()) break; } else { size_t pos_semicolon = cookies.find(';', pos); value = cookies.substr(pos, pos_semicolon - pos); boost::trim(value); ctx.jar.emplace(std::move(name), std::move(value)); pos = pos_semicolon; if (pos == cookies.npos) break; pos ++; while(pos < cookies.size() && cookies[pos] == ' ') pos++; if (pos == cookies.size()) break; } } } void after_handle(request& /*req*/, response& res, context& ctx) { for(auto& cookie:ctx.cookies_to_add) { res.add_header("Set-Cookie", cookie.first + "=" + cookie.second); } } }; /* App app; A B C A::context int aa; ctx1 : public A::context ctx2 : public ctx1, public B::context ctx3 : public ctx2, public C::context C depends on A C::handle context.aaa App::context : private CookieParser::contetx, ... { jar } SimpleApp */ }