Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Connecting to an HTTPS server with boost::asio

I want to connect to an HTTPS server using boost::asio. I managed to successfully shake hands with the server, but I just can't manage to get the server to respond to my POST request.

This is the related code (I left out debugging and try-catch to save some space):

HTTPSClient::HTTPSClient()
{
    ssl::context context(ssl::context::sslv23);
    context.set_verify_mode(ssl::verify_peer);
    context.set_default_verify_paths();
    context.load_verify_file("certificate.pem");
    mSSLSocket = new ssl::stream<ip::tcp::socket>(mIOService, context);
}

void HTTPSClient::SendRequest(const ptree &crPTree, const std::string cHost, 
    const std::string cURI)
{
    tcp::resolver resolver(mIOService);
    tcp::resolver::query query(cHost, "https");
    resolver.async_resolve(query, boost::bind(&HTTPSClient::HandleResolve, this, 
        placeholders::error, placeholders::iterator, request));
}

void HTTPSClient::HandleResolve(const error_code &crError, 
    const iterator &criEndpoints, HTTPSRequest &rRequest)
{
    async_connect(mSSLSocket->lowest_layer(), criEndpoints, 
        boost::bind(&HTTPSClient::HandleConnect, this, placeholders::error, 
        rRequest));
}

void HTTPSClient::HandleConnect(const error_code &crError, HTTPSRequest &rRequest)
{
    mSSLSocket->lowest_layer().set_option(ip::tcp::no_delay(true));
    mSSLSocket->set_verify_callback(ssl::rfc2818_verification(rRequest.mcHost));
    mSSLSocket->handshake(ssl::stream_base::client);

    // Write the json into a stringstream
    std::ostringstream json;
    boost::property_tree::write_json(json, rRequest.mcPTree);
    std::string result;
    result = json.str();

    // Form the request
    streambuf request;
    std::ostream requestStream(&request);
    requestStream << "POST " << rRequest.mcURI << " HTTP/1.1\r\n";
    requestStream << "Host: " << rRequest.mcHost << "\r\n";
    requestStream << "Accept: application/json\r\n";
    requestStream << "Content-Type: application/json; charset=UTF-8\r\n";
    requestStream << "Content-Length: " << result.length() << "\r\n";
    requestStream << result << "\r\n\r\n";

    write(*mSSLSocket, request);

    streambuf response;
    read_until(*mSSLSocket, response, "\r\n");
    std::istream responseStream(&response);
}

read_until hangs until it throws the error read_until: End of file. Everything before that goes successfully, including the SSL handshake (which I just recently figured out).

I used to do everything asynchronously until I started debugging, and started trying to backtrace to the problem, to no avail. It would be awesome if someone could help me out after two painful days of debugging.

EDIT I just realized it might be useful to add the contents of requestStream after composing the header:

POST /authenticate HTTP/1.1
Host: <hostname>
Accept: application/json
Content-Type: application/json; charset=UTF-8
Content-Length: 136
{
    "username": "vijfhoek",
    "password": "test123",
    <other json content>
}
like image 704
vijfhoek Avatar asked Mar 21 '23 10:03

vijfhoek


2 Answers

You need a double linefeed before the body (POST contents)

POST /authenticate HTTP/1.1
Host: <hostname>
Accept: application/json
Content-Type: application/json; charset=UTF-8
Content-Length: 136

{
    "username": "vijfhoek",
    "password": "test123",
    <other json content>
}

Otherwise, the content will have been received by the server as header lines and the server just keeps waiting for 136 bytes of content data (also make sure that Content-Length is accurate, which it isn't in this example)

So, basically:

requestStream << "Content-Length: " << result.length() << "\r\n";
requestStream << "\r\n"; // THIS LINE ADDED
like image 125
sehe Avatar answered Apr 01 '23 03:04

sehe


I managed to figure out what I was doing wrong. For some reason, I couldn't get boost to write data using the boost::asio::streambuf and std::ostream approach. Instead, I put the POST data in a std::string and sent it like this:

write(*mSSLSocket, boost::asio::buffer(requestString));

Which worked out fine.

like image 37
vijfhoek Avatar answered Apr 01 '23 05:04

vijfhoek