Merge branch 'master' into master

This commit is contained in:
Jaeseung Ha 2017-09-17 18:01:57 +09:00 committed by GitHub
commit ef17b8cb9b
15 changed files with 5299 additions and 5071 deletions

3
.gitignore vendored
View File

@ -33,3 +33,6 @@ build
.directory .directory
crow_all.h crow_all.h
# conan.io
build/

View File

@ -11,14 +11,22 @@ compiler:
env: env:
matrix: matrix:
- COMPILER=g++-4.8 CCOMPILER=gcc-4.8 PUSH_COVERAGE=ON - COMPILER=g++-4.8 CCOMPILER=gcc-4.8 PUSH_COVERAGE=ON
- COMPILER=g++-4.9 CCOMPILER=gcc-4.9
- COMPILER=g++-5 CCOMPILER=gcc-5
- COMPILER=clang++-3.6 CCOMPILER=clang-3.6
addons: addons:
apt: apt:
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
- boost-latest - boost-latest
- llvm-toolchain-precise
- llvm-toolchain-precise-3.6
packages: packages:
- g++-4.8 - g++-4.8
- g++-4.9
- g++-5
- clang-3.6
- libboost1.55-all-dev - libboost1.55-all-dev
- python-pip - python-pip
@ -36,4 +44,4 @@ script: make && ctest
after_success: after_success:
- cd .. - cd ..
- if [ "PUSH_COVERAGE" == "ON" ]; then coveralls --gcov gcov-4.8 -i include --gcov-options '\-lp'; fi - if [ "$PUSH_COVERAGE" == "ON" ]; then coveralls --gcov gcov-4.8 -i include --gcov-options '\-lp'; fi

View File

@ -1,5 +1,11 @@
cmake_minimum_required(VERSION 2.8) cmake_minimum_required(VERSION 2.8)
project (crow_all) project (crow_all)
if(EXISTS "${CMAKE_BINARY_DIR}/conanbuildinfo.cmake")
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
endif()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
find_package(Tcmalloc) find_package(Tcmalloc)
find_package(Threads) find_package(Threads)

View File

@ -32,7 +32,7 @@ int main()
- You can also use [json11](https://github.com/dropbox/json11) or [rapidjson](https://github.com/miloyip/rapidjson) for better speed or readability - You can also use [json11](https://github.com/dropbox/json11) or [rapidjson](https://github.com/miloyip/rapidjson) for better speed or readability
- [Mustache](http://mustache.github.io/) based templating library (crow::mustache) - [Mustache](http://mustache.github.io/) based templating library (crow::mustache)
- Header only - Header only
- Provide an amalgamated header file `crow_all.h' with every features - Provide an amalgamated header file `crow_all.h` with every features
- Middleware support - Middleware support
- Websocket support - Websocket support

File diff suppressed because it is too large Load Diff

28
conanfile.py Normal file
View File

@ -0,0 +1,28 @@
from conans import ConanFile, CMake
class CrowConan(ConanFile):
name = "Crow"
version = "0.1"
url = "https://github.com/ipkn/crow"
license = "MIT; see https://github.com/ipkn/crow/blob/master/LICENSE"
generators = "cmake"
settings = "os", "compiler", "build_type", "arch"
requires = (("Boost/1.60.0@lasote/stable"),
("OpenSSL/1.0.2i@lasote/stable"))
# No exports necessary
def source(self):
# this will create a hello subfolder, take it into account
self.run("git clone https://github.com/ipkn/crow.git")
def build(self):
cmake = CMake(self.settings)
self.run('cmake %s/crow %s' % (self.conanfile_directory, cmake.command_line))
self.run("cmake --build . %s" % cmake.build_config)
self.run("make")
def package(self):
self.copy("*.h", dst="include", src="amalgamate")

View File

@ -146,6 +146,15 @@ int main()
for(const auto& countVal : count) { for(const auto& countVal : count) {
os << " - " << countVal << '\n'; os << " - " << countVal << '\n';
} }
// To get a dictionary from the request
// You have to submit something like '/params?mydict[a]=b&mydict[abcd]=42' to have a list of pairs ((a, b) and (abcd, 42))
auto mydict = req.url_params.get_dict("mydict");
os << "The key 'dict' contains " << mydict.size() << " value(s).\n";
for(const auto& mydictVal : mydict) {
os << " - " << mydictVal.first << " -> " << mydictVal.second << '\n';
}
return crow::response{os.str()}; return crow::response{os.str()};
}); });

View File

@ -33,8 +33,13 @@ int main()
CROW_ROUTE(app, "/") CROW_ROUTE(app, "/")
([]{ ([]{
char name[256];
gethostname(name, 256);
crow::mustache::context x;
x["servername"] = name;
auto page = crow::mustache::load("ws.html"); auto page = crow::mustache::load("ws.html");
return page.render(); return page.render(x);
}); });
app.port(40080) app.port(40080)

View File

@ -11,7 +11,7 @@
<textarea id="log" cols=100 rows=50> <textarea id="log" cols=100 rows=50>
</textarea> </textarea>
<script> <script>
var sock = new WebSocket("ws://i.ipkn.me:40080/ws"); var sock = new WebSocket("ws://{{servername}}:40080/ws");
sock.onopen = ()=>{ sock.onopen = ()=>{
console.log('open') console.log('open')
} }

View File

@ -374,6 +374,7 @@ namespace crow
{401, "HTTP/1.1 401 Unauthorized\r\n"}, {401, "HTTP/1.1 401 Unauthorized\r\n"},
{403, "HTTP/1.1 403 Forbidden\r\n"}, {403, "HTTP/1.1 403 Forbidden\r\n"},
{404, "HTTP/1.1 404 Not Found\r\n"}, {404, "HTTP/1.1 404 Not Found\r\n"},
{422, "HTTP/1.1 422 Unprocessable Entity\r\n"},
{500, "HTTP/1.1 500 Internal Server Error\r\n"}, {500, "HTTP/1.1 500 Internal Server Error\r\n"},
{501, "HTTP/1.1 501 Not Implemented\r\n"}, {501, "HTTP/1.1 501 Not Implemented\r\n"},

View File

@ -121,13 +121,20 @@ namespace crow
timer.async_wait(handler); timer.async_wait(handler);
init_count ++; init_count ++;
while(1)
{
try try
{ {
io_service_pool_[i]->run(); if (io_service_pool_[i]->run() == 0)
{
// when io_service.run returns 0, there are no more works to do.
break;
}
} catch(std::exception& e) } catch(std::exception& e)
{ {
CROW_LOG_ERROR << "Worker Crash: An uncaught exception occurred: " << e.what(); CROW_LOG_ERROR << "Worker Crash: An uncaught exception occurred: " << e.what();
} }
}
})); }));
if (tick_function_ && tick_interval_.count() > 0) if (tick_function_ && tick_interval_.count() > 0)
@ -193,6 +200,10 @@ namespace crow
p->start(); p->start();
}); });
} }
else
{
delete p;
}
do_accept(); do_accept();
}); });
} }

View File

@ -1264,6 +1264,23 @@ namespace crow
return *this; return *this;
} }
wvalue& operator=(std::vector<wvalue>&& v)
{
if (t_ != type::List)
reset();
t_ = type::List;
if (!l)
l = std::unique_ptr<std::vector<wvalue>>(new std::vector<wvalue>{});
l->clear();
l->resize(v.size());
size_t idx = 0;
for(auto& x:v)
{
(*l)[idx++] = std::move(x);
}
return *this;
}
template <typename T> template <typename T>
wvalue& operator=(const std::vector<T>& v) wvalue& operator=(const std::vector<T>& v)
{ {

View File

@ -4,7 +4,9 @@
#include <string.h> #include <string.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include <unordered_map>
#include <iostream> #include <iostream>
#include <boost/optional.hpp>
namespace crow namespace crow
{ {
@ -197,6 +199,48 @@ inline char * qs_k2v(const char * key, char * const * qs_kv, int qs_kv_size, int
return NULL; return NULL;
} }
inline boost::optional<std::pair<std::string, std::string>> qs_dict_name2kv(const char * dict_name, char * const * qs_kv, int qs_kv_size, int nth = 0)
{
int i;
size_t name_len, skip_to_eq, skip_to_brace_open, skip_to_brace_close;
name_len = strlen(dict_name);
#ifdef _qsSORTING
// TODO: binary search for key in the sorted qs_kv
#else // _qsSORTING
for(i=0; i<qs_kv_size; i++)
{
if ( strncmp(dict_name, qs_kv[i], name_len) == 0 )
{
skip_to_eq = strcspn(qs_kv[i], "=");
if ( qs_kv[i][skip_to_eq] == '=' )
skip_to_eq++;
skip_to_brace_open = strcspn(qs_kv[i], "[");
if ( qs_kv[i][skip_to_brace_open] == '[' )
skip_to_brace_open++;
skip_to_brace_close = strcspn(qs_kv[i], "]");
if ( skip_to_brace_open <= skip_to_brace_close &&
skip_to_brace_open > 0 &&
skip_to_brace_close > 0 &&
nth == 0 )
{
auto key = std::string(qs_kv[i] + skip_to_brace_open, skip_to_brace_close - skip_to_brace_open);
auto value = std::string(qs_kv[i] + skip_to_eq);
return boost::make_optional(std::make_pair(key, value));
}
else
{
--nth;
}
}
}
#endif // _qsSORTING
return boost::none;
}
inline char * qs_scanvalue(const char * key, const char * qs, char * val, size_t val_len) inline char * qs_scanvalue(const char * key, const char * qs, char * val, size_t val_len)
{ {
@ -336,6 +380,20 @@ namespace crow
return ret; return ret;
} }
std::unordered_map<std::string, std::string> get_dict (const std::string& name) const
{
std::unordered_map<std::string, std::string> ret;
int count = 0;
while(1)
{
if (auto element = qs_dict_name2kv(name.c_str(), key_value_pairs_.data(), key_value_pairs_.size(), count++))
ret.insert(*element);
else
break;
}
return ret;
}
private: private:
std::string url_; std::string url_;

View File

@ -156,7 +156,7 @@ namespace crow
struct Wrapped struct Wrapped
{ {
template <typename ... Args> template <typename ... Args>
void set(Func f, typename std::enable_if< void set_(Func f, typename std::enable_if<
!std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value !std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value
, int>::type = 0) , int>::type = 0)
{ {
@ -190,7 +190,7 @@ namespace crow
}; };
template <typename ... Args> template <typename ... Args>
void set(Func f, typename std::enable_if< void set_(Func f, typename std::enable_if<
std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value && std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value &&
!std::is_same<typename std::tuple_element<1, std::tuple<Args..., void, void>>::type, response&>::value !std::is_same<typename std::tuple_element<1, std::tuple<Args..., void, void>>::type, response&>::value
, int>::type = 0) , int>::type = 0)
@ -205,7 +205,7 @@ namespace crow
} }
template <typename ... Args> template <typename ... Args>
void set(Func f, typename std::enable_if< void set_(Func f, typename std::enable_if<
std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value && std::is_same<typename std::tuple_element<0, std::tuple<Args..., void>>::type, const request&>::value &&
std::is_same<typename std::tuple_element<1, std::tuple<Args..., void, void>>::type, response&>::value std::is_same<typename std::tuple_element<1, std::tuple<Args..., void, void>>::type, response&>::value
, int>::type = 0) , int>::type = 0)
@ -276,12 +276,12 @@ namespace crow
void handle_upgrade(const request& req, response&, SocketAdaptor&& adaptor) override void handle_upgrade(const request& req, response&, SocketAdaptor&& adaptor) override
{ {
new crow::websocket::Connection<SocketAdaptor>(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_); new crow::websocket::Connection<SocketAdaptor>(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
} }
#ifdef CROW_ENABLE_SSL #ifdef CROW_ENABLE_SSL
void handle_upgrade(const request& req, response&, SSLAdaptor&& adaptor) override void handle_upgrade(const request& req, response&, SSLAdaptor&& adaptor) override
{ {
new crow::websocket::Connection<SSLAdaptor>(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_); new crow::websocket::Connection<SSLAdaptor>(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
} }
#endif #endif
@ -313,11 +313,19 @@ namespace crow
return *this; return *this;
} }
template <typename Func>
self_t& onaccept(Func f)
{
accept_handler_ = f;
return *this;
}
protected: protected:
std::function<void(crow::websocket::connection&)> open_handler_; std::function<void(crow::websocket::connection&)> open_handler_;
std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_; std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_;
std::function<void(crow::websocket::connection&, const std::string&)> close_handler_; std::function<void(crow::websocket::connection&, const std::string&)> close_handler_;
std::function<void(crow::websocket::connection&)> error_handler_; std::function<void(crow::websocket::connection&)> error_handler_;
std::function<bool(const crow::request&)> accept_handler_;
}; };
template <typename T> template <typename T>
@ -410,7 +418,7 @@ namespace crow
throw std::runtime_error("route_dynamic: Handler type is mismatched with URL parameters: " + rule_); throw std::runtime_error("route_dynamic: Handler type is mismatched with URL parameters: " + rule_);
} }
auto ret = detail::routing_handler_call_helper::Wrapped<Func, typename function_t::template arg<Indices>...>(); auto ret = detail::routing_handler_call_helper::Wrapped<Func, typename function_t::template arg<Indices>...>();
ret.template set< ret.template set_<
typename function_t::template arg<Indices>... typename function_t::template arg<Indices>...
>(std::move(f)); >(std::move(f));
return ret; return ret;
@ -456,10 +464,16 @@ namespace crow
static_assert(!std::is_same<void, decltype(f(std::declval<Args>()...))>::value, static_assert(!std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
"Handler function cannot have void return type; valid return types: string, int, crow::resposne, crow::json::wvalue"); "Handler function cannot have void return type; valid return types: string, int, crow::resposne, crow::json::wvalue");
handler_ = [f = std::move(f)](const request&, response& res, Args ... args){ handler_ = (
#ifdef CROW_CAN_USE_CPP14
[f = std::move(f)]
#else
[f]
#endif
(const request&, response& res, Args ... args){
res = response(f(args...)); res = response(f(args...));
res.end(); res.end();
}; });
} }
template <typename Func> template <typename Func>
@ -475,10 +489,16 @@ namespace crow
static_assert(!std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<Args>()...))>::value, static_assert(!std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<Args>()...))>::value,
"Handler function cannot have void return type; valid return types: string, int, crow::resposne, crow::json::wvalue"); "Handler function cannot have void return type; valid return types: string, int, crow::resposne, crow::json::wvalue");
handler_ = [f = std::move(f)](const crow::request& req, crow::response& res, Args ... args){ handler_ = (
#ifdef CROW_CAN_USE_CPP14
[f = std::move(f)]
#else
[f]
#endif
(const crow::request& req, crow::response& res, Args ... args){
res = response(f(req, args...)); res = response(f(req, args...));
res.end(); res.end();
}; });
} }
template <typename Func> template <typename Func>

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#include <boost/array.hpp>
#include "crow/socket_adaptors.h" #include "crow/socket_adaptors.h"
#include "crow/http_request.h" #include "crow/http_request.h"
#include "crow/TinySHA1.hpp" #include "crow/TinySHA1.hpp"
@ -39,8 +40,10 @@ namespace crow
std::function<void(crow::websocket::connection&)> open_handler, std::function<void(crow::websocket::connection&)> open_handler,
std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler, std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler,
std::function<void(crow::websocket::connection&, const std::string&)> close_handler, std::function<void(crow::websocket::connection&, const std::string&)> close_handler,
std::function<void(crow::websocket::connection&)> error_handler) std::function<void(crow::websocket::connection&)> error_handler,
std::function<bool(const crow::request&)> accept_handler)
: adaptor_(std::move(adaptor)), open_handler_(std::move(open_handler)), message_handler_(std::move(message_handler)), close_handler_(std::move(close_handler)), error_handler_(std::move(error_handler)) : adaptor_(std::move(adaptor)), open_handler_(std::move(open_handler)), message_handler_(std::move(message_handler)), close_handler_(std::move(close_handler)), error_handler_(std::move(error_handler))
, accept_handler_(std::move(accept_handler))
{ {
if (!boost::iequals(req.get_header_value("upgrade"), "websocket")) if (!boost::iequals(req.get_header_value("upgrade"), "websocket"))
{ {
@ -48,6 +51,17 @@ namespace crow
delete this; delete this;
return; return;
} }
if (accept_handler_)
{
if (!accept_handler_(req))
{
adaptor.close();
delete this;
return;
}
}
// Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== // Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
// Sec-WebSocket-Version: 13 // Sec-WebSocket-Version: 13
std::string magic = req.get_header_value("Sec-WebSocket-Key") + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; std::string magic = req.get_header_value("Sec-WebSocket-Key") + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
@ -486,6 +500,7 @@ namespace crow
std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_; std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_;
std::function<void(crow::websocket::connection&, const std::string&)> close_handler_; std::function<void(crow::websocket::connection&, const std::string&)> close_handler_;
std::function<void(crow::websocket::connection&)> error_handler_; std::function<void(crow::websocket::connection&)> error_handler_;
std::function<bool(const crow::request&)> accept_handler_;
}; };
} }
} }