refactor to use beast
This commit is contained in:
parent
2a4e32a274
commit
656ddf2941
12 changed files with 177 additions and 150 deletions
|
|
@ -9,7 +9,9 @@ set(BUILD_SHARED_LIBS OFF)
|
||||||
|
|
||||||
include_directories(${PROJECT_SOURCE_DIR}/includes)
|
include_directories(${PROJECT_SOURCE_DIR}/includes)
|
||||||
add_executable(Application src/main.cpp
|
add_executable(Application src/main.cpp
|
||||||
src/tcp_connection.cpp
|
src/http_connection.cpp
|
||||||
|
src/http_server.cpp
|
||||||
|
src/request_handler.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(Boost REQUIRED COMPONENTS filesystem system)
|
find_package(Boost REQUIRED COMPONENTS filesystem system)
|
||||||
|
|
|
||||||
37
includes/http_connection.hpp
Normal file
37
includes/http_connection.hpp
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef HTTP_CONNECTION_HPP
|
||||||
|
#define HTTP_CONNECTION_HPP
|
||||||
|
|
||||||
|
#include "request_handler.hpp"
|
||||||
|
#include <boost/beast/core/flat_buffer.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/beast/http/string_body.hpp>
|
||||||
|
#include <boost/beast/http.hpp>
|
||||||
|
#include <boost/beast/core.hpp>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace boost::asio;
|
||||||
|
using namespace boost::beast;
|
||||||
|
|
||||||
|
class HttpConnection : public enable_shared_from_this<HttpConnection> {
|
||||||
|
public:
|
||||||
|
typedef shared_ptr<HttpConnection> pointer;
|
||||||
|
|
||||||
|
static pointer create(io_context& io_context);
|
||||||
|
ip::tcp::socket& socket();
|
||||||
|
void process_connection();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ip::tcp::socket socket_;
|
||||||
|
boost::beast::flat_buffer buffer_;
|
||||||
|
http::request<http::string_body> request_;
|
||||||
|
http::response<http::string_body> response_;
|
||||||
|
RequestHandler request_handler_;
|
||||||
|
|
||||||
|
void read();
|
||||||
|
void write();
|
||||||
|
|
||||||
|
explicit HttpConnection(io_context& io_context);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
24
includes/http_server.hpp
Normal file
24
includes/http_server.hpp
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef HTTP_SERVER_HPP
|
||||||
|
#define HTTP_SERVER_HPP
|
||||||
|
|
||||||
|
#include "http_connection.hpp"
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/asio/io_context.hpp>
|
||||||
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
#include <boost/system/detail/error_code.hpp>
|
||||||
|
|
||||||
|
using namespace boost::asio;
|
||||||
|
|
||||||
|
class HttpServer {
|
||||||
|
public:
|
||||||
|
HttpServer(io_context& io_context, const ip::tcp::endpoint& endpoint);
|
||||||
|
|
||||||
|
private:
|
||||||
|
io_context& io_context_;
|
||||||
|
ip::tcp::acceptor acceptor_;
|
||||||
|
|
||||||
|
void accept_connection();
|
||||||
|
void handle_accept(HttpConnection::pointer& new_connection, const boost::system::error_code& error_code);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
17
includes/request_handler.hpp
Normal file
17
includes/request_handler.hpp
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef REQUEST_HANDLER_HPP
|
||||||
|
#define REQUEST_HANDLER_HPP
|
||||||
|
|
||||||
|
#include <boost/beast/http.hpp>
|
||||||
|
#include <boost/beast/http/message.hpp>
|
||||||
|
#include <boost/beast/http/string_body.hpp>
|
||||||
|
|
||||||
|
using namespace boost::beast;
|
||||||
|
|
||||||
|
class RequestHandler {
|
||||||
|
public:
|
||||||
|
http::response<http::string_body> handle(const http::request<http::string_body>& request);
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
#ifndef REQUEST_PARSER_HPP
|
|
||||||
#define REQUEST_PARSER_HPP
|
|
||||||
|
|
||||||
#include <istream>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
struct HttpRequest {
|
|
||||||
string method;
|
|
||||||
string uri;
|
|
||||||
string version;
|
|
||||||
map<string, string> headers;
|
|
||||||
string body;
|
|
||||||
};
|
|
||||||
|
|
||||||
class request_parser {
|
|
||||||
public:
|
|
||||||
HttpRequest& parse(istream& data_stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
#ifndef RESPONSE_HANDLER_HPP
|
|
||||||
#define RESPONSE_HANDLER_HPP
|
|
||||||
|
|
||||||
#include "request_parser.hpp"
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
class response_handler {
|
|
||||||
public:
|
|
||||||
string& handle(HttpRequest request);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
#ifndef TCP_CONNECTION_HPP
|
|
||||||
#define TCP_CONNECTION_HPP
|
|
||||||
|
|
||||||
#include <boost/system/detail/error_code.hpp>
|
|
||||||
#include <memory>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "request_parser.hpp"
|
|
||||||
#include "response_handler.hpp"
|
|
||||||
|
|
||||||
using namespace boost;
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
class tcp_connection : public std::enable_shared_from_this<tcp_connection> {
|
|
||||||
public:
|
|
||||||
typedef std::shared_ptr<tcp_connection> pointer;
|
|
||||||
|
|
||||||
static pointer create(asio::io_context& io_context);
|
|
||||||
asio::ip::tcp::socket& socket();
|
|
||||||
void handle();
|
|
||||||
|
|
||||||
private:
|
|
||||||
asio::ip::tcp::socket socket_;
|
|
||||||
asio::streambuf buffer_;
|
|
||||||
request_parser request_parser_;
|
|
||||||
response_handler response_handler_;
|
|
||||||
|
|
||||||
explicit tcp_connection(asio::io_context& io_context);
|
|
||||||
|
|
||||||
void handle_read(const system::error_code& error, size_t bytes_transferred);
|
|
||||||
void handle_write(const system::error_code& error, size_t bytes_transferred);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
50
src/http_connection.cpp
Normal file
50
src/http_connection.cpp
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
#include "../includes/http_connection.hpp"
|
||||||
|
#include <boost/asio/io_context.hpp>
|
||||||
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
#include <boost/beast/core/error.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
HttpConnection::HttpConnection(io_context& io_context)
|
||||||
|
: socket_(io_context) {};
|
||||||
|
|
||||||
|
HttpConnection::pointer HttpConnection::create(io_context& io_context) {
|
||||||
|
return pointer(new HttpConnection(io_context));
|
||||||
|
}
|
||||||
|
|
||||||
|
ip::tcp::socket& HttpConnection::socket() {
|
||||||
|
return socket_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpConnection::process_connection() {
|
||||||
|
read();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpConnection::read() {
|
||||||
|
auto self = shared_from_this();
|
||||||
|
http::async_read(socket_, buffer_, request_,
|
||||||
|
[self](boost::beast::error_code error_code, size_t bytes_transferred) {
|
||||||
|
if (!error_code) {
|
||||||
|
cout << "Request received:\n" << self->request_ << endl;
|
||||||
|
self->response_ = self->request_handler_.handle(self->request_);
|
||||||
|
self->write();
|
||||||
|
} else {
|
||||||
|
cerr << "Error reading: " << error_code.message() << endl;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpConnection::write() {
|
||||||
|
auto self = shared_from_this();
|
||||||
|
http::async_write(socket_, response_,
|
||||||
|
[self](boost::beast::error_code error_code, size_t bytes_transferred) {
|
||||||
|
if (!error_code) {
|
||||||
|
auto error_code_socket = self->socket_.shutdown(ip::tcp::socket::shutdown_send, error_code);
|
||||||
|
if (error_code_socket) {
|
||||||
|
cerr << "Error shuting down socket: " << error_code_socket.message() << endl;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cerr << "Error writing response: " << error_code.message() << endl;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
24
src/http_server.cpp
Normal file
24
src/http_server.cpp
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
#include "../includes/http_server.hpp"
|
||||||
|
#include "http_connection.hpp"
|
||||||
|
#include <boost/asio/io_context.hpp>
|
||||||
|
#include <boost/asio/placeholders.hpp>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
HttpServer::HttpServer(io_context& io_context, const ip::tcp::endpoint& endpoint)
|
||||||
|
: io_context_(io_context), acceptor_(io_context, endpoint) {
|
||||||
|
accept_connection();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpServer::accept_connection() {
|
||||||
|
HttpConnection::pointer new_connection = HttpConnection::create(io_context_);
|
||||||
|
acceptor_.async_accept(new_connection->socket(),
|
||||||
|
bind(&HttpServer::handle_accept, this, new_connection, boost::asio::placeholders::error)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpServer::handle_accept(HttpConnection::pointer& new_connection, const boost::system::error_code& error_code) {
|
||||||
|
if (!error_code) {
|
||||||
|
new_connection->process_connection();
|
||||||
|
}
|
||||||
|
accept_connection();
|
||||||
|
}
|
||||||
36
src/main.cpp
36
src/main.cpp
|
|
@ -1,38 +1,18 @@
|
||||||
#include "../includes/tcp_connection.hpp"
|
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
#include "../includes/http_server.hpp"
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
class tcp_server {
|
using namespace boost::asio;
|
||||||
public:
|
|
||||||
explicit tcp_server(boost::asio::io_context& io_context, const int port): io_context_(io_context), acceptor_(io_context, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)) {
|
|
||||||
start_accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
boost::asio::io_context& io_context_;
|
|
||||||
boost::asio::ip::tcp::acceptor acceptor_;
|
|
||||||
|
|
||||||
void start_accept() {
|
|
||||||
tcp_connection::pointer new_connection = tcp_connection::create(io_context_);
|
|
||||||
acceptor_.async_accept(new_connection->socket(), std::bind(&tcp_server::handle_accept, this, new_connection, boost::asio::placeholders::error));
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_accept(tcp_connection::pointer& new_connection, const boost::system::error_code& error) {
|
|
||||||
if (!error)
|
|
||||||
{
|
|
||||||
new_connection->handle();
|
|
||||||
}
|
|
||||||
start_accept();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
const int port = 8080;
|
||||||
|
|
||||||
std::cout << "Starting server!" << std::endl;
|
std::cout << "Starting server!" << std::endl;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
boost::asio::io_context io_context;
|
io_context io_context;
|
||||||
tcp_server server(io_context, 9090);
|
ip::tcp::endpoint endpoint(ip::tcp::v4(), port);
|
||||||
|
HttpServer server(io_context, endpoint);
|
||||||
io_context.run();
|
io_context.run();
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
|
|
|
||||||
14
src/request_handler.cpp
Normal file
14
src/request_handler.cpp
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
#include "../includes/request_handler.hpp"
|
||||||
|
#include <boost/beast/http/field.hpp>
|
||||||
|
#include <boost/beast/http/message.hpp>
|
||||||
|
#include <boost/beast/http/status.hpp>
|
||||||
|
#include <boost/beast/http/string_body.hpp>
|
||||||
|
|
||||||
|
http::response<http::string_body> RequestHandler::handle(const http::request<http::string_body>& request) {
|
||||||
|
http::response<http::string_body> response{http::status::ok, request.version()};
|
||||||
|
response.set(http::field::server, "Beast");
|
||||||
|
response.set(http::field::content_type, "text/plain");
|
||||||
|
response.body() = "Hello, World!";
|
||||||
|
response.prepare_payload();
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
#include "../includes/tcp_connection.hpp"
|
|
||||||
#include <boost/asio/buffer.hpp>
|
|
||||||
#include <boost/asio/write.hpp>
|
|
||||||
#include <boost/system/detail/error_code.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
#include <boost/bind/bind.hpp>
|
|
||||||
|
|
||||||
tcp_connection::tcp_connection(asio::io_context& io_context)
|
|
||||||
: socket_(io_context) {}
|
|
||||||
|
|
||||||
tcp_connection::pointer tcp_connection::create(asio::io_context& io_context) {
|
|
||||||
return pointer(new tcp_connection(io_context));
|
|
||||||
}
|
|
||||||
|
|
||||||
asio::ip::tcp::socket& tcp_connection::socket() {
|
|
||||||
return socket_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void tcp_connection::handle() {
|
|
||||||
asio::async_read_until(socket_, buffer_, "\r\n",
|
|
||||||
bind(&tcp_connection::handle_read,
|
|
||||||
shared_from_this(),
|
|
||||||
asio::placeholders::error,
|
|
||||||
asio::placeholders::bytes_transferred)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void tcp_connection::handle_read(const boost::system::error_code& error, size_t bytes_transferred) {
|
|
||||||
if (!error) {
|
|
||||||
istream request_stream(&buffer_);
|
|
||||||
auto requst = request_parser_.parse(request_stream);
|
|
||||||
string response = response_handler_.handle(requst);
|
|
||||||
|
|
||||||
asio::async_write(socket_, asio::buffer(response),
|
|
||||||
bind(&tcp_connection::handle_write,
|
|
||||||
shared_from_this(),
|
|
||||||
asio::placeholders::error,
|
|
||||||
asio::placeholders::bytes_transferred)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
std::cerr << "Error during read: " << error.message() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void tcp_connection::handle_write(const system::error_code& error, size_t bytes_transferred) {
|
|
||||||
if (error) {
|
|
||||||
std::cerr << "Error during write: " << error.message() << std::endl;
|
|
||||||
} else {
|
|
||||||
std::cout << "Successfully sent " << bytes_transferred << " bytes." << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in a new issue