Browse Source

Initial support of RESTful mapping support

master
Artyom Beilis 8 years ago
parent
commit
51cd226777
7 changed files with 675 additions and 19 deletions
  1. +53
    -0
      booster/booster/traits/type_traits.h
  2. +12
    -0
      cppcms/application.h
  3. +93
    -0
      cppcms/steal_buf.h
  4. +240
    -4
      cppcms/url_dispatcher.h
  5. +13
    -2
      src/application.cpp
  6. +101
    -13
      src/url_dispatcher.cpp
  7. +163
    -0
      tests/url_mapper_test.cpp

+ 53
- 0
booster/booster/traits/type_traits.h View File

@@ -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



+ 12
- 0
cppcms/application.h View File

@@ -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


+ 93
- 0
cppcms/steal_buf.h View File

@@ -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.
///


+ 240
- 4
cppcms/url_dispatcher.h View File

@@ -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 &parameter,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 &parameter,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 &parameter,Type &param)`
/// 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;


+ 13
- 2
src/application.cpp View File

@@ -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;


+ 101
- 13
src/url_dispatcher.cpp View File

@@ -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

+ 163
- 0
tests/url_mapper_test.cpp View File

@@ -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 &parameter,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 &param)
{
v_= "h1_s_cr:"+param;
}
void h1_s_r(std::string &param)
{
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);


Loading…
Cancel
Save