blob: a73641a2735a7fffae601d9c732250fd4b29c096 [file] [log] [blame]
#pragma once
#include "crow/http_request.h"
#include "crow/http_response.h"
#include <boost/algorithm/string/trim.hpp>
#include <boost/container/flat_map.hpp>
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 <typename AllContext>
// void before_handle(request& req, response& res, context& ctx,
// AllContext& all_ctx)
// you can access another middlewares' context by calling
// `all_ctx.template get<MW>()'
// ctx == all_ctx.template get<CurrentMiddleware>()
// after_handle
// called after handling the request.
// void after_handle(request& req, response& res, context& ctx)
// template <typename AllContext>
// void after_handle(request& req, response& res, context& ctx, AllContext&
// all_ctx)
struct CookieParser {
struct context {
boost::container::flat_map<std::string, std::string> jar;
boost::container::flat_map<std::string, std::string> cookies_to_add;
std::string get_cookie(const std::string& key) {
if (jar.count(key) != 0u) {
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 == 0) {
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<CookieParser, AnotherJarMW> 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
*/
} // namespace crow