@@ -43,8 +43,9 @@ libcppcms_la_SOURCES = \ | |||
base64.cpp \ | |||
locale_info.cpp \ | |||
base_view.cpp \ | |||
internal_file_server.cpp \ | |||
scgi_api.cpp \ | |||
fastcgi_api.cpp | |||
http_api.cpp | |||
@@ -1,6 +1,9 @@ | |||
#define CPPCMS_SOURCE | |||
#include "applications_pool.h" | |||
#include "application.h" | |||
#include "service.h" | |||
#include "global_config.h" | |||
#include "cppcms_error.h" | |||
#include <set> | |||
#include <vector> | |||
#include <boost/regex.hpp> | |||
@@ -10,15 +13,30 @@ namespace cppcms { | |||
namespace { | |||
struct app_data : public util::noncopyable { | |||
app_data(std::string e,std::auto_ptr<applications_pool::factory> f,int m) : | |||
expr(e), | |||
match(m), | |||
app_data(std::string script,std::auto_ptr<applications_pool::factory> f) : | |||
script_name(script), | |||
match(0), | |||
use_regex(0), | |||
factory(f), | |||
size(0) | |||
{ | |||
check(); | |||
} | |||
app_data(std::string script,std::string pat,int select,std::auto_ptr<applications_pool::factory> f) : | |||
script_name(script), | |||
expr(pat), | |||
match(select), | |||
use_regex(1), | |||
factory(f), | |||
size(0) | |||
{ | |||
check(); | |||
} | |||
std::string script_name; | |||
boost::regex expr; | |||
int match; | |||
bool use_regex; | |||
std::auto_ptr<applications_pool::factory> factory; | |||
int size; | |||
@@ -31,6 +49,17 @@ namespace cppcms { | |||
delete *p; | |||
} | |||
} | |||
void check() const | |||
{ | |||
if( !script_name.empty() | |||
&& script_name[0]!='/' | |||
&& script_name[0]!='.' | |||
&& script_name!="*") | |||
{ | |||
throw cppcms_error("Scipt name should be either '*', start with '.' or '/' or be empty"); | |||
} | |||
} | |||
}; | |||
} | |||
@@ -50,28 +79,63 @@ applications_pool::applications_pool(service &srv,int limit) : | |||
applications_pool::~applications_pool() | |||
{ | |||
} | |||
void applications_pool::mount(std::string pattern,std::auto_ptr<factory> aps,int select) | |||
void applications_pool::mount(std::auto_ptr<factory> aps) | |||
{ | |||
std::string script_name=srv_->settings().str("service.default_script_name","*"); | |||
d->apps.push_back(boost::shared_ptr<app_data>(new app_data(script_name,aps))); | |||
} | |||
void applications_pool::mount(std::auto_ptr<factory> aps,std::string path_info,int select) | |||
{ | |||
d->apps.push_back(boost::shared_ptr<app_data>(new app_data(pattern,aps,select))); | |||
std::string script_name=srv_->settings().str("service.default_script_name","*"); | |||
d->apps.push_back(boost::shared_ptr<app_data>(new app_data(script_name,path_info,select,aps))); | |||
} | |||
void applications_pool::mount(std::auto_ptr<factory> aps,std::string script_name) | |||
{ | |||
d->apps.push_back(boost::shared_ptr<app_data>(new app_data(script_name,aps))); | |||
} | |||
void applications_pool::mount(std::auto_ptr<factory> aps,std::string script_name,std::string path_info,int select) | |||
{ | |||
d->apps.push_back(boost::shared_ptr<app_data>(new app_data(script_name,path_info,select,aps))); | |||
} | |||
std::auto_ptr<application> applications_pool::get(std::string path,std::string &matched) | |||
std::auto_ptr<application> applications_pool::get(std::string script_name,std::string path_info,std::string &matched) | |||
{ | |||
for(unsigned i=0;i<d->apps.size();i++) { | |||
std::string const sn=d->apps[i]->script_name; | |||
if(sn!="*") { | |||
if(sn[0]=='/') | |||
if(script_name!=sn) | |||
continue; | |||
else { // if(sn[0]=='.') | |||
if( script_name.size() <= sn.size() | |||
|| script_name.substr(script_name.size() - sn.size())!=sn) | |||
{ | |||
continue; | |||
} | |||
} | |||
} | |||
boost::cmatch match; | |||
if(boost::regex_match(path.c_str(),match,d->apps[i]->expr)) { | |||
if(!d->apps[i]->use_regex) { | |||
matched=path_info; | |||
} | |||
else if(boost::regex_match(path_info.c_str(),match,d->apps[i]->expr)) { | |||
matched=match[d->apps[i]->match]; | |||
if(d->apps[i]->pool.empty()) { | |||
std::auto_ptr<application> app=(*d->apps[i]->factory)(*srv_); | |||
app->pool_id(i); | |||
return app; | |||
} | |||
d->apps[i]->size--; | |||
std::auto_ptr<application> app(*(d->apps[i]->pool.begin())); | |||
d->apps[i]->pool.erase(app.get()); | |||
} | |||
else { | |||
continue; | |||
} | |||
if(d->apps[i]->pool.empty()) { | |||
std::auto_ptr<application> app=(*d->apps[i]->factory)(*srv_); | |||
app->pool_id(i); | |||
return app; | |||
} | |||
d->apps[i]->size--; | |||
std::auto_ptr<application> app(*(d->apps[i]->pool.begin())); | |||
d->apps[i]->pool.erase(app.get()); | |||
return app; | |||
} | |||
return std::auto_ptr<application>(); | |||
} | |||
@@ -21,8 +21,12 @@ namespace cppcms { | |||
virtual ~factory(){} | |||
}; | |||
void mount(std::string pattern,std::auto_ptr<factory> aps,int select=0); | |||
std::auto_ptr<application> get(std::string path,std::string &match); | |||
void mount(std::auto_ptr<factory> aps); | |||
void mount(std::auto_ptr<factory> aps,std::string path_info,int select); | |||
void mount(std::auto_ptr<factory> aps,std::string script_name); | |||
void mount(std::auto_ptr<factory> aps,std::string script_name,std::string path_info, int select); | |||
std::auto_ptr<application> get(std::string script_name,std::string path_info,std::string &match); | |||
void put(std::auto_ptr<application> app); | |||
applications_pool(service &srv,int pool_size_limit); | |||
@@ -133,13 +133,14 @@ void connection::make_error_response(int status,char const *msg) | |||
void connection::setup_application() | |||
{ | |||
std::string path = getenv("PATH_INFO"); | |||
std::string path_info = getenv("PATH_INFO"); | |||
std::string script_name = getenv("SCRIPT_NAME"); | |||
std::string matched; | |||
application_ = service().applications_pool().get(path,matched); | |||
application_ = service().applications_pool().get(script_name,path_info,matched); | |||
url_dispatcher::dispatch_type how; | |||
if(application_.get() == 0 || (how=application_->dispatcher().dispatchable(path))==url_dispatcher::none) { | |||
if(application_.get() == 0 || (how=application_->dispatcher().dispatchable(matched))==url_dispatcher::none) { | |||
context_->response().io_mode(http::response::asynchronous); | |||
make_error_response(http::response::not_found); | |||
on_response_complete(); | |||
@@ -1,17 +1,19 @@ | |||
# Server API settings | |||
#service.port = 8001 | |||
service.procs = 5 | |||
service.port = 8080 | |||
service.procs = 0 | |||
service.ip = "127.0.0.1" | |||
service.socket = "/tmp/scgi.socket" # Default is "" -- use default socket given | |||
#service.socket = "/tmp/scgi.socket" # Default is "" -- use default socket given | |||
service.worker_threads = 5 | |||
service.api = "scgi" # fastcgi -- preferred API | |||
service.api = "http" # fastcgi -- preferred API | |||
# scgi -- simplefied FCGI API -- yet another alternative | |||
# cgi -- Use only in case you application does huge amount of work | |||
# such the fork()+exec() time in neligable | |||
# Recomended for debug purposes only | |||
http.script_names = { "/hello" } | |||
# Server work mode | |||
server.mod="thread" # process -- process runs single instance of worker thread. Very simple | |||
@@ -235,6 +235,12 @@ fi | |||
CPPCMS_LIBS="-lboost_thread-gcc-mt $CPPCMS_LIBS" | |||
],[ echo "boost::thread not found" ; exit -1])]) | |||
AC_CHECK_LIB(boost_filesystem,main,[ | |||
CPPCMS_LIBS="-lboost_filesystem $CPPCMS_LIBS" | |||
],[AC_CHECK_LIB(boost_filesystem-gcc-mt,main,[ | |||
CPPCMS_LIBS="-lboost_filesystem-gcc-mt $CPPCMS_LIBS" | |||
],[ echo "boost::date_time not found" ; exit -1])]) | |||
AC_CHECK_LIB(boost_date_time,main,[ | |||
CPPCMS_LIBS="-lboost_date_time $CPPCMS_LIBS" | |||
],[AC_CHECK_LIB(boost_date_time-gcc-mt,main,[ | |||
@@ -105,7 +105,7 @@ int main(int argc,char **argv) | |||
{ | |||
try { | |||
cppcms::service service(argc,argv); | |||
service.applications_pool().mount(".*",cppcms::applications_factory<hello>()); | |||
service.applications_pool().mount(cppcms::applications_factory<hello>()); | |||
service.run(); | |||
std::cout<<"Done..."<<std::endl; | |||
} | |||
@@ -1,12 +1,19 @@ | |||
#ifndef CPPCMS_IMPL_SCGI_API_H | |||
#define CPPCMS_IMPL_SCGI_API_H | |||
#include "cgi_api.h" | |||
#define CPPCMS_SOURCE | |||
#include "asio_config.h" | |||
#include "cgi_api.h" | |||
#include "cgi_acceptor.h" | |||
#include "service.h" | |||
#include "service_impl.h" | |||
#include "cppcms_error_category.h" | |||
#include "global_config.h" | |||
#include "http_protocol.h" | |||
#include "config.h" | |||
#include <iostream> | |||
#include <algorithm> | |||
#include <boost/lexical_cast.hpp> | |||
namespace cppcms { | |||
namespace impl { | |||
@@ -15,15 +22,29 @@ namespace cgi { | |||
public: | |||
http(cppcms::service &srv) : | |||
connection(srv), | |||
socket_(srv.impl().get_io_service()) | |||
socket_(srv.impl().get_io_service()), | |||
input_body_ptr_(0), | |||
input_parser_(input_body_,input_body_ptr_), | |||
output_body_ptr_(0), | |||
output_parser_(output_body_,output_body_ptr_), | |||
headers_done_(false), | |||
first_header_observerd_(false), | |||
total_read_(0) | |||
{ | |||
env_["SERVER_SOFTWARE"]=PACKAGE_NAME "/" PACKAGE_VERSION; | |||
env_["SERVER_NAME"]=srv.settings().str("http.server_name","127.0.0.1"); | |||
env_["SERVER_PORT"]=boost::lexical_cast<std::string>(srv.settings().integer("service.port")); | |||
env_["GATEWAY_INTERFACE"]="CGI/1.0"; | |||
env_["SERVER_PROTOCOL"]="HTTP/1.0"; | |||
} | |||
virtual void async_read_headers(handler const &h) | |||
{ | |||
body_.reserve(8192); | |||
body_.resize(8192,0); | |||
body_ptr_=0; | |||
socket_.async_read_some(boost::asio::buffer(body_), | |||
input_body_.reserve(8192); | |||
input_body_.resize(8192,0); | |||
input_body_ptr_=0; | |||
socket_.async_read_some(boost::asio::buffer(input_body_), | |||
boost::bind( &http::some_headers_data_read, | |||
shared_from_this(), | |||
boost::asio::placeholders::error, | |||
@@ -34,6 +55,59 @@ namespace cgi { | |||
void some_headers_data_read(boost::system::error_code const &e,size_t n,handler const &h) | |||
{ | |||
if(e) { h(e); return; } | |||
total_read_+=n; | |||
if(total_read_ > 16384) { | |||
h(boost::system::error_code(errc::protocol_violation,cppcms_category)); | |||
return; | |||
} | |||
input_body_.resize(n); | |||
switch(input_parser_.step()) { | |||
case parser::more_data: | |||
// Assuming body_ptr == body.size() | |||
async_read_headers(h); | |||
break; | |||
case parser::got_header: | |||
if(!first_header_observerd_) { | |||
std::string::iterator rmethod,query=input_parser_.header_.end(); | |||
rmethod=std::find(input_parser_.header_.begin(),input_parser_.header_.end(),' '); | |||
if(rmethod!=input_parser_.header_.end()) | |||
query=std::find(rmethod+1,input_parser_.header_.end(),' '); | |||
if(query!=input_parser_.header_.end()) { | |||
request_method_.assign(input_parser_.header_.begin(),rmethod); | |||
request_uri_.assign(rmethod+1,query); | |||
first_header_observerd_=true; | |||
} | |||
else { | |||
h(boost::system::error_code(errc::protocol_violation,cppcms_category)); | |||
return; | |||
} | |||
} | |||
else { // Any other header | |||
std::string name; | |||
std::string value; | |||
if(!parse_single_header(input_parser_.header_,name,value)) { | |||
h(boost::system::error_code(errc::protocol_violation,cppcms_category)); | |||
return; | |||
} | |||
if(name=="CONTENT_LENGTH" || name=="CONTENT_TYPE") | |||
env_[name]=value; | |||
else | |||
env_["HTTP_"+name]=value; | |||
} | |||
break; | |||
case parser::end_of_headers: | |||
process_request(h); | |||
break; | |||
case parser::error_observerd: | |||
h(boost::system::error_code(errc::protocol_violation,cppcms_category)); | |||
return; | |||
break; | |||
} | |||
} | |||
// should be called only after headers are read | |||
@@ -47,28 +121,39 @@ namespace cgi { | |||
} | |||
virtual void async_read_some(void *p,size_t s,io_handler const &h) | |||
{ | |||
if(!body_.empty()) { | |||
if(body_.size() - body_ptr_ > s) { | |||
s=body_.size() - body_ptr_; | |||
if(!input_body_.empty()) { | |||
if(input_body_.size() - input_body_ptr_ > s) { | |||
s=input_body_.size() - input_body_ptr_; | |||
} | |||
memcpy(p,&body_[body_ptr_],s); | |||
body_ptr_+=s; | |||
if(body_ptr_==body_.size()) { | |||
body_.clean(); | |||
body_ptr_=0; | |||
memcpy(p,&input_body_[input_body_ptr_],s); | |||
input_body_ptr_+=s; | |||
if(input_body_ptr_==input_body_.size()) { | |||
input_body_.clear(); | |||
input_body_ptr_=0; | |||
} | |||
socket_.get_io_service().post(boost::bind(h,boost::system::error_code(),s)); | |||
return; | |||
} | |||
socket_.async_read_some(boost::asio::buffer(p,s),h); | |||
} | |||
virtual void async_write_eof(handler const &h) | |||
{ | |||
boost::system::error_code e; | |||
socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_send,e); | |||
socket_.get_io_service().post(boost::bind(h,boost::system::error_code())); | |||
} | |||
virtual void async_write_some(void const *p,size_t s,io_handler const &h) | |||
{ | |||
socket_.async_write_some(boost::asio::buffer(p,s),h); | |||
if(headers_done_) | |||
socket_.async_write_some(boost::asio::buffer(p,s),h); | |||
else | |||
process_output_headers(p,s,h); | |||
} | |||
virtual size_t write_some(void const *buffer,size_t n) | |||
{ | |||
return socket_.write_some(boost::asio::buffer(buffer,n)); | |||
if(headers_done_) | |||
return socket_.write_some(boost::asio::buffer(buffer,n)); | |||
return process_output_headers(buffer,n); | |||
} | |||
virtual boost::asio::io_service &get_io_service() | |||
{ | |||
@@ -82,24 +167,166 @@ namespace cgi { | |||
virtual void close() | |||
{ | |||
boost::system::error_code e; | |||
socket_.shutdown(boost::asio::basic_stream_socket<Proto>::shutdown_both,e); | |||
socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_receive,e); | |||
socket_.close(e); | |||
} | |||
private: | |||
size_t process_output_headers(void const *p,size_t s,io_handler const &h=io_handler()) | |||
{ | |||
char const *ptr=reinterpret_cast<char const *>(p); | |||
output_body_.insert(output_body_.end(),ptr,ptr+s); | |||
switch(output_parser_.step()) { | |||
case parser::more_data: | |||
if(!h.empty()) h(boost::system::error_code(),s); | |||
return s; | |||
break; | |||
case parser::got_header: | |||
{ | |||
std::string name,value; | |||
if(!parse_single_header(output_parser_.header_,name,value)) { | |||
h(boost::system::error_code(errc::protocol_violation,cppcms_category),s); | |||
return s; | |||
} | |||
if(name=="STATUS") { | |||
response_line_ = "HTTP/1.0 "+value+"\r\n"; | |||
return write_response(h,s); | |||
} | |||
} | |||
break; | |||
case parser::end_of_headers: | |||
response_line_ = "HTTP/1.0 200 Ok\r\n"; | |||
return write_response(h,s); | |||
case parser::error_observerd: | |||
h(boost::system::error_code(errc::protocol_violation,cppcms_category),0); | |||
return 0; | |||
} | |||
return 0; | |||
} | |||
size_t write_response(io_handler const &h,size_t s) | |||
{ | |||
boost::array<boost::asio::const_buffer,2> packet = { | |||
boost::asio::buffer(response_line_), | |||
boost::asio::buffer(output_body_) | |||
}; | |||
headers_done_=true; | |||
if(h.empty()) { | |||
boost::system::error_code e; | |||
return boost::asio::write(socket_,packet,boost::asio::transfer_all(),e); | |||
if(e) return 0; | |||
return s; | |||
} | |||
boost::asio::async_write(socket_,packet, | |||
boost::bind(&http::do_write,shared_from_this(),_1,h,s)); | |||
return s; | |||
} | |||
void do_write(boost::system::error_code const &e,io_handler const &h,size_t s) | |||
{ | |||
if(e) { h(e,0); return; } | |||
h(boost::system::error_code(),s); | |||
} | |||
void process_request(handler const &h) | |||
{ | |||
if(request_method_!="GET" && request_method_!="POST" && request_method_!="HEAD") { | |||
response("HTTP/1.0 501 Not Implemented\r\n\r\n",h); | |||
return; | |||
} | |||
env_["REQUEST_METHOD"]=request_method_; | |||
if(request_uri_.empty() || request_uri_[0]!='/') { | |||
response("HTTP/1.0 400 Bad Request\r\n\r\n",h); | |||
return; | |||
} | |||
std::vector<std::string> const &script_names=service().settings().slist("http.script_names"); | |||
for(unsigned i=0;i<script_names.size();i++) { | |||
std::string const &name=script_names[i]; | |||
if( request_uri_.size() >= name.size() | |||
&& memcmp(request_uri_.c_str(),name.c_str(),name.size())==0) | |||
{ | |||
env_["SCRIPT_NAME"]==name; | |||
size_t pos=request_uri_.find('?'); | |||
env_["PATH_INFO"]=request_uri_.substr(name.size(),pos); | |||
env_["QUERY_STRING"]=request_uri_.substr(pos+1); | |||
h(boost::system::error_code()); | |||
return; | |||
} | |||
} | |||
env_["SCRIPT_NAME"]==""; | |||
size_t pos=request_uri_.find('?'); | |||
env_["PATH_INFO"]=request_uri_.substr(0,pos); | |||
env_["QUERY_STRING"]=request_uri_.substr(pos+1); | |||
h(boost::system::error_code()); | |||
} | |||
void response(char const *message,handler const &h) | |||
{ | |||
boost::asio::async_write(socket_,boost::asio::buffer(message,strlen(message)), | |||
boost::bind(h,boost::system::error_code())); | |||
} | |||
bool parse_single_header(std::string &name,std::string &value,std::string const &header) | |||
{ | |||
std::string::const_iterator p=header.begin(),e=header.end(),name_end; | |||
p=cppcms::http::protocol::skip_ws(p,e); | |||
name_end=cppcms::http::protocol::tocken(p,e); | |||
if(name_end==p) | |||
return false; | |||
name.assign(p,name_end); | |||
p=cppcms::http::protocol::skip_ws(p,e); | |||
if(p==e || *p!=':') | |||
return false; | |||
++p; | |||
p=cppcms::http::protocol::skip_ws(p,e); | |||
value.assign(p,e); | |||
for(unsigned i=0;i<name.size();i++) { | |||
if(name[i] == '-') | |||
name[i]=='_'; | |||
else if('a' <= name[i] && name[i] <='z') | |||
name[i]=name[i]-'a' + 'A'; | |||
} | |||
return true; | |||
} | |||
class parser { | |||
enum { | |||
idle, | |||
input_observed, | |||
last_lf_exptected, | |||
lf_exptected, | |||
space_or_other_exptected, | |||
quote_expected, | |||
pass_quote_exptected, | |||
closing_bracket_expected, | |||
pass_closing_bracket_expected | |||
} state_; | |||
unsigned bracket_counter_; | |||
std::vector<char> &body_; | |||
unsigned &body_ptr_; | |||
inline int getc() | |||
{ | |||
if(body_ptr_ < body_.size()) { | |||
return body_[body_ptr++]; | |||
return body_[body_ptr_++]; | |||
} | |||
else { | |||
body_.clean(); | |||
body_.clear(); | |||
body_ptr_=0; | |||
return -1; | |||
} | |||
@@ -107,95 +334,169 @@ namespace cgi { | |||
inline void ungetc(int c) | |||
{ | |||
if(body_ptr_ > 0) { | |||
body_ptr--; | |||
body_[body_ptr_]=c' | |||
body_ptr_--; | |||
body_[body_ptr_]=c; | |||
} | |||
else { | |||
body_.insert(body_.begin(),c); | |||
} | |||
} | |||
// Non copyable | |||
parser(parser const &); | |||
parser const &operator=(parser const &); | |||
public: | |||
std::string header_; | |||
parser(std::vector<char> &body,unsigned &body_ptr) : | |||
state_(idle), | |||
bracket_counter_(0), | |||
body_(body), | |||
body_ptr_(body_ptr) | |||
{ | |||
} | |||
enum { more_data, got_header, end_of_headers , error_observerd }; | |||
int step() | |||
{ | |||
int c=getc(); | |||
if(c<0) | |||
return more_data; | |||
switch(state_) { | |||
case idle: | |||
switch(c) { | |||
case '\r': | |||
state_=last_lf_exptected; | |||
for(;;) { | |||
std::cerr<<"Step: "<<std::flush; | |||
int c=getc(); | |||
if(c<0) | |||
return more_data; | |||
if(c>=32) | |||
std::cerr<<"["<<char(c)<<"] "<<state_<<std::endl; | |||
else | |||
std::cerr<<c<<" "<<state_<<std::endl; | |||
switch(state_) { | |||
case idle: | |||
switch(c) { | |||
case '\r': | |||
state_=last_lf_exptected; | |||
break; | |||
case '"': | |||
state_=quote_expected; | |||
break; | |||
case '(': | |||
state_=closing_bracket_expected; | |||
bracket_counter_++; | |||
break; | |||
default: | |||
state_=input_observed; | |||
} | |||
break; | |||
case '"': | |||
state_=quote_expected; | |||
case last_lf_exptected: | |||
if(c!='\n') return error_observerd; | |||
header_.clear(); | |||
return end_of_headers; | |||
case lf_exptected: | |||
if(c!='\n') return error_observerd; | |||
state_=space_or_other_exptected; | |||
break; | |||
default: | |||
state_=input_observed; | |||
} | |||
break; | |||
case last_lf_exptected: | |||
if(c!='\n') return error_observerd; | |||
header_.clear(); | |||
return end_of_headers; | |||
case lf_exptected: | |||
if(c!='\n') return error_observerd; | |||
state_=space_or_other_exptected; | |||
break; | |||
case space_or_other_exptected: | |||
if(c==' ' || c=='\t') { | |||
state_=input_observed; | |||
case space_or_other_exptected: | |||
if(c==' ' || c=='\t') { | |||
state_=input_observed; | |||
break; | |||
} | |||
ungetc(c); | |||
header_.resize(header_.size()-2); | |||
state_=idle; | |||
return got_header; | |||
case input_observed: | |||
switch(c) { | |||
case '\r': | |||
state_=lf_exptected; | |||
break; | |||
case '"': | |||
state_=quote_expected; | |||
break; | |||
case '(': | |||
state_=closing_bracket_expected; | |||
bracket_counter_++; | |||
break; | |||
default: | |||
state_=input_observed; | |||
} | |||
break; | |||
} | |||
ungetc(c); | |||
header_.resize(header_.size()-2); | |||
state_=idle; | |||
return got_header; | |||
case input_observed: | |||
switch(c) { | |||
case '\r': | |||
state_=lf_exptected; | |||
case quote_expected: | |||
switch(c) { | |||
case '"': | |||
state_=input_observed; | |||
break; | |||
case '\\': | |||
state_=pass_quote_exptected; | |||
break; | |||
} | |||
break; | |||
case '"': | |||
case pass_quote_exptected: | |||
if(c < 0 || c >=127) | |||
return error_observerd; | |||
state_=quote_expected; | |||
break; | |||
default: | |||
state_=input_observed; | |||
} | |||
break; | |||
case quote_expected: | |||
switch(c) { | |||
case '"': | |||
state_=input_observed; | |||
case closing_bracket_expected: | |||
switch(c) { | |||
case ')': | |||
bracket_counter_--; | |||
if(bracket_counter_==0) | |||
state_=input_observed; | |||
break; | |||
case '\\': | |||
state_=pass_closing_bracket_expected; | |||
break; | |||
} | |||
break; | |||
case '\\': | |||
state_=pass_quote_exptected; | |||
case pass_closing_bracket_expected: | |||
if(c < 0 || c >=127) | |||
return error_observerd; | |||
state_=closing_bracket_expected; | |||
break; | |||
} | |||
break; | |||
case pass_quote_exptected: | |||
if(c < 0 || c >=127) | |||
return error_observerd; | |||
state_=quote_expected; | |||
break; | |||
header_+=char(c); | |||
} | |||
header_+=char(c); | |||
} | |||
}; | |||
boost::shared_ptr<scgi<Proto> > shared_from_this() | |||
boost::shared_ptr<http> shared_from_this() | |||
{ | |||
return boost::static_pointer_cast<scgi<Proto> >(connection::shared_from_this()); | |||
return boost::static_pointer_cast<http>(connection::shared_from_this()); | |||
} | |||
friend class socket_acceptor<boost::asio::ip::tcp,http>; | |||
boost::asio::ip::tcp socket_; | |||
std::vector<char> body_; | |||
boost::asio::ip::tcp::socket socket_; | |||
std::vector<char> input_body_; | |||
unsigned input_body_ptr_; | |||
parser input_parser_; | |||
std::vector<char> output_body_; | |||
unsigned output_body_ptr_; | |||
parser output_parser_; | |||
std::map<std::string,std::string> env_; | |||
std::string script_name_; | |||
std::string response_line_; | |||
std::string request_method_; | |||
std::string request_uri_; | |||
bool headers_done_; | |||
bool first_header_observerd_; | |||
unsigned total_read_; | |||
}; | |||
typedef tcp_socket_acceptor<http> http_acceptor; | |||
std::auto_ptr<acceptor> http_api_factory(cppcms::service &srv,std::string ip,int port,int backlog) | |||
{ | |||
std::auto_ptr<acceptor> a(new http_acceptor(srv,ip,port,backlog)); | |||
return a; | |||
} | |||
} // cgi | |||
} // impl | |||
@@ -0,0 +1,19 @@ | |||
#ifndef CPPCMS_IMPL_HTTP_API_H | |||
#define CPPCMS_IMPL_HTTP_API_H | |||
#include "defs.h" | |||
#include <string> | |||
#include <memory> | |||
namespace cppcms { | |||
class service; | |||
namespace impl { | |||
namespace cgi { | |||
class acceptor; | |||
std::auto_ptr<acceptor> http_api_factory(cppcms::service &srv,std::string ip,int port,int backlog); | |||
} // cgi | |||
} // impl | |||
} // cppcms | |||
#endif | |||
@@ -0,0 +1,179 @@ | |||
#define CPPCMS_SOURCE | |||
#include "application.h" | |||
#include "url_dispatcher.h" | |||
#include "service.h" | |||
#include "http_response.h" | |||
#include "internal_file_server.h" | |||
#include "global_config.h" | |||
#include <sstream> | |||
#include <fstream> | |||
#include <boost/filesystem/operations.hpp> | |||
#include <boost/system/error_code.hpp> | |||
namespace fs = boost::filesystem; | |||
namespace cppcms { | |||
namespace impl { | |||
file_server::file_server(cppcms::service &srv) : application(srv) | |||
{ | |||
document_root_ = settings().str("http.document_root","./"); | |||
dispatcher().assign("^(.*)$",&file_server::serve_file,this,1); | |||
std::string mime_file=settings().str("http.mime_types",""); | |||
if(mime_file.empty()) { | |||
mime_[".pdf"] = "application/pdf"; | |||
mime_[".sig"] = "application/pgp-signature"; | |||
mime_[".spl"] = "application/futuresplash"; | |||
mime_[".ps"] = "application/postscript"; | |||
mime_[".torrent"]= "application/x-bittorrent"; | |||
mime_[".dvi"] = "application/x-dvi"; | |||
mime_[".gz"] = "application/x-gzip"; | |||
mime_[".pac"] = "application/x-ns-proxy-autoconfig"; | |||
mime_[".swf"] = "application/x-shockwave-flash"; | |||
mime_[".tgz"] = "application/x-tgz"; | |||
mime_[".tar"] = "application/x-tar"; | |||
mime_[".zip"] = "application/zip"; | |||
mime_[".mp3"] = "audio/mpeg"; | |||
mime_[".m3u"] = "audio/x-mpegurl"; | |||
mime_[".wma"] = "audio/x-ms-wma"; | |||
mime_[".wax"] = "audio/x-ms-wax"; | |||
mime_[".ogg"] = "application/ogg"; | |||
mime_[".wav"] = "audio/x-wav"; | |||
mime_[".gif"] = "image/gif"; | |||
mime_[".jpg"] = "image/jpeg"; | |||
mime_[".jpeg"] = "image/jpeg"; | |||
mime_[".png"] = "image/png"; | |||
mime_[".xbm"] = "image/x-xbitmap"; | |||
mime_[".xpm"] = "image/x-xpixmap"; | |||
mime_[".xwd"] = "image/x-xwindowdump"; | |||
mime_[".css"] = "text/css"; | |||
mime_[".html"] = "text/html"; | |||
mime_[".htm"] = "text/html"; | |||
mime_[".js"] = "text/javascript"; | |||
mime_[".asc"] = "text/plain"; | |||
mime_[".c"] = "text/plain"; | |||
mime_[".cpp"] = "text/plain"; | |||
mime_[".log"] = "text/plain"; | |||
mime_[".conf"] = "text/plain"; | |||
mime_[".text"] = "text/plain"; | |||
mime_[".txt"] = "text/plain"; | |||
mime_[".dtd"] = "text/xml"; | |||
mime_[".xml"] = "text/xml"; | |||
mime_[".mpeg"] = "video/mpeg"; | |||
mime_[".mpg"] = "video/mpeg"; | |||
mime_[".mov"] = "video/quicktime"; | |||
mime_[".qt"] = "video/quicktime"; | |||
mime_[".avi"] = "video/x-msvideo"; | |||
mime_[".asf"] = "video/x-ms-asf"; | |||
mime_[".asx"] = "video/x-ms-asf"; | |||
mime_[".wmv"] = "video/x-ms-wmv"; | |||
mime_[".bz2"] = "application/x-bzip"; | |||
mime_[".tbz"] = "application/x-bzip-compressed-tar"; | |||
} | |||
else { | |||
load_mime_types(mime_file); | |||
} | |||
} | |||
void file_server::load_mime_types(std::string file_name) | |||
{ | |||
std::ifstream inp(file_name.c_str()); | |||
if(!inp) { | |||
return; | |||
} | |||
std::string line; | |||
while(!inp.eof() && getline(inp,line)) { | |||
if(line.empty() || line[0]=='#') | |||
continue; | |||
std::istringstream ss(line); | |||
std::string mime; | |||
std::string ext; | |||
if(ss>>mime) { | |||
while(ss>>ext) { | |||
mime_["."+ext]=mime; | |||
} | |||
} | |||
} | |||
} | |||
file_server::~file_server() | |||
{ | |||
} | |||
void file_server::serve_file(std::string file_name) | |||
{ | |||
if(file_name.find("..") || file_name.empty() || file_name[0]!='/') { | |||
show404(); | |||
return; | |||
} | |||
fs::path full = fs::path(document_root_,fs::native) / fs::path(file_name); | |||
boost::system::error_code e; | |||
fs::file_status status = fs::status(full,e); | |||
if(e) { | |||
show404(); | |||
return; | |||
} | |||
if(fs::is_directory(status)) { | |||
full = full / fs::path("index.html"); | |||
status = fs::status(full,e); | |||
if(e) { | |||
show404(); | |||
return; | |||
} | |||
} | |||
if(!fs::is_regular_file(status)) { | |||
show404(); | |||
return; | |||
} | |||
std::string ext=full.extension(); | |||
mime_type::const_iterator p=mime_.find(ext); | |||
if(p!=mime_.end()) | |||
response().content_type(p->second); | |||
else | |||
response().content_type("application/octet-stream"); | |||
if(!settings().integer("http.allow_deflate",0)) { | |||
response().io_mode(http::response::nogzip); | |||
} | |||
std::ifstream file(full.file_string().c_str(),std::ifstream::binary | std::ifstream::in); | |||
if(!file) { | |||
show404(); | |||
} | |||
std::vector<char> buffer(4096); | |||
while(!file.eof()) { | |||
file.read(&buffer.front(),buffer.size()); | |||
response().out().write(&buffer.front(),file.gcount()); | |||
} | |||
response().out()<<std::flush; | |||
} | |||
void file_server::show404() | |||
{ | |||
response().status(http::response::not_found); | |||
response().set_html_header(); | |||
response().out() << | |||
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n" | |||
" \"http://www.w3.org/TR/html4/loose.dtd\">\n" | |||
"<html>\n" | |||
" <head>\n" | |||
" <title>404 Not Found</title>\n" | |||
" </head>\n" | |||
" <body>\n" | |||
" <h1>404 Not Found</h1>\n" | |||
" </body>\n" | |||
"</html>\n"<<std::flush; | |||
} | |||
} // impl | |||
} // cppcms |
@@ -0,0 +1,27 @@ | |||
#include "application.h" | |||
#include "applications_pool.h" | |||
#include <string> | |||
#include <map> | |||
namespace cppcms { | |||
class service; | |||
namespace impl { | |||
class file_server : public application | |||
{ | |||
public: | |||
file_server(cppcms::service &srv); | |||
~file_server(); | |||
private: | |||
void serve_file(std::string file_name); | |||
void show404(); | |||
void load_mime_types(std::string); | |||
std::string document_root_; | |||
typedef std::map<std::string,std::string> mime_type; | |||
mime_type mime_; | |||
}; | |||
} | |||
} |
@@ -1,4 +1,5 @@ | |||
#define CPPCMS_SOURCE | |||
#include "asio_config.h" | |||
#include "cgi_api.h" | |||
#include "cgi_acceptor.h" | |||
#include "asio_config.h" | |||
@@ -8,7 +8,10 @@ | |||
#include "cgi_acceptor.h" | |||
#include "cgi_api.h" | |||
#include "scgi_api.h" | |||
#include "http_api.h" | |||
#if 0 | |||
#include "fastcgi_api.h" | |||
#endif | |||
#include "locale_pool.h" | |||
#include "asio_config.h" | |||
@@ -242,8 +245,13 @@ void service::start_acceptor() | |||
if(tcp) { | |||
if(api=="scgi") | |||
impl_->acceptor_ = scgi_api_tcp_socket_factory(*this,ip,port,backlog); | |||
else if(api=="http") | |||
impl_->acceptor_ = http_api_factory(*this,ip,port,backlog); | |||
#if 0 | |||
else if(api=="fastcgi") | |||
impl_->acceptor_ = fastcgi_api_tcp_socket_factory(*this,ip,port,backlog); | |||
#endif | |||
else | |||
throw cppcms_error("Unknown service.api: " + api); | |||
} | |||
@@ -258,11 +266,13 @@ void service::start_acceptor() | |||
impl_->acceptor_ = scgi_api_unix_socket_factory(*this,backlog); | |||
else | |||
impl_->acceptor_ = scgi_api_unix_socket_factory(*this,socket,backlog); | |||
#if 0 | |||
else if(api=="fastcgi") | |||
if(socket=="stdin") | |||
impl_->acceptor_ = fastcgi_api_unix_socket_factory(*this,backlog); | |||
else | |||
impl_->acceptor_ = fastcgi_api_unix_socket_factory(*this,socket,backlog); | |||
#endif | |||
else | |||
throw cppcms_error("Unknown service.api: " + api); | |||
#endif | |||