Boost asio custom HTTP server reading HTTP post requests
Asked Answered
P

1

0

My C++ application requires of an HTTP server and I decided to make my own, which is correctly working when sending HTTP Get requests but has some problems when reading HTTP Post requests.

The problem is that when sending HTTP Posts requests, the last header isn't read properly, so I can't get the post request.

This is my code:

void HttpSession::readHeaders(std::shared_ptr<HttpSession> pThis) {
    boost::asio::async_read_until(pThis->socket_, pThis->buff_, '\r\n\r\n',
                              [pThis](const boost::system::error_code &e, std::size_t s) {
        std::istream headersStream(&pThis->buff_);
        std::string header;
        while(std::getline(headersStream, header, '\n')) {
            if(header != "\r") {
                if(header.back() == '\r') header = header.substr(0, header.length() - 1);
                qDebug() << QString::fromStdString(header);
                //Some stuff to get content-length to contentLength_
        }
    }

    if(contentLength_ > 0) {
        qDebug() << "Reading:";
        readBody(pThis);
    }

    std::shared_ptr<std::string> str = std::make_shared<std::string>(pThis->headers_.getResponse());
    boost::asio::async_write(pThis->socket_, boost::asio::buffer(str->c_str(), str->length()),
                             [pThis, str](const boost::system::error_code &e, std::size_t s) {
        qDebug() << "Written";
    });
}

void HttpSession::readBody(std::shared_ptr<HttpSession> pThis) {
    boost::asio::async_read(pThis->socket_, pThis->data_, boost::asio::transfer_at_least(1),
                              [pThis](const boost::system::error_code &e, std::size_t s) {
        std::istream body(&pThis->data_);
        std::string line;
        body >> line;
        qDebug() << QString::fromStdString(line);
    });
}

The buff_ and data_ variable are declared as: boost::asio::streambuf. And the HttpSession class is the one which stores the headers and handles all the webpage serving. I didn't include the code of that class since it's not the problem.

The output of an HTTP Post request to /url is this one:

"POST /url HTTP/1.1" 
"Host: 192.168.1.41:8080" 
"Connection: keep-alive" 
"Content-Length: 10" 
"Cache-Control: max-age=0" 
"Origin: http://192.168.1.41:8080" 
"Upgrade-Insecure-Requests: 1" 
"User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36" 
"Content-Type: application/x-www-form-urlencoded" 
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" 
"Referer: http://192.168.1.41:8080/" 
"Accept-Encoding: gzip, deflate" 
"Accept-Langua" 
Reading:  
"ge:"

So as you can see, the Accept-Language header is not read properly although in this image you can see that the POST request is made properly. this

Note that when sending GET requests everything works properly. So my guess is that it has something to do with reading asynchronously, since the async_read_until doesn't block, so async_read of the readBody function, reads before it should. Should I read synchronously, then? Will I be able to handle more than one client either way if I create one HttpSession class for each client? (I really don't need to support more than one user, but still, it would be nice).

For the client handling I do like this:

void HttpServer::run() {
    using namespace boost::asio;
    io_service io_service;
    ip::tcp::endpoint endpoint{ip::tcp::v4(), 8080};
    ip::tcp::acceptor acceptor{io_service, endpoint};
    acceptor.listen();
    runServer(acceptor, io_service);

    io_service.run();

    while(true) QThread::msleep(5000);
}

void HttpServer::runServer(boost::asio::ip::tcp::acceptor& acceptor, boost::asio::io_service& io_service) {
    std::shared_ptr<HttpSession> ses = std::make_shared<HttpSession>(io_service);
    acceptor.async_accept(ses->socket_,
    [ses, &acceptor, &io_service](const boost::system::error_code& accept_error) {
       runServer(acceptor, io_service);
       if(!accept_error) HttpSession::interact(ses);
    });
}

All kind of help will be appreciated! If you have any code improvement, please tell me. Thanks!

Parsaye answered 28/8, 2016 at 11:25 Comment(1)
for my own learning, can I see all your code? I'm trying to do something similar without much success. See: stackoverflow.com/questions/41254225Wayward
T
1

I think the problem is with '\r\n\r\n'. That's not a string but a char.

The compiler should normally warn you about this, with something like:

warning: implicit conversion from 'int' to 'char' changes value from 218762506 to 10 [-Wconstant-conversion]

Try replacing that with "\r\n\r\n".

Tarp answered 28/8, 2016 at 11:51 Comment(2)
I'll try it when I get home, thanks. But anyways, isn't it weird that it works with GET requests?Parsaye
I understand the problem now, thanks. The compiler didn't warn me, maybe it has something to do with qt compiler.Parsaye

© 2022 - 2024 — McMap. All rights reserved.