Browse Source

Merged cache brunch to trunk: 306:323 /framework/branches/cache

master
Artyom Beilis 16 years ago
parent
commit
49595a8251
28 changed files with 898 additions and 1459 deletions
  1. +2
    -2
      Makefile.am
  2. +74
    -0
      archive.h
  3. +41
    -0
      base_cache.cpp
  4. +25
    -0
      base_cache.h
  5. +141
    -0
      cache_interface.cpp
  6. +45
    -0
      cache_interface.h
  7. +15
    -0
      cppcms_error.h
  8. +0
    -77
      data.h
  9. +17
    -12
      global_config.cpp
  10. +13
    -10
      global_config.h
  11. +0
    -28
      http_error.h
  12. +0
    -71
      main.cpp
  13. +0
    -266
      main_thread.cpp
  14. +0
    -64
      main_thread.h
  15. +0
    -231
      templates.cpp
  16. +0
    -128
      templates.h
  17. +6
    -310
      text_tool.cpp
  18. +6
    -21
      text_tool.h
  19. +0
    -25
      textstream.cpp
  20. +0
    -39
      textstream.h
  21. +268
    -0
      thread_cache.cpp
  22. +56
    -0
      thread_cache.h
  23. +19
    -17
      thread_pool.cpp
  24. +55
    -46
      thread_pool.h
  25. +17
    -17
      url.cpp
  26. +17
    -15
      url.h
  27. +58
    -68
      worker_thread.cpp
  28. +23
    -12
      worker_thread.h

+ 2
- 2
Makefile.am View File

@@ -3,6 +3,6 @@
#test_fcgi_LDADD = libcppcms.la

lib_LTLIBRARIES = libcppcms.la
libcppcms_la_SOURCES = FCgiIO.cpp global_config.cpp templates.cpp textstream.cpp thread_pool.cpp url.cpp worker_thread.cpp text_tool.cpp
libcppcms_la_SOURCES = FCgiIO.cpp global_config.cpp thread_pool.cpp url.cpp worker_thread.cpp text_tool.cpp cache_interface.cpp base_cache.cpp thread_cache.cpp

nobase_pkginclude_HEADERS = global_config.h templates.h text_tool.h url.h http_error.h textstream.h thread_pool.h worker_thread.h FCgiIO.h share/bytecode.h
nobase_pkginclude_HEADERS = global_config.h text_tool.h url.h cppcms_error.h thread_pool.h worker_thread.h FCgiIO.h cache_interface.h archive.h base_cache.h thread_cache.h

+ 74
- 0
archive.h View File

@@ -0,0 +1,74 @@
#ifndef ARCHIVE_H
#define ARCHIVE_H
#include "cppcms_error.h"
#include <string>
#include <cstring>

namespace cppcms {

using namespace std;

class archive {
string data;
size_t ptr;
public:
archive() { ptr=0; };
archive(string const &s) : data(s) { ptr=0; };
void set(string const &s) { data=s; ptr=0; };
string const &get() const { return data; };
template<typename T>
archive &operator<<(T const &val) {
size_t size=sizeof(T);
data.append((char const *)&size,sizeof(size_t));
data.append((char const *)&val,size);
return *this;
}
archive &operator<<(string const &val) {
size_t size=val.size();
data.append((char const *)&size,sizeof(size_t));
data.append(val.c_str(),size);
return *this;
}
template<typename T>
archive &operator>>(T &val)
{
if(ptr+sizeof(size_t)+sizeof(T)>data.size()) {
throw cppcms_error("Format violation");
}
char const *start=data.c_str()+ptr;
if(*(size_t const *)start!=sizeof(T)) {
throw cppcms_error("Invalid size read");
}
start+=sizeof(size_t);

memcpy(&val,start,sizeof(T));

ptr+=sizeof(size_t)+sizeof(T);
return *this;
}
archive &operator>>(string &val)
{
if(ptr+sizeof(size_t)>data.size()) {
throw cppcms_error("Format violation");
}
char const *start=data.c_str()+ptr;
size_t s=*(size_t const *)start;
if(ptr+sizeof(size_t)+s>data.size()) {
throw cppcms_error("String too long");
}
start+=sizeof(size_t);
val=string(start,s);
ptr+=sizeof(size_t)+s;
return *this;
}
};

class serializable {
public:
virtual void load(archive &a) = 0;
virtual void save(archive &a) const = 0;
virtual ~serializable() {};
};
}

#endif

+ 41
- 0
base_cache.cpp View File

@@ -0,0 +1,41 @@
#include "base_cache.h"
namespace cppcms {

using namespace std;

bool base_cache::fetch_page(string const &key,string &output,bool gzip)
{
return false;
}
bool base_cache::fetch(string const &key,archive &a,set<string> &tags)
{
return false;
};

void base_cache::clear()
{
// Nothing
}
void base_cache::rise(string const &trigger)
{
// Nothing
}

void base_cache::store(string const &key,set<string> const &triggers,time_t timeout,archive const &a)
{
// Nothing
}

base_cache::~base_cache()
{
// Nothing
}

void base_cache::stats(unsigned &keys,unsigned &triggers)
{
keys=0;
triggers=0;
}

}


+ 25
- 0
base_cache.h View File

@@ -0,0 +1,25 @@
#ifndef BASE_CACHE_H
#define BASE_CACHE_H

#include <string>
#include <set>
#include "archive.h"

namespace cppcms {

using namespace std;

class base_cache {
public:
virtual bool fetch_page(string const &key,string &output,bool gzip);
virtual bool fetch(string const &key,archive &a,set<string> &tags);
virtual void rise(string const &trigger);
virtual void clear();
virtual void store(string const &key,set<string> const &triggers,time_t timeout,archive const &a);
virtual void stats(unsigned &keys,unsigned &triggers);
virtual ~base_cache();
};

}

#endif

+ 141
- 0
cache_interface.cpp View File

@@ -0,0 +1,141 @@
#include "cache_interface.h"
#include "worker_thread.h"
#include "global_config.h"
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <sstream>
#include <iostream>

namespace cppcms {
using namespace std;

void deflate(string const &text,ostream &stream)
{
using namespace boost::iostreams;
gzip_params params;
long level,length;
if((level=global_config.lval("gzip.level",-1))!=-1){
params.level=level;
}
filtering_ostream zstream;

if((length=global_config.lval("gzip.buffer",-1))!=-1){
zstream.push(gzip_compressor(params,length));
}
else {
zstream.push(gzip_compressor(params));
}

zstream.push(stream);
zstream<<text;
}

string deflate(string const &text)
{
ostringstream sstream;
deflate(text,sstream);
return sstream.str();
}


bool cache_iface::fetch_page(string const &key)
{
string tmp;
if(!cms->caching_module) return false;
if(cms->caching_module->fetch_page(key,cms->out,cms->gzip)) {
cms->gzip_done=true;
return true;
}
return false;
}

void cache_iface::store_page(string const &key,time_t timeout)
{
if(!cms->caching_module) return;
archive a;
string compr=deflate(cms->out);
a<<(cms->out)<<compr;
if(cms->gzip){
cms->out=compr;
cms->gzip_done=true;
}
cms->caching_module->store(key,triggers,timeout,a);
}

void cache_iface::add_trigger(string const &t)
{
if(!cms->caching_module) return;
triggers.insert(t);
}

void cache_iface::rise(string const &t)
{
if(!cms->caching_module) return;
cms->caching_module->rise(t);
}

bool cache_iface::fetch_data(string const &key,serializable &data)
{
if(!cms->caching_module) return false;
archive a;
set<string> new_trig;
if(cms->caching_module->fetch(key,a,new_trig)) {
data.load(a);
triggers.insert(new_trig.begin(),new_trig.end());
return true;
}
return false;
}

void cache_iface::store_data(string const &key,serializable const &data,
set<string> const &triggers,
time_t timeout)
{
if(!cms->caching_module) return;
archive a;
data.save(a);
this->triggers.insert(triggers.begin(),triggers.end());
cms->caching_module->store(key,triggers,timeout,a);
}

bool cache_iface::fetch_frame(string const &key,string &result)
{
if(!cms->caching_module) return false;
archive a;
set<string> new_trig;
if(cms->caching_module->fetch(key,a,new_trig)) {
a>>result;
triggers.insert(new_trig.begin(),new_trig.end());
return true;
}
return false;
}

void cache_iface::store_frame(string const &key,string const &data,
set<string> const &triggers,
time_t timeout)
{
if(!cms->caching_module) return;
archive a;
a<<data;
this->triggers.insert(triggers.begin(),triggers.end());
cms->caching_module->store(key,triggers,timeout,a);
}

void cache_iface::clear()
{
if(cms->caching_module)
cms->caching_module->clear();
}

bool cache_iface::stats(unsigned &k,unsigned &t)
{
if(!cms->caching_module)
return false;
cms->caching_module->stats(k,t);
return true;
}

} // End of namespace cppcms

+ 45
- 0
cache_interface.h View File

@@ -0,0 +1,45 @@
#ifndef CACHE_IFACE_H
#define CACHE_IFACE_H

#include <string>
#include <set>
#include "archive.h"

using namespace std;

namespace cppcms {

const time_t infty=(sizeof(time_t)==4 ? 0x7FFFFFFF: 0x7FFFFFFFFFFFFFFFULL );

class worker_thread;
class cache_iface {
worker_thread *cms;
set<string> triggers;
public:
void reset() { triggers.clear(); };
cache_iface(worker_thread *w) : cms (w) {};
bool fetch_page(string const &key);
void store_page(string const &key,time_t timeout=infty);
void rise(string const &trigger);
void add_trigger(string const &trigger);
bool fetch_frame(string const &key,string &result);
void store_frame(string const &key,
string const &frame,
set<string> const &triggers=set<string>(),
time_t timeout=infty);
bool fetch_data(string const &key,serializable &data);
void store_data(string const &key,serializable const &data,
set<string> const &triggers=set<string>(),
time_t timeout=infty);
void clear();
bool stats(unsigned &keys,unsigned &triggers);
};

void deflate(string const &text,ostream &stream);
string deflate(string const &text);


}

#endif

+ 15
- 0
cppcms_error.h View File

@@ -0,0 +1,15 @@
#ifndef _HTTP_ERROR_H
#define _HTTP_ERROR_H

#include <string>
#include <stdexcept>

namespace cppcms {

class cppcms_error : public std::runtime_error {
public:
cppcms_error(std::string const &error) : std::runtime_error(error) {};
};

}
#endif /* _HTTP_ERROR_H */

+ 0
- 77
data.h View File

@@ -1,77 +0,0 @@
//
// C++ Interface: data
//
// Description:
//
//
// Author: artik <artik@artyom-linux>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//

#ifndef DATA_H
#define DATA_H

#include "easy_bdb.h"

using namespace ebdb;

typedef varchar<32> username_t;
typedef varchar<16> password_t;

struct user_t {
int id;
username_t username;
password_t password;
};

struct message_t {
int id;
int user_id;
long text_id;
};

class Users {
public:
typedef Index_Auto_Increment<user_t,int,&user_t::id> id_idx;
typedef id_idx::cursor_t id_c;
id_idx id;

typedef Index_Var<user_t,username_t,&user_t::username> username_idx;
typedef username_idx::cursor_t username_c;
username_idx username;
Users(Environment &env) :
id(env,"users_id.db",DB_BTREE),
username(env,"users_username.db",DB_BTREE,&id)
{};
void open() { id.open(); username.open();};
void create() { id.create(); username.create();};
void close() { id.close(); username.close();};
};

class Messages {
public:
typedef Index_Auto_Increment<message_t,int,&message_t::id> id_idx;
typedef id_idx::cursor_t id_c;
id_idx id;

Messages(Environment &env) :
id(env,"messages_id.db",DB_BTREE)
{};
void open() { id.open();};
void create() { id.create(); };
void close() { id.close();};
};




extern auto_ptr<Users> users;
extern auto_ptr<Messages> all_messages;
extern auto_ptr<Texts_Collector> texts;




#endif

+ 17
- 12
global_config.cpp View File

@@ -2,9 +2,12 @@
#include <stdio.h>
#include <ctype.h>

Global_Config global_config;

bool Global_Config::get_tocken(FILE *f,tocken_t &T)
namespace cppcms {

cppcms_config global_config;

bool cppcms_config::get_tocken(FILE *f,tocken_t &T)
{
int c;
while((c=fgetc(f))!=EOF) {
@@ -52,7 +55,7 @@ bool Global_Config::get_tocken(FILE *f,tocken_t &T)
}
}
if(T.second=="-" || T.second=="." || T.second=="-.") {
throw HTTP_Error("Illegal charrecters");
throw cppcms_error("Illegal charrecters");
}
if(c!=EOF) {
ungetc(c,f);
@@ -75,7 +78,7 @@ bool Global_Config::get_tocken(FILE *f,tocken_t &T)
}
}
if(c==EOF){
throw HTTP_Error("Unexpected EOF ");
throw cppcms_error("Unexpected EOF ");
}
if(c=='\n') line_counter++;
if(c=='\"') {
@@ -97,13 +100,13 @@ bool Global_Config::get_tocken(FILE *f,tocken_t &T)
}
else {
throw HTTP_Error(string("Unexpected charrecter")+(char)c);
throw cppcms_error(string("Unexpected charrecter")+(char)c);
}
}
return false;
}

void Global_Config::load(char const *fname)
void cppcms_config::load(char const *fname)
{
if(loaded){
return;
@@ -111,7 +114,7 @@ void Global_Config::load(char const *fname)
FILE *f=fopen(fname,"r");
line_counter=1;
if(!f) {
throw HTTP_Error(string("Failed to open file:")+fname);
throw cppcms_error(string("Failed to open file:")+fname);
}
tocken_t T;
string key;
@@ -164,21 +167,21 @@ void Global_Config::load(char const *fname)
}
}
if(state!=0) {
throw HTTP_Error("Parsing error");
throw cppcms_error("Parsing error");
}
}
catch (HTTP_Error &err){
catch (cppcms_error &err){
fclose(f);
char stmp[32];
snprintf(stmp,32," at line %d",line_counter);
throw HTTP_Error(string(err.get())+stmp);
throw cppcms_error(string(err.what())+stmp);
}
fclose(f);
loaded=true;
}


void Global_Config::load(int argc,char *argv[],char const *def)
void cppcms_config::load(int argc,char *argv[],char const *def)
{
if(loaded) {
return;
@@ -196,7 +199,9 @@ void Global_Config::load(int argc,char *argv[],char const *def)
}
}
if(def_file==NULL) {
throw HTTP_Error("Configuration file not defined");
throw cppcms_error("Configuration file not defined");
}
load(def_file);
}

}

+ 13
- 10
global_config.h View File

@@ -3,18 +3,19 @@

#include <string>
#include <map>
#include "cppcms_error.h"

#include "http_error.h"
namespace cppcms {

using namespace std;

class Global_Config {
class cppcms_config {
enum { WORD, INT, DOUBLE, STR };
typedef pair<int,string> tocken_t;
typedef std::pair<int,string> tocken_t;
typedef pair<string,string> key_t;
typedef std::pair<string,string> key_t;
std::map<string,long> long_map;
std::map<string,double> double_map;
@@ -31,14 +32,13 @@ public:
void load(char const *filename);
void load(int argc,char *argv[],char const *def=NULL);

Global_Config() { loaded = false;};
~Global_Config() {};
cppcms_config() { loaded = false;};
long lval(string major) {
std::map<string,long>::iterator it;
if((it=long_map.find(major))!=long_map.end()) {
return it->second;
}
throw HTTP_Error("Undefined configuration "+major);
throw cppcms_error("Undefined configuration "+major);
};
long lval(string major,long def) {
std::map<string,long>::iterator it;
@@ -52,7 +52,7 @@ public:
if((it=double_map.find(major))!=double_map.end()) {
return it->second;
}
throw HTTP_Error("Undefined configuration "+major);
throw cppcms_error("Undefined configuration "+major);
};
double dval(string major,double def) {
std::map<string,double>::iterator it;
@@ -66,7 +66,7 @@ public:
if((it=string_map.find(major))!=string_map.end()) {
return it->second;
}
throw HTTP_Error("Undefined configuration "+major);
throw cppcms_error("Undefined configuration "+major);
};
string sval(string major,string def){
std::map<string,string>::iterator it;
@@ -78,6 +78,9 @@ public:
};

extern Global_Config global_config;
extern cppcms_config global_config;

} // namespace cppcms


#endif /* _GLOBAL_CONFIG_H */

+ 0
- 28
http_error.h View File

@@ -1,28 +0,0 @@
#ifndef _HTTP_ERROR_H
#define _HTTP_ERROR_H

#include <string>
using namespace std;

#define HTTP_ERROR_MAX_ERROR_MESSAGE_LEN 256


class HTTP_Error {
bool State404;
char message[HTTP_ERROR_MAX_ERROR_MESSAGE_LEN];
public:
char const *get() { return message; };
bool is_404() { return State404; };
HTTP_Error(char const *text,bool NotFound = false)
{
strncpy(message,text,HTTP_ERROR_MAX_ERROR_MESSAGE_LEN);
State404 = NotFound;
};
HTTP_Error(string const &str,bool NotFound = false)
{
strncpy(message,str.c_str(),HTTP_ERROR_MAX_ERROR_MESSAGE_LEN);
State404 = NotFound;
};
};

#endif /* _HTTP_ERROR_H */

+ 0
- 71
main.cpp View File

@@ -1,71 +0,0 @@
#include <iostream>
#include <memory>
#include "main_thread.h"
#include "thread_pool.h"
#include "global_config.h"
#include "url.h"

#include "templates.h"
#include "data.h"

using namespace std;

Templates_Set templates;


auto_ptr<Users> users;
auto_ptr<Messages> all_messages;
auto_ptr<Texts_Collector> texts;

void setup()
{
user_t user;
user.username="artik";
user.password="artik";
cerr<<"Setting up: "<<users->id.add(user)<<endl;
user.username="maas";
user.password="maas";
users->id.add(user);
}

int main(int argc,char **argv)
{
try{
global_config.load(argc,argv);

templates.load();

Environment env(global_config.sval( "bdb.path" ).c_str());
users=auto_ptr<Users>(new Users(env));
all_messages=auto_ptr<Messages>(new Messages(env));
texts=auto_ptr<Texts_Collector>(new Texts_Collector(env,"texts.db"));

env.create();

users->create();
all_messages->create();
texts->create();


if(argc>2 && strcmp(argv[1],"setup")==0) {
setup();
}

Run_Application<Main_Thread>(argc,argv);

users->close();
all_messages->close();
texts->close();
env.close();

cout<<"Exiting\n";
}
catch(HTTP_Error &s) {
cerr<<s.get()<<endl;
return 1;
}
catch(DbException &e) {
cerr<<e.what()<<endl;
}
return 0;
}

+ 0
- 266
main_thread.cpp View File

@@ -1,266 +0,0 @@
#include "main_thread.h"

#include "global_config.h"

#include "templates/look.h"
#include "templates.h"
#include "data.h"
#include "boost/format.hpp"
#include "text_tool.h"



void Main_Thread::init()
{
url.init(this);

url.add("^/?$", BIND(&Main_Thread::show_main_page,this,"end"));
url.add("^/from/(\\d+)$",BIND(&Main_Thread::show_main_page,this,$1));
url.add("^/login$", BIND(&Main_Thread::show_login,this));
url.add("^/logout$", BIND(&Main_Thread::show_logout,this));
url.add("^/newpost$", BIND(&Main_Thread::show_post_form,this));
url.add("^/post$", BIND(&Main_Thread::get_post_message,this));
url.add("^/dologin$", BIND(&Main_Thread::do_login,this));
url.add("^/edit/(\\d+)$",BIND(&Main_Thread::edit_message,this,$1));

}

void Main_Thread::edit_message(string msg)
{
// TODO
}


void Main_Thread::load_cookies()
{
const vector<HTTPCookie> &cookies = env->getCookieList();
unsigned int i;

username.clear();
visitor.clear();
email.clear();
vurl.clear();
password.clear();

for(i=0;i<cookies.size();i++) {
if(cookies[i].getName()=="username") {
username=cookies[i].getValue();
}
else if(cookies[i].getName()=="password") {
password=cookies[i].getValue();
}
else if(cookies[i].getName()=="visitor") {
visitor=cookies[i].getValue();
}
else if(cookies[i].getName()=="email") {
email=cookies[i].getValue();
}
else if(cookies[i].getName()=="url") {
vurl=cookies[i].getValue();
}
}
}

void Main_Thread::check_athentication_by_name(string name,string password)
{
Users::username_c cur(users->username);

if(cur==name){
user_t const &user=cur;
if(user.password==password.c_str()) {
authenticated=true;
user_id=user.id;
}
username=name;
}
else {
username.clear();
authenticated=false;
}
password.clear();
}

void Main_Thread::check_athentication()
{
load_cookies();
check_athentication_by_name(username,password);
}

void Main_Thread::load_inputs()
{

const vector<FormEntry> &elements=cgi->getElements();
unsigned i;

page=0;
message.clear();

for(i=0;i<elements.size();i++) {
string const &name=elements[i].getName();
if(name=="message"){
message=elements[i].getValue();
}
else if(name=="username"){
new_username=elements[i].getValue();
}
else if(name=="password"){
new_password=elements[i].getValue();
}
}
}

void Main_Thread::show_login()
{
Content c(T_VAR_NUM);

c[TV_title]="Login";
c[TV_show_content]=TT_dologin;

Renderer r(templates,TT_master,c);

while(r.render(out)!=0);

}

void Main_Thread::do_login()
{
load_inputs();

check_athentication_by_name(new_username,new_password);

if(authenticated) {
set_header(new HTTPRedirectHeader("/site/"));
HTTPCookie cookie_u("username",username,"","",7*24*3600,"/",false);
response_header->setCookie(cookie_u);
HTTPCookie cookie_p("password",new_password,"","",7*24*3600,"/",false);
response_header->setCookie(cookie_p);
}
else {
username="";
password="";
set_header(new HTTPRedirectHeader("/site/login"));
}
};

void Main_Thread::show_logout()
{
set_header(new HTTPRedirectHeader("/site/"));
HTTPCookie cookie("username","","","",0,"/",false);
response_header->setCookie(cookie);
cookie.setName("password");
cookie.setValue("");
response_header->setCookie(cookie);
}

void Main_Thread::show_main_page(string from)
{
check_athentication();

Content c(T_VAR_NUM);
Renderer t(templates,TT_master,c);

c[TV_title]="Main page";
c[TV_show_content]=TT_main;

if(authenticated) {
c[TV_username]=username;
}

Messages::id_c cur(all_messages->id);

if(from=="end") {
cur.end();
}
else {
int from_id=atoi(from.c_str());
cur<from_id;
}


int id;
string content;
int counter=0;
while((id=t.render(out))!=0) {
if(id==TV_get_message){
if(cur && counter<10){
message_t const &message=cur;
c[TV_new_message]=1;
char buf[20];
snprintf(buf,20,"%d",message.id);
c[TV_message_id]=buf;

string intext;

texts->get(message.text_id,intext);
Text_Tool conv;
conv.markdown2html(intext,content);

c[TV_message_body]=content.c_str();

Users::id_c ucur(users->id);
ucur==message.user_id;
user_t const &user=ucur;
c[TV_author]=user.username.c_str();
cur.next();
counter++;
}
else {
c[TV_new_message]=0;
}
}
}
}

void Main_Thread::show_post_form()
{
check_athentication();
if(authenticated) {
Content c(T_VAR_NUM);

c[TV_title]="New message";
c[TV_show_content]=TT_post;
Renderer t(templates,TT_master,c);
while(t.render(out));
}
else {
set_header(new HTTPRedirectHeader("/site/login"));

}
}

void Main_Thread::get_post_message()
{
check_athentication();

load_inputs();

if(!authenticated){
set_header(new HTTPRedirectHeader("/site/login"));
return;
}


message_t msg;
msg.text_id=texts->add(message);
msg.user_id=user_id;
all_messages->id.add(msg);

set_header(new HTTPRedirectHeader("/site/"));
}

void Main_Thread::show_page()
{
url.parse();
}
void Main_Thread::main()
{
try {
show_page();
}
catch (DbException &err) {
throw HTTP_Error(err.what());
}
catch(char const *s) {
throw HTTP_Error(s);
}
}

+ 0
- 64
main_thread.h View File

@@ -1,64 +0,0 @@
#ifndef _MAIN_THREAD_H
#define _MAIN_THREAD_H


#include "worker_thread.h"
#include "http_error.h"

#include <cgicc/HTTPStatusHeader.h>
#include <cgicc/HTTPRedirectHeader.h>
#include "easy_bdb.h"
#include "data.h"

using namespace cgicc;
using namespace ebdb;

extern auto_ptr<Users> users;
extern auto_ptr<Messages> all_messages;
extern auto_ptr<Texts_Collector> texts;


class Main_Thread : public Worker_Thread {
URL_Parser url;
// User Data
int user_id;
string username;
string visitor;
string email;
string vurl;
string password;
bool authenticated;
string new_username;
string new_password;
// Other
int page;
string message;
// Functions
void show_page();
void show_main_page(string from);
void show_login();
void show_logout();
void get_post_message();
void load_cookies();
void load_inputs();
void do_login();
void show_post_form();
void edit_message(string s);
protected:

void check_athentication();
void check_athentication_by_name(string,string);
void get_parameters();

virtual void main();

string protect(string const &str);

public:
virtual void init();
Main_Thread() {};
virtual ~Main_Thread() {};
};


#endif /* _MAIN_THREAD_H */

+ 0
- 231
templates.cpp View File

@@ -1,231 +0,0 @@
#include "templates.h"

#include "global_config.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#include <iostream>
using namespace std;


bool Renderer::debug_defined=false;
bool Renderer::debug=false;

Renderer::Renderer(Templates_Set &tset,int id,Content &cont)
{
if(!debug_defined) {
debug=global_config.lval("templates.debug_level",0);
debug_defined=true;
}
templates_set=&tset;
tmpl=tset.get(id);
if(!tmpl && debug) {
char buf[32];
snprintf(buf,32,"%d",id);
throw HTTP_Error(string("Failed to load template")+buf);
}
pos=0;
stack_size=0;
content=&cont;
returns_stack.reserve(10);
templates_stack.reserve(10);
}

int Renderer::render(string &s)
{
if(!tmpl) return 0;
Content &cont=*content;
Base_Template *tmp_tmpl;
int templ_id;
for(;;) {
if(pos<0 || pos>(int64_t)(tmpl->len-sizeof(Tmpl_Op))){
throw HTTP_Error("Template overflow");
}
Tmpl_Op *op=(Tmpl_Op*)(tmpl->mem_ptr+pos);
pos+=sizeof(Tmpl_Op);
Variable var;

switch(op->opcode){
case OP_VAR:
case OP_GOTO_IF_TRUE:
case OP_GOTO_IF_FALSE:
case OP_GOTO_IF_DEF:
case OP_GOTO_IF_NDEF:
case OP_INCLUDE_REF:
var=cont[op->parameter];
break;
}
switch(op->opcode) {
case OP_INLINE:
if(pos+op->parameter>tmpl->len){
throw HTTP_Error("Template overflow");
}
s.append(tmpl->mem_ptr+pos,
op->parameter);
pos+=op->parameter;
break;
case OP_CALL:
return op->parameter;
case OP_VAR:
if(var.isstr()) {
s+=var.gets();
}
else if(debug) {
throw HTTP_Error("Undefined variable");
}
break;
case OP_INCLUDE_REF:
if(var.isint()){
templ_id=var.geti();
}
else {
throw HTTP_Error("Undefined variable in INCLUDE_REF");
}
case OP_INCLUDE:
if(op->opcode==OP_INCLUDE){
templ_id=op->parameter;
}
if((tmp_tmpl=templates_set->get(templ_id))==NULL){
if(debug)
throw HTTP_Error("Undefined template");
break;
}
if(returns_stack.size()<(unsigned)stack_size+1){
returns_stack.push_back(pos);
templates_stack.push_back(tmpl);
}
else {
returns_stack[stack_size]=pos;
templates_stack[stack_size]=tmpl;
}
tmpl=tmp_tmpl;
stack_size++;
pos=0;
break;
case OP_GOTO_IF_TRUE:
if(var.isint()){
if(var.geti()){
pos=op->jump;
}
}
else if(debug){
throw HTTP_Error("Undefined variable");
}
break;
case OP_GOTO_IF_FALSE:
if(var.isint()){
if(!var.geti()){
pos=op->jump;
}
}
else if(debug){
throw HTTP_Error("Undefined variable");
}
break;
case OP_GOTO_IF_DEF:
if(var.isdef()){
pos=op->jump;
}
break;
case OP_GOTO_IF_NDEF:
if(!var.isdef()){
pos=op->jump;
}
break;
case OP_GOTO:
pos=op->jump;
break;
case OP_STOP:
if(stack_size==0){
return 0;
}
stack_size--;
pos=returns_stack[stack_size];
tmpl=templates_stack[stack_size];
break;
default:
throw HTTP_Error("Unknown opcode");
}
}
}

void Templates_Set::load()
{
load(global_config.sval("templates.file").c_str());
}

void Templates_Set::load(char const *file,int use_mmap)
{
if(use_mmap==-1) {
use_mmap=global_config.lval("templates.use_mmap",0);
}
if(use_mmap) {
// Rationale:
// In case of many processes openning same file
struct stat statbuf;
char *buf;
if((fd=open(file,O_RDONLY))<0
|| fstat (fd,&statbuf) < 0
|| (buf=(char*)mmap(0,statbuf.st_size,
PROT_READ,MAP_SHARED, fd, 0))==(char*)-1)
{
throw HTTP_Error(string("Falied to open file:")+file);
}
file_size=statbuf.st_size;
base_ptr=buf;
}
else {
FILE *f;
f=fopen(file,"r");
if(!f) {
throw HTTP_Error(string("Falied to open file:")+file);
}
fseek(f,0,SEEK_END);
file_size=ftell(f);
rewind(f);
char *buf=new char [file_size];
base_ptr=buf;
if((int)fread(buf,1,file_size,f)!=file_size) {
fclose(f);
throw HTTP_Error(string("Falied to read file:")+file);
}
fclose(f);
}
setup_map();
}

void Templates_Set::setup_map()
{
if(file_size<4) {
throw HTTP_Error("Incorrect file format");
}
map_size=*(uint32_t*)base_ptr;
if(4+4*map_size>file_size || map_size<0){
throw HTTP_Error("Incorrect file format");
}
map=((uint32_t*)base_ptr)+1;
templates.reserve(map_size);
int offset=4+4*map_size;
int i;
for(i=0;i<map_size;i++){
if((int64_t)map[i]+offset>file_size){
HTTP_Error("Incorrect file format");
}
templates.push_back(Base_Template(base_ptr+offset,map[i]));
offset+=map[i];
}
if(offset!=file_size)
HTTP_Error("Incorrect file format");
}

Base_Template *Templates_Set::get(int id)
{
if(id<0 || id>=map_size) {
return NULL;
}
return &(templates[id]);
}

+ 0
- 128
templates.h View File

@@ -1,128 +0,0 @@
#ifndef _TEMPLATES_H
#define _TEMPLATES_H


#include <string>
#include <stdio.h>
#include <vector>

#include <sys/mman.h>

#include "share/bytecode.h"
#include "textstream.h"

using namespace std;


class Variable {
public:
typedef enum { NONE, INTEGER, STRING } type_t;
private:
type_t type;
int int_val;
string str_data;
public:
Variable() { type = NONE; int_val=0; };
Variable(string const &s) {
str_data=s;
type = STRING;
};
Variable(int b) { int_val = b; type = INTEGER; };
void operator=(int b) { *this=Variable(b); };
void operator=(string const &s) { *this=Variable(s); };
void reset() { str_data.empty(); type=NONE; };
int geti() { return int_val; };
char const *gets(){ return str_data.c_str(); };
bool isstr() { return type == STRING; };
bool isdef() { return type != NONE; };
bool isint() { return type == INTEGER; };
type_t get_type() { return type; };
};

class Content {
int size;
vector<Variable> vars;
public:
void reset()
{
int i;
for(i=0;i<size;i++){
vars.push_back(Variable());
}
};
Content(int size) {
this->size=size;
vars.reserve(size);
reset();
};
Variable &operator[](int i) {
if(i<0 || i>=size) {
throw HTTP_Error("Out of content bounds");
}
return vars[i];
};
};

class Base_Template {
friend class Renderer;
friend class Templates_Set;

char const *mem_ptr;
int len;
public:
Base_Template() { mem_ptr=NULL;};
Base_Template(char const *ptr, int l) { mem_ptr=ptr; len=l; };
};

class Templates_Set {
friend class Base_Template;
char const *base_ptr;
vector<Base_Template> templates;
uint32_t *map;
int map_size;
int file_size;
int fd;
void setup_map(void);
public:
Templates_Set() {
base_ptr=0;
map=0;
fd=-1;
};
~Templates_Set() {
if(fd!=-1){
munmap((void*)base_ptr,file_size);
close(fd);
}
else {
delete [] base_ptr;
}
}
Base_Template *get(int id);
void load(char const *file,int use_mmap=-1);
void load(void);
};

class Renderer {
static bool debug;
static bool debug_defined;
vector<Base_Template *> templates_stack;
vector<int> returns_stack;
Templates_Set *templates_set;
int stack_size;

Base_Template *tmpl;
int pos;
Content *content;
public:
Renderer(Templates_Set &tset,int id,Content &cont);
int render(string &s);
int render(Text_Stream &out) { return render(out.text);};
};

extern Templates_Set templates;



#endif /* _TEMPLATES_H */

+ 6
- 310
text_tool.cpp View File

@@ -1,94 +1,13 @@
#include "text_tool.h"
using namespace std;
namespace cppcms{
namespace texttool {

#include <iostream>
#include <boost/regex.hpp>

void Text_Tool::init()
void text2html(string const &s,string &content)
{
ptr=0;
}

void Text_Tool::getline(string &in)
{
string tmp;
if(ptr==string::npos){
input=L_EOF;
return;
}
size_t pos=in.find('\n',ptr);
if(pos==string::npos){
if(ptr<in.size()) {
tmp=in.substr(ptr);
ptr=string::npos;
}
else {
input=L_EOF;
return;
}
}
else {
tmp=in.substr(ptr,pos-ptr);
ptr=pos+1;
}

int blanks=0,i;
for(i=0;i<tmp.size() && blanks<4;i++) {
char c=tmp[i];
if(c=='\r') continue;
else if(c==' ') blanks++;
else if(c=='\t') blanks = (blanks | 0x3) + 1;
else break;
}
if(blanks==4) {
basic_to_html(tmp.substr(i));
input=L_CODE;
return;
}
int c=tmp[i];
if(c=='>') {
to_html(tmp.substr(i+1));
input=L_QUOTE;
return;
}
if(c=='-' && tmp[i+1]==' ') {
to_html(tmp.substr(i+2));
input=L_UL;
return;
}
if(isdigit(c)) {
int p=i;
while(isdigit(tmp[p]))p++;
if(tmp[p]=='.' && tmp[p+1]==' ') {
to_html(tmp.substr(p+2));
input=L_OL;
return;
}
}
if(c=='#') {
int p=i+1;
header_level=1;
while(tmp[p]=='#') { p++; header_level++;}
if(header_level>6) header_level=6;
to_html(tmp.substr(p));
input=L_H;
return;
}
if(tmp.size()-i==0) {
input=L_BLANK;
return;
}
to_html(tmp);
input=L_TEXT;
}

void Text_Tool::text2html(char const *s,string &content)
{
int i;
content="";
if(s==NULL) return;
int len=strlen(s);
content.reserve(len*3/2);
for(i=0;i<len;i++) {
unsigned i;
for(i=0;i<s.size();i++) {
char c=s[i];
switch(c){
case '<': content+="&lt;"; break;
@@ -100,228 +19,5 @@ void Text_Tool::text2html(char const *s,string &content)
}
}

void Text_Tool::text2url(char const *in,string &out)
{
static boost::regex e("^(http:\\/\\/|https:\\/\\/|ftp:\\/\\/|sftp:\\/\\/|mailto:\\/).*$");
boost::cmatch m;
if(boost::regex_match(in,m,e)) {
text2html(in,out);
}
else {
string tmpstr;
text2html(in,tmpstr);
out="http://"+tmpstr;
}
}

void Text_Tool::basic_to_html(string s)
{
text2html(s,content);
content+="\n";
}

void Text_Tool::to_html(string s)
{
int i;
content="";
content.reserve(s.size()*3/2);
bool bold_on=false;
bool it_on=false;
enum { B, I };
int last;

for(i=0;i<s.size();i++) {
char c=s[i];
switch(c){
case '<': content+="&lt;"; break;
case '>': content+="&gt;"; break;
case '&': content+="&amp;"; break;
case '*':
if(s[i+1]!=' ' && (i>0 ? s[i-1]==' ':true) && !bold_on) {
content+="<b>"; bold_on=true; last=B;
}
else if(bold_on && s[i+1]==' ') {
content+="</b>"; bold_on=false;
}
else {
content+='*';
}
break;
case '_':
if(s[i+1]!=' ' && (i>0 ? s[i-1]==' ':true) && !it_on) {
content+="<i>"; it_on=true; last=I;
}
else if(it_on && s[i+1]==' ') {
content+="</i>"; it_on=false;
}
else {
content+='_';
}
break;
case '-':
if(s[i+1]=='-') {
i++;
content+="&#8212;";
}
else content+="-";
break;
case '\\':
if(s[i+1]=='\\') {
i++;
content+="<br/>\n";
}
else content+="\\";
break;
case '[':
{
size_t p1,p2;
if((p1=s.find("](",i))!=string::npos
&& (p2=s.find(")",p1))!=string::npos)
{
string text,url;
string otext=s.substr(i+1,p1-(i+1));
string ourl=s.substr(p1+2,p2-(p1+2));
text2html(otext,text);
text2url(ourl.c_str(),url);
content+="<a href=\"";
content+=url;
content+="\">";
content+=text;
content+="</a>";
i=p2;
}
else {
content+="[";
}
}
break;
default:
content+=c;
}
}
if(bold_on && it_on && last==I) {
content+="</i>";
it_on=false;
}
if(bold_on) {
content+="</b>";
}
if(it_on){
content+="</i>";
}
content+="\n";
}

void Text_Tool::markdown2html(char const *c_in,string &out)
{
#warning "Inefficient -- fix me"
string in=c_in;
init();
out="";
out.reserve(in.size()*3/2);
state=NOTHING;
while(state!=FINISH) {
getline(in);
switch(state) {
case NOTHING: break;
case QUOTE:
if(input!=L_QUOTE) {
out+="</p></blockquote>\n";
state=NOTHING;
}
else {
out+=content;
}
break;
case CODE:
if(input!=L_CODE) {
out+="</pre>\n";
state=NOTHING;
}
else {
out+=content;
}
break;
case UL:
if(input==L_TEXT){
out+=content;
}
else if(input==L_UL){
out+="</li>\n";
out+="<li>\n";
out+=content;
}
else {
out+="</li></ul>\n";
state=NOTHING;
}
break;
case OL:
if(input==L_TEXT){
out+=content;
}
else if(input==L_OL){
out+="</li>\n";
out+="<li>\n";
out+=content;
}
else {
out+="</li></ol>\n";
state=NOTHING;
}
break;
case P:
if(input!=L_TEXT) {
out+="</p>\n";
state=NOTHING;
}
else {
out+=content;
}
break;
};
if(state==NOTHING) {
switch(input){
case L_BLANK: break;
case L_TEXT:
out+="<p>\n";
out+=content;
state=P;
break;
case L_H:
{
char buf[16];
snprintf(buf,16,"<h%d>",header_level);
out+=buf;
out+=content;
snprintf(buf,16,"</h%d>\n",header_level);
out+=buf;
}
break;
case L_QUOTE:
out+="<blockquote><p>\n";
out+=content;
state=QUOTE;
break;
case L_CODE:
out+="<pre>\n";
out+=content;
state=CODE;
break;
case L_UL:
out+="<ul><li>\n";
out+=content;
state=UL;
break;
case L_OL:
out+="<ol><li>\n";
out+=content;
state=OL;
break;
case L_EOF:
state=FINISH;
break;
}
}
}
}

+ 6
- 21
text_tool.h View File

@@ -3,27 +3,12 @@

#include <string>

using std::string;

class Text_Tool {
// State definitions:
enum { NOTHING, QUOTE, CODE, UL, OL, P, FINISH };
enum { L_BLANK, L_TEXT, L_H, L_QUOTE, L_CODE, L_UL, L_OL ,L_EOF };
int state;
int input;
size_t ptr;
string content;
int header_level;
void getline(string &s);
void init();
void to_html(string s);
void basic_to_html(string s);
public:
void markdown2html(char const *in,string &out);
void markdown2html(string &in,string &out) { markdown2html(in.c_str(),out);};
void text2html(char const *s,string &);
void text2html(string &s,string &out) { text2html(s.c_str(),out);};
void text2url(char const *s,string &);
namespace cppcms {
namespace texttool {
using std::string;
void text2html(string const &s,string &out);
void text2url(string const &s,string &out);
};
};




+ 0
- 25
textstream.cpp View File

@@ -1,25 +0,0 @@
#include "textstream.h"
#include <stdarg.h>
#include <fcgiapp.h>


void Text_Stream::printf(char *format,...)
{
va_list ap;
va_start(ap,format);
int size=vsnprintf(buffer,TS_BUFFER_SIZE,format,ap);
va_end(ap);
if(size>TS_BUFFER_SIZE) {
char *str=new char[size+1];
va_start(ap,format);
vsnprintf(str,size+1,format,ap);
va_end(ap);
text += str;
delete [] str;
}
else {
text += buffer;
}
}

+ 0
- 39
textstream.h View File

@@ -1,39 +0,0 @@
#ifndef _TEXTSTREAM_H
#define _TEXTSTREAM_H

#include <string>

#include "global_config.h"

using namespace std;

#define TS_BUFFER_SIZE 1024
#define TS_INITAL_ALLOCATION 0x10000


class Text_Stream {
int inital_alloc;
char buffer[TS_BUFFER_SIZE];
string text;
friend class Renderer;
public:
void reset(void) {
text.clear();
text.reserve(inital_alloc);
};
Text_Stream() {
inital_alloc=global_config.lval("performance.textalloc",
TS_INITAL_ALLOCATION);
reset();
};
~Text_Stream() {};
void puts(char const *t) { text += t; };
void putchar(char c) { text += c; };
char const *get() { return text.c_str(); };
string &getstring() { return text; };
int len() { return text.size(); };
void printf(char *format, ...);
void puts(string &str) { text += str; };
};

#endif /* _TEXTSTREAM_H */

+ 268
- 0
thread_cache.cpp View File

@@ -0,0 +1,268 @@
#include "thread_cache.h"
#include <boost/format.hpp>
#include <unistd.h>

using boost::format;
using boost::str;

namespace cppcms {

class mutex_lock {
pthread_mutex_t &m;
public:
mutex_lock(pthread_mutex_t &p): m(p) { pthread_mutex_lock(&m); };
~mutex_lock() { pthread_mutex_unlock(&m); };
};

class rwlock_rdlock {
pthread_rwlock_t &m;
public:
rwlock_rdlock(pthread_rwlock_t &p): m(p) { pthread_rwlock_rdlock(&m); };
~rwlock_rdlock() { pthread_rwlock_unlock(&m); };
};

class rwlock_wrlock {
pthread_rwlock_t &m;
public:
rwlock_wrlock(pthread_rwlock_t &p): m(p) { pthread_rwlock_wrlock(&m); };
~rwlock_wrlock() { pthread_rwlock_unlock(&m); };
};

thread_cache::~thread_cache()
{
pthread_mutex_destroy(&lru_mutex);
pthread_rwlock_destroy(&access_lock);
}

string *thread_cache::get(string const &key,set<string> *triggers)
{
pointer p;
time_t now;
time(&now);
if(debug_mode) print_all();
if((p=primary.find(key))==primary.end() || p->second.timeout->first < now) {
if(debug_mode) {
string res;
if(p==primary.end()) {
res=str(boost::format("Not found [%1%]\n") % key);
}
else {
res=str(boost::format("Found [%1%] but timeout of %2% seconds\n")
% key % (now - p->second.timeout->first));
}
write(fd,res.c_str(),res.size());
}
return NULL;
}
if(triggers) {
list<triggers_ptr>::iterator tp;
for(tp=p->second.triggers.begin();tp!=p->second.triggers.end();tp++) {
triggers->insert((*tp)->first);
}
}
{
mutex_lock lock(lru_mutex);
lru.erase(p->second.lru);
lru.push_front(p);
p->second.lru=lru.begin();
}
if(debug_mode){
string res=str(boost::format("Fetched [%1%] triggers:") % key);
list<triggers_ptr>::iterator tp;
for(tp=p->second.triggers.begin();
tp!=p->second.triggers.end();tp++)
{
res+=(*tp)->first;
res+=" ";
}
res+="\n";
write(fd,res.c_str(),res.size());
}
return &(p->second.data);
}

bool thread_cache::fetch_page(string const &key,string &out,bool gzip)
{
rwlock_rdlock lock(access_lock);
string *r=get(key,NULL);
if(!r) return false;
size_t size=r->size();
size_t s;
char const *ptr=r->c_str();
if(size<sizeof(size_t) || (s=*(size_t const *)ptr)>size-sizeof(size_t))
return false;
if(!gzip){
out.assign(ptr+sizeof(size_t),s);
}
else {
ptr+=s+sizeof(size_t);
size-=s+sizeof(size_t);
if(size<sizeof(size_t) || (s=*(size_t const *)ptr)!=size-sizeof(size_t))
return false;
out.assign(ptr+sizeof(size_t),s);
}
return true;
}

bool thread_cache::fetch(string const &key,archive &a,set<string> &tags)
{
rwlock_rdlock lock(access_lock);
string *r=get(key,&tags);
if(!r) return false;
a.set(*r);
return true;
}

void thread_cache::clear()
{
rwlock_wrlock lock(access_lock);
timeout.clear();
lru.clear();
primary.clear();
triggers.clear();
}
void thread_cache::stats(unsigned &keys,unsigned &triggers)
{
rwlock_rdlock lock(access_lock);
keys=primary.size();
triggers=this->triggers.size();
}

void thread_cache::rise(string const &trigger)
{
rwlock_wrlock lock(access_lock);
if(debug_mode) print_all();
pair<triggers_ptr,triggers_ptr> range=triggers.equal_range(trigger);
triggers_ptr p;
list<pointer> kill_list;
for(p=range.first;p!=range.second;p++) {
kill_list.push_back(p->second);
}
list<pointer>::iterator lptr;
if(debug_mode){
string out=str(boost::format("Trigger [%1%] dropping: ") % trigger);
write(fd,out.c_str(),out.size());
}

for(lptr=kill_list.begin();lptr!=kill_list.end();lptr++) {
if(debug_mode) {
write(fd,(*lptr)->first.c_str(),(*lptr)->first.size());
write(fd," ",1);
}
delete_node(*lptr);
}
if(debug_mode)
write(fd,"\n",1);
}

void thread_cache::store(string const &key,set<string> const &triggers_in,time_t timeout_in,archive const &a)
{
rwlock_wrlock lock(access_lock);
if(debug_mode) print_all();
pointer main;
if(debug_mode) {
string res;
res=str(boost::format("Storing key [%1%], triggers:") % key);
for(set<string>::iterator ps=triggers_in.begin(),pe=triggers_in.end();ps!=pe;ps++) {
res+=*ps;
res+=" ";
}
res+="\n";
write(fd,res.c_str(),res.size());
}
main=primary.find(key);
if(main==primary.end() && primary.size()>=limit && limit>0) {
if(debug_mode) {
char const *msg="Not found, size limit\n";
write(fd,msg,strlen(msg));
}
time_t now;
time(&now);
if(timeout.begin()->first<now) {
main=timeout.begin()->second;
if(debug_mode) {
string res;
res=str(boost::format("Deleting timeout node [%1%] with "
"delta of %2% seconds\n") % main->first
% (now - main->second.timeout->first));
write(fd,res.c_str(),res.size());
}
}
else {
main=lru.back();
if(debug_mode) {
string res;
res=str(boost::format("Deleting LRU [%1%]\n") % main->first);
write(fd,res.c_str(),res.size());
}
}
}
if(main!=primary.end())
delete_node(main);
pair<pointer,bool> res=primary.insert(pair<string,container>(key,container()));
main=res.first;
container &cont=main->second;
cont.data=a.get();
lru.push_front(main);
cont.lru=lru.begin();
cont.timeout=timeout.insert(pair<time_t,pointer>(timeout_in,main));
if(triggers_in.find(key)==triggers_in.end()){
cont.triggers.push_back(triggers.insert(pair<string,pointer>(key,main)));
}
set<string>::const_iterator si;
for(si=triggers_in.begin();si!=triggers_in.end();si++) {
cont.triggers.push_back(triggers.insert(pair<string,pointer>(*si,main)));
}
}

void thread_cache::delete_node(pointer p)
{
lru.erase(p->second.lru);
timeout.erase(p->second.timeout);
list<triggers_ptr>::iterator i;
for(i=p->second.triggers.begin();i!=p->second.triggers.end();i++) {
triggers.erase(*i);
}
primary.erase(p);
}

void thread_cache::print_all()
{
string res;
res+="Printing stored keys\n";
int N_triggers=0;
int N_keys=0;
time_t now;
time(&now);
for(pointer p=primary.begin();p!=primary.end();p++) {
N_keys++;
res+=str(boost::format("%1%: timeount in %2% sec, triggers:") % p->first
% (p->second.timeout->first - now));
for(list<triggers_ptr>::iterator p1=p->second.triggers.begin(),
p2=p->second.triggers.end();
p2!=p1;p1++)
{
N_triggers++;
res+=(*p1)->first;
res+=" ";
}
res+="\n";
}
res+="LRU order:";
for(list<pointer>::iterator pl=lru.begin();pl!=lru.end();pl++) {
res+=(*pl)->first;
res+=" ";
}
res+="\n";
if(N_keys!=timeout.size() || N_keys!=lru.size() || N_triggers!=triggers.size()){
res+=str(boost::format("Internal error #prim=%1%, #lru=%2%, "
"#prim.triggers=%3% #triggers=%4%\n")
% N_keys % lru.size() % N_triggers % triggers.size());
}
else {
res+=str(boost::format("#Keys=%1% #Triggers=%2%\n") % N_keys % N_triggers);
}
write(fd,res.c_str(),res.size());
}

};

+ 56
- 0
thread_cache.h View File

@@ -0,0 +1,56 @@
#ifndef THREAD_CHACHE_H
#define THREAD_CHACHE_H
#include "base_cache.h"
#include "pthread.h"
#include <map>
#include <list>
namespace cppcms {

using namespace std;

class thread_cache : public base_cache {
pthread_mutex_t lru_mutex;
pthread_rwlock_t access_lock;
struct container {
string data;
typedef map<string,container>::iterator pointer;
list<pointer>::iterator lru;
list<multimap<string,pointer>::iterator> triggers;
multimap<time_t,pointer>::iterator timeout;
};
typedef container::pointer pointer;
map<string,container> primary;
multimap<string,pointer> triggers;
typedef multimap<string,pointer>::iterator triggers_ptr;
multimap<time_t,pointer> timeout;
typedef multimap<time_t,pointer>::iterator timeout_ptr;
list<pointer> lru;
typedef list<pointer>::iterator lru_ptr;
unsigned limit;

string *get(string const &key,set<string> *triggers);
void delete_node(pointer p);
void print_all();
bool debug_mode;
int fd;

public:
void set_debug_mode(int fd) { debug_mode=true; this->fd=fd; };
thread_cache(unsigned pages=0) : limit(pages) {
pthread_mutex_init(&lru_mutex,NULL);
pthread_rwlock_init(&access_lock,NULL);
debug_mode=false;
};
void set_size(unsigned l) { limit=l; };
virtual bool fetch_page(string const &key,string &output,bool gzip);
virtual bool fetch(string const &key,archive &a,set<string> &tags);
virtual void rise(string const &trigger);
virtual void clear();
virtual void stats(unsigned &keys,unsigned &triggers);
virtual void store(string const &key,set<string> const &triggers,time_t timeout,archive const &a);
virtual ~thread_cache();
};

}

#endif

+ 19
- 17
thread_pool.cpp View File

@@ -3,11 +3,13 @@
#include <poll.h>
#include <signal.h>

using namespace cppcms;
using namespace cppcms::details;

FastCGI_Application *FastCGI_Application::handlers_owner=NULL;
fast_cgi_application *fast_cgi_application::handlers_owner=NULL;


FastCGI_Application::FastCGI_Application(const char *socket,int backlog)
fast_cgi_application::fast_cgi_application(const char *socket,int backlog)
{
FCGX_Init();
@@ -19,14 +21,14 @@ FastCGI_Application::FastCGI_Application(const char *socket,int backlog)
this->socket=socket;
main_fd=FCGX_OpenSocket(socket,backlog);
if(main_fd<0) {
throw HTTP_Error(string("Failed to open socket ")
throw cppcms_error(string("Failed to open socket ")
+socket);
}
}
}


FastCGI_Application::event_t FastCGI_Application::wait()
fast_cgi_application::event_t fast_cgi_application::wait()
{
for(;;) {
struct pollfd fds[2];
@@ -54,22 +56,22 @@ FastCGI_Application::event_t FastCGI_Application::wait()
}
}

void FastCGI_Application::shutdown()
void fast_cgi_application::shutdown()
{
// Rise exit signal on
// the selfpipe
write(signal_pipe[1],"0",1);
}

void FastCGI_Application::handler(int id)
void fast_cgi_application::handler(int id)
{
FastCGI_Application *ptr=FastCGI_Application::get_instance();
fast_cgi_application *ptr=fast_cgi_application::get_instance();
if(ptr) {
ptr->shutdown();
}
}

void FastCGI_Application::set_signal_handlers()
void fast_cgi_application::set_signal_handlers()
{
handlers_owner=this;
/* Signals defined by standard */
@@ -78,14 +80,14 @@ void FastCGI_Application::set_signal_handlers()
/* Additional signal */
signal(SIGINT,handler);
}
void FastCGI_Single_Threaded_App::setup(Worker_Thread *worker)
void fast_cgi_single_threaded_app::setup(worker_thread *worker)
{
this->worker=worker;
worker->init();
FCGX_InitRequest(&request, main_fd, 0);
}

bool FastCGI_Single_Threaded_App::run()
bool fast_cgi_single_threaded_app::run()
{
// Blocking loop
event_t event=wait();
@@ -110,7 +112,7 @@ bool FastCGI_Single_Threaded_App::run()
};


void FastCGI_Application::execute()
void fast_cgi_application::execute()
{
set_signal_handlers();
@@ -119,12 +121,12 @@ void FastCGI_Application::execute()
}
}

void *FastCGI_Mutiple_Threaded_App::thread_func(void *p)
void *fast_cgi_multiple_threaded_app::thread_func(void *p)
{
info_t *params=(info_t *)p;

int id=params->first;
FastCGI_Mutiple_Threaded_App *me=params->second;
fast_cgi_multiple_threaded_app *me=params->second;

FCGX_Request *req;
@@ -136,7 +138,7 @@ void *FastCGI_Mutiple_Threaded_App::thread_func(void *p)
return NULL;
}

void FastCGI_Mutiple_Threaded_App::start_threads()
void fast_cgi_multiple_threaded_app::start_threads()
{
int i;

@@ -152,7 +154,7 @@ void FastCGI_Mutiple_Threaded_App::start_threads()
}
}

void FastCGI_Mutiple_Threaded_App::wait_threads()
void fast_cgi_multiple_threaded_app::wait_threads()
{
int i;
for(i=0;i<size;i++) {
@@ -160,7 +162,7 @@ void FastCGI_Mutiple_Threaded_App::wait_threads()
}
}

void FastCGI_Mutiple_Threaded_App::setup(int num,int buffer,Worker_Thread **workers)
void fast_cgi_multiple_threaded_app::setup(int num,int buffer,worker_thread **workers)
{
int i;
@@ -198,7 +200,7 @@ void FastCGI_Mutiple_Threaded_App::setup(int num,int buffer,Worker_Thread **work
start_threads();
}

bool FastCGI_Mutiple_Threaded_App::run()
bool fast_cgi_multiple_threaded_app::run()
{
int res;
if(wait()==ACCEPT) {


+ 55
- 46
thread_pool.h View File

@@ -1,16 +1,20 @@
#ifndef _THREAD_PULL_H
#define _THREAD_PULL_H
#ifndef _THREAD_POOL_H
#define _THREAD_POOL_H

#include <pthread.h>
#include <string>
#include <memory>

#include "worker_thread.h"
#include "global_config.h"

namespace cppcms {

class FastCGI_Application {
namespace details {

FastCGI_Application static *handlers_owner;
class fast_cgi_application {

fast_cgi_application static *handlers_owner;
protected:
// General control
@@ -24,40 +28,40 @@ protected:
typedef enum { EXIT , ACCEPT } event_t;
event_t wait();
void set_signal_handlers();
static FastCGI_Application *get_instance() { return handlers_owner; };
static fast_cgi_application *get_instance() { return handlers_owner; };
public:
FastCGI_Application(char const *socket,int backlog);
virtual ~FastCGI_Application() {};
fast_cgi_application(char const *socket,int backlog);
virtual ~fast_cgi_application() {};

void shutdown();
virtual bool run() { return false; };
void execute();
};

class FastCGI_Single_Threaded_App : public FastCGI_Application {
class fast_cgi_single_threaded_app : public fast_cgi_application {
/* Single thread model -- one process runs */
FCGX_Request request;
Worker_Thread *worker;
void setup(Worker_Thread *worker);
worker_thread *worker;
void setup(worker_thread *worker);
public:
virtual bool run();
FastCGI_Single_Threaded_App(Worker_Thread *worker,char const *socket=NULL)
: FastCGI_Application(socket,1) { setup(worker); };
virtual ~FastCGI_Single_Threaded_App(){};
fast_cgi_single_threaded_app(worker_thread *worker,char const *socket=NULL)
: fast_cgi_application(socket,1) { setup(worker); };
virtual ~fast_cgi_single_threaded_app(){};
};


template <class WT>
class FastCGI_ST : public FastCGI_Single_Threaded_App {
Worker_Thread *worker_thread;
class fast_cgi_st : public fast_cgi_single_threaded_app {
worker_thread *wt;
public:
FastCGI_ST(char const *socket=NULL) :
FastCGI_Single_Threaded_App((worker_thread=new WT) ,socket) {};
virtual ~FastCGI_ST(){ delete worker_thread; };
fast_cgi_st(char const *socket=NULL) :
fast_cgi_single_threaded_app((wt=new WT) ,socket) {};
virtual ~fast_cgi_st(){ delete wt; };
};

template <class T>
class Safe_Set {
class sefe_set {
pthread_mutex_t access_mutex;
pthread_cond_t new_data_availible;
pthread_cond_t new_space_availible;
@@ -76,8 +80,8 @@ public:
this->size=0;

};
Safe_Set() {};
virtual ~Safe_Set() {};
sefe_set() {};
virtual ~sefe_set() {};
virtual void push(T val) {
pthread_mutex_lock(&access_mutex);
while(size>=max) {
@@ -100,7 +104,7 @@ public:
};

template <class T>
class Safe_Queue : public Safe_Set<T>{
class sefe_queue : public sefe_set<T>{
T *queue;
int head;
int tail;
@@ -121,25 +125,25 @@ public:
void init(int size) {
if(queue) return;
queue=new T [size];
Safe_Set<T>::init(size);
sefe_set<T>::init(size);
}
Safe_Queue() { queue = NULL; head=tail=0; };
virtual ~Safe_Queue() { delete [] queue; };
sefe_queue() { queue = NULL; head=tail=0; };
virtual ~sefe_queue() { delete [] queue; };
};

class FastCGI_Mutiple_Threaded_App : public FastCGI_Application {
class fast_cgi_multiple_threaded_app : public fast_cgi_application {
int size;
long long int *stats;
Worker_Thread **workers;
worker_thread **workers;
FCGX_Request *requests;
Safe_Queue<FCGX_Request*> requests_queue;
Safe_Queue<FCGX_Request*> jobs_queue;
sefe_queue<FCGX_Request*> requests_queue;
sefe_queue<FCGX_Request*> jobs_queue;
void setup(int size,int buffer_len,Worker_Thread **workers);
void setup(int size,int buffer_len,worker_thread **workers);
pthread_t *pids;

typedef pair<int,FastCGI_Mutiple_Threaded_App*> info_t;
typedef pair<int,fast_cgi_multiple_threaded_app*> info_t;
info_t *threads_info;

@@ -147,16 +151,16 @@ class FastCGI_Mutiple_Threaded_App : public FastCGI_Application {
void start_threads();
void wait_threads();
public:
FastCGI_Mutiple_Threaded_App( int num,
fast_cgi_multiple_threaded_app( int num,
int buffer_len,
Worker_Thread **workers,
worker_thread **workers,
char const *socket=NULL) :
FastCGI_Application(socket,num+1+buffer_len)
fast_cgi_application(socket,num+1+buffer_len)
{
setup(num,num+1+buffer_len,workers);
};
virtual bool run();
virtual ~FastCGI_Mutiple_Threaded_App() {
virtual ~fast_cgi_multiple_threaded_app() {
delete [] requests;
delete [] pids;
delete [] threads_info;
@@ -166,45 +170,50 @@ public:
};

template<class WT>
class FastCGI_MT : public FastCGI_Mutiple_Threaded_App {
class fast_cgi_mt : public fast_cgi_multiple_threaded_app {
WT *threads;
Worker_Thread **ptrs;
worker_thread **ptrs;
Worker_Thread **setptrs(int num)
worker_thread **setptrs(int num)
{
threads = new WT [num];
ptrs= new Worker_Thread* [num] ;
ptrs= new worker_thread* [num] ;
for(int i=0;i<num;i++) {
ptrs[i]=threads+i;
}
return ptrs;
};
public:
FastCGI_MT(int num,int buffer, char const *socket) :
FastCGI_Mutiple_Threaded_App(num,buffer,setptrs(num),socket) {};
virtual ~FastCGI_MT() {
fast_cgi_mt(int num,int buffer, char const *socket) :
fast_cgi_multiple_threaded_app(num,buffer,setptrs(num),socket) {};
virtual ~fast_cgi_mt() {
delete [] ptrs;
delete [] threads;
};
};

} // END OF DETAILS

template<class T>
void Run_Application(int argc,char *argv[])
void run_application(int argc,char *argv[])
{
using namespace details;
int n,max;
global_config.load(argc,argv);
char const *socket=global_config.sval("server.socket","").c_str();
if((n=global_config.lval("server.threads",0))==0) {
auto_ptr<FastCGI_ST<T> > app(new FastCGI_ST<T>(socket));
auto_ptr<fast_cgi_st<T> > app(new fast_cgi_st<T>(socket));
app->execute();
}
else {
max=global_config.lval("server.buffer",1);
auto_ptr<FastCGI_MT<T> > app(new FastCGI_MT<T>(n,max,socket));
auto_ptr<fast_cgi_mt<T> > app(new fast_cgi_mt<T>(n,max,socket));
app->execute();
}
};

#endif /* _THREAD_PULL_H */
}
#endif /* _THREAD_POOL_H */

+ 17
- 17
url.cpp View File

@@ -4,8 +4,8 @@

using namespace std;
using namespace boost;
URL_Parser::~URL_Parser()
using namespace cppcms;
url_parser::~url_parser()
{
unsigned i;
for(i=0;i<patterns.size();i++) {
@@ -13,12 +13,12 @@ URL_Parser::~URL_Parser()
}
}

void URL_Parser::add(char const *exp,int id)
void url_parser::add(char const *exp,int id)
{
URL_Def url_def;
url_def url_def;
url_def.pattern=regex(exp);
url_def.type=URL_Def::ID;
url_def.type=url_def::ID;
url_def.id=id;
url_def.callback=NULL;
url_def.url=NULL;
@@ -26,12 +26,12 @@ void URL_Parser::add(char const *exp,int id)
patterns.push_back(url_def);
}

void URL_Parser::add(char const *exp,URL_Parser &url)
void url_parser::add(char const *exp,url_parser &url)
{
URL_Def url_def;
url_def url_def;
url_def.pattern=regex(exp);
url_def.type=URL_Def::URL;
url_def.type=url_def::URL;
url_def.id=0;
url_def.callback=NULL;
url_def.url=&url;
@@ -39,15 +39,15 @@ void URL_Parser::add(char const *exp,URL_Parser &url)
patterns.push_back(url_def);
}

void URL_Parser::add(char const *exp,callback_t callback)
void url_parser::add(char const *exp,callback_t callback)
{
URL_Def url_def;
url_def url_def;
callback_signal_t *signal = new callback_signal_t;
signal->connect(callback);
url_def.pattern=regex(exp);
url_def.type=URL_Def::CALLBACK;
url_def.type=url_def::CALLBACK;
url_def.id=0;
url_def.callback=signal;
url_def.url=NULL;
@@ -56,7 +56,7 @@ void URL_Parser::add(char const *exp,callback_t callback)

}

int URL_Parser::parse(string &query)
int url_parser::parse(string &query)
{
unsigned i;
for(i=0;i<patterns.size();i++) {
@@ -64,12 +64,12 @@ int URL_Parser::parse(string &query)
string tmp;
if(boost::regex_match(query.c_str(),result,r)){
switch(patterns[i].type) {
case URL_Def::ID:
case url_def::ID:
return patterns[i].id;
case URL_Def::URL:
case url_def::URL:
tmp=result[1];
return patterns[i].url->parse(tmp);
case URL_Def::CALLBACK:
case url_def::CALLBACK:
(*patterns[i].callback)(result[1],result[2],
result[3],result[4],
result[5],result[6],
@@ -82,7 +82,7 @@ int URL_Parser::parse(string &query)
return -1;
}

int URL_Parser::parse()
int url_parser::parse()
{
string query;
if(worker){
@@ -94,7 +94,7 @@ int URL_Parser::parse()
return parse(query);
}

string URL_Parser::operator[](int i)
string url_parser::operator[](int i)
{
return result[i];
}

+ 17
- 15
url.h View File

@@ -10,8 +10,6 @@

// Some defines:

#define BIND boost::bind

#define $0 _9
#define $1 _1
#define $2 _2
@@ -22,45 +20,49 @@
#define $7 _7
#define $8 _8

namespace cppcms {

class worker_thread;

class Worker_Thread;
using std::string;


typedef boost::signal<void(string,string,string,string,
string,string,string,string,
string)> callback_signal_t;
typedef callback_signal_t::slot_type callback_t;

class URL_Parser;
class url_parser;

struct URL_Def {
struct url_def {
boost::regex pattern;
enum { ID, CALLBACK , URL } type;
int id;
URL_Parser *url;
url_parser *url;
callback_signal_t *callback;
URL_Def() { callback=NULL; };
url_def() { callback=NULL; };
};

class URL_Parser {
std::vector<URL_Def>patterns;
Worker_Thread *worker;
class url_parser {
std::vector<url_def>patterns;
worker_thread *worker;
boost::cmatch result;
void set_regex(char const *r);
public:
static const int not_found=-1;
static const int ok=0;
URL_Parser() {};
URL_Parser(Worker_Thread * w) { worker=w;};
~URL_Parser();
url_parser() {};
url_parser(worker_thread * w) { worker=w;};
~url_parser();
void add(char const *exp,int id);
void add(char const *exp,URL_Parser &url);
void add(char const *exp,url_parser &url);
void add(char const *exp,callback_t callback);
void init(Worker_Thread *w) { worker=w; };
void init(worker_thread *w) { worker=w; };
int parse();
int parse(string &s);
std::string operator[](int);
};

} // Namespace cppcms

#endif /* _URL_H */

+ 58
- 68
worker_thread.cpp View File

@@ -1,106 +1,96 @@
#include "worker_thread.h"
#include "global_config.h"
#include "thread_cache.h"

#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/gzip.hpp>

using namespace cgicc;
namespace cppcms {

Worker_Thread::Worker_Thread()
void worker_thread::main()
{
}

void Worker_Thread::main()
{
out.puts("<h1>Hello World</h2>\n");
out="<h1>Hello World</h2>\n";
}


void Worker_Thread::run(FCGX_Request *fcgi)
void worker_thread::run(FCGX_Request *fcgi)
{
#ifdef FCGX_API_ACCEPT_ONLY_EXISTS
if(FCGX_Continue_r(fcgi)<0){
return;
}
#endif
io = auto_ptr<FCgiIO>(new FCgiIO(*fcgi));
cgi = auto_ptr<Cgicc>( new Cgicc(&*io) );
cgi = auto_ptr<Cgicc>(new Cgicc(&*io));
env=&(cgi->getEnvironment());

out.clear();
out.reserve(global_config.lval("performance.textalloc",65500));
cache.reset();

set_header(new HTTPHTMLHeader);

gzip=gzip_done=false;
char *ptr;
if((ptr=FCGX_GetParam("HTTP_ACCEPT_ENCODING",fcgi->envp))!=NULL) {
if(strstr(ptr,"gzip")!=NULL) {
gzip=global_config.lval("gzip.enable",0);
}
}

try {
/**********/
main();
/**********/
if(response_header.get() == NULL) {
throw HTTP_Error("Looks like a bug");
throw cppcms_error("Looks like a bug");
}
}
catch( HTTP_Error &error_message) {
int err_code;
out.reset();
string msg;
if(error_message.is_404()) {
err_code=404;
msg="Not Found: ";
msg+=error_message.get();
}
else {
err_code=500;
msg=error_message.get();
}
set_header(new HTTPStatusHeader(err_code,msg));
out.puts(msg.c_str());
catch(cppcms_error const &e) {
string msg=e.what();
set_header(new HTTPStatusHeader(500,msg));
out="<html><body><p>"+msg+"</p><body></html>";
}

char *ptr;
bool gzip=false;

if((ptr=FCGX_GetParam("HTTP_ACCEPT_ENCODING",fcgi->envp))!=NULL) {
if(strstr(ptr,"gzip")!=NULL) {
gzip=true;
}
}

if(global_config.lval("gzip.enable",0)==0) {
gzip=false;
}
if(gzip) {
using namespace boost::iostreams;
*io<<"Content-Encoding: gzip\r\n";
*io<<*response_header;
gzip_params params;
long level,length;

if((level=global_config.lval("gzip.level",-1))!=-1){
params.level=level;
}

filtering_ostream zstream;

if((length=global_config.lval("gzip.buffer",-1))!=-1){
zstream.push(gzip_compressor(params,length));
}
else {
zstream.push(gzip_compressor(params));
}

zstream.push(*io);
zstream<<out.get();
if(gzip_done)
*io<<out;
else
deflate(out,*io);
}
else {
*io<<*response_header;
*io<<out.get();
*io<<out;
}
out.reset();

// Clean Up
out.clear();
response_header.reset();
cgi.reset();
io.reset();
FCGX_Finish_r(fcgi);
}

void worker_thread::init_internal()
{
string backend=global_config.sval("cache.backend","none");
caching_module=NULL;
if(backend=="threaded") {
static thread_cache tc;
tc.set_size(global_config.lval("cache.limit",100));
caching_module=&tc;
if(global_config.lval("cache.debug",0)==1) {
tc.set_debug_mode(2);
}
}
}

worker_thread::~worker_thread()
{
}



}




+ 23
- 12
worker_thread.h View File

@@ -5,8 +5,6 @@
#include <sstream>
#include <string>

#include "textstream.h"

#include "cgicc/Cgicc.h"
#include "cgicc/HTTPHTMLHeader.h"
#include "cgicc/HTTPStatusHeader.h"
@@ -14,8 +12,13 @@
#include <memory>

#include "FCgiIO.h"
#include "http_error.h"
#include "cppcms_error.h"
#include "url.h"
#include "cache_interface.h"
#include "base_cache.h"


namespace cppcms {

using namespace std;
using cgicc::CgiEnvironment;
@@ -24,29 +27,37 @@ using cgicc::Cgicc;
using cgicc::HTTPHeader;




class Worker_Thread {
friend class URL_Parser;
class worker_thread {
friend class url_parser;
friend class cache_iface;
protected:
auto_ptr<FCgiIO>io;
auto_ptr<Cgicc> cgi;
CgiEnvironment const *env;

Text_Stream out;
auto_ptr<HTTPHeader> response_header;
void set_header(HTTPHeader*h){response_header=auto_ptr<HTTPHeader>(h);};
virtual void main();
// Output and Cahce

cache_iface cache;
base_cache *caching_module;
bool gzip;
bool gzip_done;
string out;
void init_internal();
public:
int id;
pthread_t pid;


void run(FCGX_Request *req);

Worker_Thread();
virtual ~Worker_Thread(){ };
virtual void init() { };
worker_thread() : cache(this) { init_internal(); } ;
virtual ~worker_thread();
virtual void init() {};
};

}

#endif

Loading…
Cancel
Save