///////////////////////////////////////////////////////////////////////////////
//                                                                             
//  Copyright (C) 2008-2012  Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>     
//                                                                             
//  See accompanying file COPYING.TXT file for licensing details.
//
///////////////////////////////////////////////////////////////////////////////
#define CPPCMS_SOURCE
#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 <cppcms/cstdint.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <cppcms/config.h>

#include <booster/aio/buffer.h>
#include "binder.h"
#include <utility>
#include "cached_settings.h"


#ifdef CPPCMS_WIN_NATIVE
#  ifndef NOMINMAX 
#    define NOMINMAX
#  endif
#  include <winsock2.h>
#else
#  include <arpa/inet.h>
#endif

namespace io = booster::aio;


namespace cppcms {
namespace impl {
namespace cgi {


	template<typename API,typename Factory> class socket_acceptor;

	class fastcgi : public connection {
	public:
		fastcgi(cppcms::service &srv) :
			connection(srv),
			socket_(srv.impl().get_io_service()),
			header_(),
			full_header_(),
			eof_(),
			eof_callback_(false)
		{
			reset_all();
			int procs=srv.procs_no();
			if(procs < 1) procs=1;
			int threads=srv.threads_no();
			cuncurrency_hint_=srv.cached_settings().fastcgi.cuncurrency_hint;
			if(cuncurrency_hint_ < 0)
				cuncurrency_hint_ = procs*threads;
		}
		~fastcgi()
		{
			if(socket_.native()!=io::invalid_socket) {
				booster::system::error_code e;
				socket_.shutdown(io::stream_socket::shut_rdwr,e);
			}
		}
		virtual void async_read_headers(handler const &h)
		{
			reset_all();
			async_read_record(mfunc_to_event_handler(&fastcgi::on_start_request,self(),h));
		}

		virtual void async_read_some(void *p,size_t s,io_handler const &h)
		{
			if(read_length_ == content_length_) {
				socket_.get_io_service().post(h,booster::system::error_code(errc::protocol_violation,cppcms_category),0);
				return;
			}
			if(body_ptr_ < body_.size()) {
				size_t rest=body_.size() - body_ptr_;
				if(s > rest)
					s=rest;
				memcpy(p,&body_[body_ptr_],s);
				body_ptr_ += s;
				read_length_ += s;

				if(body_ptr_ == body_.size()) {
					body_ptr_ = 0;
					body_.clear();
				}

				if(read_length_ >= content_length_) {
					async_read_record(mfunc_to_event_handler(
						&fastcgi::on_read_stdin_eof_expected,
						self(),
						h,
						s));
					return;
				}
				socket_.get_io_service().post(h,booster::system::error_code(),s);
				return;
			}
			else {
				async_read_record(mfunc_to_event_handler(
					&fastcgi::on_some_input_recieved,
					self(),
					h,std::make_pair(p,s)));
				return;
			}
		}
		virtual void on_async_write_start(){}
		virtual void on_async_write_progress(bool){}
	private:
		void on_some_input_recieved(booster::system::error_code const &e,io_handler const &h,std::pair<void *,size_t> in)
		{
			if(e) { h(e,0); return; }
			if(	header_.type!=fcgi_stdin 
				|| header_.request_id!=request_id_ 
				|| header_.content_length==0)
			{
				h(booster::system::error_code(errc::protocol_violation,cppcms_category),0);
				return;
			}
			async_read_some(in.first,in.second,h);
		}
		void on_read_stdin_eof_expected(booster::system::error_code const &e,io_handler const &h,size_t s)
		{
			if(e) { h(e,s); return; }
			if(	header_.type!=fcgi_stdin 
				|| header_.request_id!=request_id_ 
				|| header_.content_length!=0)
			{
				h(booster::system::error_code(errc::protocol_violation,cppcms_category),s);
				return;
			}
			h(booster::system::error_code(),s);
		}
	public:

		virtual booster::aio::const_buffer format_output(booster::aio::const_buffer const &in,bool completed,booster::system::error_code &)
		{
			booster::aio::const_buffer packet;
			booster::aio::const_buffer::entry const *chunks = in.get().first;
//#define DEBUG_FASTCGI
#ifdef DEBUG_FASTCGI
			{
				size_t n=in.get().second;
				printf("Format output of %d:\n",int(in.bytes_count()));
				for(size_t i=0;i<n;i++) {
					printf("[%.*s]",int(chunks[i].size),chunks[i].ptr);
				}
				if(completed) {
					printf("\nEOF\n");
				}
				else {
					printf("\n---\n");
				}
			}
#endif
			size_t reminder = in.bytes_count();
			size_t in_size = reminder;
			size_t chunk_consumed = 0;
			while(reminder > 0) {
				static const char pad[8]={0,0,0,0,0,0,0,0};
				static const size_t max_packet_len = 65535;
				size_t chunk = 0;
				int pad_len = 0;
				if(reminder > max_packet_len) {
					chunk = max_packet_len;
					if(in_size > max_packet_len && reminder == in_size) {
						// prepare only once
						full_header_.version = fcgi_version_1;
						full_header_.type=fcgi_stdout;
						full_header_.request_id=request_id_;
						full_header_.content_length = max_packet_len;
						full_header_.padding_length = pad_len = 1;
						full_header_.to_net();
					}
					else {
						pad_len = 1;
					}
					packet += io::buffer(&full_header_,sizeof(full_header_));
				}
				else {
					chunk = reminder;
					memset(&header_,0,sizeof(header_));
					header_.version=fcgi_version_1;
					header_.type=fcgi_stdout;
					header_.request_id=request_id_;
					header_.content_length = reminder;
					header_.padding_length =pad_len = (8 - (reminder % 8)) % 8;
					header_.to_net();

					packet += io::buffer(&header_,sizeof(header_));
				}

				reminder -= chunk;
				while(chunk > 0) {
					size_t next_size = chunks->size - chunk_consumed;
					if(next_size > chunk)
						next_size = chunk;

					packet += io::buffer(chunks->ptr + chunk_consumed, next_size);
					chunk_consumed += next_size;
					chunk -= next_size;
					if(chunk_consumed == chunks->size) {
						chunks++;
						chunk_consumed = 0;
					}
				}

				packet += io::buffer(pad,pad_len);
			}
			if(completed) {
				prepare_eof();
				packet += io::buffer(&eof_,sizeof(eof_));
			}
			#ifdef DEBUG_FASTCGI
			std::pair<booster::aio::const_buffer::entry const *,size_t> cnk = packet.get();
			for(size_t i=0;i<cnk.second;i++) {
				std::cerr << "[ " << (void const *)(cnk.first[i].ptr) << " " << cnk.first[i].size << "]\n" << std::endl;
			}
			#endif
			return packet;
		}
		virtual booster::aio::stream_socket &socket() { return socket_; }
		virtual booster::aio::io_service &get_io_service()
		{
			return socket_.get_io_service();
		}
		virtual bool keep_alive()
		{
			bool ka_value = keep_alive_;
			reset_all();
			return ka_value;
		}

		void prepare_eof()
		{
			memset(&eof_,0,sizeof(eof_));
			for(unsigned i=0;i<2;i++) {
				eof_.headers_[i].version=fcgi_version_1;
				eof_.headers_[i].request_id=request_id_;
			}
			eof_.headers_[0].type=fcgi_stdout;
			eof_.headers_[1].type=fcgi_end_request;
			eof_.headers_[1].content_length=8;
			eof_.record_.protocol_status=fcgi_request_complete;
			eof_.headers_[0].to_net();
			eof_.headers_[1].to_net();
			eof_.record_.to_net();
		}
		virtual void do_eof()
		{
			if(eof_callback_)
				socket_.cancel();
			eof_callback_ = false;
		}

		struct io_handler_to_handler {
			callback h;
			io_handler_to_handler(callback const &c) : h(c) {}
			void operator()(booster::system::error_code const &,size_t)
			{
				h();
			}
		};

		struct io_handler_to_event_handler {
			handler h;
			io_handler_to_event_handler(handler const &c) : h(c) {}
			void operator()(booster::system::error_code const &e,size_t)
			{
				h(e);
			}
		};

		// This is not really correct because server may try
		// to multiplex or ask control... But meanwhile it is good enough
		virtual void async_read_eof(callback const &h)
		{
			eof_callback_ = true;
			static char a;
			async_read_from_socket(&a,1,io_handler_to_handler(h));
		}
	
	private:

		// 
		//
		// defs
		//
		//

		struct fcgi_header {
			unsigned char version;
			unsigned char type;
			uint16_t request_id;
			uint16_t content_length;
			unsigned char padding_length;
			unsigned char reserved;
			void to_host() { 
				request_id = ntohs(request_id);
				content_length = ntohs(content_length);
			}
			void to_net() { 
				request_id = htons(request_id);
				content_length = htons(content_length);
			}
		};

		enum {
			fcgi_header_len = 8,
			fcgi_version_1 = 1,
						

			fcgi_begin_request     = 1,
			fcgi_abort_request     = 2,
			fcgi_end_request       = 3,
			fcgi_params            = 4,
			fcgi_stdin             = 5,
			fcgi_stdout            = 6,
			fcgi_stderr            = 7,
			fcgi_data              = 8,
			fcgi_get_values        = 9,
			fcgi_get_values_result =10,
			fcgi_unknown_type      =11,
			fcgi_maxtype 	       =11,

			fcgi_null_request_id = 0
		};
		
		struct fcgi_request_body {
			uint16_t role;
			unsigned char flags;
			unsigned char reserved [5];
			void to_host() { role = ntohs(role); }
			void to_net() { role = htons(role); }
		};

		struct fcgi_begin_request_record {
			fcgi_header header;
			fcgi_request_body body;
			void to_host() { header.to_host(); body.to_host(); }
			void to_net() {  header.to_net(); body.to_net(); }
		};
		enum {
			fcgi_keep_conn = 1
		};
		enum {
			fcgi_responder = 1,
			fcgi_authorizer = 2,
			fcgi_filter = 3
		};

		struct fcgi_end_request_body {
			uint32_t app_status;
			unsigned char protocol_status;
			unsigned char reserved[3];
			void to_host() {  app_status = ntohl(app_status); }
			void to_net() { app_status = htonl(app_status); }
		}; 

		struct fcgi_end_request_record {
			fcgi_header header;
			fcgi_end_request_body body;
			void to_host() { header.to_host(); body.to_host(); }
			void to_net() {  header.to_net(); body.to_net(); }
		};

		enum {
			fcgi_request_complete = 0,
			fcgi_cant_mpx_conn    = 1,
			fcgi_overloaded       = 2,
			fcgi_unknown_role     = 3
		};


		struct fcgi_unknown_type_body {
			unsigned char type;
			unsigned char reserved[7];
		};

		struct fcgi_unknown_type_record {
			fcgi_header header;
			fcgi_unknown_type_body body;
			void to_host() { header.to_host();}
			void to_net() {  header.to_net(); }
		};

		union fcgi_record {
			fcgi_header header;
			fcgi_begin_request_record begin ;
			fcgi_end_request_record  end;
			fcgi_unknown_type_record unknown;
		};
		
		//
		//
		// end of defs
		//
		//
		//
		//
	
		void on_start_request(booster::system::error_code const &e,handler const &h)
		{
			if(e) {
				h(e);
				return;
			}
			if(header_.version!=fcgi_version_1)
			{
				h(booster::system::error_code(errc::protocol_violation,cppcms_category));
				return;
			}
			if(header_.type==fcgi_get_values) {
				header_.type = fcgi_get_values_result;
				std::vector<std::pair<std::string,std::string> > pairs;
				pairs.reserve(4);
				if(!parse_pairs(pairs)) {
					h(booster::system::error_code(errc::protocol_violation,cppcms_category));
					return;
				}
				body_.clear();
				std::locale l("C");
				std::ostringstream ss;
				ss.imbue(l);
				ss<<cuncurrency_hint_;
				for(unsigned i=0;i<pairs.size();i++) {
					std::string name=pairs[i].first;
					if(name=="FCGI_MAX_CONNS" || name=="FCGI_MAX_REQS")
						add_pair(name,ss.str());
					else if(name=="FCGI_MPXS_CONNS")
						add_pair(name,"0");
				}
				async_send_respnse(mfunc_to_event_handler(&fastcgi::on_params_response_sent,
								self(),
								h));
			}
			else if(header_.type!=fcgi_begin_request) {
				async_read_headers(h);
				return;
			}
			
			if(body_.size()!=sizeof(fcgi_request_body)) {
				h(booster::system::error_code(errc::protocol_violation,cppcms_category));
				return;
			}
			fcgi_request_body *body=reinterpret_cast<fcgi_request_body*>(&body_.front());
			body->to_host();
			keep_alive_=body->flags & fcgi_keep_conn;
			
			if(body->role!=fcgi_responder) {
				header_.type=fcgi_end_request;
				body_.assign(0,8);
				fcgi_end_request_body *body=reinterpret_cast<fcgi_end_request_body*>(&body_.front());
				body->protocol_status=fcgi_unknown_role;
				body->to_net();
				async_send_respnse(mfunc_to_event_handler(&fastcgi::on_params_response_sent,
								self(),
								h));
				return;
			}
			request_id_=header_.request_id;
			
			body_.clear();
			if(non_blocking_read_record()) {
				params_record_expected(booster::system::error_code(),h);
			}
			else {
				async_read_record(mfunc_to_event_handler(&fastcgi::params_record_expected,self(),h));
			}
		}

		uint32_t read_len(unsigned char const *&p,unsigned char const *e)
		{
			if (p<e && *p < 0x80) {
				return *p++;
			}
			else if(e-p >= 4) {
				uint32_t B3=*p++;
				uint32_t B2=*p++;
				uint32_t B1=*p++;
				uint32_t B0=*p++;
				uint32_t len = ((B3 & 0x7fU) << 24) + (B2 << 16) + (B1 << 8) + B0;
				return len;
			}
			else {
				return 0xFFFFFFFFu;
			}
		}

		bool parse_pairs()
		{
			unsigned char const *p=reinterpret_cast<unsigned char const *>(&body_.front());
			unsigned char const *e=p + body_.size();
			while(p<e) {
				uint32_t nlen=read_len(p,e);
				uint32_t vlen=read_len(p,e);
				if(nlen == 0xFFFFFFFFu || vlen == 0xFFFFFFFFu)
					return false;
				char *name=0;
				if(uint32_t(e - p) >= nlen) { // don't chage order -- prevent integer overflow
					name = pool_.add(reinterpret_cast<char const *>(p),nlen);
					p+=nlen;
				}
				else {
					return false;
				}
				char *value = 0;
				if(uint32_t(e - p) >= vlen) { // don't chage order -- prevent integer overflow
					value = pool_.add(reinterpret_cast<char const *>(p),vlen);
					p+=vlen;
				}
				else {
					return false;
				}
				env_.add(name,value);
			}
			return true;
		}

		bool parse_pairs(std::vector<std::pair<std::string,std::string> > &container)
		{
			unsigned char const *p=reinterpret_cast<unsigned char const *>(&body_.front());
			unsigned char const *e=p + body_.size();
			while(p<e) {
				uint32_t nlen=read_len(p,e);
				uint32_t vlen=read_len(p,e);
				if(nlen == 0xFFFFFFFFu || vlen == 0xFFFFFFFFu)
					return false;
				std::string name;
				if(uint32_t(e - p) >= nlen) { // don't chage order -- prevent integer overflow
					name.assign(reinterpret_cast<char const *>(p),nlen);
					p+=nlen;
				}
				else {
					return false;
				}
				std::string value;
				if(uint32_t(e - p) >= vlen) { // don't chage order -- prevent integer overflow
					value.assign(reinterpret_cast<char const *>(p),vlen);
					p+=vlen;
				}
				else {
					return false;
				}
				container.resize(container.size()+1);
				container.back().first.swap(name);
				container.back().second.swap(value);
			}
			return true;
		}

		void params_record_expected(booster::system::error_code const &e,handler const &h)
		{
			if(e) {
				h(e);
				return;
			}
			for(;;){
				if(header_.type!=fcgi_params || header_.request_id!=request_id_)
					h(booster::system::error_code(errc::protocol_violation,cppcms_category));
				if(header_.content_length!=0) { // eof
					if(body_.size() < 16384) { 
						if(non_blocking_read_record()) {
							continue;
						}
						else {
							async_read_record(mfunc_to_event_handler(&fastcgi::params_record_expected,
											self(),
											h));
						}
					}
					else {
						h(booster::system::error_code(errc::protocol_violation,cppcms_category));
					}
					return;
				}
				break;
			}


			parse_pairs();

			body_.clear();

			char const *s_length = env_.get("CONTENT_LENGTH");
			if(!s_length || *s_length == 0 || (content_length_ =  atoll(s_length)) <=0)
				content_length_=0;
			if(content_length_==0) {
				if(non_blocking_read_record()) {
					stdin_eof_expected(booster::system::error_code(),h);
				}
				else {
					async_read_record(mfunc_to_event_handler(
						&fastcgi::stdin_eof_expected,
						self(),
						h));
				}
				return;
			}
			h(booster::system::error_code());
		}

		void stdin_eof_expected(booster::system::error_code const &e,handler const &h)
		{
			if(e) { h(e); return; }
			if(header_.type!=fcgi_stdin || header_.content_length!=0) {
				h(booster::system::error_code(errc::protocol_violation,cppcms_category));
				return;				
			}
			h(booster::system::error_code());
		}

		void on_params_response_sent(booster::system::error_code const &e,handler const &h)
		{
			if(e) { h(e); return; }
			async_read_headers(h);
		}

		// this is internal function for short messages
		void async_send_respnse(handler const &h)
		{
			header_.content_length=body_.size();
			if(body_.size() % 8 != 0) {
				header_.padding_length=8 - (body_.size() % 8);
				body_.resize(body_.size() + header_.padding_length);
			
			}
			io::const_buffer packet =
					io::buffer(&header_,sizeof(header_))
					+ io::buffer(body_);
			
			header_.to_net();
			socket_.async_write(packet,io_handler_to_event_handler(h));

		}

		bool non_blocking_read_record()
		{
			fcgi_header hdr;
			if(!peek_bytes(&hdr,sizeof(hdr)))
				return false;
			hdr.to_host();
			size_t buffer_size = get_buffer_size();
			
			if(buffer_size < sizeof(hdr) + hdr.content_length + hdr.padding_length)
				return false;
			skip_bytes(sizeof(hdr));
			header_ = hdr;
			size_t cur_size=body_.size();
			size_t rec_size=header_.content_length+header_.padding_length;
			if(rec_size==0) {
				return true;
			}
			body_.resize(cur_size + rec_size);
			read_bytes(&body_[cur_size],rec_size);
			body_.resize(cur_size + header_.content_length);
			return true;
		}
			
		void async_read_record(handler const &h)
		{
			async_read_from_socket(&header_,sizeof(header_),
					mfunc_to_io_handler(
							&fastcgi::on_header_read,
							self(),
							h));
		}
		
		struct on_header_read_binder : public booster::callable<void(booster::system::error_code const &,size_t)> {
			handler h;
			booster::shared_ptr<fastcgi> self;
			on_header_read_binder(handler const &_h,booster::shared_ptr<fastcgi> const &s) : h(_h),self(s)
			{
			}
			virtual void operator()(booster::system::error_code const &e,size_t /*read*/)
			{
				self->on_body_read(e,h);
			}
		};

		void on_header_read(booster::system::error_code const &e,size_t /*unused read*/,handler const &h)
		{
			if(e) { h(e); return; }
			header_.to_host();
			size_t cur_size=body_.size();
			size_t rec_size=header_.content_length+header_.padding_length;
			if(rec_size==0) {
				h(booster::system::error_code());
				return;
			}
			body_.resize(cur_size + rec_size);
			std::auto_ptr<booster::callable<void(booster::system::error_code const &,size_t)> > cb;
			cb.reset(new on_header_read_binder(h,self()));
			async_read_from_socket(
				&body_[cur_size],rec_size,
				cb);
		}
		void on_body_read(booster::system::error_code const &e,handler const &h)
		{
			if(e) { h(e);  return; }
			body_.resize(body_.size() - header_.padding_length);
			h(booster::system::error_code());
		}

		void add_value(std::string const &value)
		{
			if(value.size() <128) {
				body_.push_back(char(value.size()));
			}
			else {
				uint32_t size=value.size();
				size |= 0x80000000u;
				size=htonl(size);
				char *p=(char*)&size;
				body_.insert(body_.end(),p,p+sizeof(size));
			}
		}
		void add_pair(std::string const &name,std::string const &value)
		{
			add_value(name);
			add_value(value);
			body_.insert(body_.end(),name.begin(),name.end());
			body_.insert(body_.end(),value.begin(),value.end());
		}


		booster::shared_ptr<fastcgi> self()
		{
			return booster::static_pointer_cast<fastcgi>(shared_from_this());
		}
		
		
		friend class socket_acceptor<fastcgi>;
		io::stream_socket socket_;

		fcgi_header header_;
		fcgi_header full_header_;
		std::vector<char> body_;


		long long read_length_;
		long long content_length_;
		unsigned body_ptr_;
		int request_id_;
		bool keep_alive_;
		int cuncurrency_hint_;

		struct eof_str {
			fcgi_header headers_[2];
			fcgi_end_request_body record_;
		} eof_;

		std::vector<char> cache_;
		size_t cache_start_,cache_end_;
		bool eof_callback_;

		void reset_all()
		{
			memset(&header_,0,sizeof(header_));
			body_.clear();
			read_length_=content_length_=0;
			body_ptr_=0;
			request_id_=0;
			keep_alive_=false;
			env_.clear();
			pool_.clear();
			memset(&eof_,0,sizeof(eof_));
			if(cache_.empty()) {
				cache_start_ = 0;
				cache_end_ = 0;
			}
		}

		bool peek_bytes(void *ptr,size_t n)
		{
			if(cache_end_ - cache_start_ >= n) {
				memcpy(ptr,&cache_[cache_start_],n);
				return true;
			}
			return false;
		}
		size_t get_buffer_size()
		{
			return cache_end_ - cache_start_;
		}
		void skip_bytes(size_t n)
		{
			assert(cache_end_ - cache_start_ >= n);
			cache_start_ +=n;
		}
		void read_bytes(void *ptr,size_t n)
		{
			assert(cache_end_ - cache_start_ >=n);
			memcpy(ptr,&cache_[cache_start_],n);
			cache_start_+=n;
		}

		void async_read_from_socket(void *ptr,size_t n,booster::aio::io_handler const &cb)
		{
			if(cache_end_ - cache_start_ >=n) {
				memcpy(ptr,&cache_[cache_start_],n);
				cache_start_+=n;
				socket_.get_io_service().post(cb,booster::system::error_code(),n);
				return;
			}
			if(cache_start_ == cache_end_) {
				cache_start_ = cache_end_ = 0;
			}
			else if(cache_start_!=0) {
				memmove(&cache_[0],&cache_[cache_start_],cache_end_ - cache_start_);
				cache_end_ = cache_end_ - cache_start_;
				cache_start_ = 0;
			}

			size_t min_size = std::max(n,size_t(16384));
			if(cache_.size() < n) {
				cache_.resize(min_size,0);
			}
			
			socket_.async_read_some(
				booster::aio::buffer(&cache_[cache_end_],cache_.size() - cache_end_),
					mfunc_to_io_handler(
						&fastcgi::on_some_read_from_socket,
						self(),
						cb,
						std::make_pair(ptr,n)));
		}
		void on_some_read_from_socket(	booster::system::error_code const &e,
						size_t read_size, 
						booster::callback<void(booster::system::error_code const &e,size_t read)> const &cb,
						std::pair<void *,size_t> inp)
		{
			void *ptr = inp.first;
			size_t expected_read_size = inp.second;
			cache_end_ += read_size;
			if(e) {
				cb(e,0);
				return;
			}
			async_read_from_socket(ptr,expected_read_size,cb);
		}
	};

	std::auto_ptr<acceptor> fastcgi_api_tcp_socket_factory(cppcms::service &srv,std::string ip,int port,int backlog)
	{
		std::auto_ptr<acceptor> a(new socket_acceptor<fastcgi>(srv,ip,port,backlog));
		return a;
	}

#if !defined(CPPCMS_WIN32)

	std::auto_ptr<acceptor> fastcgi_api_unix_socket_factory(cppcms::service &srv,std::string socket,int backlog)
	{
		std::auto_ptr<acceptor> a(new socket_acceptor<fastcgi>(srv,socket,backlog));
		return a;
	}
	std::auto_ptr<acceptor> fastcgi_api_unix_socket_factory(cppcms::service &srv,int backlog)
	{
		std::auto_ptr<acceptor> a(new socket_acceptor<fastcgi>(srv,backlog));
		return a;
	}
#endif


} // cgi
} // impl
} // cppcms

