aboutsummaryrefslogtreecommitdiffstats
path: root/amalgamate
diff options
context:
space:
mode:
authorAntony Woods <acron1@gmail.com>2014-10-24 09:40:09 +0100
committerAntony Woods <acron1@gmail.com>2014-10-24 09:40:09 +0100
commit693aac108de4dc62811b9a2737d895ada474cfb2 (patch)
tree44cc722afea8b6ef26095cd27aa64b97ec48d74e /amalgamate
parent27bf11d35c8ce44fd9fef656e2975712846b9bb2 (diff)
parent4b3b8070e75ce0fc181e5c012c47da2a1e7a918e (diff)
downloadcrow-693aac108de4dc62811b9a2737d895ada474cfb2.tar.gz
crow-693aac108de4dc62811b9a2737d895ada474cfb2.zip
Post-pull commit
Diffstat (limited to 'amalgamate')
-rw-r--r--amalgamate/crow_all.h452
1 files changed, 394 insertions, 58 deletions
diff --git a/amalgamate/crow_all.h b/amalgamate/crow_all.h
index 7991dcc..a0b15cf 100644
--- a/amalgamate/crow_all.h
+++ b/amalgamate/crow_all.h
@@ -364,62 +364,312 @@ template <typename F, typename Set>
#pragma once
+#include <stdio.h>
+#include <string.h>
+#include <string>
+#include <vector>
+#include <iostream>
+// ----------------------------------------------------------------------------
+// qs_parse (modified)
+// https://github.com/bartgrantham/qs_parse
+// ----------------------------------------------------------------------------
+/* Similar to strncmp, but handles URL-encoding for either string */
+int qs_strncmp(const char * s, const char * qs, size_t n);
-namespace crow
+/* Finds the beginning of each key/value pair and stores a pointer in qs_kv.
+ * Also decodes the value portion of the k/v pair *in-place*. In a future
+ * enhancement it will also have a compile-time option of sorting qs_kv
+ * alphabetically by key. */
+int qs_parse(char * qs, char * qs_kv[], int qs_kv_size);
+
+
+/* Used by qs_parse to decode the value portion of a k/v pair */
+int qs_decode(char * qs);
+
+
+/* Looks up the value according to the key on a pre-processed query string
+ * A future enhancement will be a compile-time option to look up the key
+ * in a pre-sorted qs_kv array via a binary search. */
+//char * qs_k2v(const char * key, char * qs_kv[], int qs_kv_size);
+ char * qs_k2v(const char * key, char * const * qs_kv, int qs_kv_size, int nth);
+
+
+/* Non-destructive lookup of value, based on key. User provides the
+ * destinaton string and length. */
+char * qs_scanvalue(const char * key, const char * qs, char * val, size_t val_len);
+
+// TODO: implement sorting of the qs_kv array; for now ensure it's not compiled
+#undef _qsSORTING
+
+// isxdigit _is_ available in <ctype.h>, but let's avoid another header instead
+#define CROW_QS_ISHEX(x) ((((x)>='0'&&(x)<='9') || ((x)>='A'&&(x)<='F') || ((x)>='a'&&(x)<='f')) ? 1 : 0)
+#define CROW_QS_HEX2DEC(x) (((x)>='0'&&(x)<='9') ? (x)-48 : ((x)>='A'&&(x)<='F') ? (x)-55 : ((x)>='a'&&(x)<='f') ? (x)-87 : 0)
+#define CROW_QS_ISQSCHR(x) ((((x)=='=')||((x)=='#')||((x)=='&')||((x)=='\0')) ? 0 : 1)
+
+inline int qs_strncmp(const char * s, const char * qs, size_t n)
{
- namespace detail
+ int i=0;
+ unsigned char u1, u2, unyb, lnyb;
+
+ while(n-- > 0)
{
- template <typename ... Middlewares>
- struct partial_context
- : public black_magic::pop_back<Middlewares...>::template rebind<partial_context>
- , public black_magic::last_element_type<Middlewares...>::type::context
+ u1 = (unsigned char) *s++;
+ u2 = (unsigned char) *qs++;
+
+ if ( ! CROW_QS_ISQSCHR(u1) ) { u1 = '\0'; }
+ if ( ! CROW_QS_ISQSCHR(u2) ) { u2 = '\0'; }
+
+ if ( u1 == '+' ) { u1 = ' '; }
+ if ( u1 == '%' ) // easier/safer than scanf
{
- using parent_context = typename black_magic::pop_back<Middlewares...>::template rebind<::crow::detail::partial_context>;
- template <int N>
- using partial = typename std::conditional<N == sizeof...(Middlewares)-1, partial_context, typename parent_context::template partial<N>>::type;
+ unyb = (unsigned char) *s++;
+ lnyb = (unsigned char) *s++;
+ if ( CROW_QS_ISHEX(unyb) && CROW_QS_ISHEX(lnyb) )
+ u1 = (CROW_QS_HEX2DEC(unyb) * 16) + CROW_QS_HEX2DEC(lnyb);
+ else
+ u1 = '\0';
+ }
- template <typename T>
- typename T::context& get()
+ if ( u2 == '+' ) { u2 = ' '; }
+ if ( u2 == '%' ) // easier/safer than scanf
+ {
+ unyb = (unsigned char) *qs++;
+ lnyb = (unsigned char) *qs++;
+ if ( CROW_QS_ISHEX(unyb) && CROW_QS_ISHEX(lnyb) )
+ u2 = (CROW_QS_HEX2DEC(unyb) * 16) + CROW_QS_HEX2DEC(lnyb);
+ else
+ u2 = '\0';
+ }
+
+ if ( u1 != u2 )
+ return u1 - u2;
+ if ( u1 == '\0' )
+ return 0;
+ i++;
+ }
+ if ( CROW_QS_ISQSCHR(*qs) )
+ return -1;
+ else
+ return 0;
+}
+
+
+inline int qs_parse(char * qs, char * qs_kv[], int qs_kv_size)
+{
+ int i, j;
+ char * substr_ptr;
+
+ for(i=0; i<qs_kv_size; i++) qs_kv[i] = NULL;
+
+ // find the beginning of the k/v substrings
+ if ( (substr_ptr = strchr(qs, '?')) != NULL )
+ substr_ptr++;
+ else
+ substr_ptr = qs;
+
+ i=0;
+ while(i<qs_kv_size)
+ {
+ qs_kv[i] = substr_ptr;
+ j = strcspn(substr_ptr, "&");
+ if ( substr_ptr[j] == '\0' ) { break; }
+ substr_ptr += j + 1;
+ i++;
+ }
+ i++; // x &'s -> means x iterations of this loop -> means *x+1* k/v pairs
+
+ // we only decode the values in place, the keys could have '='s in them
+ // which will hose our ability to distinguish keys from values later
+ for(j=0; j<i; j++)
+ {
+ substr_ptr = qs_kv[j] + strcspn(qs_kv[j], "=&#");
+ if ( substr_ptr[0] == '&' ) // blank value: skip decoding
+ substr_ptr[0] = '\0';
+ else
+ qs_decode(++substr_ptr);
+ }
+
+#ifdef _qsSORTING
+// TODO: qsort qs_kv, using qs_strncmp() for the comparison
+#endif
+
+ return i;
+}
+
+
+inline int qs_decode(char * qs)
+{
+ int i=0, j=0;
+
+ while( CROW_QS_ISQSCHR(qs[j]) )
+ {
+ if ( qs[j] == '+' ) { qs[i] = ' '; }
+ else if ( qs[j] == '%' ) // easier/safer than scanf
+ {
+ if ( ! CROW_QS_ISHEX(qs[j+1]) || ! CROW_QS_ISHEX(qs[j+2]) )
{
- return static_cast<typename T::context&>(*this);
+ qs[i] = '\0';
+ return i;
}
- };
+ qs[i] = (CROW_QS_HEX2DEC(qs[j+1]) * 16) + CROW_QS_HEX2DEC(qs[j+2]);
+ j+=2;
+ }
+ else
+ {
+ qs[i] = qs[j];
+ }
+ i++; j++;
+ }
+ qs[i] = '\0';
- template <>
- struct partial_context<>
+ return i;
+}
+
+
+inline char * qs_k2v(const char * key, char * const * qs_kv, int qs_kv_size, int nth = 0)
+{
+ int i;
+ size_t key_len, skip;
+
+ key_len = strlen(key);
+
+#ifdef _qsSORTING
+// TODO: binary search for key in the sorted qs_kv
+#else // _qsSORTING
+ for(i=0; i<qs_kv_size; i++)
+ {
+ // we rely on the unambiguous '=' to find the value in our k/v pair
+ if ( qs_strncmp(key, qs_kv[i], key_len) == 0 )
+ {
+ skip = strcspn(qs_kv[i], "=");
+ if ( qs_kv[i][skip] == '=' )
+ skip++;
+ // return (zero-char value) ? ptr to trailing '\0' : ptr to value
+ if(nth == 0)
+ return qs_kv[i] + skip;
+ else
+ --nth;
+ }
+ }
+#endif // _qsSORTING
+
+ return NULL;
+}
+
+
+inline char * qs_scanvalue(const char * key, const char * qs, char * val, size_t val_len)
+{
+ size_t i, key_len;
+ const char * tmp;
+
+ // find the beginning of the k/v substrings
+ if ( (tmp = strchr(qs, '?')) != NULL )
+ qs = tmp + 1;
+
+ key_len = strlen(key);
+ while(qs[0] != '#' && qs[0] != '\0')
+ {
+ if ( qs_strncmp(key, qs, key_len) == 0 )
+ break;
+ qs += strcspn(qs, "&") + 1;
+ }
+
+ if ( qs[0] == '\0' ) return NULL;
+
+ qs += strcspn(qs, "=&#");
+ if ( qs[0] == '=' )
+ {
+ qs++;
+ i = strcspn(qs, "&=#");
+ strncpy(val, qs, (val_len-1)<(i+1) ? (val_len-1) : (i+1));
+ qs_decode(val);
+ }
+ else
+ {
+ if ( val_len > 0 )
+ val[0] = '\0';
+ }
+
+ return val;
+}
+// ----------------------------------------------------------------------------
+
+
+namespace crow
+{
+ class query_string
+ {
+ public:
+ static const int MAX_KEY_VALUE_PAIRS_COUNT = 256;
+
+ query_string()
{
- template <int>
- using partial = partial_context;
- };
- template <int N, typename Context, typename Container, typename CurrentMW, typename ... Middlewares>
- bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx);
+ }
- template <typename ... Middlewares>
- struct context : private partial_context<Middlewares...>
- //struct context : private Middlewares::context... // simple but less type-safe
+ query_string(std::string url)
+ : url_(std::move(url))
{
- template <int N, typename Context, typename Container>
- friend typename std::enable_if<(N==0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res);
- template <int N, typename Context, typename Container>
- friend typename std::enable_if<(N>0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res);
+ if (url_.empty())
+ return;
- template <int N, typename Context, typename Container, typename CurrentMW, typename ... Middlewares2>
- friend bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx);
+ key_value_pairs_.resize(MAX_KEY_VALUE_PAIRS_COUNT);
- template <typename T>
- typename T::context& get()
+ int count = qs_parse(&url_[0], &key_value_pairs_[0], MAX_KEY_VALUE_PAIRS_COUNT);
+ key_value_pairs_.resize(count);
+ }
+
+ void clear()
+ {
+ key_value_pairs_.clear();
+ url_.clear();
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, const query_string& qs)
+ {
+ os << "[ ";
+ for(size_t i = 0; i < qs.key_value_pairs_.size(); ++i) {
+ if (i)
+ os << ", ";
+ os << qs.key_value_pairs_[i];
+ }
+ os << " ]";
+ return os;
+
+ }
+
+ char* get (const std::string& name) const
+ {
+ char* ret = qs_k2v(name.c_str(), key_value_pairs_.data(), key_value_pairs_.size());
+ return ret;
+ }
+
+ std::vector<char*> get_list (const std::string& name) const
+ {
+ std::vector<char*> ret;
+ std::string plus = name + "[]";
+ char* element = nullptr;
+
+ int count = 0;
+ while(1)
{
- return static_cast<typename T::context&>(*this);
+ element = qs_k2v(plus.c_str(), key_value_pairs_.data(), key_value_pairs_.size(), count++);
+ if (!element)
+ break;
+ ret.push_back(element);
}
+ return ret;
+ }
- template <int N>
- using partial = typename partial_context<Middlewares...>::template partial<N>;
- };
- }
-}
+
+ private:
+ std::string url_;
+ std::vector<char*> key_value_pairs_;
+ };
+
+} // end namespace
@@ -435,8 +685,6 @@ namespace crow
-using namespace std;
-
namespace crow
{
enum class LogLevel
@@ -450,13 +698,13 @@ namespace crow
class ILogHandler {
public:
- virtual void log(string message, LogLevel level) = 0;
+ virtual void log(std::string message, LogLevel level) = 0;
};
class CerrLogHandler : public ILogHandler {
public:
- void log(string message, LogLevel level) override {
- cerr << message;
+ void log(std::string message, LogLevel level) override {
+ std::cerr << message;
}
};
@@ -464,18 +712,18 @@ namespace crow
private:
//
- static string timestamp()
+ static std::string timestamp()
{
char date[32];
time_t t = time(0);
strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", gmtime(&t));
- return string(date);
+ return std::string(date);
}
public:
- logger(string prefix, LogLevel level) : level_(level) {
+ logger(std::string prefix, LogLevel level) : level_(level) {
#ifdef CROW_ENABLE_LOGGING
stringstream_ << "(" << timestamp() << ") [" << prefix << "] ";
#endif
@@ -484,7 +732,7 @@ namespace crow
~logger() {
#ifdef CROW_ENABLE_LOGGING
if(level_ >= get_current_log_level()) {
- stringstream_ << endl;
+ stringstream_ << std::endl;
get_handler_ref()->log(stringstream_.str(), level_);
}
#endif
@@ -530,7 +778,7 @@ namespace crow
}
//
- ostringstream stringstream_;
+ std::ostringstream stringstream_;
LogLevel level_;
};
}
@@ -566,6 +814,7 @@ namespace crow
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/operators.hpp>
+#include <vector>
#if defined(__GNUG__) || defined(__clang__)
#define crow_json_likely(x) __builtin_expect(x, 1)
@@ -5313,8 +5562,10 @@ namespace crow
#pragma once
+#include <vector>
#include <string>
#include <stdexcept>
+#include <iostream>
@@ -5440,7 +5691,9 @@ constexpr crow::HTTPMethod operator "" _method(const char* str, size_t len)
#pragma once
+#include <boost/algorithm/string/predicate.hpp>
#include <boost/functional/hash.hpp>
+#include <unordered_map>
namespace crow
{
@@ -5480,6 +5733,8 @@ namespace crow
+
+
namespace crow
{
template <typename T>
@@ -5496,7 +5751,9 @@ namespace crow
struct request
{
HTTPMethod method;
+ std::string raw_url;
std::string url;
+ query_string url_params;
ci_map headers;
std::string body;
@@ -5507,8 +5764,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 raw_url, std::string url, query_string url_params, ci_map headers, std::string body)
+ : method(method), raw_url(std::move(raw_url)), url(std::move(url)), url_params(std::move(url_params)), headers(std::move(headers)), body(std::move(body))
{
}
@@ -5532,6 +5789,10 @@ namespace crow
#include <string>
#include <unordered_map>
#include <boost/algorithm/string.hpp>
+#include <boost/tokenizer.hpp>
+#include <algorithm>
+
+
@@ -5550,7 +5811,7 @@ namespace crow
static int on_url(http_parser* self_, const char* at, size_t length)
{
HTTPParser* self = static_cast<HTTPParser*>(self_);
- self->url.insert(self->url.end(), at, at+length);
+ self->raw_url.insert(self->raw_url.end(), at, at+length);
return 0;
}
static int on_header_field(http_parser* self_, const char* at, size_t length)
@@ -5606,6 +5867,11 @@ namespace crow
static int on_message_complete(http_parser* self_)
{
HTTPParser* self = static_cast<HTTPParser*>(self_);
+
+ // url params
+ self->url = self->raw_url.substr(0, self->raw_url.find("?"));
+ self->url_params = query_string(self->raw_url);
+
self->process_message();
return 0;
}
@@ -5641,10 +5907,12 @@ namespace crow
void clear()
{
url.clear();
+ raw_url.clear();
header_building_state = 0;
header_field.clear();
header_value.clear();
headers.clear();
+ url_params.clear();
body.clear();
}
@@ -5660,7 +5928,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(raw_url), std::move(url), std::move(url_params), std::move(headers), std::move(body)};
}
bool check_version(int major, int minor) const
@@ -5668,11 +5936,14 @@ namespace crow
return http_major == major && http_minor == minor;
}
+ std::string raw_url;
std::string url;
+
int header_building_state = 0;
std::string header_field;
std::string header_value;
ci_map headers;
+ query_string url_params;
std::string body;
Handler* handler_;
@@ -5807,6 +6078,7 @@ namespace crow
#include <unordered_map>
#include <memory>
#include <boost/lexical_cast.hpp>
+#include <vector>
@@ -6435,16 +6707,13 @@ public:
void handle(const request& req, response& res)
{
- // remove url params
- auto editedUrl = req.url.substr(0, req.url.find("?"));
-
- auto found = trie_.find(editedUrl);
+ auto found = trie_.find(req.url);
unsigned rule_index = found.first;
if (!rule_index)
{
- CROW_LOG_DEBUG << "Cannot match rules " << editedUrl;
+ CROW_LOG_DEBUG << "Cannot match rules " << req.url;
res = response(404);
res.end();
return;
@@ -6455,7 +6724,7 @@ public:
if ((rules_[rule_index]->methods() & (1<<(uint32_t)req.method)) == 0)
{
- CROW_LOG_DEBUG << "Rule found but method mismatch: " << editedUrl << " with " << method_name(req.method) << "(" << (uint32_t)req.method << ") / " << rules_[rule_index]->methods();
+ CROW_LOG_DEBUG << "Rule found but method mismatch: " << req.url << " with " << method_name(req.method) << "(" << (uint32_t)req.method << ") / " << rules_[rule_index]->methods();
res = response(404);
res.end();
return;
@@ -6480,6 +6749,71 @@ public:
#pragma once
+
+
+
+
+
+
+
+
+namespace crow
+{
+ namespace detail
+ {
+ template <typename ... Middlewares>
+ struct partial_context
+ : public black_magic::pop_back<Middlewares...>::template rebind<partial_context>
+ , public black_magic::last_element_type<Middlewares...>::type::context
+ {
+ using parent_context = typename black_magic::pop_back<Middlewares...>::template rebind<::crow::detail::partial_context>;
+ template <int N>
+ using partial = typename std::conditional<N == sizeof...(Middlewares)-1, partial_context, typename parent_context::template partial<N>>::type;
+
+ template <typename T>
+ typename T::context& get()
+ {
+ return static_cast<typename T::context&>(*this);
+ }
+ };
+
+ template <>
+ struct partial_context<>
+ {
+ template <int>
+ using partial = partial_context;
+ };
+
+ template <int N, typename Context, typename Container, typename CurrentMW, typename ... Middlewares>
+ bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx);
+
+ template <typename ... Middlewares>
+ struct context : private partial_context<Middlewares...>
+ //struct context : private Middlewares::context... // simple but less type-safe
+ {
+ template <int N, typename Context, typename Container>
+ friend typename std::enable_if<(N==0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res);
+ template <int N, typename Context, typename Container>
+ friend typename std::enable_if<(N>0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res);
+
+ template <int N, typename Context, typename Container, typename CurrentMW, typename ... Middlewares2>
+ friend bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx);
+
+ template <typename T>
+ typename T::context& get()
+ {
+ return static_cast<typename T::context&>(*this);
+ }
+
+ template <int N>
+ using partial = typename partial_context<Middlewares...>::template partial<N>;
+ };
+ }
+}
+
+
+
+#pragma once
#include <boost/algorithm/string/trim.hpp>
@@ -6665,6 +6999,7 @@ namespace crow
#include <boost/array.hpp>
#include <atomic>
#include <chrono>
+#include <vector>
@@ -6900,7 +7235,7 @@ namespace crow
void complete_request()
{
- CROW_LOG_INFO << "Response: " << this << ' ' << req_.url << ' ' << res.code << ' ' << close_connection_;
+ CROW_LOG_INFO << "Response: " << this << ' ' << req_.raw_url << ' ' << res.code << ' ' << close_connection_;
if (need_to_call_after_handlers_)
{
@@ -7173,6 +7508,7 @@ namespace crow
#include <cstdint>
#include <atomic>
#include <future>
+#include <vector>
#include <memory>