#include #include #include #include #include #include #include namespace fc { namespace http { class server::response::impl : public fc::retainable { public: impl( const fc::http::connection_ptr& c, const std::function& cont = std::function() ) :body_bytes_sent(0),body_length(0),con(c),handle_next_req(cont) {} void send_header() { //ilog( "sending header..." ); fc::stringstream ss; ss << "HTTP/1.1 " << rep.status << " "; switch( rep.status ) { case fc::http::reply::OK: ss << "OK\n\r"; break; case fc::http::reply::RecordCreated: ss << "Record Created\n\r"; break; case fc::http::reply::NotFound: ss << "Not Found\n\r"; break; case fc::http::reply::Found: ss << "Found\n\r"; break; case fc::http::reply::InternalServerError: ss << "Internal Server Error\r\n"; break; } for( uint32_t i = 0; i < rep.headers.size(); ++i ) { ss << rep.headers[i].key <<": "<get_socket().write( s.c_str(), s.size() ); } http::reply rep; int64_t body_bytes_sent; uint64_t body_length; http::connection_ptr con; std::function handle_next_req; }; class server::impl { public: impl(){} impl(const fc::ip::endpoint& p ) { tcp_serv.listen(p); accept_complete = fc::async([this](){ this->accept_loop(); }); } fc::future accept_complete; ~impl() { try { tcp_serv.close(); if( accept_complete.valid() ) accept_complete.wait(); }catch(...){} } void accept_loop() { while( !accept_complete.canceled() ) { http::connection_ptr con = std::make_shared(); tcp_serv.accept( con->get_socket() ); //ilog( "Accept Connection" ); fc::async( [=](){ handle_connection( con, on_req ); } ); } } void handle_connection( const http::connection_ptr& c, std::function do_on_req ) { try { http::server::response rep( fc::shared_ptr( new response::impl(c) ) ); auto req = c->read_request(); if( do_on_req ) do_on_req( req, rep ); c->get_socket().close(); } catch ( fc::exception& e ) { wlog( "unable to read request ${1}", ("1", e.to_detail_string() ) );//fc::except_str().c_str()); } //wlog( "done handle connection" ); } std::function on_req; fc::tcp_server tcp_serv; }; server::server():my( new impl() ){} server::server( uint16_t port ) :my( new impl(fc::ip::endpoint( fc::ip::address(),port)) ){} server::server( server&& s ):my(fc::move(s.my)){} server& server::operator=(server&& s) { fc_swap(my,s.my); return *this; } server::~server(){} void server::listen( const fc::ip::endpoint& p ) { my.reset( new impl(p) ); } server::response::response(){} server::response::response( const server::response& s ):my(s.my){} server::response::response( server::response&& s ):my(fc::move(s.my)){} server::response::response( const fc::shared_ptr& m ):my(m){} server::response& server::response::operator=(const server::response& s) { my = s.my; return *this; } server::response& server::response::operator=(server::response&& s) { fc_swap(my,s.my); return *this; } void server::response::add_header( const fc::string& key, const fc::string& val )const { my->rep.headers.push_back( fc::http::header( key, val ) ); } void server::response::set_status( const http::reply::status_code& s )const { if( my->body_bytes_sent != 0 ) { wlog( "Attempt to set status after sending headers" ); } my->rep.status = s; } void server::response::set_length( uint64_t s )const { if( my->body_bytes_sent != 0 ) { wlog( "Attempt to set length after sending headers" ); } my->body_length = s; } void server::response::write( const char* data, uint64_t len )const { if( my->body_bytes_sent + len > my->body_length ) { wlog( "Attempt to send to many bytes.." ); len = my->body_bytes_sent + len - my->body_length; } if( my->body_bytes_sent == 0 ) { my->send_header(); } my->body_bytes_sent += len; my->con->get_socket().write( data, static_cast(len) ); if( my->body_bytes_sent == int64_t(my->body_length) ) { if( false || my->handle_next_req ) { ilog( "handle next request..." ); //fc::async( std::function(my->handle_next_req) ); fc::async( my->handle_next_req ); } } } server::response::~response(){} void server::on_request( const std::function& cb ) { my->on_req = cb; } } }