aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authoripkn <ipknhama@gmail.com>2014-10-23 23:44:31 +0900
committeripkn <ipknhama@gmail.com>2014-10-23 23:44:31 +0900
commit4965d495a7ada4fb7c6aeed1fd6c4b5830bb99a9 (patch)
treea65f112b1df3035807c3b5dfab9aca86d86d1d1e /include
parenta5fab23f70e6e33c633ba4b646a41d0851169ad1 (diff)
parentada303970732f00e2d3017b726effad65d27a4d2 (diff)
downloadcrow-4965d495a7ada4fb7c6aeed1fd6c4b5830bb99a9.tar.gz
crow-4965d495a7ada4fb7c6aeed1fd6c4b5830bb99a9.zip
Merge pull request #28 from acron0/url-params-in-req
Added URL params (request.url_params)
Diffstat (limited to 'include')
-rw-r--r--include/http_connection.h2
-rw-r--r--include/http_request.h7
-rw-r--r--include/logging.h18
-rw-r--r--include/parser.h16
-rw-r--r--include/query_string.h303
-rw-r--r--include/routing.h9
6 files changed, 334 insertions, 21 deletions
diff --git a/include/http_connection.h b/include/http_connection.h
index 016c270..537be89 100644
--- a/include/http_connection.h
+++ b/include/http_connection.h
@@ -232,7 +232,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/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/parser.h b/include/parser.h
index 869061c..6ead8fd 100644
--- a/include/parser.h
+++ b/include/parser.h
@@ -3,6 +3,8 @@
#include <string>
#include <unordered_map>
#include <boost/algorithm/string.hpp>
+#include <boost/tokenizer.hpp>
+#include <algorithm>
#include "http_request.h"
@@ -20,7 +22,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 +78,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 +118,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 +139,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 +147,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..2d53ea0
--- /dev/null
+++ b/include/query_string.h
@@ -0,0 +1,303 @@
+#pragma once
+
+#include <stdio.h>
+#include <string>
+
+// ----------------------------------------------------------------------------
+// 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, register 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 ISHEX(x) ((((x)>='0'&&(x)<='9') || ((x)>='A'&&(x)<='F') || ((x)>='a'&&(x)<='f')) ? 1 : 0)
+#define HEX2DEC(x) (((x)>='0'&&(x)<='9') ? (x)-48 : ((x)>='A'&&(x)<='F') ? (x)-55 : ((x)>='a'&&(x)<='f') ? (x)-87 : 0)
+#define ISQSCHR(x) ((((x)=='=')||((x)=='#')||((x)=='&')||((x)=='\0')) ? 0 : 1)
+
+inline int qs_strncmp(const char * s, const char * qs, register size_t n)
+{
+ int i=0;
+ register unsigned char u1, u2, unyb, lnyb;
+
+ while(n-- > 0)
+ {
+ u1 = (unsigned char) *s++;
+ u2 = (unsigned char) *qs++;
+
+ if ( ! ISQSCHR(u1) ) { u1 = '\0'; }
+ if ( ! ISQSCHR(u2) ) { u2 = '\0'; }
+
+ if ( u1 == '+' ) { u1 = ' '; }
+ if ( u1 == '%' ) // easier/safer than scanf
+ {
+ unyb = (unsigned char) *s++;
+ lnyb = (unsigned char) *s++;
+ if ( ISHEX(unyb) && ISHEX(lnyb) )
+ u1 = (HEX2DEC(unyb) * 16) + HEX2DEC(lnyb);
+ else
+ u1 = '\0';
+ }
+
+ if ( u2 == '+' ) { u2 = ' '; }
+ if ( u2 == '%' ) // easier/safer than scanf
+ {
+ unyb = (unsigned char) *qs++;
+ lnyb = (unsigned char) *qs++;
+ if ( ISHEX(unyb) && ISHEX(lnyb) )
+ u2 = (HEX2DEC(unyb) * 16) + HEX2DEC(lnyb);
+ else
+ u2 = '\0';
+ }
+
+ if ( u1 != u2 )
+ return u1 - u2;
+ if ( u1 == '\0' )
+ return 0;
+ i++;
+ }
+ if ( 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( ISQSCHR(qs[j]) )
+ {
+ if ( qs[j] == '+' ) { qs[i] = ' '; }
+ else if ( qs[j] == '%' ) // easier/safer than scanf
+ {
+ if ( ! ISHEX(qs[j+1]) || ! ISHEX(qs[j+2]) )
+ {
+ qs[i] = '\0';
+ return i;
+ }
+ qs[i] = (HEX2DEC(qs[j+1]) * 16) + 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;
+}
+// ----------------------------------------------------------------------------
+
+// TODO to save allocs, capping url size to 2048 seems sane and reasonable but
+// crow should *technically* return a 413 if a URL is longer than this.
+#define MAX_URL_SIZE (2048)
+
+#define NUM_KV_PAIRS (256)
+#define VAL_SIZE (256)
+
+namespace crow
+{
+ class query_string
+ {
+ public:
+ query_string()
+ {
+
+ }
+ query_string(std::string url)
+ {
+ if(url.length() <= MAX_URL_SIZE) {
+ memset(_url, 0, MAX_URL_SIZE); // overkill?
+ memcpy(_url, url.c_str(), url.length());
+ }
+ _kv_size = qs_parse(_url, _kv_pairs, NUM_KV_PAIRS);
+ }
+ void clear() {
+ _url[0] = 0;
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, const query_string& qs)
+ {
+ os << "[ ";
+ for(int i = 0; i < qs._kv_size; ++i) {
+ os << qs._kv_pairs[i];
+ if((i + 1) < qs._kv_size) {
+ os << ", ";
+ }
+ }
+ os << " ]";
+ return os;
+
+ }
+
+ char* get (const std::string name) const
+ {
+ char* ret = qs_k2v(name.c_str(), _kv_pairs, _kv_size);
+ return ret != 0 ? ret : nullptr;
+ }
+
+ std::vector<char*> get_list (const std::string name) const
+ {
+ std::vector<char*> ret;
+ std::string plus = name + "[]";
+ char* tmp = nullptr;
+ int count = 0;
+ do
+ {
+ tmp = qs_k2v(plus.c_str(), _kv_pairs, _kv_size, count++);
+ if(tmp != nullptr) {
+ ret.push_back(tmp);
+ }
+ } while(tmp != nullptr);
+ return move(ret);
+ }
+
+
+ private:
+ char _url[MAX_URL_SIZE];
+ char* _kv_pairs[NUM_KV_PAIRS];
+ int _kv_size;
+ };
+
+} // end namespace \ No newline at end of file
diff --git a/include/routing.h b/include/routing.h
index e62cbce..87d492b 100644
--- a/include/routing.h
+++ b/include/routing.h
@@ -629,16 +629,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 +646,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;