deuseux12 deuseux12 - 11 months ago 307
C++ Question

Boost asio custom HTTP server reading HTTP post requests

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:";

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);

variable are declared as:
. And the
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"
"Connection: keep-alive"
"Content-Length: 10"
"Cache-Control: max-age=0"
"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"
"Accept-Encoding: gzip, deflate"

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
doesn't block, so
of the
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};
runServer(acceptor, io_service);;

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);
[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!

Answer Source

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".