Browse Source

Added implementation of http web server

master
Artyom Beilis 15 years ago
parent
commit
e035e85a00
13 changed files with 720 additions and 105 deletions
  1. +2
    -1
      Makefile.am
  2. +80
    -16
      applications_pool.cpp
  3. +6
    -2
      applications_pool.h
  4. +4
    -3
      cgi_api.cpp
  5. +6
    -4
      config.txt
  6. +6
    -0
      configure.in
  7. +1
    -1
      hello_world.cpp
  8. +379
    -78
      http_api.cpp
  9. +19
    -0
      http_api.h
  10. +179
    -0
      internal_file_server.cpp
  11. +27
    -0
      internal_file_server.h
  12. +1
    -0
      scgi_api.cpp
  13. +10
    -0
      service.cpp

+ 2
- 1
Makefile.am View File

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





+ 80
- 16
applications_pool.cpp View File

@@ -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>();
}


+ 6
- 2
applications_pool.h View File

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


+ 4
- 3
cgi_api.cpp View File

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


+ 6
- 4
config.txt View File

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


+ 6
- 0
configure.in View File

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


+ 1
- 1
hello_world.cpp View File

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


+ 379
- 78
http_api.cpp View File

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


+ 19
- 0
http_api.h View File

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


+ 179
- 0
internal_file_server.cpp View File

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

+ 27
- 0
internal_file_server.h View File

@@ -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
- 0
scgi_api.cpp View File

@@ -1,4 +1,5 @@
#define CPPCMS_SOURCE
#include "asio_config.h"
#include "cgi_api.h"
#include "cgi_acceptor.h"
#include "asio_config.h"


+ 10
- 0
service.cpp View File

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


Loading…
Cancel
Save