void execute(std::function<void()> func, unsigned int iterations = 1) { if (iterations < 1) return; if (iterations == 1) { threads_.push([=](int i) { unused(i); try { func(); } catch (std::exception& e) { error_handler_(e); }; }); } else { threads_.push([=](int i) { unused(i); try { for (unsigned int itr = 0; itr < iterations; ++itr) { func(); } } catch (std::exception& e) { // if task failed we shouldn't try additional iterations. error_handler_(e); }; }); } }
bool TraceBufferReader::ReadChunks(const void* buffer, size_t buffer_size) { fbl::unique_ptr<BufferHeaderReader> header; auto error = BufferHeaderReader::Create(buffer, buffer_size, &header); if (error != "") { error_handler_(error); return false; } CallChunkConsumerIfNonEmpty(header->GetDurableBuffer(buffer), header->durable_data_end()); // There's only two buffers, thus the earlier one is not the current one. // It's important to process them in chronological order on the off // chance that the earlier buffer provides a stringref or threadref // referenced by the later buffer. int later_buffer = header->GetBufferNumber(header->wrapped_count()); int earlier_buffer = 0; if (header->wrapped_count() > 0) earlier_buffer = header->GetBufferNumber(header->wrapped_count() - 1); if (earlier_buffer != later_buffer) { CallChunkConsumerIfNonEmpty(header->GetRollingBuffer(buffer, earlier_buffer), header->rolling_data_end(earlier_buffer)); } CallChunkConsumerIfNonEmpty(header->GetRollingBuffer(buffer, later_buffer), header->rolling_data_end(later_buffer)); return true; }
void Connection::onError_() { boost::recursive_mutex::scoped_lock sentinel(error_handler_lock_); if (error_handler_) { error_handler_(); } else { /* no-op */ } }
void run() { HTTPServer s(num_threads_); s.set_request_handler([this](const Request& req, Response& res) { for (auto router : routers_) { if (router.handle(req, res)) { LOG_DBG << "Route " << req.path(); return true; } } LOG_DBG << "Request handler not found in dynamic router"; return false; }) .set_error_handler([this](int code, const std::string& msg, const Request& req, Response& res) { LOG_DBG << "Handle error:" << code << " " << msg << "with path " << req.path(); if (error_handler_ && error_handler_(code,msg,req,res)) { return true; } LOG_DBG << "In defaule error handler"; std::string html; auto s = status_header(code); html = "<html><head><title>" + s.second + "</title></head>"; html += "<body>"; html += "<h1>" + boost::lexical_cast<std::string>(s.first) + " " + s.second + " " + "</h1>"; if (!msg.empty()) { html += "<br> <h2>Message: " + msg + "</h2>"; } html += "</body></html>"; res.set_status_code(s.first); res.write(html); return true; }) .public_dir(public_dir_) .listen(listen_addr_, listen_port_) .run(); }
void response_5xx(const std::string& msg, const boost::asio::yield_context& yield) { Request req; Response res; error_handler_(500, msg, req, res); boost::system::error_code ignored_ec; boost::asio::async_write(socket_, boost::asio::buffer(res.get_header_str()), yield[ignored_ec]); boost::asio::async_write(socket_, res.buffer_, boost::asio::transfer_exactly(res.buffer_.size()), yield[ignored_ec]); shutdown(); }
void run() { HTTPServer s(num_threads_); s.set_request_handler([this](Request& req, Response& res) { return Invoke<sizeof...(Aspect)>(res, &Cinatra::dispatch, this, req, res); }) .set_error_handler([this](int code, const std::string& msg, Request& req, Response& res) { LOG_DBG << "Handle error:" << code << " " << msg << " with path " << req.path(); if (error_handler_ && error_handler_(code,msg,req,res)) { return true; } LOG_DBG << "In defaule error handler"; std::string html; auto s = status_header(code); html = "<html><head><title>" + s.second + "</title></head>"; html += "<body>"; html += "<h1>" + boost::lexical_cast<std::string>(s.first) + " " + s.second + " " + "</h1>"; if (!msg.empty()) { html += "<br> <h2>Message: " + msg + "</h2>"; } html += "</body></html>"; res.set_status_code(s.first); res.write(html); return true; }) .static_dir(static_dir_) #ifdef CINATRA_ENABLE_HTTPS .https_config(config_) #endif // CINATRA_ENABLE_HTTPS .listen(listen_addr_, listen_port_) .run(); }
void do_work(const boost::asio::yield_context& yield) { //FIXME: 拆分成多个子函数.. for (;;) { try { std::array<char, 8192> buffer; RequestParser parser; std::size_t total_size = 0; for (;;) { std::size_t n = socket_.async_read_some(boost::asio::buffer(buffer), yield); total_size += n; if (total_size > 2 * 1024 * 1024) { throw std::runtime_error("Request toooooooo large"); } auto ret = parser.parse(buffer.data(), buffer.data() + n); if (ret == RequestParser::good) { break; } if (ret == RequestParser::bad) { throw std::runtime_error("HTTP Parser error"); } } /* 如果是http1.0,规则是这样的: 如果request里面的connection是keep-alive,那就说明浏览器想要长连接,服务器如果也同意长连接, 那么返回的response的connection也应该有keep-alive通知浏览器,如果不想长链接,response里面就不应该有keep-alive; 如果是1.1的,规则是这样的: 如果request里面的connection是close,那就说明浏览器不希望长连接,如果没有close,就是默认保持长链接, 本来是跟keep-alive没关系,但是如果浏览器发了keep-alive,那你返回的时候也应该返回keep-alive; 惯例是根据有没有close判断是否长链接,但是如果没有close但是有keep-alive,你response也得加keep-alive; 如果没有close也没有keep-alive 那就是长链接但是不用返回keep-alive */ Request req = parser.get_request(); Response res; LOG_DBG << "New request,path:" << req.path(); auto self = shared_from_this(); res.direct_write_func_ = [&yield, self, this] (const char* data, std::size_t len)->bool { boost::system::error_code ec; boost::asio::async_write(socket_, boost::asio::buffer(data, len), yield[ec]); if (ec) { // TODO: log ec.message(). std::cout << "direct_write_func error" << ec.message() << std::endl; return false; } return true; }; // 是否已经处理了这个request. bool found = false; bool keep_alive{}; bool close_connection{}; if (parser.check_version(1, 0)) { // HTTP/1.0 LOG_DBG << "http/1.0"; if (req.header().val_ncase_equal("Connetion", "Keep-Alive")) { LOG_DBG << "Keep-Alive"; keep_alive = true; close_connection = false; } else { keep_alive = false; close_connection = true; } res.set_version(1, 0); } else if (parser.check_version(1, 1)) { // HTTP/1.1 LOG_DBG << "http/1.1"; if (req.header().val_ncase_equal("Connetion", "close")) { keep_alive = false; close_connection = true; } else if (req.header().val_ncase_equal("Connetion", "Keep-Alive")) { keep_alive = true; close_connection = false; } else { keep_alive = false; close_connection = false; } if (req.header().get_count("host") == 0) { found = error_handler_(400,"", req, res); } res.set_version(1, 1); } else { LOG_DBG << "Unsupported http version"; found = error_handler_(400, "Unsupported HTTP version.", req, res); } if (!found && request_handler_) { found = request_handler_(req, res); } if (!found && response_file(req, keep_alive, yield)) { continue; } //如果都没有找到,404 if (!found) { LOG_DBG << "404 Not found"; error_handler_(404, "", req, res); } if (keep_alive) { res.header.add("Connetion", "Keep-Alive"); } //用户没有指定Content-Type,默认设置成text/html if (res.header.get_count("Content-Type") == 0) { res.header.add("Content-Type", "text/html"); } if (!res.is_complete_) { res.end(); } if (!res.is_chunked_encoding_) { // 如果是chunked编码数据应该都发完了. std::string header_str = res.get_header_str(); boost::asio::async_write(socket_, boost::asio::buffer(header_str), yield); boost::asio::async_write(socket_, res.buffer_, boost::asio::transfer_exactly(res.buffer_.size()), yield); } if (close_connection) { shutdown(); } } catch (boost::system::system_error& e) { //网络通信异常,关socket. if (e.code() == boost::asio::error::eof) { LOG_DBG << "Socket shutdown"; } else { LOG_DBG << "Network exception: " << e.code().message(); } boost::system::error_code ignored_ec; socket_.close(ignored_ec); return; } catch (std::exception& e) { LOG_ERR << "Error occurs,response 500: " << e.what(); response_5xx(e.what(), yield); } catch (...) { response_5xx("", yield); } } }