From 9eb96b7f4c2e134e768555de2650823a863843ce Mon Sep 17 00:00:00 2001 From: ipknHama Date: Thu, 11 Sep 2014 06:32:41 +0900 Subject: Implement example CookieParser middleware and test --- include/ci_map.h | 32 +++++++++++ include/http_connection.h | 6 +-- include/http_request.h | 24 ++++++++- include/http_response.h | 22 +++++++- include/middleware.h | 133 +++++++++++++++++++++++++++++++++++++++++----- include/parser.h | 4 +- 6 files changed, 201 insertions(+), 20 deletions(-) create mode 100644 include/ci_map.h (limited to 'include') diff --git a/include/ci_map.h b/include/ci_map.h new file mode 100644 index 0000000..9b48f0b --- /dev/null +++ b/include/ci_map.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace crow +{ + struct ci_hash + { + size_t operator()(const std::string& key) const + { + std::size_t seed = 0; + std::locale locale; + + for(auto c : key) + { + boost::hash_combine(seed, std::toupper(c, locale)); + } + + return seed; + } + }; + + struct ci_key_eq + { + bool operator()(const std::string& l, const std::string& r) const + { + return boost::iequals(l, r); + } + }; + + using ci_map = std::unordered_multimap; +} diff --git a/include/http_connection.h b/include/http_connection.h index 3a9fe65..54d0860 100644 --- a/include/http_connection.h +++ b/include/http_connection.h @@ -155,7 +155,7 @@ namespace crow void handle_header() { // HTTP 1.1 Expect: 100-continue - if (parser_.check_version(1, 1) && parser_.headers.count("expect") && parser_.headers["expect"] == "100-continue") + if (parser_.check_version(1, 1) && parser_.headers.count("expect") && get_header_value(parser_.headers, "expect") == "100-continue") { buffers_.clear(); static std::string expect_100_continue = "HTTP/1.1 100 Continue\r\n\r\n"; @@ -174,13 +174,13 @@ namespace crow if (parser_.check_version(1, 0)) { // HTTP/1.0 - if (!(req.headers.count("connection") && boost::iequals(req.headers["connection"],"Keep-Alive"))) + if (!(req.headers.count("connection") && boost::iequals(req.get_header_value("connection"),"Keep-Alive"))) close_connection_ = true; } else if (parser_.check_version(1, 1)) { // HTTP/1.1 - if (req.headers.count("connection") && req.headers["connection"] == "close") + if (req.headers.count("connection") && req.get_header_value("connection") == "close") close_connection_ = true; if (!req.headers.count("host")) { diff --git a/include/http_request.h b/include/http_request.h index af623c6..7d2da67 100644 --- a/include/http_request.h +++ b/include/http_request.h @@ -1,16 +1,38 @@ #pragma once #include "common.h" +#include "ci_map.h" namespace crow { + template + inline const std::string& get_header_value(const T& headers, const std::string& key) + { + if (headers.count(key)) + { + return headers.find(key)->second; + } + static std::string empty; + return empty; + } + struct request { HTTPMethod method; std::string url; - std::unordered_map headers; + ci_map headers; std::string body; + void add_header(std::string key, std::string value) + { + headers.emplace(std::move(key), std::move(value)); + } + + const std::string& get_header_value(const std::string& key) + { + return crow::get_header_value(headers, key); + } + void* middleware_context{}; }; } diff --git a/include/http_response.h b/include/http_response.h index bc468b7..69c6d72 100644 --- a/include/http_response.h +++ b/include/http_response.h @@ -2,6 +2,8 @@ #include #include #include "json.h" +#include "http_request.h" +#include "ci_map.h" namespace crow { @@ -15,7 +17,25 @@ namespace crow std::string body; json::wvalue json_value; int code{200}; - std::unordered_map headers; + + // `headers' stores HTTP headers. + ci_map headers; + + void set_header(std::string key, std::string value) + { + headers.erase(key); + headers.emplace(std::move(key), std::move(value)); + } + void add_header(std::string key, std::string value) + { + headers.emplace(std::move(key), std::move(value)); + } + + const std::string& get_header_value(const std::string& key) + { + return crow::get_header_value(headers, key); + } + response() {} explicit response(int code) : code(code) {} diff --git a/include/middleware.h b/include/middleware.h index 5e358af..ec54476 100644 --- a/include/middleware.h +++ b/include/middleware.h @@ -1,4 +1,5 @@ #pragma once +#include #include "http_request.h" #include "http_response.h" @@ -9,35 +10,143 @@ namespace crow // struct context; // storing data for the middleware; can be read from another middleware or handlers - // template - // void before_handle(request& req, response& res, context& ctx, AllContext& all_ctx) + // 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() - // template - // void after_handle(request& req, response& res, context& ctx, AllContext& all_ctx) + // 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) - class CookieParser + 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); + } }; - template - void before_handle(request& req, response& res, context& ctx, AllContext& all_ctx) + void before_handle(request& req, response& res, context& ctx) { - // ctx == all_ctx.bind() - // ctx.jar[] = ; + 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; + } + } } - template - void after_handle(request& req, response& res, context& ctx, AllContext& all_ctx) + 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; diff --git a/include/parser.h b/include/parser.h index 1b8240f..869061c 100644 --- a/include/parser.h +++ b/include/parser.h @@ -31,7 +31,6 @@ namespace crow case 0: if (!self->header_value.empty()) { - boost::algorithm::to_lower(self->header_field); self->headers.emplace(std::move(self->header_field), std::move(self->header_value)); } self->header_field.assign(at, at+length); @@ -63,7 +62,6 @@ namespace crow HTTPParser* self = static_cast(self_); if (!self->header_field.empty()) { - boost::algorithm::to_lower(self->header_field); self->headers.emplace(std::move(self->header_field), std::move(self->header_value)); } self->process_header(); @@ -144,7 +142,7 @@ namespace crow int header_building_state = 0; std::string header_field; std::string header_value; - std::unordered_map headers; + ci_map headers; std::string body; Handler* handler_; -- cgit v1.2.3-54-g00ecf