Search code examples
c++sslhttpsboost-asioboost-beast

Why does this C++ ASIO, BEAST server go into a bad state when a HTTPS/SSL request is performed


I want to write a server that answers HTTP requests. I do not care about HTTPS. If somebody performs a HTTPS request I want to reject the request and continue with other HTTP requests. My code looks as follows:

#include <boost/asio.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <thread>
#include <iostream>
using namespace std;

int main(){
    boost::asio::io_context ioc;
    boost::asio::ip::tcp::acceptor acceptor(ioc, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), 8000));
    boost::beast::flat_buffer request_buffer;
    for(;;){
        boost::asio::ip::tcp::socket socket(ioc);
        acceptor.accept(socket);

        boost::beast::http::request<boost::beast::http::string_body> request;
        try{
            boost::beast::http::read(socket, request_buffer, request);
            cout << "OK" << endl;

            boost::beast::http::response<boost::beast::http::string_body> response(boost::beast::http::status::ok, request.version());
            std::string body = "hello";
            response.content_length(body.size());
            response.body() = std::move(body);
            boost::beast::http::write(socket, response);

        }catch(...){
            cout << "BAD" << endl;
        }
        socket.close();
    }
}

I run the code and perform in the following order requests using Firefox:

  1. a http request
  2. a https request
  3. a http request

I expect the following server output:

OK
BAD
OK

However, what I get is:

OK
BAD
BAD
BAD
BAD
BAD
BAD
BAD
BAD
BAD
BAD
BAD
BAD

The function that throws is boost::beast::http::read. However, I do not understand why. Between calls I create a new socket object and therefore my understanding is that the result of the second request should not impact the third. However, obviously my understanding is not correct. Can somebody please explain where my understanding of how ASIO and/or BEAST works is wrong. Thanks :)


Solution

  • HTTPS is HTTP over a SSL stream. A SSL stream is implemented over a normal TCP socket. Beast is throwing in the "Read" because it's not seeing a HTTP request, it's just seen what looks like random binary data. It doesn't know (and doesn't detect that it's a SSL stream).

    The reason you are seeing so many "BAD" lines is because Firefox is seeing a TLS stream trying to connect then failing. It's just retrying the SSL stream connection. So you end up multiple BAD connection lines.

    This is why the default HTTP port is 80 and the default HTTPS port is 443. A HTTPS client connection to a HTTP server will never work and will produce the types of results you are seeing.

    UPDATE: The reason you get into a loop of BAD is because the request_buffer is not being cleared on errors, so it contains the first SSL connection of data which keeps getting appended to with each reading of data from new sockets.

    The easiest fix is to move the defining of the request_buffer to the same place as the request object. Or you can consume the unprocessed data before you loop back around.