aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile8
-rw-r--r--crow.h5
-rw-r--r--example.cpp2
-rw-r--r--example_chat.cpp80
-rw-r--r--example_chat.html48
-rw-r--r--http_connection.h77
-rw-r--r--http_response.h32
-rw-r--r--mustache.h10
-rw-r--r--routing.h95
-rw-r--r--unittest.cpp28
10 files changed, 304 insertions, 81 deletions
diff --git a/Makefile b/Makefile
index e71ff37..1927722 100644
--- a/Makefile
+++ b/Makefile
@@ -8,9 +8,13 @@ FLAGS_DEBUG = -g
FLAGS_GCOV = -r
endif
-binaries=covtest example
+binaries=covtest example example_chat
+
+all: covtest example example_chat
+
+example_chat: example_chat.cpp settings.h crow.h http_server.h http_connection.h parser.h http_response.h routing.h common.h utility.h json.h datetime.h logging.h mustache.h
+ ${CXX} -Wall $(FLAGS_DEBUG) -std=c++1y -o example_chat example_chat.cpp http-parser/http_parser.c -pthread -lboost_system $(FLAGS_BOOST_THREAD) -I http-parser/
-all: covtest example
example: example.cpp settings.h crow.h http_server.h http_connection.h parser.h http_response.h routing.h common.h utility.h json.h datetime.h logging.h mustache.h
${CXX} -Wall $(FLAGS_DEBUG) -O3 -std=c++1y -o example example.cpp http-parser/http_parser.c -pthread -lboost_system $(FLAGS_BOOST_THREAD) -ltcmalloc_minimal -I http-parser/
diff --git a/crow.h b/crow.h
index 025d174..55572bf 100644
--- a/crow.h
+++ b/crow.h
@@ -29,9 +29,9 @@ namespace crow
{
}
- response handle(const request& req)
+ void handle(const request& req, response& res)
{
- return router_.handle(req);
+ return router_.handle(req, res);
}
template <uint64_t Tag>
@@ -84,5 +84,6 @@ namespace crow
Router router_;
};
+ using App = Crow;
};
diff --git a/example.cpp b/example.cpp
index 57f8e09..f21a7d6 100644
--- a/example.cpp
+++ b/example.cpp
@@ -46,7 +46,7 @@ int main()
([](const crow::request& req, crow::response& res, int a, int b){
std::ostringstream os;
os << a+b;
- res.send(os.str());
+ res.write(os.str());
res.end();
});
diff --git a/example_chat.cpp b/example_chat.cpp
new file mode 100644
index 0000000..994269e
--- /dev/null
+++ b/example_chat.cpp
@@ -0,0 +1,80 @@
+#include "crow.h"
+#include "json.h"
+#include "mustache.h"
+#include <string>
+#include <vector>
+
+using namespace std;
+
+vector<string> msgs;
+vector<crow::response*> ress;
+
+void broadcast(const string& msg)
+{
+ msgs.push_back(msg);
+ crow::json::wvalue x;
+ x["msgs"][0] = msgs.back();
+ x["last"] = msgs.size();
+ string body = crow::json::dump(x);
+ for(auto* res:ress)
+ {
+ CROW_LOG_DEBUG << res->p << " replied: " << body;
+ res->end(body);
+ }
+ ress.clear();
+}
+
+int main()
+{
+ crow::App app;
+ crow::mustache::set_base(".");
+
+ CROW_ROUTE(app, "/")
+ ([]{
+ crow::mustache::context ctx;
+ return crow::mustache::load("example_chat.html").render();
+ });
+
+ CROW_ROUTE(app, "/logs")
+ ([]{
+ CROW_LOG_INFO << "logs requested";
+ crow::json::wvalue x;
+ for(int i = max(0, (int)msgs.size()-100); i < (int)msgs.size(); i++)
+ x["msgs"][i] = msgs[i];
+ x["last"] = msgs.size();
+ CROW_LOG_INFO << "logs completed";
+ return x;
+ });
+
+ CROW_ROUTE(app, "/logs/<int>")
+ ([](const crow::request& req, crow::response& res, int after){
+ CROW_LOG_INFO << "logs with last " << after;
+ if (after < (int)msgs.size())
+ {
+ crow::json::wvalue x;
+ for(int i = after; i < (int)msgs.size(); i ++)
+ x["msgs"][i-after] = msgs[i];
+ x["last"] = msgs.size();
+
+ res.write(crow::json::dump(x));
+ res.end();
+ }
+ else
+ {
+ ress.push_back(&res);
+ CROW_LOG_DEBUG << res.p << " stored";
+ }
+ });
+
+ CROW_ROUTE(app, "/send")
+ ([](const crow::request& req)
+ {
+ CROW_LOG_INFO << "msg from client: " << req.body;
+ broadcast(req.body);
+ return "";
+ });
+
+ app.port(18080)
+ //.multithreaded()
+ .run();
+}
diff --git a/example_chat.html b/example_chat.html
new file mode 100644
index 0000000..0fe35dd
--- /dev/null
+++ b/example_chat.html
@@ -0,0 +1,48 @@
+<html>
+<head>
+<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
+</head>
+<body>
+<input id="msg" type="text">
+<button id="send">Send</button>
+<div id="logs">
+</div>
+<script>
+$(document).ready(function(){
+ $("#send").click(function(){
+ var msg = $("#msg").val();
+ console.log(msg);
+ if (msg.length > 0)
+ $.post("/send", msg);
+ $("#msg").val("");
+ });
+ $("#msg").keyup(function(event){
+ if(event.keyCode == 13){
+ $("#send").click();
+ }
+ });
+ var lastLog = 0;
+ var updateLog;
+ updateLog = function(data)
+ {
+ console.log("recv ");
+ console.log(data);
+ var lastLog = data.last*1;
+ console.log("lastLog: " + lastLog);
+ var s = "";
+ for(var x in data.msgs)
+ {
+ s += data.msgs[x] + "<BR>";
+ }
+ $("#logs").html(s+$("#logs").html());
+ var failFunction;
+ failFunction = function(){
+ $.getJSON("/logs/"+lastLog, updateLog).fail(failFunction);
+ };
+ $.getJSON("/logs/"+lastLog, updateLog).fail(failFunction);
+ }
+ $.getJSON("/logs", updateLog);
+});
+</script>
+</body>
+</html>
diff --git a/http_connection.h b/http_connection.h
index 27aa5cb..0f23ab7 100644
--- a/http_connection.h
+++ b/http_connection.h
@@ -36,13 +36,15 @@ namespace crow
CROW_LOG_DEBUG << "Connection open, total " << connectionCount << ", " << this;
#endif
}
-#ifdef CROW_ENABLE_DEBUG
+
~Connection()
{
+ res.complete_request_handler_ = nullptr;
+#ifdef CROW_ENABLE_DEBUG
connectionCount --;
CROW_LOG_DEBUG << "Connection closed, total " << connectionCount << ", " << this;
- }
#endif
+ }
void start()
{
@@ -66,28 +68,6 @@ namespace crow
void handle()
{
- static std::unordered_map<int, std::string> statusCodes = {
- {200, "HTTP/1.1 200 OK\r\n"},
- {201, "HTTP/1.1 201 Created\r\n"},
- {202, "HTTP/1.1 202 Accepted\r\n"},
- {204, "HTTP/1.1 204 No Content\r\n"},
-
- {300, "HTTP/1.1 300 Multiple Choices\r\n"},
- {301, "HTTP/1.1 301 Moved Permanently\r\n"},
- {302, "HTTP/1.1 302 Moved Temporarily\r\n"},
- {304, "HTTP/1.1 304 Not Modified\r\n"},
-
- {400, "HTTP/1.1 400 Bad Request\r\n"},
- {401, "HTTP/1.1 401 Unauthorized\r\n"},
- {403, "HTTP/1.1 403 Forbidden\r\n"},
- {404, "HTTP/1.1 404 Not Found\r\n"},
-
- {500, "HTTP/1.1 500 Internal Server Error\r\n"},
- {501, "HTTP/1.1 501 Not Implemented\r\n"},
- {502, "HTTP/1.1 502 Bad Gateway\r\n"},
- {503, "HTTP/1.1 503 Service Unavailable\r\n"},
- };
-
bool is_invalid_request = false;
request req = parser_.to_request();
@@ -109,14 +89,54 @@ namespace crow
}
}
+ CROW_LOG_INFO << "Request: "<< this << " HTTP/" << parser_.http_major << "." << parser_.http_minor << ' '
+ << method_name(req.method) << " " << req.url;
+
+
if (!is_invalid_request)
{
- res = handler_->handle(req);
+ deadline_.cancel();
+ auto self = this->shared_from_this();
+ res.complete_request_handler_ = [self]{ self->complete_request(); };
+ handler_->handle(req, res);
}
+ else
+ {
+ complete_request();
+ }
+ }
+
+ void complete_request()
+ {
+ CROW_LOG_INFO << "Response: " << this << ' ' << res.code << ' ' << close_connection_;
+
+ if (!socket_.is_open())
+ return;
+
+ auto self = this->shared_from_this();
+ res.complete_request_handler_ = nullptr;
+
+ static std::unordered_map<int, std::string> statusCodes = {
+ {200, "HTTP/1.1 200 OK\r\n"},
+ {201, "HTTP/1.1 201 Created\r\n"},
+ {202, "HTTP/1.1 202 Accepted\r\n"},
+ {204, "HTTP/1.1 204 No Content\r\n"},
- CROW_LOG_INFO << "HTTP/" << parser_.http_major << "." << parser_.http_minor << ' '
- << method_name(req.method) << " " << req.url
- << " " << res.code << ' ' << close_connection_;
+ {300, "HTTP/1.1 300 Multiple Choices\r\n"},
+ {301, "HTTP/1.1 301 Moved Permanently\r\n"},
+ {302, "HTTP/1.1 302 Moved Temporarily\r\n"},
+ {304, "HTTP/1.1 304 Not Modified\r\n"},
+
+ {400, "HTTP/1.1 400 Bad Request\r\n"},
+ {401, "HTTP/1.1 401 Unauthorized\r\n"},
+ {403, "HTTP/1.1 403 Forbidden\r\n"},
+ {404, "HTTP/1.1 404 Not Found\r\n"},
+
+ {500, "HTTP/1.1 500 Internal Server Error\r\n"},
+ {501, "HTTP/1.1 501 Not Implemented\r\n"},
+ {502, "HTTP/1.1 502 Bad Gateway\r\n"},
+ {503, "HTTP/1.1 503 Service Unavailable\r\n"},
+ };
static std::string seperator = ": ";
static std::string crlf = "\r\n";
@@ -186,6 +206,7 @@ namespace crow
buffers_.emplace_back(res.body.data(), res.body.size());
do_write();
+ res.clear();
}
private:
diff --git a/http_response.h b/http_response.h
index eff1d0f..1a4cbd3 100644
--- a/http_response.h
+++ b/http_response.h
@@ -5,8 +5,13 @@
namespace crow
{
+ template <typename T>
+ class Connection;
struct response
{
+ template <typename T>
+ friend class crow::Connection;
+
std::string body;
json::wvalue json_value;
int code{200};
@@ -24,22 +29,42 @@ namespace crow
*this = std::move(r);
}
+ response& operator = (const response& r) = delete;
+
response& operator = (response&& r)
{
body = std::move(r.body);
json_value = std::move(r.json_value);
code = r.code;
headers = std::move(r.headers);
+ completed_ = r.completed_;
return *this;
}
- void send(const std::string& body_part)
+ void clear()
+ {
+ body.clear();
+ json_value.clear();
+ code = 200;
+ headers.clear();
+ completed_ = false;
+ }
+
+ void write(const std::string& body_part)
{
body += body_part;
}
void end()
{
+ if (!completed_)
+ {
+ completed_ = true;
+ if (complete_request_handler_)
+ {
+ complete_request_handler_();
+ }
+ }
}
void end(const std::string& body_part)
@@ -47,5 +72,10 @@ namespace crow
body += body_part;
end();
}
+
+ void* p;
+ private:
+ bool completed_{};
+ std::function<void()> complete_request_handler_;
};
}
diff --git a/mustache.h b/mustache.h
index 7f878e3..7218ae8 100644
--- a/mustache.h
+++ b/mustache.h
@@ -282,6 +282,16 @@ namespace crow
out.insert(out.size(), body_, fragment.first, fragment.second-fragment.first);
}
public:
+ std::string render()
+ {
+ context empty_ctx;
+ std::vector<context*> stack;
+ stack.emplace_back(&empty_ctx);
+
+ std::string ret;
+ render_internal(0, fragments_.size()-1, stack, ret, 0);
+ return ret;
+ }
std::string render(context& ctx)
{
std::vector<context*> stack;
diff --git a/routing.h b/routing.h
index 36891f1..28700c1 100644
--- a/routing.h
+++ b/routing.h
@@ -24,7 +24,7 @@ namespace crow
virtual void validate() = 0;
- virtual response handle(const request&, const routing_params&) = 0;
+ virtual void handle(const request&, response&, const routing_params&) = 0;
protected:
@@ -42,6 +42,7 @@ namespace crow
H3& handler_with_req_res;
const routing_params& params;
const request& req;
+ response& res;
};
template <typename F, int NInt, int NUint, int NDouble, int NString, typename S1, typename S2>
@@ -52,75 +53,83 @@ namespace crow
template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2>
struct call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>, black_magic::S<Args2...>>
{
- response operator()(F cparams)
+ void operator()(F cparams)
{
using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<int64_t, NInt>>;
- return call<F, NInt+1, NUint, NDouble, NString,
- black_magic::S<Args1...>, pushed>()(cparams);
+ call<F, NInt+1, NUint, NDouble, NString,
+ black_magic::S<Args1...>, pushed>()(cparams);
}
};
template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2>
struct call<F, NInt, NUint, NDouble, NString, black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
{
- response operator()(F cparams)
+ void operator()(F cparams)
{
using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<uint64_t, NUint>>;
- return call<F, NInt, NUint+1, NDouble, NString,
- black_magic::S<Args1...>, pushed>()(cparams);
+ call<F, NInt, NUint+1, NDouble, NString,
+ black_magic::S<Args1...>, pushed>()(cparams);
}
};
template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2>
struct call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>, black_magic::S<Args2...>>
{
- response operator()(F cparams)
+ void operator()(F cparams)
{
using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<double, NDouble>>;
- return call<F, NInt, NUint, NDouble+1, NString,
- black_magic::S<Args1...>, pushed>()(cparams);
+ call<F, NInt, NUint, NDouble+1, NString,
+ black_magic::S<Args1...>, pushed>()(cparams);
}
};
template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2>
struct call<F, NInt, NUint, NDouble, NString, black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
{
- response operator()(F cparams)
+ void operator()(F cparams)
{
using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<std::string, NString>>;
- return call<F, NInt, NUint, NDouble, NString+1,
- black_magic::S<Args1...>, pushed>()(cparams);
+ call<F, NInt, NUint, NDouble, NString+1,
+ black_magic::S<Args1...>, pushed>()(cparams);
}
};
template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1>
struct call<F, NInt, NUint, NDouble, NString, black_magic::S<>, black_magic::S<Args1...>>
{
- response operator()(F cparams)
+ void operator()(F cparams)
{
if (cparams.handler)
- return cparams.handler(
+ {
+ cparams.res = cparams.handler(
cparams.params.template get<typename Args1::type>(Args1::pos)...
);
+ cparams.res.end();
+ return;
+ }
if (cparams.handler_with_req)
- return cparams.handler_with_req(
+ {
+ cparams.res = cparams.handler_with_req(
cparams.req,
cparams.params.template get<typename Args1::type>(Args1::pos)...
);
+ cparams.res.end();
+ return;
+ }
if (cparams.handler_with_req_res)
{
- crow::response res;
cparams.handler_with_req_res(
cparams.req,
- res,
+ cparams.res,
cparams.params.template get<typename Args1::type>(Args1::pos)...
);
- return res;
+ return;
}
#ifdef CROW_ENABLE_LOGGING
std::cerr << "ERROR cannot find handler" << std::endl;
#endif
- return response(500);
+ // we already found matched url; this is server error
+ cparams.res = response(500);
}
};
public:
@@ -223,24 +232,23 @@ namespace crow
(*this).template operator()<Func>(std::forward(f));
}
- response handle(const request& req, const routing_params& params)
- {
- return
- call<
- call_params<
- decltype(handler_),
- decltype(handler_with_req_),
- decltype(handler_with_req_res_)>,
- 0, 0, 0, 0,
- black_magic::S<Args...>,
- black_magic::S<>
- >()(
- call_params<
- decltype(handler_),
- decltype(handler_with_req_),
- decltype(handler_with_req_res_)>
- {handler_, handler_with_req_, handler_with_req_res_, params, req}
- );
+ void handle(const request& req, response& res, const routing_params& params) override
+ {
+ call<
+ call_params<
+ decltype(handler_),
+ decltype(handler_with_req_),
+ decltype(handler_with_req_res_)>,
+ 0, 0, 0, 0,
+ black_magic::S<Args...>,
+ black_magic::S<>
+ >()(
+ call_params<
+ decltype(handler_),
+ decltype(handler_with_req_),
+ decltype(handler_with_req_res_)>
+ {handler_, handler_with_req_, handler_with_req_res_, params, req, res}
+ );
}
private:
@@ -614,21 +622,26 @@ public:
}
}
- response handle(const request& req)
+ void handle(const request& req, response& res)
{
auto found = trie_.find(req);
unsigned rule_index = found.first;
if (!rule_index)
- return response(404);
+ {
+ CROW_LOG_DEBUG << "Cannot match rules " << req.url;
+ res = response(404);
+ res.end();
+ return;
+ }
if (rule_index >= rules_.size())
throw std::runtime_error("Trie internal structure corrupted!");
CROW_LOG_DEBUG << "Matched rule '" << ((TaggedRule<>*)rules_[rule_index].get())->rule_ << "'";
- return rules_[rule_index]->handle(req, found.second);
+ rules_[rule_index]->handle(req, res, found.second);
}
void debug_print()
diff --git a/unittest.cpp b/unittest.cpp
index e4f8105..918d027 100644
--- a/unittest.cpp
+++ b/unittest.cpp
@@ -72,9 +72,11 @@ TEST(Rule)
r.validate();
+ response res;
+
// executing handler
ASSERT_EQUAL(0, x);
- r.handle(request(), routing_params());
+ r.handle(request(), res, routing_params());
ASSERT_EQUAL(1, x);
// registering handler with request argument
@@ -84,7 +86,7 @@ TEST(Rule)
// executing handler
ASSERT_EQUAL(1, x);
- r.handle(request(), routing_params());
+ r.handle(request(), res, routing_params());
ASSERT_EQUAL(2, x);
}
@@ -143,13 +145,24 @@ TEST(RoutingTest)
app.validate();
//app.debug_print();
+ {
+ request req;
+ response res;
+
+ req.url = "/-1";
+
+ app.handle(req, res);
+
+ ASSERT_EQUAL(404, res.code);
+ }
{
request req;
+ response res;
req.url = "/0/1001999";
- auto res = app.handle(req);
+ app.handle(req, res);
ASSERT_EQUAL(200, res.code);
@@ -158,10 +171,11 @@ TEST(RoutingTest)
{
request req;
+ response res;
req.url = "/1/-100/1999";
- auto res = app.handle(req);
+ app.handle(req, res);
ASSERT_EQUAL(200, res.code);
@@ -170,11 +184,12 @@ TEST(RoutingTest)
}
{
request req;
+ response res;
req.url = "/4/5000/3/-2.71828/hellhere";
req.headers["TestHeader"] = "Value";
- auto res = app.handle(req);
+ app.handle(req, res);
ASSERT_EQUAL(200, res.code);
@@ -185,11 +200,12 @@ TEST(RoutingTest)
}
{
request req;
+ response res;
req.url = "/5/-5/999/3.141592/hello_there/a/b/c/d";
req.headers["TestHeader"] = "Value";
- auto res = app.handle(req);
+ app.handle(req, res);
ASSERT_EQUAL(200, res.code);