Browse Source

Lots of small updates merged

master
Artyom Beilis 14 years ago
parent
commit
2562c73a32
23 changed files with 1025 additions and 253 deletions
  1. +22
    -4
      CMakeLists.txt
  2. +1
    -2
      TODO
  3. +0
    -6
      application.cpp
  4. +10
    -1
      application.h
  5. +16
    -0
      applications_pool.h
  6. +1
    -1
      base_content.h
  7. +2
    -2
      base_view.h
  8. +4
    -0
      cache_interface.h
  9. +196
    -0
      cgi_api.cpp
  10. +4
    -0
      config.cmake.h
  11. +26
    -8
      encoding.cpp
  12. +19
    -1
      filters.h
  13. +7
    -186
      http_api.cpp
  14. +74
    -4
      http_context.h
  15. +67
    -2
      http_cookie.h
  16. +224
    -0
      http_parser.h
  17. +2
    -0
      http_protocol.h
  18. +209
    -0
      logger.cpp
  19. +58
    -23
      logger.h
  20. +6
    -1
      regex.cpp
  21. +34
    -12
      service.cpp
  22. +24
    -0
      tests/toscgi.py
  23. +19
    -0
      util.h

+ 22
- 4
CMakeLists.txt View File

@@ -20,6 +20,9 @@ option(DISABLE_STATIC "Disable static libraries build" OFF)
option(DISABLE_ICONV "Disable usage iconv library - use ICU for conversion" OFF)
option(DISABLE_GCRYPT "Disable usage of gcrypt library, no AES cookies encryption would be available" OFF)
option(USE_EXTERNAL_BOOST "Use external Boost library and not internal one, boost version provided by BOOST_SUFFIX variable" OFF)
option(DISABLE_FCGI "Disable fastcgi web server api" OFF)
option(DISABLE_SCGI "Disable scgi web server api" OFF)
option(DISABLE_HTTP "Disable http web server" OFF)

#############################################################################
#
@@ -154,6 +157,8 @@ include_directories(${CMAKE_SOURCE_DIR})
include_directories(localization)




set(CPPCMS_LIBRARY_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX})
set(CPPCMS_LIBRARY_PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX})
check_cxx_source_compiles(
@@ -274,7 +279,6 @@ set(CPPCMS_PUBLIC_HEADERS
filters.h
form.h
hold_ptr.h
http_connection.h
http_context.h
http_cookie.h
http_file.h
@@ -350,9 +354,6 @@ set(CPPCMS_SOURCES
base_view.cpp
views_pool.cpp
internal_file_server.cpp
scgi_api.cpp
fastcgi_api.cpp
http_api.cpp
atomic_counter.cpp
aio_timer.cpp
json.cpp
@@ -380,6 +381,23 @@ if(NOT DISABLE_GCRYPT)
find_library(LIB_GCRYPT gcrypt)
endif(NOT DISABLE_GCRYPT)

if(NOT DISABLE_FCGI)
set(CPPCMS_SOURCES ${CPPCMS_SOURCES} fastcgi_api.cpp)
set(CPPCMS_HAS_FCGI 1)
endif()

if(NOT DISABLE_SCGI)
set(CPPCMS_SOURCES ${CPPCMS_SOURCES} scgi_api.cpp)
set(CPPCMS_HAS_SCGI 1)
endif()

if(NOT DISABLE_HTTP)
set(CPPCMS_SOURCES ${CPPCMS_SOURCES} http_api.cpp)
set(CPPCMS_HAS_HTTP 1)
endif()


if(LIB_GCRYPT)
set(CPPCMS_SOURCES ${CPPCMS_SOURCES} aes_encryptor.cpp)
set(HAVE_GCRYPT 1)


+ 1
- 2
TODO View File

@@ -1,9 +1,8 @@
- form.*
Implement, Select, Radio, Date-Time
Date-Time
- http_request.*, cgi_api.*
Implement File Upload
- Implement Asynchronous API
- Implement JSON-RPC
- Reintergrate Sessions over TCP/IP
- Reintegrate Cache Support over TCP/IP and shared_memory
- Update cppcms_tmpl_cc according to new code

+ 0
- 6
application.cpp View File

@@ -52,12 +52,6 @@ application::~application()
}
}

long application::refs()
{
return refs_;
}


cppcms::service &application::service()
{
return *d->service;


+ 10
- 1
application.h View File

@@ -233,8 +233,17 @@ namespace cppcms {
/// Returns true if current application was created as asynchronous application.
///
bool is_asynchronous();
long refs();


///
/// This is main function of the application that is called when it is matched
/// according to the regular expression in the applications_pool class.
///
/// By default, main calls dispatcher().dispatch(url). And if the last fails, it
/// creates 404 Error page. This allows developers to create its own hooks for
/// reaction on incoming URL as, initialization and cleanup of general resources,
/// Custom 404 and error handlers etc.
///
virtual void main(std::string url);

private:


+ 16
- 0
applications_pool.h View File

@@ -48,6 +48,8 @@ namespace cppcms {
/// Mount an application factory \a aps for processing of any incoming requests. Application
/// would receive PATH_INFO CGI variable for URL matching.
///
/// This member function is thread safe.
///
void mount(std::auto_ptr<factory> aps);
///
@@ -55,42 +57,56 @@ namespace cppcms {
/// matches the regular expression \a path_info. The marched part of an regular expression \a select would
/// be passed for URL matching.
///
/// This member function is thread safe.
///
void mount(std::auto_ptr<factory> aps,std::string path_info,int select);

///
/// Mount an application factory \a aps for processing of requests for which CGI SCRIPT_NAME exactly
/// matches \a script_name parameter. CGI PATH_INFO is passed to application for URL matching.
///
/// This member function is thread safe.
///
void mount(std::auto_ptr<factory> aps,std::string script_name);
///
/// Mount an application factory \a aps for processing of requests for which CGI SCRIPT_NAME exactly
/// matches \a script_name parameter. And PATH_INFO patches regular expression \a path_info.
/// The matched part \a select is passed to application for URL matching.
///
/// This member function is thread safe.
///
void mount(std::auto_ptr<factory> aps,std::string script_name,std::string path_info, int select);

///
/// Mount an asynchronous application \a app for processing of any incoming requests. Application
/// would receive PATH_INFO CGI variable for URL matching.
///
/// This member function is thread safe.
///
void mount(intrusive_ptr<application> app);
///
/// Mount an asynchronous application \a app for processing of requests for which CGI PATH_INFO
/// matches the regular expression \a path_info. The marched part of an regular expression \a select would
/// be passed for URL matching.
///
/// This member function is thread safe.
///
void mount(intrusive_ptr<application> app,std::string path_info,int select);
///
/// Mount an asynchronous application \a app for processing of requests for which CGI SCRIPT_NAME exactly
/// matches \a script_name parameter. And PATH_INFO patches regular expression \a path_info.
/// The matched part \a select is passed to application for URL matching.
///
/// This member function is thread safe.
///
void mount(intrusive_ptr<application> app,std::string script_name);
///
/// Mount an asynchronous application \a app for processing of requests for which CGI SCRIPT_NAME exactly
/// matches \a script_name parameter. And PATH_INFO patches regular expression \a path_info.
/// The matched part \a select is passed to application for URL matching.
///
/// This member function is thread safe.
///
void mount(intrusive_ptr<application> app,std::string script_name,std::string path_info, int select);

///


+ 1
- 1
base_content.h View File

@@ -5,7 +5,7 @@

namespace cppcms {
///
/// \brief This is a simple polymorphic class that every content for templates rendering should be dervided from it.
/// \brief This is a simple polymorphic class that every content for templates rendering should be derided from it.
/// It does not carry much information with exception of RTTI that allows type-safe casting of user provided
/// content instances to target content class that is used by specific template.
///


+ 2
- 2
base_view.h View File

@@ -21,14 +21,14 @@ namespace cppcms {
/// \brief This class is base class for all views (skins) rendered by CppCMS template engine.
///
/// Users are not expected to derive from this class or use it directly. CppCMS template compiler
/// create skins that are useable with template engine and each template is derived from the \a base_view
/// create skins that are usable with template engine and each template is derived from the \a base_view
/// class.
///

class CPPCMS_API base_view : util::noncopyable {
public:
///
/// The main randering function -- render the main HTML page. It is usually overriden in template engine.
/// The main rendering function -- render the main HTML page. It is usually overridden in template engine.
///
virtual void render();
virtual ~base_view();


+ 4
- 0
cache_interface.h View File

@@ -66,6 +66,10 @@ namespace cppcms {
class CPPCMS_API cache_interface : public util::noncopyable {
public:

///
/// Internal API, don't use it
///
cache_interface(http::context &context);
~cache_interface();



+ 196
- 0
cgi_api.cpp View File

@@ -22,6 +22,202 @@


namespace cppcms { namespace impl { namespace cgi {
/*
class multipart_separator {
public:
multipart_separator(std::vector<char> &body,unsigned &body_ptr,std::string boundary) :
body_(body),
body_ptr_(body_ptr)
{
boundary_ = "--" + boundary;
pos_ = 0;
last_ = false;
}
int getc()
{
if(body_ptr_ < body_.size()) {
return body_[body_ptr_++];
}
else {
body_.clear();
body_ptr_=0;
return -1;
}
}

enum { incomplete, last_chunk, chunk };
int next()
{
for(;;){
int c=getc();
if(c < 0)
return incomplete;
if(pos_ < bodundary_.size()) {
if(c == boundary_[pos_]) {
pos_++;
}
else {
if(pos_ != 0)
output_.append(boundary_.substr(0,pos_));
output_.append(char(c));
pos_ = 0;
}
}
else if(pos_ == boundary_.size()) {
c == '-';
last_ = true;
pos_ = 0x10001;
}
else {
unsigned diff = pos_ & 0xFFFF;
if(last_){
if(last_ending[diff]==c) {
pos_ ++;
diff ++ ;
}
else {
output_.append(last_ending,diff);
output_.append(char(c));
pos_ = 0;
last_ = false;
}
if(diff == 4)
return last_chunk;
}
else {
if(ending[diff] == c) {
pos_ ++;
diff ++;
}
else {
output_.append(ending,diff);
output_.append(char(c));
pos_ = 0;
}
if(diff == 2) {
pos_ = 0;
return chunk;
}
}
}
}
}

private:
static char const last_ending[]="--\r\n"
static char const ending[]="\r\n"
};


class multipart_parser : public util::noncopyable {
public:
multipart_parser(std::vector<char> &body, unsigned &ptr) :
separator_(body,ptr),
parser_(body,ptr)
{
}

struct none{};
struct eof{};
typedef std::pair<std::string,std::string> pair_type;
typedef boost::shared_ptr<http::file> file_type;
typedef enum { none, eof, error } result_type;
typedef boost::variant<result_type,pair_type,file_type,eof> variant_type;

void append(bool final = false)
{
if(result_.which() == 1) {
std::string &last=boost::get<pair_type>(result_).second;
last.append(separator_.output());
}
else if(result_.which() == 2) {
file_type &file == boost::get<file_type>(result_);
file.write(separator_.output());
if(final)
file.close();
}
}

variant_type next()
{
switch(state:) {
case done:
return eof;
case reading_data:
switch(separator_.next()) {
case multipart_separator::incomplete:
append();
return none;
case multipart_separator::chunk;
{
append(final);
variant_type tmp = result_;
result_=none;
state_ = reading_headers;
return tmp;
}
case multipart_separator::last_chunk;
{
append(final);
variant_type tmp = result_;
result_=none;
state_ = done;
return tmp;
}
default:
throw cppcms_error(
(boost::format("Internal error at " __FILE__ "line %d") % __LINE__).str());
}
break;
case reading_headers:
switch(parser_.step())
case parset::mode_data:
return none;
case parser::error_observerd:
return error;
case parser::end_of_headers:
if(result_.which() == 0)
return error;
state_ = reading_data;
return none;
case parser::got_header:
{
std::string const header = parser.header_;
parser.header_.clean();
std::string::const_iterator m,p=header.begin();
std::string::const_iterator e=header.end();
p=http::protocol::skip_ws(p,e);
m=p;
p=http::protocol::tocken(p,e);
std::string type(m,p);
if(http::protocol::compare("Content-Disposition",type)==0)
{
while(p!=e) {
if(http::protocol::separator(*p)) {
++p;
continue;
}
m=p;
if((p=http::protocol::tocken(p,e))!=m) {
if(http::protocol::compare(std::string(m,p),"name"))
}
}
}
}
}
}


private:
multipart_separator separator_;
cppcms::http::impl::parser parser_;
};
*/


connection::connection(cppcms::service &srv) :
service_(&srv),


+ 4
- 0
config.cmake.h View File

@@ -95,6 +95,10 @@

#cmakedefine CPPCMS_USE_EXTERNAL_BOOST

#cmakedefine CPPCMS_HAS_FCGI
#cmakedefine CPPCMS_HAS_SCGI
#cmakedefine CPPCMS_HAS_HTTP

/* Version number of package */
#define VERSION PACKAGE_VERSION



+ 26
- 8
encoding.cpp View File

@@ -298,14 +298,32 @@ namespace impl{
predefined_["iso-8859-8"]=&iso_8859_8_valid<char const *>;
predefined_["iso-8859-11"]=&iso_8859_11_valid<char const *>;

predefined_["cp1250"]=predefined_["windows-1250"]=&windows_1250_valid<char const *>;
predefined_["cp1251"]=predefined_["windows-1251"]=&windows_1251_valid<char const *>;
predefined_["cp1252"]=predefined_["windows-1252"]=&windows_1252_valid<char const *>;
predefined_["cp1253"]=predefined_["windows-1253"]=&windows_1253_valid<char const *>;
predefined_["cp1255"]=predefined_["windows-1255"]=&windows_1255_valid<char const *>;
predefined_["cp1256"]=predefined_["windows-1256"]=&windows_1256_valid<char const *>;
predefined_["cp1257"]=predefined_["windows-1257"]=&windows_1257_valid<char const *>;
predefined_["cp1258"]=predefined_["windows-1258"]=&windows_1258_valid<char const *>;
predefined_["windows-1250"]=&windows_1250_valid<char const *>;
predefined_["windows-1251"]=&windows_1251_valid<char const *>;
predefined_["windows-1252"]=&windows_1252_valid<char const *>;
predefined_["windows-1253"]=&windows_1253_valid<char const *>;
predefined_["windows-1255"]=&windows_1255_valid<char const *>;
predefined_["windows-1256"]=&windows_1256_valid<char const *>;
predefined_["windows-1257"]=&windows_1257_valid<char const *>;
predefined_["windows-1258"]=&windows_1258_valid<char const *>;

predefined_["cp1250"]=&windows_1250_valid<char const *>;
predefined_["cp1251"]=&windows_1251_valid<char const *>;
predefined_["cp1252"]=&windows_1252_valid<char const *>;
predefined_["cp1253"]=&windows_1253_valid<char const *>;
predefined_["cp1255"]=&windows_1255_valid<char const *>;
predefined_["cp1256"]=&windows_1256_valid<char const *>;
predefined_["cp1257"]=&windows_1257_valid<char const *>;
predefined_["cp1258"]=&windows_1258_valid<char const *>;

predefined_["1250"]=&windows_1250_valid<char const *>;
predefined_["1251"]=&windows_1251_valid<char const *>;
predefined_["1252"]=&windows_1252_valid<char const *>;
predefined_["1253"]=&windows_1253_valid<char const *>;
predefined_["1255"]=&windows_1255_valid<char const *>;
predefined_["1256"]=&windows_1256_valid<char const *>;
predefined_["1257"]=&windows_1257_valid<char const *>;
predefined_["1258"]=&windows_1258_valid<char const *>;

predefined_["koi8r"]=predefined_["koi8-r"]=&koi8_valid<char const *>;
predefined_["koi8u"]=predefined_["koi8-u"]=&koi8_valid<char const *>;


+ 19
- 1
filters.h View File

@@ -279,6 +279,9 @@ namespace cppcms {
streamable obj_;
struct data;
util::copy_ptr<data> d;
};

inline std::ostream &operator<<(std::ostream &out,raw const &obj)
@@ -286,7 +289,12 @@ namespace cppcms {
obj(out);
return out;
}
///
/// \brief Format date to ouput stream
///
/// Formats date to the stream, date is represented as time_t
///


class CPPCMS_API date {
@@ -310,6 +318,11 @@ namespace cppcms {
return out;
}
///
/// \brief Format time to ouput stream
///
/// Formats time to the stream, time is represented as time_t
///
class CPPCMS_API time {
public:
time();
@@ -330,6 +343,11 @@ namespace cppcms {
obj(out);
return out;
}
///
/// \brief Format date and time to ouput stream
///
/// Formats date and time to the stream, date and time is represented as time_t
///
class CPPCMS_API datetime {
public:
datetime();


+ 7
- 186
http_api.cpp View File

@@ -1,4 +1,5 @@
#define CPPCMS_SOURCE
//#define DEBUG_HTTP_PARSER
#include "asio_config.h"
#include "cgi_api.h"
#include "cgi_acceptor.h"
@@ -6,7 +7,7 @@
#include "service_impl.h"
#include "cppcms_error_category.h"
#include "json.h"
#include "http_protocol.h"
#include "http_parser.h"
#include "config.h"
#include <string.h>
#include <iostream>
@@ -18,7 +19,6 @@
namespace boost = cppcms_boost;
#endif

//#define DEBUG_HTTP_PARSER


namespace cppcms {
@@ -85,7 +85,7 @@ namespace cgi {
input_body_.resize(n);

for(;;) {
using ::cppcms::http::impl::parser;
switch(input_parser_.step()) {
case parser::more_data:
// Assuming body_ptr == body.size()
@@ -215,6 +215,8 @@ namespace cgi {
char const *ptr=reinterpret_cast<char const *>(p);
output_body_.insert(output_body_.end(),ptr,ptr+s);

using cppcms::http::impl::parser;

for(;;) {
switch(output_parser_.step()) {
case parser::more_data:
@@ -388,187 +390,6 @@ namespace cgi {
return true;
}

class parser {
enum {
idle,
input_observed,
last_lf_exptected,
lf_exptected,
space_or_other_exptected,
quote_expected,
pass_quote_exptected,
closing_bracket_expected,
pass_closing_bracket_expected
} state_;
unsigned bracket_counter_;

std::vector<char> &body_;
unsigned &body_ptr_;

inline int getc()
{
if(body_ptr_ < body_.size()) {
return body_[body_ptr_++];
}
else {
body_.clear();
body_ptr_=0;
return -1;
}
}
inline void ungetc(int c)
{
if(body_ptr_ > 0) {
body_ptr_--;
body_[body_ptr_]=c;
}
else {
body_.insert(body_.begin(),c);
}
}

// Non copyable

parser(parser const &);
parser const &operator=(parser const &);

public:
std::string header_;

parser(std::vector<char> &body,unsigned &body_ptr) :
state_(idle),
bracket_counter_(0),
body_(body),
body_ptr_(body_ptr)
{
}
enum { more_data, got_header, end_of_headers , error_observerd };
int step()
{
#ifdef DEBUG_HTTP_PARSER
static char const *states[]= {
"idle",
"input_observed",
"last_lf_exptected",
"lf_exptected",
"space_or_other_exptected",
"quote_expected",
"pass_quote_exptected",
"closing_bracket_expected",
"pass_closing_bracket_expected"
};
#endif
for(;;) {
int c=getc();
#if defined DEBUG_HTTP_PARSER
std::cerr<<"Step("<<body_ptr_<<":"<<body_.size()<<": "<<std::flush;
if(c>=32)
std::cerr<<"["<<char(c)<<"] "<<states[state_]<<std::endl;
else
std::cerr<<c<<" "<<states[state_]<<std::endl;
#endif

if(c<0)
return more_data;


switch(state_) {
case idle:
header_.clear();
switch(c) {
case '\r':
state_=last_lf_exptected;
break;
case '"':
state_=quote_expected;
break;
case '(':
state_=closing_bracket_expected;
bracket_counter_++;
break;
default:
state_=input_observed;
}
break;
case last_lf_exptected:
if(c!='\n') return error_observerd;
header_.clear();
return end_of_headers;
case lf_exptected:
if(c!='\n') return error_observerd;
state_=space_or_other_exptected;
break;
case space_or_other_exptected:
if(c==' ' || c=='\t') {
// Convert LWS to space as required by
// RFC, so remove last CRLF
header_.resize(header_.size() - 2);
state_=input_observed;
break;
}
ungetc(c);
header_.resize(header_.size()-2);
state_=idle;
#ifdef DEBUG_HTTP_PARSER
std::cerr<<"["<<header_<<"]"<<std::endl;
#endif
return got_header;
case input_observed:
switch(c) {
case '\r':
state_=lf_exptected;
break;
case '"':
state_=quote_expected;
break;
case '(':
state_=closing_bracket_expected;
bracket_counter_++;
break;
default:
state_=input_observed;
}
break;
case quote_expected:
switch(c) {
case '"':
state_=input_observed;
break;
case '\\':
state_=pass_quote_exptected;
break;
}
break;
case pass_quote_exptected:
if(c < 0 || c >=127)
return error_observerd;
state_=quote_expected;
break;
case closing_bracket_expected:
switch(c) {
case ')':
bracket_counter_--;
if(bracket_counter_==0)
state_=input_observed;
break;
case '\\':
state_=pass_closing_bracket_expected;
break;
}
break;
case pass_closing_bracket_expected:
if(c < 0 || c >=127)
return error_observerd;
state_=closing_bracket_expected;
break;
}

header_+=char(c);
}
}

};

intrusive_ptr<http> self()
{
@@ -581,10 +402,10 @@ namespace cgi {

std::vector<char> input_body_;
unsigned input_body_ptr_;
parser input_parser_;
::cppcms::http::impl::parser input_parser_;
std::vector<char> output_body_;
unsigned output_body_ptr_;
parser output_parser_;
::cppcms::http::impl::parser output_parser_;


std::map<std::string,std::string> env_;


+ 74
- 4
http_context.h View File

@@ -23,32 +23,102 @@ namespace cppcms {
class request;
class response;

///
/// \brief context is a central class that holds all specific connection related information.
/// It encapsulates CGI request and response, cache, session and locale information
///
/// Instance of this class is created upon client requests, it provides access to all
/// connection related interfaces. This class is unique per each applications hierarchy
/// and destroyed when HTTP request/response is completed
///
class CPPCMS_API context : public refcounted
{
public:
///
/// Internal API, don't use it
///
context(intrusive_ptr<impl::cgi::connection> conn);
///
/// Destructor.
~context();

///
/// Internal API, don't use it
///
impl::cgi::connection &connection();

///
/// Get an interface to HTTP request
///
http::request &request();

///
/// Get an interface to HTTP response
///
http::response &response();

///
/// Get global settings. Same as cppcms::service::settings
///
json::value const &settings();

///
/// Get an interface to CppCMS Cache
///
cache_interface &cache();

///
/// Get an interface to current session
///
/// Note, when using asynchronous CppCMS applications, session data is not fetched
/// and is not updated, because session access may be not cheap, So when using
/// session_interface in asynchronous application make sure you call session_inerface::load
/// member function
///
session_interface &session();

///
/// Get current context locale
///
std::locale locale();

///
/// Set locale explicitly. Note, it changes the locale of the response().out() stream as
/// well
///
void locale(std::locale const &new_locale);

///
/// Set locale by name. Similar to locale(service().generator(name)).
///
/// Note: it changes the locale of the response().out() stream as well
///
void locale(std::string const &name);
///
/// Get the central service instance
///
cppcms::service &service();

///
/// Get current views skin name
///
std::string skin();

///
/// Set current views skin name
///
void skin(std::string const &name);

///
/// Internal API, Don't use it
///
void run();

typedef enum {
operation_completed,
operation_aborted
} complition_type;
operation_completed, ///< Asynchronous operation completed successfully
operation_aborted ///< Asynchronous operation was canceled
} complition_type;

typedef util::callback1<complition_type> handler;



+ 67
- 2
http_cookie.h View File

@@ -12,39 +12,104 @@ namespace cppcms { namespace http {
class cookie;
std::ostream CPPCMS_API &operator<<(std::ostream &,cookie const &);

///
/// \brief Class that represents single HTTP Cookie
/// Generally used in context of http::request and http::response
///

class CPPCMS_API cookie {
public:
///
/// Cookie's Name
///
std::string name() const;

///
/// Cookie's value
///
std::string value() const;
///
/// Cookie's path
///
std::string path() const;

///
/// Cookie's domain
///
std::string domain() const;
///
/// Cookie's comment
///
std::string comment() const;

///
/// Check if the cookie is transferred over secure connection only
///
bool secure() const;

///
/// Set cookie's name
///
void name(std::string n);

///
/// Set cookie's value
///
void value(std::string v);

///
/// Set cookie's path
///
void path(std::string p);

///
/// Set cookie's domain
///
void domain(std::string);
///
/// Set cookie's comment
///
void comment(std::string);

///
/// Set max cookie's age
///
void max_age(unsigned a);
///
/// Set age according to browser's session (i.e. no Max-Age)
///
void browser_age();

///
/// Set secure property on the cookies
///
void secure(bool v);


// Mandatory set
cookie();
~cookie();
cookie(cookie const &);
cookie const &operator=(cookie const &);

// Additional
///
/// Create cookie with name and value, age - browser, rest properties undefined.
///
cookie(std::string name,std::string value);
///
/// Create cookies with name, value and max-age, rest properties undefined.
///
cookie(std::string name,std::string value,unsigned age);
///
/// Create cookie with name, value, max-age, path, domain and command
///
cookie(std::string name,std::string value,unsigned age,std::string path,std::string domain = std::string(),std::string comment=std::string());
///
/// Create cookie with name, value, path, domain and comment, age - browser.
cookie(std::string name,std::string value,std::string path,std::string domain=std::string(),std::string comment=std::string());

private:
friend std::ostream &operator<<(std::ostream &,cookie const &);

void write(std::ostream &) const;
// for future use
struct data;


+ 224
- 0
http_parser.h View File

@@ -0,0 +1,224 @@
#ifndef CPPCMS_HTTP_PARSER_H
#define CPPCMS_HTTP_PARSER_H
#include "http_protocol.h"
#include <vector>
#include <string>

namespace cppcms {
namespace http {
namespace impl {


class parser {
enum {
idle,
input_observed,
last_lf_exptected,
lf_exptected,
space_or_other_exptected,
quote_expected,
pass_quote_exptected,
closing_bracket_expected,
pass_closing_bracket_expected
} state_;
unsigned bracket_counter_;

std::vector<char> &body_;
unsigned &body_ptr_;


// Non copyable

parser(parser const &);
parser const &operator=(parser const &);
protected:
inline int getc()
{
if(body_ptr_ < body_.size()) {
return body_[body_ptr_++];
}
else {
body_.clear();
body_ptr_=0;
return -1;
}
}
inline void ungetc(int c)
{
if(body_ptr_ > 0) {
body_ptr_--;
body_[body_ptr_]=c;
}
else {
body_.insert(body_.begin(),c);
}
}

public:
std::string header_;

parser(std::vector<char> &body,unsigned &body_ptr) :
state_(idle),
bracket_counter_(0),
body_(body),
body_ptr_(body_ptr)
{
}
void reset()
{
state_ = idle;
bracket_counter_ = 0;
header_.clear();
}
enum { more_data, got_header, end_of_headers , error_observerd };
int step()
{
#ifdef DEBUG_HTTP_PARSER
static char const *states[]= {
"idle",
"input_observed",
"last_lf_exptected",
"lf_exptected",
"space_or_other_exptected",
"quote_expected",
"pass_quote_exptected",
"closing_bracket_expected",
"pass_closing_bracket_expected"
};
#endif
for(;;) {
int c=getc();
#if defined DEBUG_HTTP_PARSER
std::cerr<<"Step("<<body_ptr_<<":"<<body_.size()<<": "<<std::flush;
if(c>=32)
std::cerr<<"["<<char(c)<<"] "<<states[state_]<<std::endl;
else
std::cerr<<c<<" "<<states[state_]<<std::endl;
#endif

if(c<0)
return more_data;


switch(state_) {
case idle:
header_.clear();
switch(c) {
case '\r':
state_=last_lf_exptected;
break;
case '"':
state_=quote_expected;
break;
case '(':
state_=closing_bracket_expected;
bracket_counter_++;
break;
default:
state_=input_observed;
}
break;
case last_lf_exptected:
if(c!='\n') return error_observerd;
header_.clear();
return end_of_headers;
case lf_exptected:
if(c!='\n') return error_observerd;
state_=space_or_other_exptected;
break;
case space_or_other_exptected:
if(c==' ' || c=='\t') {
// Convert LWS to space as required by
// RFC, so remove last CRLF
header_.resize(header_.size() - 2);
state_=input_observed;
break;
}
ungetc(c);
header_.resize(header_.size()-2);
state_=idle;
#ifdef DEBUG_HTTP_PARSER
std::cerr<<"["<<header_<<"]"<<std::endl;
#endif
return got_header;
case input_observed:
switch(c) {
case '\r':
state_=lf_exptected;
break;
case '"':
state_=quote_expected;
break;
case '(':
state_=closing_bracket_expected;
bracket_counter_++;
break;
default:
state_=input_observed;
}
break;
case quote_expected:
switch(c) {
case '"':
state_=input_observed;
break;
case '\\':
state_=pass_quote_exptected;
break;
}
break;
case pass_quote_exptected:
if(c < 0 || c >=127)
return error_observerd;
state_=quote_expected;
break;
case closing_bracket_expected:
switch(c) {
case ')':
bracket_counter_--;
if(bracket_counter_==0)
state_=input_observed;
break;
case '\\':
state_=pass_closing_bracket_expected;
break;
}
break;
case pass_closing_bracket_expected:
if(c < 0 || c >=127)
return error_observerd;
state_=closing_bracket_expected;
break;
}

header_+=char(c);

/* switch(state_) {
case idle:
case input_observed:
case last_lf_exptected:
case lf_exptected:
case space_or_other_exptected:
if(separator(c)) {
parsed_.push_back(element_type(c,std::string()));
}
else if(0x20 <= c && c<=0x7E) {
if(parsed_.empty() || parsed_.back().first != token_element) {
parsed.push_back(element_type(token_element,std::string()));
}
parsed.back().second+=c;
}
break;
default:
; // Nothing
}*/
}
}

}; // class parser


}}} // cppcms::http::impl

#endif

+ 2
- 0
http_protocol.h View File

@@ -107,6 +107,8 @@ namespace protocol {
}
if(lsize<rsize)
return -1;
if(lsize>rsize)
return 1;
return 0;
}



+ 209
- 0
logger.cpp View File

@@ -0,0 +1,209 @@
#define CPPCMS_SOURCE
#include "logger.h"
#include "cppcms_error.h"
#include "config.h"
#ifdef CPPCMS_USE_EXTERNAL_BOOST
# include <boost/format.hpp>
# include <boost/thread.hpp>
# include <boost/shared_ptr.hpp>
#else // Internal Boost
# include <cppcms_boost/format.hpp>
# include <cppcms_boost/thread.hpp>
# include <cppcms_boost/shared_ptr.hpp>
namespace boost = cppcms_boost;
#endif

#include <sstream>
#include <fstream>


namespace cppcms {
namespace impl {
class log_device : public util::noncopyable {
public:
virtual void write(std::string const &msg) = 0;
virtual ~log_device() {}
};
class file_log_device : public log_device {
public:
file_log_device(std::string const &file_name,logger::open_mode_type how)
{
switch(how) {
case logger::overwrite:
output_.open(file_name.c_str());
break;
case logger::append:
default:
output_.open(file_name.c_str(),std::fstream::app);
}
if(!output_)
throw cppcms_error("Failed to open log file:" + file_name);
}
virtual void write(std::string const &msg)
{
output_ << msg <<std::endl;
}
private:
std::ofstream output_;
};

class stream_log_device : public log_device {
public:
stream_log_device(std::ostream &out) : output_(out)
{
}
virtual void write(std::string const &msg)
{
output_ << msg <<std::endl;
}
private:
std::ostream &output_;
};

} /// impl

struct logger::data {
boost::shared_mutex lock;
logger::level_type global_level;
bool empty_map;
typedef std::map<std::string,logger::level_type> module_level_type;
module_level_type module_level;
std::auto_ptr<std::fstream> output;
std::vector<boost::shared_ptr<impl::log_device> > devices;
};

logger::level_type logger::module_level(char const *module)
{
if(d->empty_map) {
return d->global_level;
}
else {
boost::shared_lock<boost::shared_mutex> guard(d->lock);
data::module_level_type::const_iterator p;
if((p=d->module_level.find(module)) != d->module_level.end())
return p->second;
return d->global_level;
}
}
void logger::module_level(char const *module,level_type l)
{
boost::shared_lock<boost::shared_mutex> guard(d->lock);
d->module_level[module] = l;
d->empty_map = false;
}

void logger::reset_module_level(char const *module)
{
boost::shared_lock<boost::shared_mutex> guard(d->lock);
d->module_level.erase(module);
d->empty_map = d->module_level.empty();
}

bool logger::level(level_type l,char const *module)
{
return l <= module_level(module);
}

void logger::default_level(level_type l)
{
d->global_level = l;
}
logger::level_type logger::default_level()
{
return d->global_level;
}

logger::ostream_proxy logger::proxy(level_type l,char const *module,char const *file,int line)
{
ostream_proxy proxy(l,module,file,line,this);
return proxy;
}

logger &logger::instance()
{
static std::auto_ptr<logger> instance_ptr;
static boost::once_flag flag;
boost::call_once(flag,boost::bind(init,boost::ref(instance_ptr)));
}

void logger::init(std::auto_ptr<logger> &logger_ref)
{
logger_ref.reset(new logger());
}
logger::ostream_proxy::ostream_proxy() :
level_(all),
line_(0),
file_(""),
module_(""),
log_(&logger::instance()),
output_(new std::ostringstream)
{
}

logger::ostream_proxy::ostream_proxy(level_type lv,char const *m,char const *f,int line,logger *log) :
level_(lv),
line_(line),
file_(f),
module_(m),
log_(log),
output_(new std::ostringstream)
{
}
logger::ostream_proxy::ostream_proxy(logger::ostream_proxy &other) :
level_(other.level_),
line_(other.line_),
file_(other.file_),
module_(other.module_),
log_(other.log_)
{
output_ = other.output_;
}

logger::ostream_proxy &logger::ostream_proxy::operator=(logger::ostream_proxy &other)
{
if(&other!=this) {
level_ = other.level_;
line_ = other.line_;
file_ = other.file_;
module_ = other.module_;
log_ = other.log_;
output_ = other.output_;
}
return *this;
}

std::ostream &logger::ostream_proxy::out()
{
return *output_;
}
logger::ostream_proxy::~ostream_proxy()
{
try {
log_->write_to_log(level_,module_,file_,line_,output_->str());
}
catch(...)
{
}
}

void logger::write_to_log(level_type l,char const *module,char const *file,int line,std::string const &msg)
{
switch(l) {
case fatal: level = "fatal"; break;
case critical: level = "critical"; break;
case error: level = "error"; break;
case warning: level = "warning"; break;
case message: level = "message"; break;
case info: level = "info"; break;
case debug: level = "debug"; break;
default:
level="unknown";
}
std::string result = (boost::format("%1%, %2%:%3% %4%:%5%")
<<module << file << line << level << msg).str();
for(unsigned i=0;i<d->devices.size();i++)
d->devices[i]->write(result);
}
} // cppcms

+ 58
- 23
logger.h View File

@@ -1,27 +1,18 @@
#ifndef CPPCMS_LOGGER_H
#define CPPCMS_LOGGER_H

#include "defs.h"
#include <iosfwd>
#include <memory>

#include "copy_ptr.h"
#include "hold_ptr.h"
#include "noncopyable.h"

namespace cppcms {
class CPPCMS_API logger {
class CPPCMS_API logger : public util::noncopyable {
public:
class CPPCMS_API ostream_proxy {
public:
ostream_proxy(level_type level,char const *module);
~ostream_proxy()
ostream_proxy(ostream_proxy const &other);
ostream_proxy const &operator=(ostream_proxy const &other);

template<typename T>
ostream_proxy &operator<<(T const &v) const
{
output() << v;
}
private:
struct data;
intrusive_ptr<data> d;
};
typedef enum {
none = 0,
fatal = 10,
critical= 20,
error = 30,
@@ -32,14 +23,57 @@ namespace cppcms {
all = 100
} level_type;

class CPPCMS_API ostream_proxy {
public:
ostream_proxy();
ostream_proxy(level_type level,char const *module,char const *file,int line,logger *log);
~ostream_proxy();
ostream_proxy(ostream_proxy &other);
ostream_proxy &operator=(ostream_proxy &other);
std::ostream &out();
private:
level_type level_;
int line_;
char const *file_;
char const *module_;
logger *log_;
struct data;
util::copy_ptr<data> d;
std::auto_ptr<std::ostringstream> output_;
};

void write_to_log(level_type l,char const *module,char const *file,int line,std::string const &message);

bool level(level_type l,char const *module);
ostream_proxy proxy(level_type l,char const *module);
ostream_proxy proxy(level_type l,char const *module,char const *file,int line);

void default_level(level_type l);
level_type default_level();
void module_level(char const *module,level_type l);
level_type module_level(char const *module);
void reset_module_level(char const *module);
typedef enum {
overwrite = 0,
append = 1
} open_mode_type;
void log_to_file(std::string const &file_name,open_mode_type mode = append);
void log_to_stdout();
void log_to_stderr();
static logger &instance();
private:
static void init(std::auto_ptr<logger> &logger_ref);
struct data;
util::hold_ptr<data> d;
};

#define CPPCMS_LOG(_l,_m) \
::cppcms::logger::instance().level(::cppcms::logger::_l,_m) \
&& ::cppcms::logger::instance().proxy(::cppcms::logger::_l,_m)
#define CPPCMS_LOG(_l,_m) \
::cppcms::logger::instance().level(::cppcms::logger::_l,_m) \
&& ::cppcms::logger::instance().proxy(::cppcms::logger::_l,_m,__FILE__,__LINE__).out()

#define CPPCMS_FATAL(_m) CPPCMS_LOG(fatal,_m)
#define CPPCMS_CRITICAL(_m) CPPCMS_LOG(critical,_m)
@@ -49,6 +83,7 @@ namespace cppcms {
#define CPPCMS_INFO(_m) CPPCMS_LOG(info,_m)
#define CPPCMS_DEBUG(_m) CPPCMS_LOG(debug,_m)

}

} // CppCMS

#endif

+ 6
- 1
regex.cpp View File

@@ -11,6 +11,7 @@
namespace cppcms { namespace util {

struct regex_result::data {
std::string str;
boost::cmatch match;
};
regex_result::regex_result() : d(new data)
@@ -37,7 +38,11 @@ namespace cppcms { namespace util {
}
bool regex::match(std::string const &str,regex_result &res) const
{
return boost::regex_match(str.c_str(),res.d->match,d->r);
//
// Make sure that cmatch is valid, even if original string already is not
//
res.d->str = str;
return boost::regex_match(res.d->str.c_str(),res.d->match,d->r);
}
bool regex::match(std::string const &str) const
{


+ 34
- 12
service.cpp View File

@@ -14,10 +14,17 @@
#include "cppcms_error.h"
#include "cgi_acceptor.h"
#include "cgi_api.h"
#include "scgi_api.h"
#include "http_api.h"
#ifdef CPPCMS_HAS_SCGI
# include "scgi_api.h"
#endif
#ifdef CPPCMS_HAS_HTTP
# include "http_api.h"
#endif
#ifdef CPPCMS_HAS_FCGI
# include "fastcgi_api.h"
#endif

#include "cache_pool.h"
#include "fastcgi_api.h"
#include "internal_file_server.h"
#include "json.h"
#include "localization.h"
@@ -468,16 +475,21 @@ void service::start_acceptor()
tcp=false;
}

impl_->acceptor_.reset();

if(tcp) {
#ifdef CPPCMS_HAS_SCGI
if(api=="scgi")
impl_->acceptor_ = scgi_api_tcp_socket_factory(*this,ip,port,backlog);
else if(api=="fastcgi")
#endif
#ifdef CPPCMS_HAS_FCGI
if(api=="fastcgi")
impl_->acceptor_ = fastcgi_api_tcp_socket_factory(*this,ip,port,backlog);
else if(api=="http")
#endif
#ifdef CPPCMS_HAS_HTTP
if(api=="http")
impl_->acceptor_ = http_api_factory(*this,ip,port,backlog);
else
throw cppcms_error("Unknown service.api: " + api);
#endif
}
else {
#ifdef CPPCMS_WIN_NATIVE
@@ -485,22 +497,32 @@ void service::start_acceptor()
#elif defined CPPCMS_CYGWIN
throw cppcms_error("CppCMS uses native Win32 sockets under cygwin, so Unix sockets are not supported");
#else
if(api=="scgi")
#ifdef CPPCMS_HAS_SCGI
if(api=="scgi") {
if(socket=="stdin")
impl_->acceptor_ = scgi_api_unix_socket_factory(*this,backlog);
else
impl_->acceptor_ = scgi_api_unix_socket_factory(*this,socket,backlog);
else if(api=="fastcgi")
}
#endif
#ifdef CPPCMS_HAS_FCGI
if(api=="fastcgi") {
if(socket=="stdin")
impl_->acceptor_ = fastcgi_api_unix_socket_factory(*this,backlog);
else
impl_->acceptor_ = fastcgi_api_unix_socket_factory(*this,socket,backlog);
else if(api=="http")
}
#endif

#ifdef CPPCMS_HAS_HTTP
if(api=="http")
throw cppcms_error("HTTP API is not supported over Unix Domain sockets");
else
throw cppcms_error("Unknown service.api: " + api);
#endif
#endif
}
if(!impl_->acceptor_.get())
throw cppcms_error("Unknown service.api: " + api);

}



+ 24
- 0
tests/toscgi.py View File

@@ -0,0 +1,24 @@
#!/usr/bin/env python
# coding=UTF-8
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

def toscgi(text):
def to_headers():
res = []
hdr = text[:text.find('\n\n')]
for element in hrd.split('\n'):
[key,value]=element.split(':')
res.append((key.upper().replace('-','_'),value))
return res
def to_body()
return text[text.find('\n\n')+2:]
def format_headers(headers):
result = ''
for [key,value] in headers:
result+=key+'\0'+value+'\0'
return result
headers=to_headers(text)
body=to_body(text)
headers = [('CONTENT_LENGTH',len(body))] + headers
headers=format_headers(headers)
return str(len(headers))+':' + headers +',' + body

+ 19
- 0
util.h View File

@@ -7,9 +7,28 @@
namespace cppcms {

namespace util {
///
/// Escape string for inclusion in HTML page, i.e.
/// < --- &lt;
/// > --- &gt;
/// & --- &amp;
/// " --- &quot;
///
/// Note, this function does not deal with encodings, so it's up to you to
/// provide valid text encoding
///
std::string CPPCMS_API escape(std::string const &s);
///
/// Encode string for URL (percent encoding)
///
std::string CPPCMS_API urlencode(std::string const &s);
///
/// Decode string from URL-encoding (percent-encoding)
///
std::string CPPCMS_API urldecode(std::string const &s);
///
/// Decode text in range [begin,end) from URL-encoding (percent-encoding)
///
std::string CPPCMS_API urldecode(char const *begin,char const *end);
}
}


Loading…
Cancel
Save