├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── README.md ├── async_stream.h ├── calculator.cpp ├── calculator.h ├── condition_variable.hpp ├── coroserver.xcodeproj └── project.pbxproj ├── http_protocol.cpp ├── http_protocol.h ├── main.cpp ├── routing.cpp ├── routing.h ├── server.cpp └── server.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X 2 | .DS_Store 3 | *.swp 4 | *.lock 5 | 6 | # Xcode 7 | *.pbxuser 8 | *.mode1v3 9 | *.mode2v3 10 | *.perspectivev3 11 | *.xcuserstate 12 | *.moved-aside 13 | *~.nib 14 | project.xcworkspace/ 15 | xcuserdata 16 | DerivedData/ 17 | build/ 18 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "http-parser"] 2 | path = http-parser 3 | url = https://github.com/joyent/http-parser.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(coroserver) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++11 -ftemplate-depth=256") 5 | if(APPLE) 6 | set(CMAKE_LINK_FLAGS "${CMAKE_LINK_FLAGS} -stdlib=libc++") 7 | endif(APPLE) 8 | 9 | find_package(Boost 1.54.0 COMPONENTS system thread coroutine context REQUIRED) 10 | 11 | INCLUDE_DIRECTORIES( 12 | ${Boost_INCLUDE_DIRS} 13 | ) 14 | 15 | file(GLOB SOURCE_LIST "*.cpp" "http-parser/http_parser.c") 16 | add_executable(coroserver ${SOURCE_LIST}) 17 | TARGET_LINK_LIBRARIES(coroserver ${Boost_LIBRARIES} pthread) 18 | 19 | 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | coroserver 2 | ========== 3 | 4 | Multi thread socket server with Boost.Asio and Boost.Coroutine 5 | 6 | This program uses [Boost 1.55](http://www.boost.org/users/history/version_1_55_0.html) and [http-parser](https://github.com/joyent/http-parser) from joyent. 7 | 8 | * The project needs C++11 compliant compiler and standard lib, it has been tested on: 9 | 10 | + Clang 3.3 with libc++ on FreeBSD 10 BETA3 11 | 12 | + GCC 4.8 on Ubuntu Linux 13.10 13 | 14 | + Xcode 5/Clang 3.3 with libc++ on Mac OS X 10.9 15 | 16 | * To build on FreeBSD or Linux: 17 | 18 | + Run `git submodule update --init` in source directory 19 | 20 | + Run `cmake [options] path/to/source` 21 | 22 | * To build on Mac OS X 10.9 23 | 24 | + Use Xcode 5, or 25 | 26 | + Follow the building process on FreeBSD/Linux. 27 | 28 | * `coroserver` listens on port 20000 with a HTTP server, 20001 with a half-worked HTTP proxy, and 30000 with a line-oriented calculator. 29 | -------------------------------------------------------------------------------- /async_stream.h: -------------------------------------------------------------------------------- 1 | // 2 | // async_stream.h 3 | // coroserver 4 | // 5 | // Created by Windoze on 13-2-24. 6 | // Copyright (c) 2013 0d0a.com. All rights reserved. 7 | // 8 | 9 | #ifndef async_stream_h_included 10 | #define async_stream_h_included 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | namespace net { 23 | /** 24 | * Default stream input and output buffer size 25 | */ 26 | constexpr size_t bf_size=4096; 27 | constexpr std::streamsize pb_size=4; 28 | 29 | template 30 | struct endpoint_resolver; 31 | 32 | template<> 33 | struct endpoint_resolver { 34 | boost::asio::ip::tcp::endpoint resolve(const std::string &endpoint_desc, 35 | const std::string &default_port, 36 | boost::asio::io_service &ios) 37 | { 38 | using namespace boost::asio::ip; 39 | tcp::resolver resolver(ios); 40 | size_t pos=endpoint_desc.find_last_of(':'); 41 | std::string host; 42 | std::string port; 43 | if (pos==std::string::npos) { 44 | host=endpoint_desc; 45 | port=default_port; 46 | } else { 47 | host.assign(endpoint_desc.begin(), endpoint_desc.begin()+pos); 48 | if (*host.begin()=='[' && *host.rbegin()==']') { 49 | host=std::string(host.begin()+1, host.end()-1); 50 | } 51 | port.assign(endpoint_desc.begin()+pos+1, endpoint_desc.end()); 52 | } 53 | tcp::resolver::query query(host, port); 54 | return *resolver.resolve(query); 55 | } 56 | 57 | boost::asio::ip::tcp::endpoint resolve(const std::string &endpoint_desc, 58 | const std::string &default_port, 59 | boost::asio::yield_context yield) 60 | { 61 | using namespace boost::asio::ip; 62 | boost::asio::io_service &ios=yield.handler_.dispatcher_.get_io_service(); 63 | tcp::resolver resolver(ios); 64 | size_t pos=endpoint_desc.find_last_of(':'); 65 | std::string host; 66 | std::string port; 67 | if (pos==std::string::npos) { 68 | host=endpoint_desc; 69 | port=default_port; 70 | } else { 71 | host.assign(endpoint_desc.begin(), endpoint_desc.begin()+pos); 72 | if (*host.begin()=='[' && *host.rbegin()==']') { 73 | host=std::string(host.begin()+1, host.end()-1); 74 | } 75 | port.assign(endpoint_desc.begin()+pos+1, endpoint_desc.end()); 76 | } 77 | tcp::resolver::query query(host, port); 78 | return *resolver.async_resolve(query, yield); 79 | } 80 | 81 | std::string default_port_; 82 | }; 83 | 84 | /* 85 | * std::iostream with coroutine support 86 | * Yields on blocking of reading or writing, and gets resumed on io completion 87 | * 88 | * @param StreamDescriptor an asychronized stream descriptor, i.e. boost::asio::ip::tcp::socket, or boost::asio::stream_descriptor with native handle 89 | */ 90 | template 91 | class async_stream : public std::iostream { 92 | public: 93 | typedef async_stream this_t; 94 | 95 | // Constructors 96 | 97 | /** 98 | * Construct async_stream with existing socket and yield context 99 | */ 100 | async_stream(StreamDescriptor &&socket, boost::asio::yield_context yield) 101 | : async_stream(make_streambuf(std::move(socket), yield)) 102 | {} 103 | 104 | /** 105 | * Construct async_stream by connecting to specific endpoint 106 | * 107 | * NOTE: Why variadic template arg can only be placed at the end? 108 | */ 109 | template 110 | async_stream(boost::asio::yield_context yield, 111 | const Args&... args) 112 | : async_stream(make_streambuf(yield, args...)) 113 | {} 114 | 115 | // Movable 116 | async_stream(async_stream &&src) 117 | : std::iostream(src.sbuf_.get()) 118 | , sbuf_(std::move(src.sbuf_)) 119 | {} 120 | 121 | // Non-copyable 122 | async_stream(const async_stream&) = delete; 123 | async_stream& operator=(const async_stream&) = delete; 124 | 125 | /** 126 | * Close underlying stream device, flushing if necessary 127 | */ 128 | inline void close() { 129 | if(sbuf_->is_open()) { 130 | flush(); 131 | } 132 | sbuf_->close(); 133 | } 134 | 135 | inline bool is_open() const 136 | { return sbuf_->is_open(); } 137 | 138 | inline boost::asio::yield_context yield_context() 139 | { return sbuf_->yield_; } 140 | 141 | inline StreamDescriptor &stream_descriptor() 142 | { return sbuf_->sd_; } 143 | 144 | // TODO: Find a proper way to retrieve strand object 145 | inline boost::asio::strand &strand() 146 | { return yield_context().handler_.dispatcher_; } 147 | 148 | inline boost::asio::io_service &io_service() 149 | { return yield_context().handler_.dispatcher_.get_io_service(); } 150 | 151 | template 152 | inline void spawn(BOOST_ASIO_MOVE_ARG(Function) function) { 153 | boost::asio::spawn(strand(), BOOST_ASIO_MOVE_CAST(Function)(function)); 154 | } 155 | 156 | inline int read_timeout() const 157 | { return sbuf_->read_timeout_; } 158 | 159 | inline int write_timeout() const 160 | { return sbuf_->write_timeout_; } 161 | 162 | inline void read_timeout(int timeout) 163 | { sbuf_->read_timeout_=timeout; } 164 | 165 | inline void write_timeout(int timeout) 166 | { sbuf_->write_timeout_=timeout; } 167 | 168 | private: 169 | class async_streambuf : public std::streambuf { 170 | public: 171 | typedef std::function timeout_callback_t; 172 | /** 173 | * Constructor 174 | * 175 | * @param sd underlying stream device, such as socket or pipe 176 | * @param yield the yield_context used by coroutines 177 | */ 178 | async_streambuf(StreamDescriptor &&sd, 179 | boost::asio::yield_context yield) 180 | : sd_(std::move(sd)) 181 | , yield_(yield) 182 | , buffer_in_() 183 | , buffer_out_() 184 | , read_timer_(io_service()) 185 | , write_timer_(io_service()) 186 | { 187 | setg(buffer_in_ + pb_size, 188 | buffer_in_ + pb_size, 189 | buffer_in_ + pb_size); 190 | setp(buffer_out_, 191 | buffer_out_ + bf_size - 1 ); 192 | } 193 | 194 | // Movable 195 | async_streambuf(async_streambuf &&src) 196 | : sd_(std::move(src.sd_)) 197 | , yield_(std::move(src.yield_)) 198 | , buffer_in_(std::move(src.buffer_in_)) 199 | , buffer_out_(std::move(src.buffer_out_)) 200 | , read_timer_(std::move(src.read_timer_)) 201 | , write_timer_(std::move(src.write_timer_)) 202 | {} 203 | 204 | // Non-copyable 205 | async_streambuf(const async_streambuf&) = delete; 206 | async_streambuf& operator=(const async_streambuf&) = delete; 207 | 208 | ~async_streambuf() 209 | { close(); } 210 | 211 | inline bool is_open() const 212 | { return sd_.is_open(); } 213 | 214 | inline void close() { 215 | if(sd_.is_open()) { 216 | boost::system::error_code ec; 217 | sd_.close(ec); 218 | } 219 | } 220 | 221 | protected: 222 | virtual int_type overflow(int_type c) { 223 | if ( c != traits_type::eof() ) { 224 | *pptr() = c; 225 | pbump(1); 226 | if (nudge_()==0) 227 | return c; 228 | } else { 229 | c = 0; 230 | } 231 | 232 | return traits_type::eof(); 233 | } 234 | 235 | virtual int_type underflow() { 236 | if ( gptr() < egptr() ) 237 | return traits_type::to_int_type( *gptr() ); 238 | 239 | if ( 0 > fetch_() ) 240 | return traits_type::eof(); 241 | else 242 | return traits_type::to_int_type( *gptr() ); 243 | } 244 | 245 | virtual int sync() 246 | { return nudge_()==0 ? 0 : -1; } 247 | 248 | virtual std::streamsize showmanyc() { 249 | if ( gptr() == egptr() ) { 250 | // Getting area is empty 251 | return fetch_(); 252 | } 253 | return egptr()-gptr(); 254 | } 255 | 256 | private: 257 | typedef boost::asio::steady_timer timer_t; 258 | 259 | inline boost::asio::strand &strand() 260 | { return yield_.handler_.dispatcher_; } 261 | 262 | inline boost::asio::io_service &io_service() 263 | { return yield_.handler_.dispatcher_.get_io_service(); } 264 | 265 | inline size_t async_read_some_with_timeout(boost::system::error_code &ec) { 266 | if (read_timeout_>0) { 267 | // Setup read timer 268 | read_timer_.expires_from_now(std::chrono::seconds(read_timeout_)); 269 | read_timer_.async_wait(strand().wrap(read_timeout_callback_)); 270 | size_t ret=sd_.async_read_some(boost::asio::buffer(buffer_in_ + pb_size, 271 | bf_size - pb_size), 272 | yield_[ec]); 273 | boost::system::error_code ec1; 274 | read_timer_.cancel(ec1); 275 | return ret; 276 | } else { 277 | return sd_.async_read_some(boost::asio::buffer(buffer_in_ + pb_size, 278 | bf_size - pb_size), 279 | yield_[ec]); 280 | } 281 | } 282 | 283 | inline void async_write_with_timeout(boost::system::error_code &ec) { 284 | if (write_timeout_>0) { 285 | // Setup write timer 286 | write_timer_.expires_from_now(std::chrono::seconds(write_timeout_)); 287 | write_timer_.async_wait(strand().wrap(write_timeout_callback_)); 288 | boost::asio::async_write(sd_, 289 | boost::asio::const_buffers_1(pbase(), 290 | pptr()-pbase()), 291 | yield_[ec]); 292 | boost::system::error_code ec1; 293 | write_timer_.cancel(ec1); 294 | } else { 295 | boost::asio::async_write(sd_, 296 | boost::asio::const_buffers_1(pbase(), 297 | pptr()-pbase()), 298 | yield_[ec]); 299 | } 300 | } 301 | 302 | inline int_type fetch_() { 303 | std::streamsize num = std::min(static_cast(gptr() - eback()), 304 | pb_size); 305 | 306 | std::memmove(buffer_in_ + (pb_size - num), 307 | gptr() - num, 308 | num); 309 | 310 | boost::system::error_code ec; 311 | std::size_t n = async_read_some_with_timeout(ec); 312 | if (ec) { 313 | setg(0, 0, 0); 314 | return -1; 315 | } 316 | setg(buffer_in_ + pb_size - num, 317 | buffer_in_ + pb_size, 318 | buffer_in_ + pb_size + n); 319 | return n; 320 | } 321 | 322 | inline int_type nudge_() { 323 | // Don't flush empty buffer 324 | if(pptr()<=pbase()) return 0; 325 | boost::system::error_code ec; 326 | async_write_with_timeout(ec); 327 | setp(buffer_out_, 328 | buffer_out_ + bf_size - 1); 329 | return ec ? traits_type::eof() : 0; 330 | } 331 | 332 | StreamDescriptor sd_; 333 | boost::asio::yield_context yield_; 334 | char buffer_in_[bf_size]; 335 | char buffer_out_[bf_size]; 336 | int read_timeout_=0; 337 | int write_timeout_=0; 338 | timer_t read_timer_; 339 | timer_t write_timer_; 340 | timeout_callback_t read_timeout_callback_; 341 | timeout_callback_t write_timeout_callback_; 342 | 343 | friend class async_stream; 344 | }; 345 | 346 | typedef std::shared_ptr streambuf_ptr; 347 | 348 | /** 349 | * Constructor 350 | * 351 | * @param sb a shared pointer to the async_streambuf 352 | */ 353 | async_stream(streambuf_ptr sb) 354 | : sbuf_(sb) 355 | , std::iostream(sb.get()) 356 | { setup_callback(); } 357 | 358 | static inline streambuf_ptr make_streambuf(StreamDescriptor &&socket, 359 | boost::asio::yield_context yield) 360 | { return std::make_shared(std::move(socket), yield); } 361 | 362 | template 363 | static inline streambuf_ptr make_streambuf(boost::asio::yield_context yield, 364 | const Args &... args) 365 | { 366 | endpoint_resolver resolver; 367 | StreamDescriptor s(yield.handler_.dispatcher_.get_io_service()); 368 | s.async_connect(resolver.resolve(args..., yield), yield); 369 | return std::make_shared(std::move(s), yield); 370 | } 371 | 372 | inline void setup_callback() { 373 | // NOTE: shared_ptr is needed here as the callback may happen *after* the destruction of async_stream 374 | // Cancelling in destructor is not reliable as the callback may already be posted in the queue hence not cancelable 375 | std::weak_ptr sbp(sbuf_); 376 | sbuf_->read_timeout_callback_=[sbp](boost::system::error_code error){ 377 | if (!error && !sbp.expired()) sbp.lock()->close(); 378 | }; 379 | sbuf_->write_timeout_callback_=[sbp](boost::system::error_code error){ 380 | if (!error && !sbp.expired()) sbp.lock()->close(); 381 | }; 382 | } 383 | 384 | streambuf_ptr sbuf_; 385 | }; 386 | 387 | typedef async_stream async_tcp_stream; 388 | } // End of namespace net 389 | 390 | #endif /* defined(async_stream_h_included) */ 391 | -------------------------------------------------------------------------------- /calculator.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // stream_calc.cpp 3 | // coroserver 4 | // 5 | // Created by Xu Chen on 13-2-24. 6 | // Copyright (c) 2013 Xu Chen. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include "calculator.h" 12 | 13 | namespace calculator { 14 | namespace details { 15 | typedef double value_t; 16 | 17 | /* 18 | * Calculator of value_t 19 | */ 20 | template 21 | struct calc_parser : boost::spirit::qi::grammar { 22 | calc_parser() : calc_parser::base_type(expression) { 23 | using boost::spirit::qi::double_; 24 | using boost::spirit::qi::_val; 25 | using boost::spirit::qi::_1; 26 | 27 | expression = term [_val=_1] 28 | >> *( ('+' >> term [_val=_val+_1]) 29 | | ('-' >> term [_val=_val-_1]) 30 | ) 31 | ; 32 | 33 | term = factor [_val=_1] 34 | >> *( ('*' >> factor [_val=_val*_1]) 35 | | ('/' >> factor [_val=_val/_1]) 36 | ) 37 | ; 38 | 39 | factor = double_ [_val=_1] 40 | | '(' >> expression [_val=_1] >> ')' 41 | | ('+' >> factor [_val=_1]) 42 | | ('-' >> factor [_val=-_1]) 43 | ; 44 | 45 | BOOST_SPIRIT_DEBUG_NODE(expression); 46 | BOOST_SPIRIT_DEBUG_NODE(term); 47 | BOOST_SPIRIT_DEBUG_NODE(factor); 48 | } 49 | 50 | boost::spirit::qi::rule expression; 51 | boost::spirit::qi::rule term; 52 | boost::spirit::qi::rule factor; 53 | }; 54 | } // End of namespace details 55 | 56 | void protocol_handler(net::async_tcp_stream &s) { 57 | s << "Hello!" << std::endl; 58 | 59 | using boost::spirit::ascii::space; 60 | typedef std::string::const_iterator iterator_type; 61 | 62 | details::calc_parser calc; 63 | std::string line; 64 | while (std::getline(s, line)) { 65 | boost::trim(line); 66 | if(line=="quit") break; 67 | iterator_type first=line.cbegin(); 68 | iterator_type last=line.cend(); 69 | details::value_t v; 70 | bool r=phrase_parse(first, last, calc, space, v); 71 | if (r && first==last) { 72 | s << v << std::endl; 73 | } else { 74 | s << "Parse error" << std::endl; 75 | } 76 | } 77 | s << "Bye!" << std::endl; 78 | } 79 | } // End of namespace calculator -------------------------------------------------------------------------------- /calculator.h: -------------------------------------------------------------------------------- 1 | // 2 | // stream_calc.h 3 | // coroserver 4 | // 5 | // Created by Xu Chen on 13-2-24. 6 | // Copyright (c) 2013 Xu Chen. All rights reserved. 7 | // 8 | 9 | #ifndef stream_calc_h_included 10 | #define stream_calc_h_included 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "async_stream.h" 19 | 20 | namespace calculator { 21 | /* 22 | * Line-oriented calculator protocol 23 | */ 24 | void protocol_handler(net::async_tcp_stream &s); 25 | } 26 | 27 | #endif /* defined(stream_calc_h_included) */ 28 | -------------------------------------------------------------------------------- /condition_variable.hpp: -------------------------------------------------------------------------------- 1 | /*////////////////////////////////////////////////////////////////////////////// 2 | Copyright (c) 2013 Jamboree 3 | 4 | Distributed under the Boost Software License, Version 1.0. (See accompanying 5 | file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | //////////////////////////////////////////////////////////////////////////////*/ 7 | #ifndef BOOST_ASIO_CONDITION_VARIABLE_HPP_INCLUDED 8 | #define BOOST_ASIO_CONDITION_VARIABLE_HPP_INCLUDED 9 | 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace boost { 16 | namespace asio { 17 | class condition_variable { 18 | struct handler_t { 19 | explicit handler_t(yield_context const ctx) 20 | : coro_(ctx.coro_) 21 | {} 22 | 23 | void operator()() { 24 | auto h(coro_.lock()); 25 | if(h) (*h)(); 26 | } 27 | 28 | detail::weak_ptr coro_; 29 | }; 30 | 31 | public: 32 | explicit condition_variable(yield_context ctx) 33 | : ctx_(ctx) 34 | {} 35 | 36 | /** 37 | * Wait for the condition variable 38 | * Will only be effective in the thread running the strand 39 | */ 40 | void wait() { 41 | if (get_strand().running_in_this_thread()) { 42 | suspended_.push_back(handler_t(ctx_)); 43 | ctx_.ca_(); 44 | } 45 | } 46 | 47 | /** 48 | * Notify one handler in the waiting queue 49 | * The notification procedure will be invoked in the strand 50 | */ 51 | void notify_one() { 52 | if (get_strand().running_in_this_thread()) { 53 | if (suspended_.empty()) return; 54 | get_strand().post(suspended_.front()); 55 | suspended_.pop_front(); 56 | } else { 57 | get_strand().post([this](){ 58 | if (suspended_.empty()) return; 59 | get_strand().post(suspended_.front()); 60 | suspended_.pop_front(); 61 | }); 62 | } 63 | } 64 | 65 | /** 66 | * Notify all handlers in the waiting queue 67 | * The notification procedure will be invoked in the strand 68 | */ 69 | void notify_all() { 70 | if (get_strand().running_in_this_thread()) { 71 | for (handler_t &h : suspended_) get_strand().post(h); 72 | suspended_.clear(); 73 | } else { 74 | get_strand().post([this](){ 75 | for (handler_t &h : suspended_) get_strand().post(h); 76 | suspended_.clear(); 77 | }); 78 | } 79 | } 80 | 81 | strand &get_strand() 82 | { return ctx_.handler_.dispatcher_; } 83 | 84 | yield_context get_yield_context() const 85 | { return ctx_; } 86 | 87 | private: 88 | boost::asio::yield_context ctx_; 89 | std::deque suspended_; 90 | }; 91 | 92 | struct condition_flag { 93 | explicit condition_flag(yield_context ctx) 94 | : b_() 95 | , cond_(ctx) 96 | {} 97 | 98 | void wait() 99 | { while (!b_) cond_.wait(); } 100 | 101 | condition_flag &operator=(bool b) { 102 | if ((b_ = b)==true) cond_.notify_all(); 103 | return *this; 104 | } 105 | 106 | private: 107 | bool b_; 108 | condition_variable cond_; 109 | }; 110 | } // End of namespace asio 111 | } // End of namespace boost 112 | #endif 113 | -------------------------------------------------------------------------------- /coroserver.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2D14BC951854BE5D00D3E709 /* calculator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2D14BC931854BE5D00D3E709 /* calculator.cpp */; }; 11 | 2D40A6D017E0B88B00282132 /* http_protocol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2D40A6CE17E0B88B00282132 /* http_protocol.cpp */; }; 12 | 2DB9AE6316DB01CF0001779C /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DB9AE5B16DB01CF0001779C /* main.cpp */; }; 13 | 2DB9AE6416DB01CF0001779C /* server.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DB9AE5C16DB01CF0001779C /* server.cpp */; }; 14 | 2DCE5ED71847272C009284E6 /* http_parser.c in Sources */ = {isa = PBXBuildFile; fileRef = 2DCE5ED51847272C009284E6 /* http_parser.c */; }; 15 | 2DCEAB5817E0722400204B89 /* libboost_context-mt.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DCEAB5517E0722400204B89 /* libboost_context-mt.a */; }; 16 | 2DCEAB5C17E0794C00204B89 /* libboost_coroutine-mt.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DCEAB5B17E0794C00204B89 /* libboost_coroutine-mt.a */; }; 17 | 2DCEAB5E17E0885C00204B89 /* libboost_system-mt.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DCEAB5D17E0885C00204B89 /* libboost_system-mt.a */; }; 18 | 2DD67738184C9FCC003AF10A /* routing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD67736184C9FCC003AF10A /* routing.cpp */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXCopyFilesBuildPhase section */ 22 | 2DB9AE4A16DB01AB0001779C /* CopyFiles */ = { 23 | isa = PBXCopyFilesBuildPhase; 24 | buildActionMask = 2147483647; 25 | dstPath = /usr/share/man/man1/; 26 | dstSubfolderSpec = 0; 27 | files = ( 28 | ); 29 | runOnlyForDeploymentPostprocessing = 1; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 2D14BC931854BE5D00D3E709 /* calculator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = calculator.cpp; sourceTree = ""; }; 35 | 2D14BC941854BE5D00D3E709 /* calculator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = calculator.h; sourceTree = ""; }; 36 | 2D40A6CE17E0B88B00282132 /* http_protocol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = http_protocol.cpp; sourceTree = ""; }; 37 | 2D40A6CF17E0B88B00282132 /* http_protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = http_protocol.h; sourceTree = ""; }; 38 | 2DABC3FD184A44E6003E683E /* condition_variable.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = condition_variable.hpp; sourceTree = ""; }; 39 | 2DB9AE4C16DB01AB0001779C /* coroserver */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = coroserver; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | 2DB9AE5A16DB01CF0001779C /* async_stream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = async_stream.h; sourceTree = ""; }; 41 | 2DB9AE5B16DB01CF0001779C /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 42 | 2DB9AE5C16DB01CF0001779C /* server.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = server.cpp; sourceTree = ""; }; 43 | 2DB9AE5D16DB01CF0001779C /* server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = server.h; sourceTree = ""; }; 44 | 2DB9AE6F16DB029D0001779C /* CMakeLists.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; 45 | 2DB9AE7016DB0A2D0001779C /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.md; sourceTree = ""; }; 46 | 2DB9AE7116DB0B310001779C /* .gitignore */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = .gitignore; sourceTree = ""; }; 47 | 2DCE5ED51847272C009284E6 /* http_parser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = http_parser.c; path = "http-parser/http_parser.c"; sourceTree = ""; }; 48 | 2DCE5ED61847272C009284E6 /* http_parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = http_parser.h; path = "http-parser/http_parser.h"; sourceTree = ""; }; 49 | 2DCEAB5517E0722400204B89 /* libboost_context-mt.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libboost_context-mt.a"; path = "/usr/local/lib/libboost_context-mt.a"; sourceTree = ""; }; 50 | 2DCEAB5B17E0794C00204B89 /* libboost_coroutine-mt.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libboost_coroutine-mt.a"; path = "/usr/local/lib/libboost_coroutine-mt.a"; sourceTree = ""; }; 51 | 2DCEAB5D17E0885C00204B89 /* libboost_system-mt.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libboost_system-mt.a"; path = "/usr/local/lib/libboost_system-mt.a"; sourceTree = ""; }; 52 | 2DD67736184C9FCC003AF10A /* routing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = routing.cpp; sourceTree = ""; }; 53 | 2DD67737184C9FCC003AF10A /* routing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = routing.h; sourceTree = ""; }; 54 | /* End PBXFileReference section */ 55 | 56 | /* Begin PBXFrameworksBuildPhase section */ 57 | 2DB9AE4916DB01AB0001779C /* Frameworks */ = { 58 | isa = PBXFrameworksBuildPhase; 59 | buildActionMask = 2147483647; 60 | files = ( 61 | 2DCEAB5E17E0885C00204B89 /* libboost_system-mt.a in Frameworks */, 62 | 2DCEAB5C17E0794C00204B89 /* libboost_coroutine-mt.a in Frameworks */, 63 | 2DCEAB5817E0722400204B89 /* libboost_context-mt.a in Frameworks */, 64 | ); 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | /* End PBXFrameworksBuildPhase section */ 68 | 69 | /* Begin PBXGroup section */ 70 | 2DB9AE4316DB01AB0001779C = { 71 | isa = PBXGroup; 72 | children = ( 73 | 2DCE5ED81847273F009284E6 /* http-parser */, 74 | 2DCEAB5D17E0885C00204B89 /* libboost_system-mt.a */, 75 | 2DCEAB5B17E0794C00204B89 /* libboost_coroutine-mt.a */, 76 | 2DCEAB5517E0722400204B89 /* libboost_context-mt.a */, 77 | 2DB9AE7116DB0B310001779C /* .gitignore */, 78 | 2DB9AE7016DB0A2D0001779C /* README.md */, 79 | 2DB9AE6F16DB029D0001779C /* CMakeLists.txt */, 80 | 2DB9AE6816DB01F80001779C /* include */, 81 | 2DB9AE6716DB01EA0001779C /* src */, 82 | 2DB9AE4D16DB01AB0001779C /* Products */, 83 | ); 84 | sourceTree = ""; 85 | }; 86 | 2DB9AE4D16DB01AB0001779C /* Products */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 2DB9AE4C16DB01AB0001779C /* coroserver */, 90 | ); 91 | name = Products; 92 | sourceTree = ""; 93 | }; 94 | 2DB9AE6716DB01EA0001779C /* src */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 2DB9AE5B16DB01CF0001779C /* main.cpp */, 98 | 2DB9AE5C16DB01CF0001779C /* server.cpp */, 99 | 2D14BC931854BE5D00D3E709 /* calculator.cpp */, 100 | 2D40A6CE17E0B88B00282132 /* http_protocol.cpp */, 101 | 2DD67736184C9FCC003AF10A /* routing.cpp */, 102 | ); 103 | name = src; 104 | sourceTree = ""; 105 | }; 106 | 2DB9AE6816DB01F80001779C /* include */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 2DB9AE5A16DB01CF0001779C /* async_stream.h */, 110 | 2DABC3FD184A44E6003E683E /* condition_variable.hpp */, 111 | 2DB9AE5D16DB01CF0001779C /* server.h */, 112 | 2D14BC941854BE5D00D3E709 /* calculator.h */, 113 | 2D40A6CF17E0B88B00282132 /* http_protocol.h */, 114 | 2DD67737184C9FCC003AF10A /* routing.h */, 115 | ); 116 | name = include; 117 | sourceTree = ""; 118 | }; 119 | 2DCE5ED81847273F009284E6 /* http-parser */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 2DCE5ED51847272C009284E6 /* http_parser.c */, 123 | 2DCE5ED61847272C009284E6 /* http_parser.h */, 124 | ); 125 | name = "http-parser"; 126 | sourceTree = ""; 127 | }; 128 | /* End PBXGroup section */ 129 | 130 | /* Begin PBXNativeTarget section */ 131 | 2DB9AE4B16DB01AB0001779C /* coroserver */ = { 132 | isa = PBXNativeTarget; 133 | buildConfigurationList = 2DB9AE5516DB01AB0001779C /* Build configuration list for PBXNativeTarget "coroserver" */; 134 | buildPhases = ( 135 | 2DB9AE4816DB01AB0001779C /* Sources */, 136 | 2DB9AE4916DB01AB0001779C /* Frameworks */, 137 | 2DB9AE4A16DB01AB0001779C /* CopyFiles */, 138 | ); 139 | buildRules = ( 140 | ); 141 | dependencies = ( 142 | ); 143 | name = coroserver; 144 | productName = coroserver; 145 | productReference = 2DB9AE4C16DB01AB0001779C /* coroserver */; 146 | productType = "com.apple.product-type.tool"; 147 | }; 148 | /* End PBXNativeTarget section */ 149 | 150 | /* Begin PBXProject section */ 151 | 2DB9AE4416DB01AB0001779C /* Project object */ = { 152 | isa = PBXProject; 153 | attributes = { 154 | LastUpgradeCheck = 0500; 155 | ORGANIZATIONNAME = 0d0a.com; 156 | }; 157 | buildConfigurationList = 2DB9AE4716DB01AB0001779C /* Build configuration list for PBXProject "coroserver" */; 158 | compatibilityVersion = "Xcode 3.2"; 159 | developmentRegion = English; 160 | hasScannedForEncodings = 0; 161 | knownRegions = ( 162 | en, 163 | ); 164 | mainGroup = 2DB9AE4316DB01AB0001779C; 165 | productRefGroup = 2DB9AE4D16DB01AB0001779C /* Products */; 166 | projectDirPath = ""; 167 | projectRoot = ""; 168 | targets = ( 169 | 2DB9AE4B16DB01AB0001779C /* coroserver */, 170 | ); 171 | }; 172 | /* End PBXProject section */ 173 | 174 | /* Begin PBXSourcesBuildPhase section */ 175 | 2DB9AE4816DB01AB0001779C /* Sources */ = { 176 | isa = PBXSourcesBuildPhase; 177 | buildActionMask = 2147483647; 178 | files = ( 179 | 2DCE5ED71847272C009284E6 /* http_parser.c in Sources */, 180 | 2DB9AE6316DB01CF0001779C /* main.cpp in Sources */, 181 | 2D14BC951854BE5D00D3E709 /* calculator.cpp in Sources */, 182 | 2DD67738184C9FCC003AF10A /* routing.cpp in Sources */, 183 | 2DB9AE6416DB01CF0001779C /* server.cpp in Sources */, 184 | 2D40A6D017E0B88B00282132 /* http_protocol.cpp in Sources */, 185 | ); 186 | runOnlyForDeploymentPostprocessing = 0; 187 | }; 188 | /* End PBXSourcesBuildPhase section */ 189 | 190 | /* Begin XCBuildConfiguration section */ 191 | 2DB9AE5316DB01AB0001779C /* Debug */ = { 192 | isa = XCBuildConfiguration; 193 | buildSettings = { 194 | ALWAYS_SEARCH_USER_PATHS = NO; 195 | CLANG_CXX_LANGUAGE_STANDARD = "c++11"; 196 | CLANG_CXX_LIBRARY = "libc++"; 197 | CLANG_WARN_CONSTANT_CONVERSION = YES; 198 | CLANG_WARN_EMPTY_BODY = YES; 199 | CLANG_WARN_ENUM_CONVERSION = YES; 200 | CLANG_WARN_INT_CONVERSION = YES; 201 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 202 | COPY_PHASE_STRIP = NO; 203 | GCC_C_LANGUAGE_STANDARD = gnu99; 204 | GCC_DYNAMIC_NO_PIC = NO; 205 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 206 | GCC_OPTIMIZATION_LEVEL = 0; 207 | GCC_PREPROCESSOR_DEFINITIONS = ( 208 | "DEBUG=1", 209 | "$(inherited)", 210 | ); 211 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 212 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 213 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 214 | GCC_WARN_UNUSED_VARIABLE = YES; 215 | HEADER_SEARCH_PATHS = /usr/local/include; 216 | ONLY_ACTIVE_ARCH = YES; 217 | OTHER_CPLUSPLUSFLAGS = ( 218 | "$(OTHER_CFLAGS)", 219 | "-ftemplate-depth=256", 220 | ); 221 | }; 222 | name = Debug; 223 | }; 224 | 2DB9AE5416DB01AB0001779C /* Release */ = { 225 | isa = XCBuildConfiguration; 226 | buildSettings = { 227 | ALWAYS_SEARCH_USER_PATHS = NO; 228 | CLANG_CXX_LANGUAGE_STANDARD = "c++11"; 229 | CLANG_CXX_LIBRARY = "libc++"; 230 | CLANG_WARN_CONSTANT_CONVERSION = YES; 231 | CLANG_WARN_EMPTY_BODY = YES; 232 | CLANG_WARN_ENUM_CONVERSION = YES; 233 | CLANG_WARN_INT_CONVERSION = YES; 234 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 235 | COPY_PHASE_STRIP = YES; 236 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 237 | GCC_C_LANGUAGE_STANDARD = gnu99; 238 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 239 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 240 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 241 | GCC_WARN_UNUSED_VARIABLE = YES; 242 | HEADER_SEARCH_PATHS = /usr/local/include; 243 | OTHER_CPLUSPLUSFLAGS = ( 244 | "$(OTHER_CFLAGS)", 245 | "-ftemplate-depth=256", 246 | ); 247 | }; 248 | name = Release; 249 | }; 250 | 2DB9AE5616DB01AB0001779C /* Debug */ = { 251 | isa = XCBuildConfiguration; 252 | buildSettings = { 253 | LIBRARY_SEARCH_PATHS = ( 254 | /usr/local/lib, 255 | /usr/local/Cellar/boost/1.55.0/lib, 256 | ); 257 | PRODUCT_NAME = "$(TARGET_NAME)"; 258 | }; 259 | name = Debug; 260 | }; 261 | 2DB9AE5716DB01AB0001779C /* Release */ = { 262 | isa = XCBuildConfiguration; 263 | buildSettings = { 264 | LIBRARY_SEARCH_PATHS = ( 265 | /usr/local/lib, 266 | /usr/local/Cellar/boost/1.55.0/lib, 267 | ); 268 | PRODUCT_NAME = "$(TARGET_NAME)"; 269 | }; 270 | name = Release; 271 | }; 272 | /* End XCBuildConfiguration section */ 273 | 274 | /* Begin XCConfigurationList section */ 275 | 2DB9AE4716DB01AB0001779C /* Build configuration list for PBXProject "coroserver" */ = { 276 | isa = XCConfigurationList; 277 | buildConfigurations = ( 278 | 2DB9AE5316DB01AB0001779C /* Debug */, 279 | 2DB9AE5416DB01AB0001779C /* Release */, 280 | ); 281 | defaultConfigurationIsVisible = 0; 282 | defaultConfigurationName = Release; 283 | }; 284 | 2DB9AE5516DB01AB0001779C /* Build configuration list for PBXNativeTarget "coroserver" */ = { 285 | isa = XCConfigurationList; 286 | buildConfigurations = ( 287 | 2DB9AE5616DB01AB0001779C /* Debug */, 288 | 2DB9AE5716DB01AB0001779C /* Release */, 289 | ); 290 | defaultConfigurationIsVisible = 0; 291 | defaultConfigurationName = Release; 292 | }; 293 | /* End XCConfigurationList section */ 294 | }; 295 | rootObject = 2DB9AE4416DB01AB0001779C /* Project object */; 296 | } 297 | -------------------------------------------------------------------------------- /http_protocol.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // http_protocol.cpp 3 | // coroserver 4 | // 5 | // Created by Windoze on 13-9-11. 6 | // Copyright (c) 2013 0d0a.com. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | #include "http-parser/http_parser.h" 13 | #include "http_protocol.h" 14 | 15 | namespace http { 16 | const char *server_name=HTTP_SERVER_NAME "/" HTTP_SERVER_VERSION; 17 | typedef std::function parse_callback_t; 18 | 19 | namespace details { 20 | const std::map status_code_msg_map={ 21 | {CONTINUE , "Continue"}, 22 | {SWITCHING_PROTOCOLS , "Switching Protocols"}, 23 | {OK , "OK"}, 24 | {CREATED , "Created"}, 25 | {ACCEPTED , "Accepted"}, 26 | {NON_AUTHORITATIVE_INFORMATION , "Non-Authoritative Information"}, 27 | {NO_CONTENT , "No Content"}, 28 | {RESET_CONTENT , "Reset Content"}, 29 | {PARTIAL_CONTENT , "Partial Content"}, 30 | {MULTIPLE_CHOICES , "Multiple Choices"}, 31 | {MOVED_PERMANENTLY , "Moved Permanently"}, 32 | {FOUND , "Found"}, 33 | {SEE_OTHER , "See Other"}, 34 | {NOT_MODIFIED , "Not Modified"}, 35 | {USE_PROXY , "Use Proxy"}, 36 | {TEMPORARY_REDIRECT , "Temporary Redirect"}, 37 | {BAD_REQUEST , "Bad Request"}, 38 | {UNAUTHORIZED , "Unauthorized"}, 39 | {PAYMENT_REQUIRED , "Payment Required"}, 40 | {FORBIDDEN , "Forbidden"}, 41 | {NOT_FOUND , "Not Found"}, 42 | {METHOD_NOT_ALLOWED , "Method Not Allowed"}, 43 | {NOT_ACCEPTABLE , "Not Acceptable"}, 44 | {PROXY_AUTHENTICATION_REQUIRED , "Proxy Authentication Required"}, 45 | {REQUEST_TIMEOUT , "Request Timeout"}, 46 | {CONFLICT , "Conflict"}, 47 | {GONE , "Gone"}, 48 | {LENGTH_REQUIRED , "Length Required"}, 49 | {PRECONDITION_FAILED , "Precondition Failed"}, 50 | {REQUEST_ENTITY_TOO_LARGE , "Request Entity Too Large"}, 51 | {REQUEST_URI_TOO_LONG , "Request-URI Too Long"}, 52 | {UNSUPPORTED_MEDIA_TYPE , "Unsupported Media Type"}, 53 | {REQUESTED_RANGE_NOT_SATISFIABLE, "Requested Range Not Satisfiable"}, 54 | {EXPECTATION_FAILED , "Expectation Failed"}, 55 | {INTERNAL_SERVER_ERROR , "Internal Server Error"}, 56 | {NOT_IMPLEMENTED , "Not Implemented"}, 57 | {BAD_GATEWAY , "Bad Gateway"}, 58 | {SERVICE_UNAVAILABLE , "Service Unavailable"}, 59 | {GATEWAY_TIMEOUT , "Gateway Timeout"}, 60 | {HTTP_VERSION_NOT_SUPPORTED , "HTTP Version Not Supported"}, 61 | }; 62 | 63 | const std::map method_name_map={ 64 | {DELETE, "DELETE"}, 65 | {GET, "GET"}, 66 | {HEAD, "HEAD"}, 67 | {POST, "POST"}, 68 | {PUT, "PUT"}, 69 | /* pathological */ 70 | {CONNECT, "CONNECT"}, 71 | {OPTIONS, "OPTIONS"}, 72 | {TRACE, "TRACE"}, 73 | /* webdav */ 74 | {COPY, "COPY"}, 75 | {LOCK, "LOCK"}, 76 | {MKCOL, "MKCOL"}, 77 | {MOVE, "MOVE"}, 78 | {PROPFIND, "PROPFIND"}, 79 | {PROPPATCH, "PROPPATCH"}, 80 | {SEARCH, "SEARCH"}, 81 | {UNLOCK, "UNLOCK"}, 82 | /* subversion */ 83 | {REPORT, "REPORT"}, 84 | {MKACTIVITY, "MKACTIVITY"}, 85 | {CHECKOUT, "CHECKOUT"}, 86 | {MERGE, "MERGE"}, 87 | /* upnp */ 88 | {MSEARCH, "MSEARCH"}, 89 | {NOTIFY, "NOTIFY"}, 90 | {SUBSCRIBE, "SUBSCRIBE"}, 91 | {UNSUBSCRIBE, "UNSUBSCRIBE"}, 92 | /* RFC-5789 */ 93 | {PATCH, "PATCH"}, 94 | {PURGE, "PURGE"}, 95 | }; 96 | 97 | namespace request { 98 | struct parser { 99 | enum parser_state{ 100 | none, 101 | start, 102 | url, 103 | field, 104 | value, 105 | body, 106 | end 107 | }; 108 | 109 | request_t &req() { return session_.request(); } 110 | 111 | int on_message_begin() { 112 | req().clear(); 113 | url_.clear(); 114 | state_=start; 115 | return 0; 116 | } 117 | int on_url(const char *at, size_t length) { 118 | if(state_==url) 119 | url_.append(at, length); 120 | else { 121 | url_.reserve(1024); 122 | url_.assign(at, length); 123 | } 124 | state_=url; 125 | return 0; 126 | } 127 | int on_status_complete() { 128 | return 0; 129 | } 130 | int on_header_field(const char *at, size_t length) { 131 | if (state_==field) { 132 | req().headers().rbegin()->first.append(at, length); 133 | } else { 134 | req().headers().push_back(header_t()); 135 | req().headers().rbegin()->first.reserve(256); 136 | req().headers().rbegin()->first.assign(at, length); 137 | } 138 | state_=field; 139 | return 0; 140 | } 141 | int on_header_value(const char *at, size_t length) { 142 | if (state_==value) 143 | req().headers().rbegin()->second.append(at, length); 144 | else { 145 | req().headers().rbegin()->second.reserve(256); 146 | req().headers().rbegin()->second.assign(at, length); 147 | } 148 | state_=value; 149 | return 0; 150 | } 151 | int on_headers_complete() { 152 | return 0; 153 | } 154 | int on_body(const char *at, size_t length) { 155 | req().body_stream().write(at, length); 156 | state_=body; 157 | return 0; 158 | } 159 | int on_message_complete() { 160 | state_=end; 161 | req().method((method)(parser_.method)); 162 | req().http_major(parser_.http_major); 163 | req().http_minor(parser_.http_minor); 164 | http_parser_url u; 165 | http_parser_parse_url(url_.c_str(), 166 | url_.size(), 167 | req().method()==CONNECT, 168 | &u); 169 | // Components for proxy requests 170 | // NOTE: Schema, user info, host, and port may only exist in proxy requests 171 | if(u.field_set & 1 << UF_SCHEMA) { 172 | req().schema(std::string(url_.begin()+u.field_data[UF_SCHEMA].off, 173 | url_.begin()+u.field_data[UF_SCHEMA].off+u.field_data[UF_SCHEMA].len)); 174 | } 175 | if(u.field_set & 1 << UF_USERINFO) { 176 | req().user_info(std::string(url_.begin()+u.field_data[UF_USERINFO].off, 177 | url_.begin()+u.field_data[UF_USERINFO].off+u.field_data[UF_USERINFO].len)); 178 | } 179 | if(u.field_set & 1 << UF_HOST) { 180 | req().host(std::string(url_.begin()+u.field_data[UF_HOST].off, 181 | url_.begin()+u.field_data[UF_HOST].off+u.field_data[UF_HOST].len)); 182 | } 183 | if(u.field_set & 1 << UF_PORT) { 184 | req().port(u.port); 185 | } else { 186 | req().port(0); 187 | } 188 | // Common components 189 | if(u.field_set & 1 << UF_PATH) { 190 | req().path(std::string(url_.begin()+u.field_data[UF_PATH].off, 191 | url_.begin()+u.field_data[UF_PATH].off+u.field_data[UF_PATH].len)); 192 | } 193 | if(u.field_set & 1 << UF_QUERY) { 194 | req().query(std::string(url_.begin()+u.field_data[UF_QUERY].off, 195 | url_.begin()+u.field_data[UF_QUERY].off+u.field_data[UF_QUERY].len)); 196 | } 197 | req().keep_alive(http_should_keep_alive(&parser_)); 198 | return (should_continue_=cb_(session_)) ? 0 : -1; 199 | } 200 | 201 | http_parser parser_; 202 | std::string url_; 203 | session_t &session_; 204 | parse_callback_t &cb_; 205 | parser_state state_; 206 | bool should_continue_; 207 | 208 | parser(session_t &session, parse_callback_t &cb); 209 | bool parse(); 210 | }; 211 | 212 | static int on_message_begin(http_parser*p) { 213 | return reinterpret_cast(p->data)->on_message_begin(); 214 | } 215 | static int on_url(http_parser*p, const char *at, size_t length) { 216 | return reinterpret_cast(p->data)->on_url(at, length); 217 | } 218 | static int on_status_complete(http_parser*p) { 219 | return reinterpret_cast(p->data)->on_status_complete(); 220 | } 221 | static int on_header_field(http_parser*p, const char *at, size_t length) { 222 | return reinterpret_cast(p->data)->on_header_field(at, length); 223 | } 224 | static int on_header_value(http_parser*p, const char *at, size_t length) { 225 | return reinterpret_cast(p->data)->on_header_value(at, length); 226 | } 227 | static int on_headers_complete(http_parser*p) { 228 | return reinterpret_cast(p->data)->on_headers_complete(); 229 | } 230 | static int on_body(http_parser*p, const char *at, size_t length) { 231 | return reinterpret_cast(p->data)->on_body(at, length); 232 | } 233 | static int on_message_complete(http_parser*p) { 234 | return reinterpret_cast(p->data)->on_message_complete(); 235 | } 236 | 237 | static constexpr http_parser_settings settings_={ 238 | &on_message_begin, 239 | &on_url, 240 | &on_status_complete, 241 | &on_header_field, 242 | &on_header_value, 243 | &on_headers_complete, 244 | &on_body, 245 | &on_message_complete, 246 | }; 247 | 248 | parser::parser(session_t &session, parse_callback_t &cb) 249 | : session_(session) 250 | , cb_(cb) 251 | { 252 | parser_.data=reinterpret_cast(this); 253 | http_parser_init(&parser_, HTTP_REQUEST); 254 | } 255 | 256 | bool parser::parse() { 257 | should_continue_=true; 258 | state_=none; 259 | constexpr int buf_size=1024; 260 | char buf[buf_size]; 261 | int recved=0; 262 | int nparsed=0; 263 | while (session_.raw_stream()) { 264 | // Read some data 265 | recved = session_.raw_stream().readsome(buf, buf_size); 266 | if (recved<=0) { 267 | // Connection closed 268 | return true; 269 | } 270 | nparsed=http_parser_execute(&parser_, &settings_, buf, recved); 271 | if (!should_continue_) { 272 | break; 273 | } 274 | if (nparsed!=recved) { 275 | // Parse error 276 | return false; 277 | } 278 | } 279 | return true; 280 | } 281 | } // End of namespace request 282 | namespace response { 283 | struct parser { 284 | enum parser_state{ 285 | none, 286 | start, 287 | status, 288 | field, 289 | value, 290 | body, 291 | end 292 | }; 293 | 294 | response_t &resp() { return response_; } 295 | 296 | int on_message_begin() { 297 | resp().clear(); 298 | state_=start; 299 | return 0; 300 | } 301 | int on_url(const char *at, size_t length) { 302 | return 0; 303 | } 304 | int on_status_complete() { 305 | return 0; 306 | } 307 | int on_header_field(const char *at, size_t length) { 308 | if (state_==field) { 309 | resp().headers().rbegin()->first.append(at, length); 310 | } else { 311 | resp().headers().push_back(header_t()); 312 | resp().headers().rbegin()->first.reserve(256); 313 | resp().headers().rbegin()->first.assign(at, length); 314 | } 315 | state_=field; 316 | return 0; 317 | } 318 | int on_header_value(const char *at, size_t length) { 319 | if (state_==value) 320 | resp().headers().rbegin()->second.append(at, length); 321 | else { 322 | resp().headers().rbegin()->second.reserve(256); 323 | resp().headers().rbegin()->second.assign(at, length); 324 | } 325 | state_=value; 326 | return 0; 327 | } 328 | int on_headers_complete() { 329 | return 0; 330 | } 331 | int on_body(const char *at, size_t length) { 332 | resp().body_stream().write(at, length); 333 | state_=body; 334 | return 0; 335 | } 336 | int on_message_complete() { 337 | state_=end; 338 | resp().http_major(parser_.http_major); 339 | resp().http_minor(parser_.http_minor); 340 | resp().code(status_code(parser_.status_code)); 341 | resp().keep_alive(http_should_keep_alive(&parser_)); 342 | should_continue_=false; 343 | return 0; 344 | } 345 | 346 | std::istream &is_; 347 | response_t &response_; 348 | http_parser parser_; 349 | std::string url_; 350 | parser_state state_; 351 | bool should_continue_; 352 | 353 | parser(std::istream &is, response_t &resp); 354 | bool parse(); 355 | }; 356 | 357 | static int on_message_begin(http_parser*p) { 358 | return reinterpret_cast(p->data)->on_message_begin(); 359 | } 360 | static int on_url(http_parser*p, const char *at, size_t length) { 361 | return reinterpret_cast(p->data)->on_url(at, length); 362 | } 363 | static int on_status_complete(http_parser*p) { 364 | return reinterpret_cast(p->data)->on_status_complete(); 365 | } 366 | static int on_header_field(http_parser*p, const char *at, size_t length) { 367 | return reinterpret_cast(p->data)->on_header_field(at, length); 368 | } 369 | static int on_header_value(http_parser*p, const char *at, size_t length) { 370 | return reinterpret_cast(p->data)->on_header_value(at, length); 371 | } 372 | static int on_headers_complete(http_parser*p) { 373 | return reinterpret_cast(p->data)->on_headers_complete(); 374 | } 375 | static int on_body(http_parser*p, const char *at, size_t length) { 376 | return reinterpret_cast(p->data)->on_body(at, length); 377 | } 378 | static int on_message_complete(http_parser*p) { 379 | return reinterpret_cast(p->data)->on_message_complete(); 380 | } 381 | 382 | static constexpr http_parser_settings settings_={ 383 | &on_message_begin, 384 | &on_url, 385 | &on_status_complete, 386 | &on_header_field, 387 | &on_header_value, 388 | &on_headers_complete, 389 | &on_body, 390 | &on_message_complete, 391 | }; 392 | 393 | parser::parser(std::istream &is, response_t &resp) 394 | : is_(is) 395 | , response_(resp) 396 | { 397 | parser_.data=reinterpret_cast(this); 398 | http_parser_init(&parser_, HTTP_RESPONSE); 399 | } 400 | 401 | bool parser::parse() { 402 | should_continue_=true; 403 | state_=none; 404 | constexpr int buf_size=1024; 405 | char buf[buf_size]; 406 | int recved=0; 407 | int nparsed=0; 408 | while (is_) { 409 | // Read some data 410 | recved = is_.readsome(buf, buf_size); 411 | if (recved<=0) { 412 | // Connection closed 413 | return true; 414 | } 415 | nparsed=http_parser_execute(&parser_, &settings_, buf, recved); 416 | if (!should_continue_) { 417 | // TODO: Should I put extra data back into stream? 418 | break; 419 | } 420 | if (nparsed!=recved) { 421 | // Parse error 422 | return false; 423 | } 424 | } 425 | return true; 426 | } 427 | } // End of namespace response 428 | } // End of namespace details 429 | 430 | void request_t::clear() { 431 | http_major_=0; 432 | http_minor_=0; 433 | method_=GET; 434 | schema_.clear(); 435 | user_info_.clear(); 436 | host_.clear(); 437 | port_=0; 438 | path_.clear(); 439 | query_.clear(); 440 | headers_.clear(); 441 | keep_alive_=false; 442 | // NOTE: Why there is no clear() in ovectorstream? 443 | if (!body().empty()) { 444 | std::string empty; 445 | body_stream().swap_vector(empty); 446 | } 447 | } 448 | 449 | void response_t::clear() { 450 | http_major_=0; 451 | http_minor_=0; 452 | code_=OK; 453 | status_message_.clear(); 454 | headers_.clear(); 455 | keep_alive_=false; 456 | // NOTE: Why there is no clear() in ovectorstream? 457 | if (!body().empty()) { 458 | std::string empty; 459 | body_stream().swap_vector(empty); 460 | } 461 | } 462 | 463 | bool parse_request(session_t &session, parse_callback_t &req_cb) { 464 | details::request::parser p(session, req_cb); 465 | return p.parse(); 466 | } 467 | 468 | bool request_callback(session_t &session, request_handler_t &handler) { 469 | bool ret=true; 470 | try { 471 | session.response().clear(); 472 | // Returning false from handle_request indicates the handler doesn't want the connection to keep alive 473 | ret = handler(session) && session.keep_alive(); 474 | } catch(...) { 475 | session.raw_stream() << "HTTP/1.1 500 Internal Server Error\r\n"; 476 | } 477 | 478 | if (session.raw()) { 479 | // Do nothing here 480 | // Handler handles whole HTTP response by itself, include status, headers, and body 481 | } else { 482 | if (ret) { 483 | char buf[100]; 484 | if (session.max_keepalive()!=0) { 485 | if (session.max_keepalive()>session.count()) { 486 | // Keep-alive in progress 487 | session.response().headers().push_back({"Connection", "keep-alive"}); 488 | sprintf(buf, "timeout=%d, max=%d", session.read_timeout(), session.max_keepalive()-session.count()); 489 | session.response().headers().push_back({"Keep-Alive", buf}); 490 | } else { 491 | // Max limit reached, stop keeping alive 492 | session.response().headers().push_back({"Connection", "close"}); 493 | ret=false; 494 | } 495 | } else { 496 | // Limitless keep-alive 497 | session.response().headers().push_back({"Connection", "keep-alive"}); 498 | sprintf(buf, "timeout=%d", session.read_timeout()); 499 | session.response().headers().push_back({"Keep-Alive", buf}); 500 | } 501 | } else { 502 | session.response().headers().push_back({"Connection", "close"}); 503 | ret=false; 504 | } 505 | session.raw_stream() << session.response(); 506 | } 507 | session.raw_stream().flush(); 508 | session.inc_count(); 509 | return ret; 510 | } 511 | 512 | std::ostream &operator<<(std::ostream &s, response_t &resp) { 513 | std::map::const_iterator i=details::status_code_msg_map.find(resp.code()); 514 | if (i==details::status_code_msg_map.end() && resp.status_message().empty()) { 515 | // Unknown HTTP status code 516 | s << "HTTP/1.1 500 Internal Server Error\r\n"; 517 | } else { 518 | char buf[100]; 519 | sprintf(buf, "%lu", resp.body().size()); 520 | 521 | s << "HTTP/1.1 " << resp.code() << ' '; 522 | if (!resp.status_message().empty()) { 523 | // Supplied status message 524 | s << resp.status_message(); 525 | } else { 526 | s << i->second << "\r\n"; 527 | } 528 | 529 | bool server_found=false; 530 | bool content_len_found=false; 531 | for (auto &i : resp.headers()) { 532 | s << i.first << ": " << i.second << "\r\n"; 533 | if (boost::algorithm::iequals(i.first, "server")) server_found=true; 534 | if (boost::algorithm::iequals(i.first, "content-length")) content_len_found=true; 535 | } 536 | if (!server_found) s << "Server: " << server_name << "\r\n"; 537 | if (!content_len_found) s << "Content-Length: " << buf << "\r\n"; 538 | s << "\r\n" << resp.body(); 539 | } 540 | return s; 541 | } 542 | 543 | // Client side 544 | 545 | // Send request 546 | std::ostream &operator<<(std::ostream &s, request_t &req) { 547 | std::map::const_iterator i=details::method_name_map.find(req.method()); 548 | if (i==details::method_name_map.end()) { 549 | // Unknow method 550 | return s; 551 | } 552 | 553 | s << i->second << ' ' << req.path(); 554 | if (!req.query().empty()) { 555 | s << '?' << req.query(); 556 | } 557 | s << " HTTP/" << req.http_major() << '.' << req.http_minor() << "\r\n"; 558 | for (header_t &h : req.headers()) { 559 | s << h.first << ": " << h.second << "\r\n"; 560 | } 561 | s << "Content-Length: " << req.body().size() << "\r\n"; 562 | if (req.keep_alive()) { 563 | s << "Connection: keep-alive\r\n"; 564 | } else { 565 | s << "Connection: close\r\n"; 566 | } 567 | s << "\r\n"; 568 | if (!req.body().empty()) { 569 | s << req.body(); 570 | } 571 | s.flush(); 572 | return s; 573 | } 574 | 575 | // Parse response 576 | bool parse_response(std::istream &is, response_t &resp) { 577 | details::response::parser p(is, resp); 578 | return p.parse(); 579 | } 580 | } // End of namespace http 581 | 582 | -------------------------------------------------------------------------------- /http_protocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // http_protocol.h 3 | // coroserver 4 | // 5 | // Created by Windoze on 13-9-11. 6 | // Copyright (c) 2013 0d0a.com. All rights reserved. 7 | // 8 | 9 | #ifndef __coroserver__http_protocol__ 10 | #define __coroserver__http_protocol__ 11 | 12 | #define HTTP_SERVER_NAME "coroserver" 13 | #define HTTP_SERVER_VERSION "0.1" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "async_stream.h" 23 | 24 | namespace http { 25 | extern const char *server_name; 26 | 27 | enum method { 28 | DELETE, 29 | GET, 30 | HEAD, 31 | POST, 32 | PUT, 33 | /* pathological */ 34 | CONNECT, 35 | OPTIONS, 36 | TRACE, 37 | /* webdav */ 38 | COPY, 39 | LOCK, 40 | MKCOL, 41 | MOVE, 42 | PROPFIND, 43 | PROPPATCH, 44 | SEARCH, 45 | UNLOCK, 46 | /* subversion */ 47 | REPORT, 48 | MKACTIVITY, 49 | CHECKOUT, 50 | MERGE, 51 | /* upnp */ 52 | MSEARCH, 53 | NOTIFY, 54 | SUBSCRIBE, 55 | UNSUBSCRIBE, 56 | /* RFC-5789 */ 57 | PATCH, 58 | PURGE, 59 | }; 60 | 61 | enum status_code { 62 | CONTINUE =100, 63 | SWITCHING_PROTOCOLS =101, 64 | OK =200, 65 | CREATED =201, 66 | ACCEPTED =202, 67 | NON_AUTHORITATIVE_INFORMATION =203, 68 | NO_CONTENT =204, 69 | RESET_CONTENT =205, 70 | PARTIAL_CONTENT =206, 71 | MULTIPLE_CHOICES =300, 72 | MOVED_PERMANENTLY =301, 73 | FOUND =302, 74 | SEE_OTHER =303, 75 | NOT_MODIFIED =304, 76 | USE_PROXY =305, 77 | //UNUSED =306, 78 | TEMPORARY_REDIRECT =307, 79 | BAD_REQUEST =400, 80 | UNAUTHORIZED =401, 81 | PAYMENT_REQUIRED =402, 82 | FORBIDDEN =403, 83 | NOT_FOUND =404, 84 | METHOD_NOT_ALLOWED =405, 85 | NOT_ACCEPTABLE =406, 86 | PROXY_AUTHENTICATION_REQUIRED =407, 87 | REQUEST_TIMEOUT =408, 88 | CONFLICT =409, 89 | GONE =410, 90 | LENGTH_REQUIRED =411, 91 | PRECONDITION_FAILED =412, 92 | REQUEST_ENTITY_TOO_LARGE =413, 93 | REQUEST_URI_TOO_LONG =414, 94 | UNSUPPORTED_MEDIA_TYPE =415, 95 | REQUESTED_RANGE_NOT_SATISFIABLE =416, 96 | EXPECTATION_FAILED =417, 97 | INTERNAL_SERVER_ERROR =500, 98 | NOT_IMPLEMENTED =501, 99 | BAD_GATEWAY =502, 100 | SERVICE_UNAVAILABLE =503, 101 | GATEWAY_TIMEOUT =504, 102 | HTTP_VERSION_NOT_SUPPORTED =505, 103 | }; 104 | 105 | typedef boost::interprocess::basic_ovectorstream body_stream_t; 106 | typedef std::pair header_t; 107 | typedef std::vector headers_t; 108 | 109 | inline headers_t::const_iterator find_header(const headers_t &headers, const std::string &key, bool case_sensitive=false) { 110 | for (headers_t::const_iterator i=headers.begin(); i!=headers.end(); ++i) { 111 | if (case_sensitive) { 112 | if (boost::algorithm::equals(i->first, key)) { 113 | return i; 114 | } 115 | } else { 116 | if (boost::algorithm::iequals(i->first, key)) { 117 | return i; 118 | } 119 | } 120 | } 121 | return headers.end(); 122 | } 123 | 124 | struct request_t { 125 | /** 126 | * Clear request 127 | */ 128 | void clear(); 129 | 130 | /** 131 | * HTTP Major version 132 | */ 133 | short http_major() const 134 | { return http_major_; } 135 | 136 | void http_major(short v) 137 | { http_major_=v; } 138 | 139 | /** 140 | * HTTP Minor version 141 | */ 142 | short http_minor() const 143 | { return http_minor_; } 144 | 145 | void http_minor(short v) 146 | { http_minor_=v; } 147 | 148 | /** 149 | * Method 150 | */ 151 | http::method method() const 152 | { return method_; } 153 | 154 | void method(http::method v) 155 | { method_=v; } 156 | 157 | /** 158 | * Schema in URL, may only exist in proxy requests 159 | */ 160 | const std::string &schema() const 161 | { return schema_; } 162 | 163 | void schema(const std::string &v) 164 | { schema_=v; } 165 | 166 | /** 167 | * User info in URL, may only exist in proxy requests 168 | */ 169 | const std::string &user_info() const 170 | { return user_info_; } 171 | 172 | void user_info(const std::string &v) 173 | { user_info_=v; } 174 | 175 | /** 176 | * Host in URL, may only exist in proxy requests 177 | */ 178 | const std::string &host() const 179 | { return host_; } 180 | 181 | void host(const std::string &v) 182 | { host_=v; } 183 | 184 | /** 185 | * Port in URL, may only exist in proxy requests 186 | */ 187 | int port() const 188 | { return port_; } 189 | 190 | void port(int v) 191 | { port_=v; } 192 | 193 | /** 194 | * Path in URL 195 | */ 196 | const std::string &path() const 197 | { return path_; } 198 | 199 | void path(const std::string &v) 200 | { path_=v; } 201 | 202 | /** 203 | * Query part in URL 204 | */ 205 | const std::string &query() const 206 | { return query_; } 207 | 208 | void query(const std::string &v) 209 | { query_=v; } 210 | 211 | /** 212 | * HTTP Headers 213 | */ 214 | const headers_t &headers() const 215 | { return headers_; } 216 | 217 | headers_t &headers() 218 | { return headers_; } 219 | 220 | /** 221 | * Keep-alive flag 222 | */ 223 | bool keep_alive() const 224 | { return keep_alive_; } 225 | 226 | void keep_alive(bool v) 227 | { keep_alive_=v; } 228 | 229 | /** 230 | * Request body 231 | */ 232 | const std::string &body() const 233 | { return body_stream_.vector(); } 234 | 235 | /** 236 | * Output stream for response body 237 | */ 238 | body_stream_t &body_stream() 239 | { return body_stream_; } 240 | 241 | private: 242 | short http_major_; 243 | short http_minor_; 244 | http::method method_; 245 | std::string schema_; 246 | std::string user_info_; 247 | std::string host_; 248 | int port_; 249 | std::string path_; 250 | std::string query_; 251 | headers_t headers_; 252 | bool keep_alive_; 253 | body_stream_t body_stream_; 254 | }; 255 | 256 | struct response_t { 257 | /** 258 | * Clear response 259 | */ 260 | void clear(); 261 | 262 | /** 263 | * HTTP Major version 264 | */ 265 | short http_major() const 266 | { return http_major_; } 267 | 268 | void http_major(short v) 269 | { http_major_=v; } 270 | 271 | /** 272 | * HTTP Minor version 273 | */ 274 | short http_minor() const 275 | { return http_minor_; } 276 | 277 | void http_minor(short v) 278 | { http_minor_=v; } 279 | 280 | /** 281 | * HTTP status code 282 | */ 283 | status_code code() const 284 | { return code_; } 285 | 286 | void code(status_code v) 287 | { code_=v; } 288 | 289 | void code(status_code v, const std::string &s) 290 | { code_=v; status_message_=s; } 291 | 292 | /** 293 | * HTTP status message 294 | */ 295 | const std::string &status_message() const 296 | { return status_message_; } 297 | 298 | /** 299 | * HTTP Headers 300 | */ 301 | const headers_t &headers() const 302 | { return headers_; } 303 | 304 | headers_t &headers() 305 | { return headers_; } 306 | 307 | /** 308 | * Keep-alive flag 309 | */ 310 | bool keep_alive() const 311 | { return keep_alive_; } 312 | 313 | void keep_alive(bool v) 314 | { keep_alive_=v; } 315 | 316 | /** 317 | * The response body 318 | */ 319 | const std::string &body() const 320 | { return body_stream_.vector(); } 321 | 322 | /** 323 | * Output stream for response body 324 | */ 325 | body_stream_t &body_stream() 326 | { return body_stream_; } 327 | 328 | private: 329 | short http_major_; 330 | short http_minor_; 331 | status_code code_=OK; 332 | std::string status_message_; 333 | headers_t headers_; 334 | bool keep_alive_; 335 | body_stream_t body_stream_; 336 | }; 337 | 338 | /** 339 | * Represent a HTTP session, which is a request/response roundtrip 340 | */ 341 | struct session_t { 342 | /** 343 | * Constructor 344 | * 345 | * @param raw_stream the socket stream 346 | */ 347 | session_t(net::async_tcp_stream &raw_stream) 348 | : raw_stream_(raw_stream) 349 | {} 350 | 351 | /** 352 | * HTTP request 353 | */ 354 | request_t &request() 355 | { return request_; } 356 | 357 | const request_t &request() const 358 | { return request_; } 359 | 360 | /** 361 | * HTTP response 362 | */ 363 | response_t &response() 364 | { return response_; } 365 | 366 | const response_t &response() const 367 | { return response_; } 368 | 369 | /** 370 | * Read timeout 371 | * 372 | * Timeout for single read operation, may also apply to idle time between request 373 | */ 374 | int read_timeout() const 375 | { return raw_stream().read_timeout(); } 376 | 377 | inline void read_timeout(int sec) 378 | { raw_stream().read_timeout(sec); } 379 | 380 | /** 381 | * Write timeout 382 | */ 383 | int write_timeout() const 384 | { return raw_stream().write_timeout(); } 385 | 386 | inline void write_timeout(int sec) 387 | { raw_stream().write_timeout(sec); } 388 | 389 | /** 390 | * Raw mode means the response should be handled by the request handler 391 | */ 392 | inline bool raw() const 393 | { return raw_; } 394 | 395 | inline void raw(bool r) 396 | { raw_=r; } 397 | 398 | /** 399 | * Max number of requests allowed in one connection, 0 means unlimited 400 | */ 401 | inline int max_keepalive() const 402 | { return max_keepalive_; } 403 | 404 | inline void max_keepalive(int n) 405 | { max_keepalive_=n; } 406 | 407 | /** 408 | * Return true means the remote peer wants the connection keep alive 409 | */ 410 | inline bool keep_alive() const 411 | { return request_.keep_alive(); } 412 | 413 | /** 414 | * The number of requests have been processed 415 | */ 416 | int count() const 417 | { return count_; } 418 | 419 | /** 420 | * Increment the request count 421 | */ 422 | void inc_count() 423 | { count_++; } 424 | 425 | /** 426 | * Underlying socket stream 427 | */ 428 | inline net::async_tcp_stream &raw_stream() 429 | { return raw_stream_; } 430 | 431 | inline const net::async_tcp_stream &raw_stream() const 432 | { return raw_stream_; } 433 | 434 | /** 435 | * The strand object accociated to the socket 436 | */ 437 | inline boost::asio::strand &strand() 438 | { return raw_stream().strand(); } 439 | 440 | /** 441 | * The io_service object accociated to the socket 442 | */ 443 | inline boost::asio::io_service &io_service() 444 | { return raw_stream().strand().get_io_service(); } 445 | 446 | /** 447 | * The yield context accociated to the coroutine handling this connection 448 | */ 449 | inline boost::asio::yield_context yield_context() 450 | { return raw_stream().yield_context(); } 451 | 452 | /** 453 | * Convenience 454 | */ 455 | operator boost::asio::yield_context() 456 | { return yield_context(); } 457 | 458 | /** 459 | * Spawn new coroutine within the strand 460 | */ 461 | template 462 | void spawn(BOOST_ASIO_MOVE_ARG(Function) function) { 463 | boost::asio::spawn(strand(), BOOST_ASIO_MOVE_CAST(Function)(function)); 464 | } 465 | 466 | private: 467 | request_t request_; 468 | response_t response_; 469 | bool raw_=false; 470 | net::async_tcp_stream &raw_stream_; 471 | int count_=0; 472 | int max_keepalive_=0; 473 | }; 474 | 475 | typedef std::function request_handler_t; 476 | 477 | template 478 | bool default_open_handler(session_t &session, const Args &...) 479 | { return true; } 480 | 481 | template 482 | bool default_req_handler(session_t &session, const Args &...) { 483 | session.response().code(NOT_IMPLEMENTED); 484 | return false; 485 | } 486 | 487 | template 488 | bool default_close_handler(session_t &session, const Args &...) 489 | { return true; } 490 | 491 | // Server side 492 | bool parse_request(session_t &session, std::function &req_cb); 493 | bool request_callback(session_t &session, std::function &handler); 494 | std::ostream &operator<<(std::ostream &s, response_t &resp); 495 | 496 | /** 497 | * Handle HTTP protocol handler with argument 498 | */ 499 | // Base template 500 | // TODO: There is no easy way to store-and-forward variadic template arguments in C++11 501 | template 502 | struct protocol_handler; 503 | 504 | template 505 | struct protocol_handler { 506 | typedef protocol_handler this_t; 507 | typedef std::function handler_t; 508 | 509 | protocol_handler() 510 | : open_handler_(&(default_open_handler)) 511 | , req_handler_(&(default_req_handler)) 512 | , close_handler_(&(default_close_handler)) 513 | {} 514 | 515 | void set_default_argument(const Arg &arg) 516 | { arg_=arg; } 517 | 518 | void set_open_handler(const handler_t &handler) 519 | { open_handler_=handler; } 520 | 521 | void set_request_handler(const handler_t &handler) 522 | { req_handler_=handler; } 523 | 524 | void set_close_handler(const handler_t &handler) 525 | { close_handler_=handler; } 526 | 527 | void operator()(net::async_tcp_stream &s) { 528 | session_t session(s); 529 | { 530 | if(!open_handler_(session, arg_)) 531 | return; 532 | struct guard_t { 533 | guard_t(this_t *p, session_t &s) : protocol_handler(p), session(s) {} 534 | ~guard_t() 535 | { protocol_handler->close_handler_(session, protocol_handler->arg_); } 536 | this_t *protocol_handler; 537 | session_t &session; 538 | } guard(this, session); 539 | request_handler_t h([this](session_t &session)->bool{ 540 | return req_handler_(session, arg_); 541 | }); 542 | std::function cb([this, &h](session_t &session)->bool{ 543 | return request_callback(session, h); 544 | }); 545 | parse_request(session, cb); 546 | } 547 | } 548 | 549 | handler_t open_handler_; 550 | handler_t req_handler_; 551 | handler_t close_handler_; 552 | Arg arg_; 553 | }; 554 | 555 | /** 556 | * Handle HTTP protocol handler without argument 557 | */ 558 | template<> 559 | struct protocol_handler<> { 560 | typedef protocol_handler<> this_t; 561 | typedef std::function handler_t; 562 | 563 | protocol_handler() 564 | : open_handler_(&(default_open_handler<>)) 565 | , req_handler_(&(default_req_handler<>)) 566 | , close_handler_(&(default_close_handler<>)) 567 | {} 568 | 569 | void set_open_handler(const handler_t &handler) 570 | { open_handler_=handler; } 571 | 572 | void set_request_handler(const handler_t &handler) 573 | { req_handler_=handler; } 574 | 575 | void set_close_handler(const handler_t &handler) 576 | { close_handler_=handler; } 577 | 578 | void operator()(net::async_tcp_stream &s) { 579 | session_t session(s); 580 | { 581 | if(!open_handler_(session)) 582 | return; 583 | struct guard_t { 584 | guard_t(this_t *p, session_t &s) : protocol_handler(p), session(s) {} 585 | ~guard_t() { protocol_handler->close_handler_(session); } 586 | this_t *protocol_handler; 587 | session_t &session; 588 | } guard(this, session); 589 | std::function cb([this](session_t &session)->bool{ 590 | return request_callback(session, req_handler_); 591 | }); 592 | parse_request(session, cb); 593 | } 594 | } 595 | 596 | handler_t open_handler_; 597 | handler_t req_handler_; 598 | handler_t close_handler_; 599 | }; 600 | 601 | // Client side 602 | std::ostream &operator<<(std::ostream &s, request_t &req); 603 | bool parse_response(std::istream &is, response_t &resp); 604 | inline std::istream &operator>>(std::istream &is, response_t &resp) 605 | { parse_response(is, resp); return is; } 606 | } // End of namespace http 607 | 608 | #endif /* defined(__coroserver__http_protocol__) */ 609 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // main.cpp 3 | // coroserver 4 | // 5 | // Created by Windoze on 13-2-23. 6 | // Copyright (c) 2013 0d0a.com. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include "server.h" 12 | #include "http_protocol.h" 13 | #include "routing.h" 14 | #include "calculator.h" 15 | 16 | #include "condition_variable.hpp" 17 | 18 | 19 | // Some stupid tests... 20 | 21 | typedef int arg_t; 22 | 23 | // Test redirection 24 | bool handle_alt_index(http::session_t &session, arg_t &arg) { 25 | session.response().code(http::SEE_OTHER); 26 | http::headers_t::const_iterator i=http::find_header(session.request().headers(), "host"); 27 | if (i!=session.request().headers().end()) { 28 | session.response().headers().push_back(*i); 29 | } 30 | session.response().headers().push_back({"Location", "/index.html"}); 31 | return true; 32 | } 33 | 34 | // Test stock response 35 | bool handle_not_found(http::session_t &session, arg_t &arg) { 36 | session.response().code(http::NOT_FOUND); 37 | return true; 38 | } 39 | 40 | // Test normal process 41 | bool handle_index(http::session_t &session, arg_t &arg) { 42 | using namespace std; 43 | session.response().body_stream().reserve(16384); 44 | ostream &ss=session.response().body_stream(); 45 | ss << "\r\nIndex\r\n"; 46 | ss << "

This is the index page


\r\n"; 47 | ss << "

Changing session argument from " << arg << " to " << arg+2 << "


\r\n"; 48 | arg+=2; 49 | ss << "

" << session.count() << " requests have been processed in this session.

\r\n"; 50 | ss << "\r\n"; 51 | ss << "\r\n"; 52 | ss << "\r\n"; 53 | ss << "\r\n"; 54 | ss << "\r\n"; 55 | ss << "\r\n"; 56 | ss << "\r\n"; 57 | ss << "
Schema" << session.request().schema() << "
User Info" << session.request().user_info() << "
Host" << session.request().host() << "
Port" << session.request().port() << "
Path" << session.request().path() << "
Query" << session.request().query() << "
\r\n"; 58 | ss << "\r\n"; 59 | for (auto &h : session.request().headers()) { 60 | ss << "\r\n"; 61 | } 62 | ss << "
" << h.first << "" << h.second << "
\r\n"; 63 | for(int i=0; i<1000; i++) { 64 | ss << "Line" << i << "
\r\n"; 65 | } 66 | ss << "\r\n"; 67 | return true; 68 | } 69 | 70 | // Test deferred process with spawn 71 | bool handle_other(http::session_t &session, arg_t &arg) { 72 | boost::asio::condition_flag flag(session); 73 | session.spawn([&session, &flag, &arg](boost::asio::yield_context yield){ 74 | using namespace std; 75 | ostream &ss=session.response().body_stream(); 76 | ss << "\r\n" << session.request().path() << "\r\n"; 77 | ss << "

" << session.request().path() << "


\r\n"; 78 | ss << "

Changing session argument from " << arg << " to " << arg+2 << "


\r\n"; 79 | arg+=2; 80 | ss << "

" << session.count() << " requests have been processed in this session.

\r\n"; 81 | ss << "\r\n"; 82 | ss << "\r\n"; 83 | ss << "\r\n"; 84 | ss << "\r\n"; 85 | ss << "\r\n"; 86 | ss << "\r\n"; 87 | ss << "\r\n"; 88 | ss << "
Schema" << session.request().schema() << "
User Info" << session.request().user_info() << "
Host" << session.request().host() << "
Port" << session.request().port() << "
Path" << session.request().path() << "
Query" << session.request().query() << "
\r\n"; 89 | ss << "\r\n"; 90 | for (auto &h : session.request().headers()) { 91 | ss << "\r\n"; 92 | } 93 | ss << "
" << h.first << "" << h.second << "
\r\n"; 94 | flag=true; 95 | }); 96 | flag.wait(); 97 | return true; 98 | } 99 | 100 | // Test client connection 101 | bool handle_proxy(http::session_t &session) { 102 | http::headers_t::const_iterator i=http::find_header(session.request().headers(), "host"); 103 | if (i==session.request().headers().end()) { 104 | session.response().code(http::BAD_REQUEST); 105 | return false; 106 | } 107 | session.raw(true); 108 | net::async_tcp_stream s(session.yield_context(), i->second, "80"); 109 | s << session.request(); 110 | s >> session.response(); 111 | session.raw_stream() << session.response(); 112 | session.raw_stream().flush(); 113 | return true; 114 | } 115 | 116 | int main(int argc, const char *argv[]) { 117 | std::size_t num_threads = 3; 118 | try { 119 | http::protocol_handler<> hproxy; 120 | hproxy.set_request_handler(&handle_proxy); 121 | 122 | http::protocol_handler handler; 123 | handler.set_default_argument(42); 124 | handler.set_open_handler([](http::session_t &session, arg_t &arg)->bool{ 125 | session.read_timeout(5); 126 | session.write_timeout(5); 127 | session.max_keepalive(3); 128 | return true; 129 | }); 130 | handler.set_request_handler(http::router({ 131 | {http::url_equals("/"), &handle_alt_index}, 132 | {http::url_equals("/index.html"), &handle_index}, 133 | {http::url_starts_with("/index") && http::url_ends_with(".htm"), &handle_alt_index}, 134 | {http::url_equals("/favicon.ico"), &handle_not_found}, 135 | {http::any(), &handle_other}, 136 | })); 137 | net::server s({{"[0::0]:20000", handler}, {"[0::0]:20001", hproxy}, {"[0::0]:30000", &calculator::protocol_handler}}, 138 | [](boost::asio::io_service &)->bool { return true; }, 139 | [](boost::asio::io_service &){}, 140 | num_threads); 141 | if (!s.initialized()) { 142 | // TODO: Log error 143 | } 144 | s(); 145 | } catch (std::exception& e) { 146 | std::cerr << "exception: " << e.what() << "\n"; 147 | } 148 | return 0; 149 | } 150 | -------------------------------------------------------------------------------- /routing.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // routing.cpp 3 | // coroserver 4 | // 5 | // Created by Windoze on 13-12-2. 6 | // Copyright (c) 2013 0d0a.com. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | #include "http_protocol.h" 13 | #include "routing.h" 14 | 15 | namespace http { 16 | routing_pred_t any() { 17 | return [](session_t &session)->bool{ 18 | return true; 19 | }; 20 | } 21 | 22 | routing_pred_t url_equals(const std::string &s, bool case_sensitive) { 23 | if (case_sensitive) { 24 | return [s](session_t &session)->bool{ 25 | return session.request().path()==s; 26 | }; 27 | } else { 28 | return [s](session_t &session)->bool{ 29 | return boost::algorithm::iequals(session.request().path(), s); 30 | }; 31 | } 32 | } 33 | 34 | routing_pred_t url_starts_with(const std::string &s, bool case_sensitive) { 35 | if (case_sensitive) { 36 | return [s](session_t &session)->bool{ 37 | return boost::algorithm::starts_with(session.request().path(), s); 38 | }; 39 | } else { 40 | return [s](session_t &session)->bool{ 41 | return boost::algorithm::istarts_with(session.request().path(), s); 42 | }; 43 | } 44 | } 45 | 46 | routing_pred_t url_ends_with(const std::string &s, bool case_sensitive) { 47 | if (case_sensitive) { 48 | return [s](session_t &session)->bool{ 49 | return boost::algorithm::ends_with(session.request().path(), s); 50 | }; 51 | } else { 52 | return [s](session_t &session)->bool{ 53 | return boost::algorithm::iends_with(session.request().path(), s); 54 | }; 55 | } 56 | } 57 | 58 | routing_pred_t operator &&(routing_pred_t c1, routing_pred_t c2) { 59 | return [c1, c2](session_t &session)->bool { 60 | return c1(session) && c2(session); 61 | }; 62 | } 63 | 64 | routing_pred_t operator ||(routing_pred_t c1, routing_pred_t c2) { 65 | return [c1, c2](session_t &session)->bool { 66 | return c1(session) || c2(session); 67 | }; 68 | } 69 | 70 | routing_pred_t operator !(routing_pred_t c) { 71 | return [c](session_t &session)->bool { 72 | return !c(session); 73 | }; 74 | } 75 | } // End of namespace http 76 | -------------------------------------------------------------------------------- /routing.h: -------------------------------------------------------------------------------- 1 | // 2 | // routing.h 3 | // coroserver 4 | // 5 | // Created by Windoze on 13-12-2. 6 | // Copyright (c) 2013 0d0a.com. All rights reserved. 7 | // 8 | 9 | #ifndef __coroserver__routing__ 10 | #define __coroserver__routing__ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace http { 19 | struct session_t; 20 | typedef std::function routing_pred_t; 21 | 22 | routing_pred_t any(); 23 | routing_pred_t url_equals(const std::string &s, bool case_sensitive=false); 24 | routing_pred_t url_starts_with(const std::string &s, bool case_sensitive=false); 25 | routing_pred_t url_ends_with(const std::string &s, bool case_sensitive=false); 26 | routing_pred_t operator &&(routing_pred_t c1, routing_pred_t c2); 27 | routing_pred_t operator ||(routing_pred_t c1, routing_pred_t c2); 28 | routing_pred_t operator !(routing_pred_t c); 29 | 30 | template 31 | struct router; 32 | 33 | template 34 | struct router { 35 | typedef std::function handler_t; 36 | typedef std::pair routing_entry_t; 37 | typedef std::list routing_table_t; 38 | typedef std::shared_ptr routing_table_ptr; 39 | 40 | router(const routing_table_t &table) 41 | : table_(new routing_table_t(table)) 42 | {} 43 | 44 | router(routing_table_t &&table) 45 | : table_(new routing_table_t(std::move(table))) 46 | {} 47 | 48 | bool operator()(session_t &session, Arg &arg) { 49 | for (routing_entry_t &ent : *table_) { 50 | if(ent.first(session)) 51 | return ent.second(session, arg); 52 | } 53 | return false; 54 | } 55 | 56 | routing_table_ptr table_; 57 | }; 58 | 59 | template<> 60 | struct router<> { 61 | typedef std::function handler_t; 62 | typedef std::pair routing_entry_t; 63 | typedef std::list routing_table_t; 64 | typedef std::shared_ptr routing_table_ptr; 65 | 66 | router(const routing_table_t &table) 67 | : table_(new routing_table_t(table)) 68 | {} 69 | 70 | router(routing_table_t &&table) 71 | : table_(new routing_table_t(std::move(table))) 72 | {} 73 | 74 | bool operator()(session_t &session) { 75 | for (routing_entry_t &ent : *table_) { 76 | if(ent.first(session)) 77 | return ent.second(session); 78 | } 79 | return false; 80 | } 81 | 82 | routing_table_ptr table_; 83 | }; 84 | } // End of namespace http 85 | 86 | #endif /* defined(__coroserver__routing__) */ 87 | -------------------------------------------------------------------------------- /server.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // server.cpp 3 | // coroserver 4 | // 5 | // Created by Windoze on 13-2-24. 6 | // Copyright (c) 2013 0d0a.com. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include "server.h" 12 | 13 | namespace net { 14 | using namespace boost; 15 | using namespace boost::asio; 16 | using namespace boost::asio::ip; 17 | 18 | server::server(const sap_desc_list_t &sap_desc_list, 19 | const initialization_handler_t &initialization_handler, 20 | const finalization_handler_t &finalization_handler, 21 | std::size_t thread_pool_size) 22 | : thread_pool_size_(thread_pool_size) 23 | , io_service_() 24 | , initialization_handler_(initialization_handler) 25 | , finalization_handler_(finalization_handler) 26 | , init_state_(initialization_handler_(io_service_)) 27 | , signals_(io_service_) 28 | { 29 | if (init_state_) { 30 | // Register to handle the signals that indicate when the server should exit. 31 | // It is safe to register for the same signal multiple times in a program, 32 | // provided all registration for the specified signal is made through Asio. 33 | signals_.add(SIGINT); 34 | signals_.add(SIGTERM); 35 | #if defined(SIGQUIT) 36 | signals_.add(SIGQUIT); 37 | #endif // defined(SIGQUIT) 38 | signals_.async_wait([this](system::error_code, int){ close(); }); 39 | 40 | // Start listening on endpoints 41 | for (const sap_desc_t &sd : sap_desc_list) 42 | listen(sd); 43 | 44 | // Accept incomint connections 45 | open(); 46 | } 47 | } 48 | 49 | 50 | server::server(const sap_desc_list_t &sap_desc_list, 51 | std::size_t thread_pool_size) 52 | : server(sap_desc_list, 53 | [](boost::asio::io_service &)->bool { return true; }, 54 | [](boost::asio::io_service &){}, 55 | thread_pool_size) 56 | {} 57 | 58 | server::~server() 59 | { if(init_state_) finalization_handler_(io_service_); } 60 | 61 | void server::listen(const sap_desc_t &sd) { 62 | const endpoint_t &ep=sd.first; 63 | // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR). 64 | endpoint_resolver resolver; 65 | tcp::endpoint endpoint = resolver.resolve(ep, "", io_service_); 66 | 67 | sap_ptr sap(new sap_t(tcp::acceptor(io_service_), sd.second)); 68 | saps_.push_back(sap); 69 | sap_list_t::reverse_iterator i=saps_.rbegin(); 70 | (*i)->first.open(endpoint.protocol()); 71 | (*i)->first.set_option(tcp::acceptor::reuse_address(true)); 72 | (*i)->first.bind(endpoint); 73 | (*i)->first.listen(); 74 | } 75 | 76 | void server::run() { 77 | if (!init_state_) { 78 | // TODO: Log error 79 | return; 80 | } 81 | // Create a pool of threads to run all of the io_services. 82 | std::vector threads; 83 | for (std::size_t i=0; ifirst.async_accept(socket, yield[ec]); 101 | if (!ec) 102 | handle_connect(std::move(socket), sap->second); 103 | } 104 | }); 105 | } 106 | } 107 | 108 | void server::close() 109 | { io_service_.stop(); } 110 | 111 | void server::handle_connect(tcp::socket &&socket, const protocol_handler_t &handler) { 112 | spawn(strand(io_service_), 113 | // Create a new protocol handler for each connection 114 | [this, &socket, handler](yield_context yield) { 115 | async_tcp_stream s(std::move(socket), yield); 116 | try { 117 | handler(s); 118 | } catch (std::exception const& e) { 119 | // TODO: Log error 120 | } catch(...) { 121 | // TODO: Log error 122 | } 123 | }); 124 | } 125 | } // End of namespace net 126 | -------------------------------------------------------------------------------- /server.h: -------------------------------------------------------------------------------- 1 | // 2 | // server.h 3 | // coroserver 4 | // 5 | // Created by Windoze on 13-2-24. 6 | // Copyright (c) 2013 0d0a.com. All rights reserved. 7 | // 8 | 9 | #ifndef server_h_included 10 | #define server_h_included 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "async_stream.h" 19 | 20 | namespace net { 21 | typedef std::function initialization_handler_t; 22 | typedef std::function finalization_handler_t; 23 | typedef std::string endpoint_t; 24 | typedef std::function protocol_handler_t; 25 | typedef std::pair sap_desc_t; 26 | typedef std::vector sap_desc_list_t; 27 | 28 | /** 29 | * Stream-oriented socket server 30 | */ 31 | class server { 32 | public: 33 | /** 34 | * Constructor 35 | * 36 | * @param sap_desc_list protocols and their service access points (addresses and ports) 37 | * @param thread_pool_size number of threads that run simultaneously to process client connections 38 | */ 39 | server(const sap_desc_list_t &sap_desc_list, 40 | std::size_t thread_pool_size); 41 | 42 | /** 43 | * Constructor 44 | * 45 | * @param sap_desc_list protocols and their service access points (addresses and ports) 46 | * @param initialization_handler called before setting up server 47 | * @param finalization_handler called before server destruction 48 | * @param thread_pool_size number of threads that run simultaneously to process client connections 49 | */ 50 | server(const sap_desc_list_t &sap_desc_list, 51 | const initialization_handler_t &initialization_handler, 52 | const finalization_handler_t &finalization_handler, 53 | std::size_t thread_pool_size); 54 | 55 | // Non-copyable 56 | server(const server&) = delete; 57 | server& operator=(const server&) = delete; 58 | 59 | ~server(); 60 | 61 | /** 62 | * Start server 63 | */ 64 | inline void operator()() 65 | { run(); } 66 | 67 | /** 68 | * Initialization result 69 | */ 70 | inline bool initialized() const 71 | { return init_state_; } 72 | 73 | private: 74 | void listen(const sap_desc_t &sd); 75 | void open(); 76 | void close(); 77 | void run(); 78 | void handle_connect(boost::asio::ip::tcp::socket &&socket, const protocol_handler_t &handler); 79 | 80 | std::size_t thread_pool_size_; 81 | boost::asio::io_service io_service_; 82 | boost::asio::signal_set signals_; 83 | initialization_handler_t initialization_handler_; 84 | finalization_handler_t finalization_handler_; 85 | bool init_state_; 86 | typedef std::pair sap_t; 87 | typedef std::shared_ptr sap_ptr; 88 | typedef std::vector sap_list_t; 89 | sap_list_t saps_; 90 | }; 91 | } // End of namespace net 92 | 93 | #endif /* defined(server_h_included) */ 94 | --------------------------------------------------------------------------------