diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/ci_map.h | 2 | ||||
-rw-r--r-- | include/common.h | 2 | ||||
-rw-r--r-- | include/http_connection.h | 3 | ||||
-rw-r--r-- | include/http_request.h | 7 | ||||
-rw-r--r-- | include/http_server.h | 1 | ||||
-rw-r--r-- | include/json.h | 1 | ||||
-rw-r--r-- | include/logging.h | 18 | ||||
-rw-r--r-- | include/middleware_context.h | 2 | ||||
-rw-r--r-- | include/parser.h | 17 | ||||
-rw-r--r-- | include/query_string.h | 308 | ||||
-rw-r--r-- | include/routing.h | 10 |
11 files changed, 350 insertions, 21 deletions
diff --git a/include/ci_map.h b/include/ci_map.h index 9b48f0b..155d54f 100644 --- a/include/ci_map.h +++ b/include/ci_map.h @@ -1,6 +1,8 @@ #pragma once +#include <boost/algorithm/string/predicate.hpp> #include <boost/functional/hash.hpp> +#include <unordered_map> namespace crow { diff --git a/include/common.h b/include/common.h index 5720b46..619f4d5 100644 --- a/include/common.h +++ b/include/common.h @@ -1,7 +1,9 @@ #pragma once +#include <vector> #include <string> #include <stdexcept> +#include <iostream> #include "utility.h" namespace crow diff --git a/include/http_connection.h b/include/http_connection.h index 46eae18..db069c3 100644 --- a/include/http_connection.h +++ b/include/http_connection.h @@ -5,6 +5,7 @@ #include <boost/array.hpp> #include <atomic> #include <chrono> +#include <vector> #include "http_parser_merged.h" @@ -232,7 +233,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_) { diff --git a/include/http_request.h b/include/http_request.h index 331bc80..1319b2d 100644 --- a/include/http_request.h +++ b/include/http_request.h @@ -2,6 +2,7 @@ #include "common.h" #include "ci_map.h" +#include "query_string.h" namespace crow { @@ -19,7 +20,9 @@ namespace crow struct request { HTTPMethod method; + std::string raw_url; std::string url; + query_string url_params; ci_map headers; std::string body; @@ -30,8 +33,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)) { } diff --git a/include/http_server.h b/include/http_server.h index 32e0e42..bd35ba1 100644 --- a/include/http_server.h +++ b/include/http_server.h @@ -5,6 +5,7 @@ #include <cstdint> #include <atomic> #include <future> +#include <vector> #include <memory> diff --git a/include/json.h b/include/json.h index 9306e2a..d79fe88 100644 --- a/include/json.h +++ b/include/json.h @@ -10,6 +10,7 @@ #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) diff --git a/include/logging.h b/include/logging.h index 8f5b833..0d77071 100644 --- a/include/logging.h +++ b/include/logging.h @@ -9,8 +9,6 @@ #include "settings.h" -using namespace std; - namespace crow { enum class LogLevel @@ -24,13 +22,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; } }; @@ -38,18 +36,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 @@ -58,7 +56,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 @@ -104,7 +102,7 @@ namespace crow } // - ostringstream stringstream_; + std::ostringstream stringstream_; LogLevel level_; }; } diff --git a/include/middleware_context.h b/include/middleware_context.h index 980a821..daaaa5c 100644 --- a/include/middleware_context.h +++ b/include/middleware_context.h @@ -1,6 +1,8 @@ #pragma once #include "utility.h" +#include "http_request.h" +#include "http_response.h" namespace crow { diff --git a/include/parser.h b/include/parser.h index 869061c..f6b748b 100644 --- a/include/parser.h +++ b/include/parser.h @@ -3,7 +3,10 @@ #include <string> #include <unordered_map> #include <boost/algorithm/string.hpp> +#include <boost/tokenizer.hpp> +#include <algorithm> +#include "http_parser_merged.h" #include "http_request.h" namespace crow @@ -20,7 +23,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) @@ -76,6 +79,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; } @@ -111,10 +119,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(); } @@ -130,7 +140,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 @@ -138,11 +148,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_; diff --git a/include/query_string.h b/include/query_string.h new file mode 100644 index 0000000..d0a93ea --- /dev/null +++ b/include/query_string.h @@ -0,0 +1,308 @@ +#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); + + +/* 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) +{ + int i=0; + unsigned char u1, u2, unyb, lnyb; + + while(n-- > 0) + { + 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 + { + 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'; + } + + 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]) ) + { + 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'; + + 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() + { + + } + + query_string(std::string url) + : url_(std::move(url)) + { + if (url_.empty()) + return; + + key_value_pairs_.resize(MAX_KEY_VALUE_PAIRS_COUNT); + + 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) + { + element = qs_k2v(plus.c_str(), key_value_pairs_.data(), key_value_pairs_.size(), count++); + if (!element) + break; + ret.push_back(element); + } + return ret; + } + + + private: + std::string url_; + std::vector<char*> key_value_pairs_; + }; + +} // end namespace diff --git a/include/routing.h b/include/routing.h index e62cbce..9f607f1 100644 --- a/include/routing.h +++ b/include/routing.h @@ -6,6 +6,7 @@ #include <unordered_map> #include <memory> #include <boost/lexical_cast.hpp> +#include <vector> #include "common.h" #include "http_response.h" @@ -629,16 +630,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; @@ -649,7 +647,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; |