@@ -0,0 +1,53 @@ | |||
// | |||
// Copyright (C) 2009-2012 Artyom Beilis (Tonkikh) | |||
// | |||
// Distributed under the Boost Software License, Version 1.0. (See | |||
// accompanying file LICENSE_1_0.txt or copy at | |||
// http://www.boost.org/LICENSE_1_0.txt) | |||
// | |||
#ifndef BOOSTER_TYPE_TRAITS_H | |||
#define BOOSTER_TYPE_TRAITS_H | |||
#include <booster/traits/enable_if.h> | |||
#include <booster/traits/is_base_of.h> | |||
namespace booster{ | |||
template<typename T> | |||
struct remove_const | |||
{ | |||
typedef T type; | |||
}; | |||
template<typename T> | |||
struct remove_const<T const> | |||
{ | |||
typedef T type; | |||
}; | |||
template<typename T> | |||
struct remove_reference | |||
{ | |||
typedef T type; | |||
}; | |||
template<typename T> | |||
struct remove_reference<T &> | |||
{ | |||
typedef T type; | |||
}; | |||
template<typename T> | |||
struct remove_const_reference { | |||
typedef typename remove_const<typename remove_reference<T>::type>::type type; | |||
}; | |||
} // booster | |||
#endif | |||
@@ -291,6 +291,18 @@ namespace cppcms { | |||
/// | |||
bool is_asynchronous(); | |||
/// | |||
/// Returns true if there is a context added or assigned to application | |||
/// | |||
/// \ver{v1_2} | |||
bool has_context(); | |||
/// | |||
/// Returns true if the application owns a context (that can be released for example) | |||
/// | |||
/// \ver{v1_2} | |||
bool owns_context(); | |||
/// | |||
/// This is main function of the application that is called when it is matched | |||
@@ -19,6 +19,99 @@ namespace cppcms { | |||
namespace util { | |||
/// | |||
/// Simple std::streambuf to create input from [char const *,char const *) range | |||
/// | |||
/// \ver{v1_2} | |||
class const_char_buf : public std::streambuf { | |||
public: | |||
/// | |||
/// Create Empty buffer | |||
/// | |||
const_char_buf() | |||
{ | |||
range(0,0); | |||
} | |||
/// | |||
/// Create a buffer from a range | |||
/// | |||
const_char_buf(char const *begin,char const *end) | |||
{ | |||
range(begin,end); | |||
} | |||
/// | |||
/// Define the range for existing buffer, pointer is reset to begin | |||
/// | |||
void range(char const *cbegin,char const *cend) | |||
{ | |||
char *begin = const_cast<char*>(cbegin); | |||
char *end = const_cast<char*>(cend); | |||
setg(begin,begin,end); | |||
} | |||
/// | |||
/// Begin of range | |||
/// | |||
char const *begin() const | |||
{ | |||
return eback(); | |||
} | |||
/// | |||
/// End of range | |||
/// | |||
char const *end() const | |||
{ | |||
return egptr(); | |||
} | |||
}; | |||
/// | |||
/// Simple std::istream implementation for range of [char const *,char const *) | |||
/// | |||
/// \ver{v1_2} | |||
class const_char_istream : public std::istream { | |||
public: | |||
/// | |||
/// Create new empty stream | |||
/// | |||
const_char_istream() : std::istream(0) | |||
{ | |||
init(&buf_); | |||
} | |||
/// | |||
/// Create stream initialized with range [begin,end) | |||
/// | |||
const_char_istream(char const *begin,char const *end) : std::istream(0) | |||
{ | |||
buf_.range(begin,end); | |||
init(&buf_); | |||
} | |||
/// | |||
/// Get begin of the range | |||
/// | |||
char const *begin() const | |||
{ | |||
return buf_.begin(); | |||
} | |||
/// | |||
/// Get end of the range | |||
/// | |||
char const *end() const | |||
{ | |||
return buf_.end(); | |||
} | |||
/// | |||
/// Set range, resets pointer to start and clears flags | |||
/// | |||
void range(char const *begin,char const *end) | |||
{ | |||
buf_.range(begin,end); | |||
clear(); | |||
} | |||
private: | |||
const_char_buf buf_; | |||
}; | |||
/// | |||
/// \brief Very simple output stream buffer that uses stack for small chunks of | |||
/// text and then allocates memory of the default buffer is too small. | |||
/// | |||
@@ -14,14 +14,37 @@ | |||
#include <booster/hold_ptr.h> | |||
#include <booster/traits/enable_if.h> | |||
#include <booster/traits/is_base_of.h> | |||
#include <booster/traits/type_traits.h> | |||
#include <booster/regex.h> | |||
#include <cppcms/application.h> | |||
#include <cppcms/steal_buf.h> | |||
#include <string> | |||
#include <list> | |||
namespace cppcms { | |||
class application; | |||
/// | |||
/// Sets the content of parameter to output | |||
/// | |||
/// \ver{v1_2} | |||
inline bool parse_url_parameter(util::const_char_istream ¶meter,std::string &output) | |||
{ | |||
output.assign(parameter.begin(),parameter.end()); | |||
return true; | |||
} | |||
/// | |||
/// Parses general value | |||
/// | |||
/// \ver{v1_2} | |||
template<typename ParamType> | |||
bool parse_url_parameter(util::const_char_istream ¶meter,ParamType &value) | |||
{ | |||
parameter >> value; | |||
if(!parameter || !parameter.eof()) | |||
return false; | |||
return true; | |||
} | |||
/// | |||
/// \brief This class is used to glue between member function of application class and urls | |||
@@ -29,22 +52,61 @@ namespace cppcms { | |||
/// This class is used in context of \a cppcms::application with its \a url member function. | |||
/// It uses regular expression to bind between url and callbacks that actually process the | |||
/// request. It also allows mount sub-applications to the root of | |||
/// the primary application. For example: | |||
/// the primary application. | |||
/// | |||
/// There are two families of functions, assign and map. | |||
/// Older `assign` interface allows matching only URL against regular expression. | |||
/// and passes std::string as parameter. All the validation must be | |||
/// performed by regular expression or the code that was called. | |||
/// | |||
/// Newer `map` interface allows both matching an URL and an HTTP request method. | |||
/// Parameters are parsed using `bool parse_url_parameter(util::const_char_istream ¶meter,Type ¶m)` | |||
/// that by default uses std::istream to perform casting. | |||
/// | |||
/// Additionally every matched parameter is checked to contain valid text encoding | |||
/// | |||
/// Newer API uses map member functions family that was introduced in CppCMS 1.1. | |||
/// | |||
/// For example: | |||
/// | |||
/// \code | |||
/// class my_web_project : public cppcms::application { | |||
/// users_app users_; | |||
/// /* Older Handlers */ | |||
/// void display_page(std::string) | |||
/// ... | |||
/// /* Newer CppCMS 1.1 handlers */ | |||
/// void get_resource(int id); | |||
/// void update_resource(int id); | |||
/// void new_resource(); | |||
/// void id_by_name(std::string const &name); /* name checked for valid encoding */ | |||
/// void display_page_by_id(int id) | |||
/// public: | |||
/// my_web_project() { | |||
/// url().assign("^/page/(\\d+)/?$",bind1st(mem_fun(&my_web_project::display_page),this),1); | |||
/// /* Older API */ | |||
/// dispatcher().assign("/page/(\\d+)/?",&my_web_project::display_page,this,1); | |||
/// | |||
/// /* New API - CppCMS 1.1 and above */ | |||
/// | |||
/// dispatcher().map("GET","/resource/(\\d+)",&my_web_project::get_resource,this,1); | |||
/// dispatcher().map("PUT","/resource/(\\d+)",&my_web_project::update_resource,this,1); | |||
/// dispatcher().map("POST","/resources",&my_web_project::new_resource,this); | |||
/// dispatcher().map("GET"."/id_by_name/(.*)",&my_web_project::id_by_name,this,1); | |||
/// dispatcher().map("/page/(\\d+)",&my_web_project::display_page_by_id,this,1); /* any method */ | |||
/// ... | |||
/// \endcode | |||
/// | |||
/// | |||
class CPPCMS_API url_dispatcher : public booster::noncopyable { | |||
public: | |||
/// | |||
/// \brief RESTful API Handler that validates parameters and executes a method. | |||
/// | |||
/// If validation fails it should return false and thus the matching would continue to next handler | |||
/// | |||
/// \ver{v1_2} | |||
typedef booster::function<bool(cppcms::application &,booster::cmatch const &)> generic_handler; | |||
// Handlers | |||
typedef booster::function<void()> handler; | |||
typedef booster::function<void(booster::cmatch const &)> rhandler; | |||
@@ -55,6 +117,100 @@ namespace cppcms { | |||
typedef booster::function<void(std::string,std::string,std::string,std::string,std::string)> handler5; | |||
typedef booster::function<void(std::string,std::string,std::string,std::string,std::string,std::string)> handler6; | |||
/// | |||
/// Map a callback \a h to a URL matching regular expression \a re and an HTTP \a method | |||
/// | |||
/// \param method - HTTP method to match like GET, POST, note regular expression can be used as well, for example "(POST|PUT)" | |||
/// \param re - matched URL | |||
/// \param h - handler to execute | |||
/// | |||
/// \ver{v1_2} | |||
void map_generic(std::string const &method,std::string const &re,generic_handler const &h); | |||
/// | |||
/// Map a callback \a h to a URL matching regular expression \a re | |||
/// | |||
/// \param re - matched URL | |||
/// \param h - handler to execute | |||
/// | |||
/// \ver{v1_2} | |||
void map_generic(std::string const &re,generic_handler const &h); | |||
/// | |||
/// \brief Map \a member of \a app as a URL handler that matches regualr expression \a re and HTTP method \a method | |||
/// | |||
/// \param method - HTTP method to match like GET, POST, note regular expression can be used as well, for example "(POST|PUT)" | |||
/// \param re - matched URL | |||
/// \param member - member function of application \a app | |||
/// \param app - application that its \a member is called | |||
/// | |||
/// In addition to calling \a member function it calls app->init() before call | |||
/// and app->clean() after the call of the C is derived from cppcms::application | |||
/// | |||
/// \ver{v1_2} | |||
template<typename C> | |||
void map(std::string const &method,std::string const &re,void (C::*member)(),C *app) | |||
{ | |||
map_generic(method,re,url_binder0<C>(member,app)); | |||
} | |||
/// | |||
/// \brief Map \a member of \a app as a URL handler that matches regualr expression \a re | |||
/// | |||
/// \param re - matched URL | |||
/// \param member - member function of application \a app | |||
/// \param app - application that its \a member is called | |||
/// | |||
/// In addition to calling \a member function it calls app->init() before call | |||
/// and app->clean() after the call of the C is derived from cppcms::application | |||
/// | |||
/// \ver{v1_2} | |||
template<typename C> | |||
void map(std::string const &re,void (C::*member)(),C *app) | |||
{ | |||
map_generic(re,url_binder0<C>(member,app)); | |||
} | |||
/// | |||
/// \brief Map \a member of \a app as a URL handler that matches regualr expression \a re and HTTP method \a method | |||
/// | |||
/// \param method - HTTP method to match like GET, POST, note regular expression can be used as well, for example "(POST|PUT)" | |||
/// \param re - matched URL | |||
/// \param member - member function of application \a app | |||
/// \param app - application that its \a member is called | |||
/// \param g1 - a matched group passed as first parameter of \a member after validation and conversion using parse_url_parameter function | |||
/// | |||
/// In addition to calling \a member function it calls app->init() before call | |||
/// and app->clean() after the call of the C is derived from cppcms::application | |||
/// | |||
/// | |||
/// \ver{v1_2} | |||
template<typename C,typename P1> | |||
void map(std::string const &method,std::string const &re,void (C::*member)(P1),C *app,int g1) | |||
{ | |||
map_generic(method,re,url_binder1<C,P1>(member,app,g1)); | |||
} | |||
/// | |||
/// \brief Map \a member of \a app as a URL handler that matches regualr expression \a re and HTTP method \a method | |||
/// | |||
/// \param re - matched URL | |||
/// \param member - member function of application \a app | |||
/// \param app - application that its \a member is called | |||
/// \param g1 - a matched group passed as first parameter of \a member after validation and conversion using parse_url_parameter function | |||
/// | |||
/// In addition to calling \a member function it calls app->init() before call | |||
/// and app->clean() after the call of the C is derived from cppcms::application | |||
/// | |||
/// | |||
/// \ver{v1_2} | |||
template<typename C,typename P1> | |||
void map(std::string const &re,void (C::*member)(P1),C *app,int g1) | |||
{ | |||
map_generic(re,url_binder1<C,P1>(member,app,g1)); | |||
} | |||
/// | |||
/// Assign \a handler to pattern \a regex thus if URL that matches | |||
/// this pattern requested, \a handler is called with matched results | |||
@@ -118,8 +274,11 @@ namespace cppcms { | |||
bool dispatch(std::string url); | |||
/// \cond INTERNAL | |||
url_dispatcher(application *app); | |||
url_dispatcher(); | |||
~url_dispatcher(); | |||
/// \endcond | |||
/// | |||
/// This template function is a shortcut to assign(regex,callback). It allows | |||
@@ -242,6 +401,8 @@ namespace cppcms { | |||
class page_guard { | |||
public: | |||
page_guard(C * /*o*/) {} | |||
page_guard(C * /*o*/,std::istream &) {} | |||
application *app() { return 0; } | |||
}; | |||
template<typename C> | |||
@@ -252,6 +413,7 @@ namespace cppcms { | |||
{ | |||
object_->init(); | |||
} | |||
application *app() { return object_; } | |||
~page_guard() | |||
{ | |||
object_->clear(); | |||
@@ -384,6 +546,9 @@ namespace cppcms { | |||
} | |||
}; | |||
static bool validate_encoding(application &app,char const *begin,char const *end); | |||
static void setup_stream(application &app,std::istream &s); | |||
template<typename C> | |||
struct binder6{ | |||
typedef void (C::*member_type)(std::string,std::string,std::string,std::string,std::string,std::string); | |||
@@ -401,7 +566,78 @@ namespace cppcms { | |||
(object->*member)(p1,p2,p3,p4,p5,p6); | |||
} | |||
}; | |||
template<typename T> | |||
static bool parse(application &app,util::const_char_istream &p,booster::cmatch const &m,int group,T &v) | |||
{ | |||
if(!validate_encoding(app,m[group].first,m[group].second)) | |||
return false; | |||
p.range(m[group].first,m[group].second); | |||
return parse_url_parameter(p,v); | |||
} | |||
template<typename C> | |||
struct url_binder0 { | |||
typedef void (C::*member_type)(); | |||
member_type member; | |||
C *self; | |||
url_binder0(member_type m,C *s) : member(m),self(s) {} | |||
bool operator()(application &,booster::cmatch const &) | |||
{ | |||
page_guard<C> guard(self); | |||
(self->*member)(); | |||
return true; | |||
} | |||
}; | |||
template<typename C,typename P1> | |||
struct url_binder1 { | |||
typedef void (C::*member_type)(P1); | |||
member_type member; | |||
C *self; | |||
int g1; | |||
url_binder1(member_type m,C *s,int p1) : member(m),self(s),g1(p1) {} | |||
bool operator()(application &app,booster::cmatch const &m) | |||
{ | |||
util::const_char_istream s; | |||
setup_stream(app,s); | |||
typename booster::remove_const_reference<P1>::type p1; | |||
bool res = parse(app,s,m,g1,p1); | |||
if(!res) return false; | |||
page_guard<C> guard(self); | |||
(self->*member)(p1); | |||
return true; | |||
} | |||
}; | |||
template<typename C,typename P1,typename P2> | |||
struct url_binder2 { | |||
typedef void (C::*member_type)(P1,P2); | |||
member_type member; | |||
C *self; | |||
int g1,g2; | |||
url_binder2(member_type m,C *s,int p1,int p2) : member(m),self(s),g1(p1),g2(p2) {} | |||
bool operator()(application &app,booster::cmatch const &m) | |||
{ | |||
util::const_char_istream s; | |||
setup_stream(app,s); | |||
typename booster::remove_const_reference<P1>::type p1; | |||
typename booster::remove_const_reference<P2>::type p2; | |||
bool res = parse(app,s,m,g1,p1) && parse(app,s,m,g2,p2); | |||
if(!res) return false; | |||
page_guard<C> guard(self); | |||
(self->*member)(p1,p2); | |||
return true; | |||
} | |||
}; | |||
struct _data; | |||
booster::hold_ptr<_data> d; | |||
@@ -38,7 +38,7 @@ struct application::_data { | |||
cppcms::service *service; | |||
booster::shared_ptr<http::context> conn; | |||
http::context *temp_conn; | |||
url_dispatcher url; | |||
booster::hold_ptr<url_dispatcher> url_disp; | |||
booster::hold_ptr<url_mapper> url_map; | |||
std::vector<application *> managed_children; | |||
booster::weak_ptr<application_specific_pool> my_pool; | |||
@@ -49,6 +49,7 @@ application::application(cppcms::service &srv) : | |||
refs_(0) | |||
{ | |||
parent_=root_=this; | |||
d->url_disp.reset(new url_dispatcher(this)); | |||
d->url_map.reset(new url_mapper(this)); | |||
} | |||
@@ -81,7 +82,7 @@ http::response &application::response() | |||
url_dispatcher &application::dispatcher() | |||
{ | |||
return d->url; | |||
return *d->url_disp; | |||
} | |||
url_mapper &application::mapper() | |||
@@ -116,6 +117,16 @@ http::context &application::context() | |||
return *root()->d->conn; | |||
} | |||
bool application::has_context() | |||
{ | |||
return root()->d->conn || root()->d->temp_conn; | |||
} | |||
bool application::owns_context() | |||
{ | |||
return root()->d->conn; | |||
} | |||
booster::shared_ptr<http::context> application::release_context() | |||
{ | |||
booster::shared_ptr<http::context> ptr=root()->d->conn; | |||
@@ -8,6 +8,9 @@ | |||
#define CPPCMS_SOURCE | |||
#include <cppcms/url_dispatcher.h> | |||
#include <cppcms/application.h> | |||
#include <cppcms/http_request.h> | |||
#include <cppcms/http_context.h> | |||
#include <cppcms/encoding.h> | |||
#include <booster/regex.h> | |||
#include <booster/shared_ptr.h> | |||
@@ -16,23 +19,51 @@ namespace cppcms { | |||
namespace /* anon */ { | |||
struct option : public booster::noncopyable { | |||
option(std::string expr) : | |||
expr_(expr) | |||
option(booster::regex const &expr) : | |||
expr_(expr), | |||
match_method_(0) | |||
{ | |||
} | |||
option(booster::regex const &expr,std::string const &method) : | |||
expr_(expr), | |||
match_method_(1), | |||
mexpr_(method), | |||
method_(method) | |||
{ | |||
for(size_t i=0;i<method.size();i++) { | |||
// common methods | |||
char c=method[i]; | |||
if(!('A'<= c && c<='Z')) { | |||
match_method_ =2; | |||
break; | |||
} | |||
} | |||
} | |||
virtual ~option() | |||
{ | |||
} | |||
bool matches(std::string const &path) | |||
bool matches(std::string const &path,char const *method) | |||
{ | |||
if(match_method_==1) { | |||
if(!method || method_ != method) | |||
return false; | |||
} | |||
else if(match_method_ == 2) { | |||
if(!method || !booster::regex_match(method,mexpr_)) | |||
return false; | |||
} | |||
return booster::regex_match(path.c_str(),match_,expr_); | |||
} | |||
virtual bool dispatch(std::string url) = 0; | |||
virtual bool dispatch(std::string const &url,char const *method,application *app) = 0; | |||
protected: | |||
booster::regex expr_; | |||
booster::cmatch match_; | |||
int match_method_; | |||
booster::regex mexpr_; | |||
std::string method_; | |||
}; | |||
struct mounted : public option { | |||
@@ -43,9 +74,9 @@ namespace cppcms { | |||
{ | |||
} | |||
virtual bool dispatch(std::string url) | |||
virtual bool dispatch(std::string const &url,char const *method,application *) | |||
{ | |||
if(matches(url)) { | |||
if(matches(url,method)) { | |||
app_->main(match_[select_]); | |||
return true; | |||
} | |||
@@ -68,9 +99,9 @@ namespace cppcms { | |||
select_[4]=e; | |||
select_[5]=f; | |||
} | |||
virtual bool dispatch(std::string url) | |||
virtual bool dispatch(std::string const &url,char const *method,application *) | |||
{ | |||
if(matches(url)) { | |||
if(matches(url,method)) { | |||
execute_handler(handle_); | |||
return true; | |||
} | |||
@@ -117,6 +148,28 @@ namespace cppcms { | |||
H handle_; | |||
}; | |||
struct generic_option : public option { | |||
generic_option(booster::regex const &r,url_dispatcher::generic_handler const &h) : | |||
option(r), | |||
handle_(h) | |||
{ | |||
} | |||
generic_option(std::string method,booster::regex const &r,url_dispatcher::generic_handler const &h) : | |||
option(r,method), | |||
handle_(h) | |||
{ | |||
} | |||
virtual bool dispatch(std::string const &url,char const *method,application *app) | |||
{ | |||
if(!app) | |||
return false; | |||
if(matches(url,method)) { | |||
return handle_(*app,match_); | |||
} | |||
return false; | |||
} | |||
url_dispatcher::generic_handler handle_; | |||
}; | |||
template<typename H> | |||
booster::shared_ptr<option> make_handler(std::string expr,H const &handler,int a=0,int b=0,int c=0,int d=0,int e=0,int f=0) | |||
@@ -128,13 +181,17 @@ namespace cppcms { | |||
struct url_dispatcher::_data { | |||
_data(application *a) : app(a) {} | |||
application *app; | |||
std::vector<booster::shared_ptr<option> > options; | |||
booster::shared_ptr<option> last_option; | |||
}; | |||
// Meanwhile nothing | |||
url_dispatcher::url_dispatcher() : | |||
d(new url_dispatcher::_data()) | |||
d(new url_dispatcher::_data(0)) | |||
{ | |||
} | |||
url_dispatcher::url_dispatcher(application *app) : | |||
d(new url_dispatcher::_data(app)) | |||
{ | |||
} | |||
url_dispatcher::~url_dispatcher() | |||
@@ -144,8 +201,18 @@ namespace cppcms { | |||
bool url_dispatcher::dispatch(std::string url) | |||
{ | |||
unsigned i; | |||
std::string method; | |||
char const *cmethod = 0; | |||
application *app = d->app; | |||
if(app && app->has_context()) { | |||
method = app->request().request_method(); | |||
cmethod = method.c_str(); | |||
} | |||
else { | |||
app = 0; | |||
} | |||
for(i=0;i<d->options.size();i++) { | |||
if(d->options[i]->dispatch(url)) | |||
if(d->options[i]->dispatch(url,cmethod,app)) | |||
return true; | |||
} | |||
return false; | |||
@@ -162,10 +229,22 @@ namespace cppcms { | |||
{ | |||
d->options.push_back(make_handler(expr,h)); | |||
} | |||
void url_dispatcher::assign_generic(std::string const &expr,rhandler h) | |||
{ | |||
d->options.push_back(make_handler(expr,h)); | |||
} | |||
void url_dispatcher::map_generic(std::string const &method,std::string const &re,generic_handler const &h) | |||
{ | |||
booster::shared_ptr<option> opt(new generic_option(method,re,h)); | |||
d->options.push_back(opt); | |||
} | |||
void url_dispatcher::map_generic(std::string const &re,generic_handler const &h) | |||
{ | |||
booster::shared_ptr<option> opt(new generic_option(re,h)); | |||
d->options.push_back(opt); | |||
} | |||
void url_dispatcher::assign(std::string const &expr,handler1 h,int p1) | |||
{ | |||
@@ -196,5 +275,14 @@ namespace cppcms { | |||
{ | |||
d->options.push_back(make_handler(expr,h,p1,p2,p3,p4,p5,p6)); | |||
} | |||
bool url_dispatcher::validate_encoding(application &app,char const *begin,char const *end) | |||
{ | |||
size_t unused; | |||
return encoding::valid(app.context().locale(),begin,end,unused); | |||
} | |||
void url_dispatcher::setup_stream(application &app,std::istream &s) | |||
{ | |||
s.imbue(app.context().locale()); | |||
} | |||
} // namespace cppcms |
@@ -7,14 +7,42 @@ | |||
/////////////////////////////////////////////////////////////////////////////// | |||
#include <cppcms/url_mapper.h> | |||
#include <cppcms/url_dispatcher.h> | |||
#include <cppcms/http_response.h> | |||
#include <cppcms/application.h> | |||
#include <cppcms/service.h> | |||
#include <cppcms/json.h> | |||
#include <cppcms/cppcms_error.h> | |||
#include <booster/locale/info.h> | |||
#include <sstream> | |||
#include "test.h" | |||
#include "dummy_api.h" | |||
struct point { | |||
int x,y; | |||
}; | |||
std::istream &operator>>(std::istream &in,point &d) | |||
{ | |||
char c; | |||
in >> d.x >> c >> d.y; | |||
if(c!=',') | |||
in.setstate(std::ios::failbit); | |||
return in; | |||
} | |||
namespace stuff { | |||
struct foo { | |||
int l; | |||
}; | |||
bool parse_url_parameter(cppcms::util::const_char_istream ¶meter,foo &f) | |||
{ | |||
f.l = parameter.end() - parameter.begin(); | |||
return true; | |||
} | |||
} // stuff | |||
std::string value(std::ostringstream &s) | |||
{ | |||
std::string res = s.str(); | |||
@@ -39,6 +67,54 @@ public: | |||
{ v_ = ':'+s1+':'+s2+':'+s3+':'+s4+':'+s5; } | |||
void h6(std::string s1,std::string s2,std::string s3,std::string s4,std::string s5,std::string s6) | |||
{ v_ = ':'+s1+':'+s2+':'+s3+':'+s4+':'+s5+':'+s6; } | |||
template<typename T> | |||
static std::string to_string(T v) | |||
{ | |||
std::ostringstream ss; | |||
ss<<v; | |||
return "to_str(" + ss.str() + ")"; | |||
} | |||
void h1_i(int param) | |||
{ | |||
v_= "h1_i:"+to_string(param); | |||
} | |||
void h1_d(double param) | |||
{ | |||
v_= "h1_d:"+to_string(param); | |||
} | |||
void h1_s(std::string param) | |||
{ | |||
v_= "h1_s:"+param; | |||
} | |||
void h1_s_cr(std::string const ¶m) | |||
{ | |||
v_= "h1_s_cr:"+param; | |||
} | |||
void h1_s_r(std::string ¶m) | |||
{ | |||
v_= "h1_s_r:"+param; | |||
} | |||
void h1_s_c(std::string const param) | |||
{ | |||
v_= "h1_s_c:"+param; | |||
} | |||
void get_0() { v_="get_0"; } | |||
void get_1(int x) { v_="get_1:" + to_string(x); } | |||
void get_2(int x,int y) { v_="get_2:" + to_string(x) + ":" + to_string(y); } | |||
void p_0() { v_="p_0"; } | |||
void p_1(int x) { v_="p_1:" + to_string(x); } | |||
void p_2(int x,int y) { v_="p_2:" + to_string(x) + ":" + to_string(y); } | |||
void point_m(point const &p) { v_="point:" + to_string(p.x) + "X" + to_string(p.y); } | |||
void foo_m(stuff::foo const &p) { v_="foo:" + to_string(p.l); } | |||
disp(cppcms::service &s) : cppcms::application(s) | |||
{ | |||
dispatcher().assign_generic("hg/(.*)",&disp::hg,this); | |||
@@ -49,8 +125,35 @@ public: | |||
dispatcher().assign("h4/(a(\\d+))/(a(\\d+))/(a(\\d+))/(a(\\d+))",&disp::h4,this,2,4,6,8); | |||
dispatcher().assign("h5/(a(\\d+))/(a(\\d+))/(a(\\d+))/(a(\\d+))/(a(\\d+))",&disp::h5,this,2,4,6,8,10); | |||
dispatcher().assign("h6/(a(\\d+))/(a(\\d+))/(a(\\d+))/(a(\\d+))/(a(\\d+))/(a(\\d+))",&disp::h6,this,2,4,6,8,10,12); | |||
dispatcher().map("/h0",&disp::h0,this); | |||
dispatcher().map("/h1_num/(.*)",&disp::h1_i,this,1); | |||
dispatcher().map("/h1_num/(.*)",&disp::h1_d,this,1); | |||
dispatcher().map("/h1_s/(\\d+)",&disp::h1_s,this,1); | |||
dispatcher().map("/h1_s_cr/((\\d+))",&disp::h1_s_cr,this,2); | |||
dispatcher().map("/h1_s_r/(\\d+)",&disp::h1_s_r,this,1); | |||
dispatcher().map("/h1_s_c/(\\d+)",&disp::h1_s_c,this,1); | |||
dispatcher().map("/name/(.*)",&disp::h1_s,this,1); | |||
dispatcher().map("GET","/res",&disp::get_0,this); | |||
dispatcher().map("GET","/res/(a(\\d+))",&disp::get_1,this,2); | |||
//dispatcher().map("GET","/res/(a(\\d+))/(a(\\d+))",&disp::get_2,this,1,2); | |||
dispatcher().map("(PUT|POST)","/res",&disp::p_0,this); | |||
dispatcher().map("(PUT|POST)","/res/(a(\\d+))",&disp::p_1,this,2); | |||
//dispatcher().map("(PUT|POST)","/res/(a(\\d+)/(a(\\d+))",&disp::p_2,this,2,4); | |||
dispatcher().map("/point/(.*)",&disp::point_m,this,1); | |||
dispatcher().map("/foo/(.*)",&disp::foo_m,this,1); | |||
} | |||
#define TESTD(x,y) do { main(x); TEST(v_==y); } while(0) | |||
#define TESTM(m,x,y) do { v_="none"; set_context((m)); main((x)); release_context() ; if(v_!=(y)) std::cerr << "v=" <<v_ << " exp="<< (y) <<std::endl; TEST(v_==(y)); } while(0) | |||
void test() | |||
{ | |||
TESTD("hg/x","m:hg/x:x"); | |||
@@ -61,8 +164,67 @@ public: | |||
TESTD("h4/a1/a2/a3/a4",":1:2:3:4"); | |||
TESTD("h5/a1/a2/a3/a4/a5",":1:2:3:4:5"); | |||
TESTD("h6/a1/a2/a3/a4/a5/a6",":1:2:3:4:5:6"); | |||
set_context("GET"); | |||
std::cout << std::use_facet<booster::locale::info>(context().locale()).encoding() << std::endl; | |||
release_context(); | |||
TESTM("GET","/h0","-"); | |||
TESTM("GET","/h1_num/123.3","h1_d:to_str(123.3)"); | |||
TESTM("GET","/h1_num/123","h1_i:to_str(123)"); | |||
TESTM("GET","/h1_num/123 23","none"); | |||
TESTM("GET","/h1_s/1111","h1_s:1111"); | |||
TESTM("GET","/h1_s_c/1111","h1_s_c:1111"); | |||
TESTM("GET","/h1_s_r/1111","h1_s_r:1111"); | |||
TESTM("GET","/h1_s_cr/1111","h1_s_cr:1111"); | |||
TESTM("GET","/h1_s_cr/1111\xFF","none"); | |||
TESTM("GET","/h1_s_cr/1111\xFF","none"); | |||
TESTM("GET","/name/\\xD7\\xA9\\xD7\\x9C\\xD7\\x95\\xD7\\x9D\\x20\\xE6\\x97\\xA5\\xE6\\x9C\\xAC\\xE8\\xAA\\x9E", | |||
"h1_s:\\xD7\\xA9\\xD7\\x9C\\xD7\\x95\\xD7\\x9D\\x20\\xE6\\x97\\xA5\\xE6\\x9C\\xAC\\xE8\\xAA\\x9E"); | |||
TESTM("GET","/name/\xFF","none"); | |||
TESTM("GET","/res","get_0"); | |||
TESTM("GET","/res/a1","get_1:to_str(1)"); | |||
//TESTM("GET","/res/a1/a2","get_2:to_str(1):to_str(2)"); | |||
TESTM("POST","/res","p_0"); | |||
TESTM("POST","/res/a1","p_1:to_str(1)"); | |||
//TESTM("POST","/res/a1/a2","p_2:to_str(1):to_str(2)"); | |||
TESTM("PUT","/res","p_0"); | |||
TESTM("PUT","/res/a1","p_1:to_str(1)"); | |||
//TESTM("PUT","/res/a1/a2","p_2:to_str(1):to_str(2)"); | |||
TESTM("DELETE","/res","none"); | |||
TESTM("DELETE","/res/a1","none"); | |||
//TESTM("DELETE","/res/a1/a2","none"); | |||
TESTM("GET","/point/123,23","point:to_str(123)Xto_str(23)"); | |||
TESTM("GET","/point/123","none"); | |||
TESTM("GET","/point/123,23.2","none"); | |||
TESTM("GET","/foo/abcd","foo:to_str(4)"); | |||
TESTM("GET","/foo/abcdefg","foo:to_str(7)"); | |||
} | |||
void set_context(std::string const &method="GET") | |||
{ | |||
std::map<std::string,std::string> env; | |||
env["HTTP_HOST"]="www.example.com"; | |||
env["SCRIPT_NAME"]="/foo"; | |||
env["PATH_INFO"]="/bar"; | |||
env["REQUEST_METHOD"]=method; | |||
env["HTTP_ACCEPT_ENCODING"]="gzip"; | |||
booster::shared_ptr<dummy_api> api(new dummy_api(service(),env,output_)); | |||
booster::shared_ptr<cppcms::http::context> cnt(new cppcms::http::context(api)); | |||
assign_context(cnt); | |||
response().io_mode(cppcms::http::response::normal); | |||
output_.clear(); | |||
} | |||
std::string v_; | |||
std::string output_; | |||
}; | |||
void dispatcher_test(cppcms::service &srv) | |||
@@ -342,6 +504,7 @@ int main() | |||
std::cout << "- Basics no throw" << std::endl; | |||
cppcms::json::value cfg; | |||
cfg["localization"]["locales"][0]="en_US.UTF-8"; | |||
{ | |||
cppcms::service srv(cfg); | |||
basic_test(srv,false); | |||