Search code examples
ajaxhttpboostxmlhttprequestboost-beast

How to detect ajax request using boost::beast


I need to detect if the request was sent through ajax or not using the boost beast http parser

Some frameworks for http in other programming languages provide this capability. But how can this be done with boost::beast?

I know that it depends on the data that the client sends and this data cannot always be trusted. But what is the correct way to determine an Ajax request based on client data, if the client sent all the data necessary for this through the browser?


Solution

  • This really has nothing to do with Beast, but taking the ideas from this page: https://www.geeksforgeeks.org/how-to-detect-ajax-request-to-normal-request-in-node-js/

    1. Check the headers of the request and if it contains HTTP_X_REQUESTED_WITH having a value of XMLHttpRequest then it is an ajax request.
    2. When you make an AJAX request, it adds content type application/json to the header of the request. So if the request contains the header then it is the AJAX
    3. If the referer header is different from the host header then it is an AJAX request
    4. If the sec-fetch-dest header is equal to empty then it is an AJAX request

    Disclaimer: I don't think these approaches are reliable let alone secure, but the demo below should give you some tools to translate your own requirements into beast code.

    Live On Coliru

    #include <boost/beast.hpp>
    #include <iostream>
    namespace beast = boost::beast;
    namespace http  = beast::http;
    namespace net   = boost::asio;
    using net::ip::tcp;
    using Request = http::request<http::string_body>;
    
    boost::optional<beast::string_view> header(Request const& req, beast::string_view name) {
        auto it = req.find(name);
        if (it != req.end())
            return it->value();
        return {};
    }
    
    int main() {
        net::io_context    ioc;
        tcp::acceptor acc(ioc, {{}, 8989});
    
        while (true) {
            Request req;
    
            auto sock = acc.accept();
            beast::flat_buffer buf;
            read(sock, buf, req);
    
            bool is_ajax = req.count("HTTP_X_REQUESTED_WITH")                    // 1.
                || header(req, "referer") != header(req, "host")                 // 3.
                || header(req, "sec-fetch-dest") == beast::string_view("empty"); // 4.
    
            std::cout << (is_ajax ? "AJAX " : "Regular ")   //
                      << req.method_string() << " request " //
                      << " for " << req.target()            //
                      << std::endl;
        }
    }
    

    For some tests:

    g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp
    ./a.out&
    sleep 1; curl -s http://127.0.0.1:8989/demo1 -d @main.cpp
    sleep 1; curl -sH 'referer: 127.0.0.1:8989' "http://127.0.0.1:8989/demo2?the=works"
    

    Prints

    AJAX POST request  for /demo1
    Regular GET request  for /demo2?the=works