From 3b4bf01a7dd9e16f1df00c70b87a533a3cd17797 Mon Sep 17 00:00:00 2001 From: Antony Woods Date: Mon, 22 Sep 2014 21:34:22 +0100 Subject: [PATCH 01/11] URL params are now present as a ci_map variable of request --- examples/example.cpp | 14 ++++++++- include/http_request.h | 5 ++-- include/parser.h | 67 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 82 insertions(+), 4 deletions(-) diff --git a/examples/example.cpp b/examples/example.cpp index b360adb8b..9f482af08 100644 --- a/examples/example.cpp +++ b/examples/example.cpp @@ -68,8 +68,20 @@ int main() return crow::response{os.str()}; }); + CROW_ROUTE(app, "/params") + ([](const crow::request& req){ + std::ostringstream os; + os << "Params:\n"; + for (auto& i : req.url_params) { + os << "key = " << i.first << ", val = " << i.second << '\n'; + } + return crow::response{os.str()}; + }); + + + // ignore all log - crow::logger::setLogLevel(crow::LogLevel::CRITICAL); + crow::logger::setLogLevel(crow::LogLevel::DEBUG); //crow::logger::setHandler(std::make_shared()); app.port(18080) diff --git a/include/http_request.h b/include/http_request.h index 331bc80ac..24edb66fb 100644 --- a/include/http_request.h +++ b/include/http_request.h @@ -20,6 +20,7 @@ namespace crow { HTTPMethod method; std::string url; + ci_map url_params; ci_map headers; std::string body; @@ -30,8 +31,8 @@ namespace crow { } - request(HTTPMethod method, std::string url, ci_map headers, std::string body) - : method(method), url(std::move(url)), headers(std::move(headers)), body(std::move(body)) + request(HTTPMethod method, std::string url, ci_map url_params, ci_map headers, std::string body) + : method(method), url(std::move(url)), url_params(std::move(url_params)), headers(std::move(headers)), body(std::move(body)) { } diff --git a/include/parser.h b/include/parser.h index 869061cfb..bda4594a9 100644 --- a/include/parser.h +++ b/include/parser.h @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include "http_request.h" @@ -11,6 +13,62 @@ namespace crow template struct HTTPParser : public http_parser { + template + struct tokenize_by_char + { + template + bool operator()(It& next, It end, std::string & tok) + { + if (next == end) + return false; + const char dels = delimiter; + const char* del = &dels; + auto pos = std::search(next, end, del, del + 1); + tok.assign(next, pos); + next = pos; + if (next != end) + std::advance(next, 1); + return true; + } + + void reset() {} + }; + + static ci_map get_url_params(std::string url) + { + const char url_delimiter = '&'; + const char param_delimiter = '='; + ci_map ret; + + int qMarkPos = url.find("?"); + if(!(qMarkPos >=0 && qMarkPos != (url.length()-1))) { + return ret; + } + + auto params = url.substr(qMarkPos+1); + + // substitute ';' for '&' for recommended process of delimintation + // (http://www.w3.org/TR/1999/REC-html401-19991224/appendix/notes.html#h-B.2.2) + std::replace(params.begin(), params.end(), ';', url_delimiter); + + // url tokenizer + for (auto i : boost::tokenizer>(params)) { + std::string key, value; + auto parts = boost::tokenizer>(i); + int count = 0; + for(auto p = parts.begin(); p != parts.end(); ++p, ++count) { + if(count == 0){ + key = *p; + } else { + value = *p; + } + } + ret.insert(std::make_pair(key, value)); + } + + return ret; + } + static int on_message_begin(http_parser* self_) { HTTPParser* self = static_cast(self_); @@ -21,6 +79,10 @@ namespace crow { HTTPParser* self = static_cast(self_); self->url.insert(self->url.end(), at, at+length); + + // url params + self->url_params = get_url_params(self->url); + return 0; } static int on_header_field(http_parser* self_, const char* at, size_t length) @@ -115,6 +177,7 @@ namespace crow header_field.clear(); header_value.clear(); headers.clear(); + url_params.clear(); body.clear(); } @@ -130,7 +193,7 @@ namespace crow request to_request() const { - return request{(HTTPMethod)method, std::move(url), std::move(headers), std::move(body)}; + return request{(HTTPMethod)method, std::move(url), std::move(url_params), std::move(headers), std::move(body)}; } bool check_version(int major, int minor) const @@ -139,10 +202,12 @@ namespace crow } std::string url; + int header_building_state = 0; std::string header_field; std::string header_value; ci_map headers; + ci_map url_params; std::string body; Handler* handler_; From 6890436742c89a01d00f26f796becb5e81c26e0b Mon Sep 17 00:00:00 2001 From: Antony Woods Date: Tue, 23 Sep 2014 21:02:26 +0100 Subject: [PATCH 02/11] Fixed sign comparisson warning --- include/parser.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/parser.h b/include/parser.h index bda4594a9..00bb155c7 100644 --- a/include/parser.h +++ b/include/parser.h @@ -40,7 +40,7 @@ namespace crow const char param_delimiter = '='; ci_map ret; - int qMarkPos = url.find("?"); + unsigned int qMarkPos = url.find("?"); if(!(qMarkPos >=0 && qMarkPos != (url.length()-1))) { return ret; } From 06842721d7da53a2235e6e4071760588ec285f90 Mon Sep 17 00:00:00 2001 From: Antony Woods Date: Tue, 14 Oct 2014 09:48:35 +0100 Subject: [PATCH 03/11] Wrapped qs_parse as query_string and added tests --- README.md | 18 + amalgamate/crow_all.h | 1882 ++++++++++++++++++++++--------------- examples/example.cpp | 17 +- include/http_connection.h | 2 +- include/http_request.h | 8 +- include/parser.h | 73 +- include/query_string.h | 305 ++++++ include/routing.h | 7 +- tests/unittest.cpp | 117 +++ 9 files changed, 1570 insertions(+), 859 deletions(-) create mode 100644 include/query_string.h diff --git a/README.md b/README.md index c6fa8a458..1400c0da8 100644 --- a/README.md +++ b/README.md @@ -115,3 +115,21 @@ ctest #### OSX brew install boost google-perftools +### Attributions + +Crow uses the following libraries. + + qs_parse + + https://github.com/bartgrantham/qs_parse + + Copyright (c) 2010 Bart Grantham + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + diff --git a/amalgamate/crow_all.h b/amalgamate/crow_all.h index d73276566..a567b5a4b 100644 --- a/amalgamate/crow_all.h +++ b/amalgamate/crow_all.h @@ -1,3 +1,824 @@ +#pragma once + +#include +#include +#include +#include + +namespace crow +{ + namespace black_magic + { + struct OutOfRange + { + OutOfRange(unsigned pos, unsigned length) {} + }; + constexpr unsigned requires_in_range( unsigned i, unsigned len ) + { + return i >= len ? throw OutOfRange(i, len) : i; + } + + class const_str + { + const char * const begin_; + unsigned size_; + + public: + template< unsigned N > + constexpr const_str( const char(&arr)[N] ) : begin_(arr), size_(N - 1) { + static_assert( N >= 1, "not a string literal"); + } + constexpr char operator[]( unsigned i ) const { + return requires_in_range(i, size_), begin_[i]; + } + + constexpr operator const char *() const { + return begin_; + } + + constexpr const char* begin() const { return begin_; } + constexpr const char* end() const { return begin_ + size_; } + + constexpr unsigned size() const { + return size_; + } + }; + + + constexpr unsigned find_closing_tag(const_str s, unsigned p) + { + return s[p] == '>' ? p : find_closing_tag(s, p+1); + } + + constexpr bool is_valid(const_str s, unsigned i = 0, int f = 0) + { + return + i == s.size() + ? f == 0 : + f < 0 || f >= 2 + ? false : + s[i] == '<' + ? is_valid(s, i+1, f+1) : + s[i] == '>' + ? is_valid(s, i+1, f-1) : + is_valid(s, i+1, f); + } + + constexpr bool is_equ_p(const char* a, const char* b, unsigned n) + { + return + *a == 0 || *b == 0 + ? false : + n == 0 + ? true : + *a != *b + ? false : + is_equ_p(a+1, b+1, n-1); + } + + constexpr bool is_equ_n(const_str a, unsigned ai, const_str b, unsigned bi, unsigned n) + { + return + ai + n > a.size() || bi + n > b.size() + ? false : + n == 0 + ? true : + a[ai] != b[bi] + ? false : + is_equ_n(a,ai+1,b,bi+1,n-1); + } + + constexpr bool is_int(const_str s, unsigned i) + { + return is_equ_n(s, i, "", 0, 5); + } + + constexpr bool is_uint(const_str s, unsigned i) + { + return is_equ_n(s, i, "", 0, 6); + } + + constexpr bool is_float(const_str s, unsigned i) + { + return is_equ_n(s, i, "", 0, 7) || + is_equ_n(s, i, "", 0, 8); + } + + constexpr bool is_str(const_str s, unsigned i) + { + return is_equ_n(s, i, "", 0, 5) || + is_equ_n(s, i, "", 0, 8); + } + + constexpr bool is_path(const_str s, unsigned i) + { + return is_equ_n(s, i, "", 0, 6); + } + + constexpr uint64_t get_parameter_tag(const_str s, unsigned p = 0) + { + return + p == s.size() + ? 0 : + s[p] == '<' ? ( + is_int(s, p) + ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 1 : + is_uint(s, p) + ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 2 : + is_float(s, p) + ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 3 : + is_str(s, p) + ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 4 : + is_path(s, p) + ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 5 : + throw std::runtime_error("invalid parameter type") + ) : + get_parameter_tag(s, p+1); + } + + template + struct S + { + template + using push = S; + template + using push_back = S; + template class U> + using rebind = U; + }; +template + struct CallHelper; + template + struct CallHelper> + { + template ()(std::declval()...)) + > + static char __test(int); + + template + static int __test(...); + + static constexpr bool value = sizeof(__test(0)) == sizeof(char); + }; + + + template + struct single_tag_to_type + { + }; + + template <> + struct single_tag_to_type<1> + { + using type = int64_t; + }; + + template <> + struct single_tag_to_type<2> + { + using type = uint64_t; + }; + + template <> + struct single_tag_to_type<3> + { + using type = double; + }; + + template <> + struct single_tag_to_type<4> + { + using type = std::string; + }; + + template <> + struct single_tag_to_type<5> + { + using type = std::string; + }; + + + template + struct arguments + { + using subarguments = typename arguments::type; + using type = + typename subarguments::template push::type>; + }; + + template <> + struct arguments<0> + { + using type = S<>; + }; + + template + struct last_element_type + { + using type = typename std::tuple_element>::type; + }; + + + template <> + struct last_element_type<> + { + }; + + + // from http://stackoverflow.com/questions/13072359/c11-compile-time-array-with-logarithmic-evaluation-depth + template using Invoke = typename T::type; + + template struct seq{ using type = seq; }; + + template struct concat; + + template + struct concat, seq> + : seq{}; + + template + using Concat = Invoke>; + + template struct gen_seq; + template using GenSeq = Invoke>; + + template + struct gen_seq : Concat, GenSeq>{}; + + template<> struct gen_seq<0> : seq<>{}; + template<> struct gen_seq<1> : seq<0>{}; + + template + struct pop_back_helper; + + template + struct pop_back_helper, Tuple> + { + template