/* * Copyright (c) 2017 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "tcp-server.h" TcpServer::TcpServer (unsigned short port, long read_timeout) : port (port), acceptor (io_service), read_timeout (read_timeout) { } TcpServer::~TcpServer () { } void TcpServer::setHandler (const HandlerFunction &handler) { this->handler = handler; } void TcpServer::start () { if (io_service.stopped ()) io_service.reset (); boost::asio::ip::tcp::endpoint endpoint; endpoint = boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4 (), port); acceptor.open (endpoint.protocol ()); acceptor.set_option (boost::asio::socket_base::reuse_address (true)); acceptor.bind (endpoint); acceptor.listen (); accept (); //Set interrupt callbacks boost::asio::io_service io_service; boost::asio::signal_set signals (io_service, SIGINT, SIGQUIT); signals.async_wait ([this] (const boost::system::error_code &errorCode, int) { std::cout << "Gracefully terminating tcp server" << std::endl; this->io_service.reset (); this->acceptor.cancel (); }); io_service.run (); } void TcpServer::accept () { //Create new socket for this connection //Shared_ptr is used to pass temporary objects to the asynchronous functions std::shared_ptr socket (new boost::asio::ip::tcp::socket (io_service)); acceptor.async_accept (*socket, [this, socket] (const boost::system::error_code &ec) { accept (); if (ec) { if (ec == boost::asio::error::operation_aborted) // when the socket is closed by someone return; } processIncomingData (socket); }); } void TcpServer::processIncomingData (std::shared_ptr socket) { // Set timeout on the following boost::asio::async-read or write function std::shared_ptr timer; if (read_timeout > 0) timer = set_timeout_on_socket (socket, read_timeout); std::shared_ptr buffer (new boost::asio::streambuf ()); boost::asio::async_read_until (*socket, *buffer, "\r\n\r\n", [this, timer, buffer, socket] (const boost::system::error_code &error, std::size_t bytes_transferred) { if (read_timeout > 0) timer->cancel (); if (error) { std::cerr << "Boost error code is not null! ERROR: " << error << std::endl; return; } std::size_t bufferSize = buffer->size (); buffer->commit (buffer->size ()); const uint8_t *data = boost::asio::buffer_cast (buffer->data ()); std::string reply = handler (data, bufferSize); if (reply != "") { boost::asio::async_write (*socket, boost::asio::buffer (reply.c_str (), reply .size ()), [this] (boost::system::error_code ec, std::size_t /*length*/) { if (!ec) { std::cout << "Reply sent!" << std::endl; } else { std::cerr << "ERROR! Reply not sent." << std::endl; } }); } }); } std::shared_ptr TcpServer::set_timeout_on_socket (std::shared_ptr socket, long seconds) { std::shared_ptr timer (new boost::asio::deadline_timer (io_service)); timer->expires_from_now (boost::posix_time::seconds (seconds)); timer->async_wait ([socket] (const boost::system::error_code &ec) { if (!ec) { boost::system::error_code ec; std::cout << "Connection timeout!" << std::endl; socket->lowest_layer ().shutdown (boost::asio::ip::tcp::socket::shutdown_both, ec); socket->lowest_layer ().close (); } }); return timer; }