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