/////////////////////////////////////////////////////////////////////////////////// Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>//// See accompanying file COPYING.TXT file for licensing details./////////////////////////////////////////////////////////////////////////////////#define CPPCMS_SOURCE//#define DEBUG_HTTP_PARSER#include "cgi_api.h"#include "cgi_acceptor.h"#include <cppcms/service.h>#include "service_impl.h"#include "cppcms_error_category.h"#include <cppcms/json.h>#include "http_parser.h"#include <cppcms/config.h>#include <cppcms/util.h>#include <string.h>#include <iostream>#include <list>#include <sstream>#include <algorithm>#include <stdio.h>#include "binder.h"// for testing only#if !defined(__linux) && !defined(CPPCMS_WIN32)# define CPPCMS_NO_SO_SNDTIMO#endif#ifdef CPPCMS_NO_SO_SNDTIMO#include <poll.h>#include <errno.h>#endif#include <booster/aio/buffer.h>#include <booster/aio/deadline_timer.h>#include <booster/aio/aio_category.h>#include <booster/log.h>#include "cached_settings.h"#include "format_number.h"#include "string_map.h"#include "send_timeout.h"#include "rewrite.h"namespace io = booster::aio;namespace cppcms {namespace impl {namespace cgi {class http;class http_watchdog : public booster::enable_shared_from_this<http_watchdog> {public:typedef booster::shared_ptr<http> http_ptr;typedef booster::weak_ptr<http> weak_http_ptr;http_watchdog(booster::aio::io_service &srv) :timer_(srv){}void check(booster::system::error_code const &e=booster::system::error_code());void add(weak_http_ptr p){connections_.insert(p);}void remove(weak_http_ptr p){connections_.erase(p);}private:typedef std::set<weak_http_ptr> connections_type;connections_type connections_;booster::aio::deadline_timer timer_;};class http_creator {public:http_creator() {} // for concepthttp_creator(booster::aio::io_service &srv,json::value const &settings,std::string const &ip="0.0.0.0",int port = 8080) :ip_(ip),port_(port),watchdog_(new http_watchdog(srv)){if(settings.find("http.rewrite").type()==json::is_array) {rewrite_.reset(new url_rewriter(settings.find("http.rewrite").array()));}watchdog_->check();}http *operator()(cppcms::service &srv) const;private:std::string ip_;int port_;booster::shared_ptr<http_watchdog> watchdog_;booster::shared_ptr<url_rewriter> rewrite_;};namespace {char non_const_empty_string[1]={0};}class http : public connection {public:http( cppcms::service &srv,std::string const &ip,int port,booster::shared_ptr<http_watchdog> wd,booster::shared_ptr<url_rewriter> rw):connection(srv),socket_(srv.impl().get_io_service()),input_body_ptr_(0),input_parser_(input_body_,input_body_ptr_),output_pbase_(0),output_pptr_(0),output_epptr_(0),output_parser_(output_pbase_,output_pptr_,output_epptr_),request_method_(non_const_empty_string),request_uri_(non_const_empty_string),headers_done_(false),first_header_observerd_(false),total_read_(0),time_to_die_(0),timeout_(0),sync_option_is_set_(false),in_watchdog_(false),watchdog_(wd),rewrite_(rw),eof_callback_(false){env_.add("SERVER_SOFTWARE",CPPCMS_PACKAGE_NAME "/" CPPCMS_PACKAGE_VERSION);env_.add("SERVER_NAME",pool_.add(ip));char *sport = pool_.alloc(10);format_number(port,sport,10);env_.add("SERVER_PORT",sport);env_.add("GATEWAY_INTERFACE","CGI/1.0");env_.add("SERVER_PROTOCOL","HTTP/1.0");timeout_ = srv.cached_settings().http.timeout;}~http(){if(socket_.native()!=io::invalid_socket) {booster::system::error_code e;socket_.shutdown(io::stream_socket::shut_rdwr,e);}}void update_time(){time_to_die_ = time(0) + timeout_;}void log_timeout(){char const *uri = request_uri_;if(!uri || *uri==0)uri = "unknown";BOOSTER_INFO("cppcms_http") << "Timeout on connection for URI: " << uri << " from " << cgetenv("REMOTE_ADDR");}void die(){log_timeout();close();}void async_die(){socket_.cancel();die();}time_t time_to_die(){return time_to_die_;}void async_read_some_headers(handler const &h){socket_.on_readable(mfunc_to_event_handler(&http::some_headers_data_read,self(),h));update_time();}virtual void async_read_headers(handler const &h){#ifdef CPPCMS_NO_SO_SNDTIMObooster::system::error_code e;socket_.set_non_blocking_if_needed(true,e);if(e) { h(e); return; }#endifupdate_time();add_to_watchdog();async_read_some_headers(h);}void some_headers_data_read(booster::system::error_code const &er,handler const &h){if(er) { h(er); return; }booster::system::error_code e;size_t n = socket_.bytes_readable(e);if(e) { h(e); return ; }if(n == 0) {h(booster::system::error_code(booster::aio::aio_error::eof,booster::aio::aio_error_cat));return;}if(n > 16384)n=16384;if(input_body_.capacity() < n) {input_body_.reserve(n);}input_body_.resize(input_body_.capacity(),0);input_body_ptr_=0;n = socket_.read_some(booster::aio::buffer(input_body_),e);total_read_+=n;input_body_.resize(n);for(;;) {using ::cppcms::http::impl::parser;switch(input_parser_.step()) {case parser::more_data:if(total_read_ > 16384) {h(booster::system::error_code(errc::protocol_violation,cppcms_category));return;}// Assuming body_ptr == body.size()async_read_some_headers(h);return;case parser::got_header:if(!first_header_observerd_) {first_header_observerd_=true;char const *header_begin = input_parser_.header_.c_str();char const *header_end = header_begin + input_parser_.header_.size();char const *query=header_end;char const *rmethod=std::find( header_begin,header_end,' ');if(rmethod!=header_end)query=std::find(rmethod+1,header_end,' ');if(query!=header_end) {request_method_ = pool_.add(header_begin,rmethod);request_uri_ = pool_.add(rmethod+1,query);first_header_observerd_=true;BOOSTER_INFO("cppcms_http") << request_method_ <<" " << request_uri_;}else {h(booster::system::error_code(errc::protocol_violation,cppcms_category));return;}}else { // Any other headerchar const *name = "";char const *value = "";if(!parse_single_header(input_parser_.header_,name,value)) {h(booster::system::error_code(errc::protocol_violation,cppcms_category));return;}if(strcmp(name,"CONTENT_LENGTH")==0 || strcmp(name,"CONTENT_TYPE")==0)env_.add(name,value);else {char *updated_name =pool_.alloc(strlen(name) + 5 + 1);strcpy(updated_name,"HTTP_");strcat(updated_name,name);env_.add(updated_name,value);}}break;case parser::end_of_headers:process_request(h);return;case parser::error_observerd:h(booster::system::error_code(errc::protocol_violation,cppcms_category));return;}}}virtual void async_read_some(void *p,size_t s,io_handler const &h){update_time();if(input_body_ptr_==input_body_.size()) {input_body_.clear();input_body_ptr_=0;}if(!input_body_.empty()) {if(input_body_.size() - input_body_ptr_ < s) {s=input_body_.size() - input_body_ptr_;}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(h,booster::system::error_code(),s);return;}if(input_body_.capacity()!=0) {std::vector<char> v;input_body_.swap(v);}socket_.async_read_some(io::buffer(p,s),h);}virtual void do_eof(){if(eof_callback_)socket_.cancel();eof_callback_ = false;booster::system::error_code e;socket_.shutdown(io::stream_socket::shut_wr,e);socket_.close(e);}#ifndef CPPCMS_NO_SO_SNDTIMOsize_t timed_write_some(booster::aio::const_buffer const &buf,booster::system::error_code &e){socket_.set_non_blocking_if_needed(false,e);if(e) return 0;if(!sync_option_is_set_) {cppcms::impl::set_send_timeout(socket_,timeout_,e);if(e)return 0;sync_option_is_set_ = true;}booster::ptime start = booster::ptime::now();size_t n = socket_.write_some(buf,e);booster::ptime end = booster::ptime::now();// it may actually return with success but return small// a small bufferif(booster::ptime::to_number(end - start) >= timeout_ - 0.1) {e=booster::system::error_code(errc::protocol_violation,cppcms_category);die();return n;}if(e && (io::basic_socket::would_block(e)#ifdef CPPCMS_WIN32|| e.value() == 10060 // WSAETIMEDOUT - do not want to include windows.h#endif)) {// handle timeout with SO_SNDTIMEO// that responds with EAGIAN or EWOULDBLOCKdie();}return n;}#elsesize_t timed_write_some(booster::aio::const_buffer const &buf,booster::system::error_code &e){socket_.set_non_blocking_if_needed(true,e);booster::ptime start = booster::ptime::now();pollfd pfd=pollfd();pfd.fd = socket_.native();pfd.events = POLLOUT;pfd.revents = 0;int msec = timeout_ * 1000;int r = 0;while((r=poll(&pfd,1,msec))<0 && errno==EINTR) {msec -= booster::ptime::milliseconds(booster::ptime::now() - start);if(msec <= 0) {r = 0;break;}}if(r < 0) {e=booster::system::error_code(errno,booster::system::system_category);return 0;}if(r==1 && pfd.revents & POLLOUT)return socket_.write_some(buf,e);e=booster::system::error_code(errc::protocol_violation,cppcms_category);die();return 0;}#endifbool write_to_socket(booster::aio::const_buffer const &bufin,booster::system::error_code &e){booster::aio::const_buffer buf = bufin;size_t total = 0;while(!buf.empty()) {size_t n = timed_write_some(buf,e);total += n;buf += n;if(e) {close();break;}}return total == bufin.bytes_count();}virtual booster::aio::io_service &get_io_service(){return socket_.get_io_service();}virtual bool keep_alive(){return false;}void close(){booster::system::error_code e;socket_.shutdown(io::stream_socket::shut_rdwr,e);socket_.close(e);}struct ignore_binder {callback h;void operator()(booster::system::error_code const &,size_t) { h(); }};virtual void async_read_eof(callback const &h){watchdog_->add(self());static char a;ignore_binder cb = { h };socket_.async_read_some(io::buffer(&a,1),cb);}void on_async_write_start(){update_time();watchdog_->add(self());}void on_async_write_progress(bool completed){update_time();if(completed)watchdog_->remove(self());}virtual booster::aio::stream_socket &socket() { return socket_; }virtual booster::aio::const_buffer format_output(booster::aio::const_buffer const &in,bool /*completed*/,booster::system::error_code &e){if(headers_done_)return in;booster::aio::const_buffer packet;std::pair<booster::aio::const_buffer::entry const *,size_t> tmp = in.get();booster::aio::const_buffer::entry const *entry = tmp.first;size_t parts = tmp.second;output_pbase_=output_pptr_=output_epptr_=0;bool r=false;while(parts > 0) {output_pbase_=static_cast<char const *>(entry->ptr);output_pptr_ = output_pbase_;output_epptr_ = output_pbase_ + entry->size;r =process_output_headers(e);parts--;entry++;if(r) {if(!e)headers_done_ = true;break;}}if(e)return packet;if(!r) {tmp = in.get();entry = tmp.first;parts = tmp.second;output_body_.reserve(output_body_.size() + in.bytes_count());while(parts > 0) {output_body_.insert(output_body_.end(),entry->ptr,entry->ptr+entry->size);parts--;entry++;}return packet;}packet+= booster::aio::buffer(response_line_);if(!output_body_.empty())packet+= booster::aio::buffer(output_body_);packet+= in;return packet;}private:bool process_output_headers(booster::system::error_code &e){static char const *addon ="Server: CppCMS-Embedded/" CPPCMS_PACKAGE_VERSION "\r\n""Connection: close\r\n";using cppcms::http::impl::parser;for(;;) {switch(output_parser_.step()) {case parser::more_data:return false;case parser::got_header:{char const *name="";char const *value="";if(!parse_single_header(output_parser_.header_,name,value)) {e=booster::system::error_code(errc::protocol_violation,cppcms_category);return true;}if(strcmp(name,"STATUS")==0) {response_line_ = "HTTP/1.0 ";response_line_ +=value;response_line_ +="\r\n";response_line_.append(addon);return true;}}break;case parser::end_of_headers:response_line_ = "HTTP/1.0 200 Ok\r\n";response_line_.append(addon);return true;case parser::error_observerd:e=booster::system::error_code(errc::protocol_violation,cppcms_category);return true;}}}void process_request(handler const &h){char const *rm = request_method_;char const *rm_end = request_method_ + strlen(request_method_);if(rm==rm_end || cppcms::http::protocol::tocken(rm,rm_end)!=rm_end) {error_response("HTTP/1.0 400 Bad Request\r\n\r\n",h);return;}env_.add("REQUEST_METHOD",request_method_);if(rewrite_)request_uri_ = rewrite_->rewrite(request_uri_,pool_);char const *remote_addr=0;if(service().cached_settings().http.proxy.behind==true) {std::vector<std::string> const &variables =service().cached_settings().http.proxy.remote_addr_cgi_variables;for(unsigned i=0;remote_addr == 0 && i<variables.size();i++) {remote_addr = env_.get(variables[i].c_str());}}if(!remote_addr) {booster::system::error_code e;remote_addr=pool_.add(socket_.remote_endpoint(e).ip());if(e) {close();h(e);return;}}env_.add("REMOTE_HOST",remote_addr);env_.add("REMOTE_ADDR",remote_addr);if(request_uri_[0]!='/') {error_response("HTTP/1.0 400 Bad Request\r\n\r\n",h);return;}char *path=non_const_empty_string;char *query = strchr(request_uri_,'?');if(query == 0) {path=request_uri_;}else {path=pool_.add(request_uri_,query - request_uri_);env_.add("QUERY_STRING",query + 1);}std::vector<std::string> const &script_names =service().cached_settings().http.script_names;size_t path_size = strlen(path);for(unsigned i=0;i<script_names.size();i++) {std::string const &name=script_names[i];size_t name_size = name.size();if(path_size >= name_size && memcmp(path,name.c_str(),name_size) == 0&& (path_size == name_size || path[name_size]=='/')){env_.add("SCRIPT_NAME",pool_.add(name));path = path + name_size;break;}}env_.add("PATH_INFO",pool_.add(util::urldecode(path,path+strlen(path))));update_time();h(booster::system::error_code());}void on_async_read_complete(){remove_from_watchdog();}void error_response(char const *message,handler const &h){socket_.async_write(io::buffer(message,strlen(message)),mfunc_to_io_handler(&http::on_error_response_written,self(),h));}void on_error_response_written(booster::system::error_code const &e,size_t,handler const &h){if(e) {h(e);return;}close();h(booster::system::error_code(errc::protocol_violation,cppcms_category));}bool parse_single_header(std::string const &header,char const *&o_name,char const *&o_value){char const *p=header.c_str();char const *e=p + header.size();char const *name_end = p;p=cppcms::http::protocol::skip_ws(p,e);name_end=cppcms::http::protocol::tocken(p,e);if(name_end==p)return false;size_t name_size = name_end - p;char *name = pool_.alloc(name_size + 1);*std::copy(p,name_end,name) = 0;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);char *value = pool_.alloc(e-p+1);*std::copy(p,e,value) = 0;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';}o_name = name;o_value = value;return true;}booster::shared_ptr<http> self(){return booster::static_pointer_cast<http>(shared_from_this());}friend class socket_acceptor<http,http_creator>;booster::aio::stream_socket socket_;std::vector<char> input_body_;unsigned input_body_ptr_;::cppcms::http::impl::parser input_parser_;std::vector<char> output_body_;char const *output_pbase_;char const *output_pptr_;char const *output_epptr_;::cppcms::http::impl::parser output_parser_;std::string response_line_;char *request_method_;char *request_uri_;bool headers_done_;bool first_header_observerd_;unsigned total_read_;time_t time_to_die_;int timeout_;bool sync_option_is_set_;bool in_watchdog_;void add_to_watchdog(){if(!in_watchdog_) {watchdog_->add(self());in_watchdog_ = true;}}void remove_from_watchdog(){if(in_watchdog_) {watchdog_->remove(self());in_watchdog_ = false;}}booster::shared_ptr<http_watchdog> watchdog_;booster::shared_ptr<url_rewriter> rewrite_;bool eof_callback_;};void http_watchdog::check(booster::system::error_code const &e){if(e)return;std::list<http_ptr> kill;time_t now = time(0);for(connections_type::iterator p=connections_.begin(),e=connections_.end();p!=e;) {booster::shared_ptr<http> ptr = p->lock();if(!ptr) {connections_type::iterator tmp = p;++p;connections_.erase(tmp);}else {if(ptr->time_to_die() < now) {kill.push_back(ptr);connections_type::iterator tmp = p;++p;connections_.erase(tmp);}else {++p;}}}for(std::list<http_ptr>::iterator p=kill.begin();p!=kill.end();++p) {(*p)->async_die();}timer_.expires_from_now(booster::ptime(1));timer_.async_wait(mfunc_to_event_handler(&http_watchdog::check,shared_from_this()));}http *http_creator::operator()(cppcms::service &srv) const{return new http(srv,ip_,port_,watchdog_,rewrite_);}std::auto_ptr<acceptor> http_api_factory(cppcms::service &srv,std::string ip,int port,int backlog){typedef socket_acceptor<http,http_creator> acceptor_type;std::auto_ptr<acceptor_type> acc(new acceptor_type(srv,ip,port,backlog));acc->factory(http_creator(srv.get_io_service(),srv.settings(),ip,port));std::auto_ptr<acceptor> a(acc);return a;}} // cgi} // impl} // cppcms
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。