Build an http server based on the beast library and implement get and post request interaction (source code attached)

1 Introduction to Beast

Beast is a library about http(s)/websocket(s) in Boost. It was first released in boost 1.66 (2016). It is a relatively new library. It mainly includes the parsing (deserialization) and encapsulation (deserialization) of http and websocket protocols ( Serialization) and regarding network operations, it is based on asio, but seems to want to isolate Asio. This article describes the construction steps and ideas in detail from 0 to 1.

2 Build process

2.1 Download and installation of boost and jsoncpp

Download these two in advance, download and preparation work, refer to the following two articles,
Installation and use of Boost library under Windows
Instructions for using Json on C++ (from downloading to generating library to using)

2.2 Project creation and configuration

1. Open visual studio and create a console application. An empty project can also be used. The project name and location are arbitrary.
2. Configure the project, right-click the project, open properties, click the VC++ directory on the left, select the include directory option, edit the configuration as follows,
Insert image description here
Insert image description here
3. Same as above, edit the configuration library directory again, see the figure below
Insert image description here
4. Configure the jsoncpp library file in the following order Configure it and replace it with your own jsoncpp path in the fourth step.
Insert image description here
Insert image description here
At this point, all configurations are complete.

2.3 Code

#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio.hpp>
#include <chrono>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <memory>
#include <string>
#include<json/json.h>
#include<json/value.h>
#include<json/reader.h>
namespace beast = boost::beast;         // from <boost/beast.hpp>
namespace http = beast::http;           // from <boost/beast/http.hpp>
namespace net = boost::asio;            // from <boost/asio.hpp>
using tcp = boost::asio::ip::tcp;       // from <boost/asio/ip/tcp.hpp>

namespace my_program_state
{
    
    
    std::size_t
        request_count()
    {
    
    
        static std::size_t count = 0;
        return ++count;
    }

    std::time_t
        now()
    {
    
    
        return std::time(0);
    }
}

class http_connection : public std::enable_shared_from_this<http_connection>
{
    
    
public:
    http_connection(tcp::socket socket)
        : socket_(std::move(socket))
    {
    
    
    }

    // Initiate the asynchronous operations associated with the connection.
    void
        start()
    {
    
    
        read_request();
        check_deadline();
    }

private:
    // The socket for the currently connected client.
    tcp::socket socket_;

    // The buffer for performing reads.
    beast::flat_buffer buffer_{
    
    8192};

    // The request message.
    http::request<http::dynamic_body> request_;

    // The response message.
    http::response<http::dynamic_body> response_;

    // The timer for putting a deadline on connection processing.
    net::steady_timer deadline_{
    
    
        socket_.get_executor(), std::chrono::seconds(60)};

    // Asynchronously receive a complete request message.
    void
        read_request()
    {
    
    
        auto self = shared_from_this();

        http::async_read(
            socket_,
            buffer_,
            request_,
            [self](beast::error_code ec,
                std::size_t bytes_transferred)
            {
    
    
                boost::ignore_unused(bytes_transferred);
                if (!ec)
                    self->process_request();
            });
    }

    // Determine what needs to be done with the request message.
    void
        process_request()
    {
    
    
        response_.version(request_.version());
        response_.keep_alive(false);

        switch (request_.method())
        {
    
    
        case http::verb::get:
            response_.result(http::status::ok);
            response_.set(http::field::server, "Beast");
            create_response();
            break;
        case http::verb::post:
            response_.result(http::status::ok);
            response_.set(http::field::server, "Beast");
            create_post_response();
            break;
        default:
            // We return responses indicating an error if
            // we do not recognize the request method.
            response_.result(http::status::bad_request);
            response_.set(http::field::content_type, "text/plain");
            beast::ostream(response_.body())
                << "Invalid request-method '"
                << std::string(request_.method_string())
                << "'";
            break;
        }

        write_response();
    }

    // Construct a response message based on the program state.
    void
        create_response()
    {
    
    
        if (request_.target() == "/count")
        {
    
    
            response_.set(http::field::content_type, "text/html");
            beast::ostream(response_.body())
                << "<html>\n"
                << "<head><title>Request count</title></head>\n"
                << "<body>\n"
                << "<h1>Request count</h1>\n"
                << "<p>There have been "
                << my_program_state::request_count()
                << " requests so far.</p>\n"
                << "</body>\n"
                << "</html>\n";
        }
        else if (request_.target() == "/time")
        {
    
    
            response_.set(http::field::content_type, "text/html");
            beast::ostream(response_.body())
                << "<html>\n"
                << "<head><title>Current time</title></head>\n"
                << "<body>\n"
                << "<h1>Current time</h1>\n"
                << "<p>The current time is "
                << my_program_state::now()
                << " seconds since the epoch.</p>\n"
                << "</body>\n"
                << "</html>\n";
        }
        else
        {
    
    
            response_.result(http::status::not_found);
            response_.set(http::field::content_type, "text/plain");
            beast::ostream(response_.body()) << "File not found\r\n";
        }
    }

    // Asynchronously transmit the response message.
    void
        write_response()
    {
    
    
        auto self = shared_from_this();

        response_.content_length(response_.body().size());

        http::async_write(
            socket_,
            response_,
            [self](beast::error_code ec, std::size_t)
            {
    
    
                self->socket_.shutdown(tcp::socket::shutdown_send, ec);
                self->deadline_.cancel();
            });
    }

    // Check whether we have spent enough time on this connection.
    void
        check_deadline()
    {
    
    
        auto self = shared_from_this();

        deadline_.async_wait(
            [self](beast::error_code ec)
            {
    
    
                if (!ec)
                {
    
    
                    // Close socket to cancel any outstanding operation.
                    self->socket_.close(ec);
                }
            });
    }
    void create_post_response() {
    
    
        if (request_.target() == "/email")
        {
    
    
            auto& body = this->request_.body();
            auto body_str = boost::beast::buffers_to_string(body.data());
            std::cout << "receive body is " << body_str << std::endl;
            this->response_.set(http::field::content_type, "text/json");
            Json::Value root;
            Json::Reader reader;
            Json::Value src_root;
            bool parse_success = reader.parse(body_str, src_root);
            if (!parse_success) {
    
    
                std::cout << "Failed to parse JSON data!" << std::endl;
                root["error"] = 1001;
                std::string jsonstr = root.toStyledString();
                beast::ostream(this->response_.body()) << jsonstr;
                return;
            }
            auto email = src_root["email"].asString();
            std::cout << "email is " << email << std::endl;
            root["error"] = 0;
            root["email"] = src_root["email"];
            root["msg"] = "recevie email post success";
            std::string jsonstr = root.toStyledString();
            beast::ostream(this->response_.body()) << jsonstr;
        }
        else
        {
    
    
            response_.result(http::status::not_found);
            response_.set(http::field::content_type, "text/plain");
            beast::ostream(response_.body()) << "File not found\r\n";
        }
    }
};

void
http_server(tcp::acceptor& acceptor, tcp::socket& socket)
{
    
    
    acceptor.async_accept(socket,
        [&](beast::error_code ec)
        {
    
    
            if (!ec)
                std::make_shared<http_connection>(std::move(socket))->start();
            http_server(acceptor, socket);
        });
}

int main(int argc, char* argv[])
{
    
    
    try
    {
    
    
        // Check command line arguments.
        /*if (argc != 3)
        {
            std::cerr << "Usage: " << argv[0] << " <address> <port>\n";
            std::cerr << "  For IPv4, try:\n";
            std::cerr << "    receiver 0.0.0.0 80\n";
            std::cerr << "  For IPv6, try:\n";
            std::cerr << "    receiver 0::0 80\n";
            return EXIT_FAILURE;
        }*/

        auto const address = net::ip::make_address("127.0.0.1");
        unsigned short port = static_cast<unsigned short>(8080);

        net::io_context ioc{
    
    1};

        tcp::acceptor acceptor{
    
    ioc, {
    
     address, port }};
        tcp::socket socket{
    
    ioc};
        http_server(acceptor, socket);

        ioc.run();
    }
    catch (std::exception const& e)
    {
    
    
        std::cerr << "Error: " << e.what() << std::endl;
        return EXIT_FAILURE;
    }
}

2.4 Testing

get request, the url is localhost:8080/time
get request
post request, the url is localhost:8080/email
post request

3 source code

Click here to get the source code

Guess you like

Origin blog.csdn.net/qq_52431815/article/details/131832690