@@ -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 |
@@ -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 |
@@ -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; | |||
} | |||
} | |||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 */ |
@@ -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 |
@@ -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); | |||
} | |||
} |
@@ -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 */ |
@@ -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 */ |
@@ -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; | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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 */ |
@@ -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]); | |||
} |
@@ -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 */ |
@@ -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+="<"; 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+="<"; break; | |||
case '>': content+=">"; break; | |||
case '&': content+="&"; 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+="—"; | |||
} | |||
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; | |||
} | |||
} | |||
} | |||
} |
@@ -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); | |||
}; | |||
}; | |||
@@ -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; | |||
} | |||
} |
@@ -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 */ |
@@ -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()); | |||
} | |||
}; |
@@ -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 |
@@ -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) { | |||
@@ -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 */ |
@@ -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]; | |||
} |
@@ -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 */ |
@@ -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() | |||
{ | |||
} | |||
} | |||
@@ -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 |