cateof
cateof

Reputation: 6768

boost::asio problems when trying to get the streambuf response as string

I use the basic example from boost::asio to connect to an enpoint and get back a json reponse. Here is the code

boost::asio::streambuf response;
boost::asio::read_until(socket_, response, "\r\n");

// Check that response is OK. 
std::istream response_stream(&response);
std::string http_version;
response_stream >> http_version;
unsigned int status_code;
response_stream >> status_code;
std::string status_message;
std::getline(response_stream, status_message);
if (!response_stream || http_version.substr(0, 5) != "HTTP/") {
        LOG_ERROR << "invalid response";
        return -1; 
}

resp_status_code_ = status_code;

resp_status_code_ = status_code;

if (status_code != 200) {
        LOG_ERROR << "response did not returned 200 but " << status_code;
        return -1; 
}

//read until the response headers termination 
boost::asio::read_until(socket_, response, "\r\n\r\n");

//read the headers.
std::string header;
while (std::getline(response_stream, header) && header != "\r") {
         ;
}

std::ostringstream ostringstream_content;
if (response.size() > 0)
        ostringstream_content << &response;

boost::system::error_code error;

boost::system::error_code error;
while (true) {
        size_t n = boost::asio::read(socket_, response, boost::asio::transfer_at_least(1), error);

        if (!error)
        {
                if (n)
                        ostringstream_content << &response;
        }

        if (error == boost::asio::error::eof)
                break;

        if (error)
                return -1; //throw boost::system::system_error(error);
}

str_response_ = ostringstream_content.str();

However when the string returns back I am reading this

  1 [5a0
  2 {"a_token":"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0Nzk5Mjk2NjcsIn...030"}
  3 0
  4 
  5 ]

The [ and ] are marks from my code to debug the begin and end of data correspondingly. I have more than one lines, 3 problematic characters at the first line, ie 5a0, the json string in the second line which is correct and then a zero and a blank line. How can I get exactly the json string only?

Upvotes: 0

Views: 739

Answers (1)

Arunmu
Arunmu

Reputation: 6901

I reworked on your example and it works fine for me. Note that I have made few shortcuts for reading the response. I am reading the status line and the header in one shot instead of reading separately (purely out of laziness)

#include <asio.hpp>
#include <iostream>
#include <sstream>
#include <istream>

int main(int argc, const char * argv[]) {

    static asio::io_service ios;
    asio::ip::tcp::endpoint
    endpoint(asio::ip::address::from_string("127.0.0.1"), 8000);
    asio::generic::stream_protocol::socket socket{ios};
    socket.connect(endpoint);

    std::string req = "GET /api\n\r\n\r\n";
    std::error_code ec;
    socket.write_some(asio::buffer(req, req.size()), ec);
    std::cout << "Request sent" << std::endl;

    if (ec == asio::error::eof) {
      std::cerr << "Premature termination\n";
      return 1;
    }
    //while (true) {
    {
          asio::streambuf response;
          // Get till all the headers
          asio::read_until(socket, response, "\r\n\r\n");

          // Check that response is OK. 
          std::istream response_stream(&response);
          std::string http_version;
          response_stream >> http_version;
          std::cout << "V : " << http_version << std::endl;

          unsigned int status_code;
          response_stream >> status_code;

          std::string status_message;
          std::getline(response_stream, status_message);

          if (!response_stream || http_version.substr(0, 5) != "HTTP/") {
            std::cerr << "invalid response";
            return -1; 
          }

          if (status_code != 200) {
            std::cerr << "response did not returned 200 but " << status_code;
            return -1; 
          }

          //read the headers.
          std::string header;
          while (std::getline(response_stream, header) && header != "\r") {
            std::cout << "H: " << header << std::endl;
          }

          std::ostringstream ostringstream_content;
          if (response.size() > 0) {
            ostringstream_content << &response;
          }

          std::error_code error;
          while (true) {
            size_t n = asio::read(socket, response, asio::transfer_at_least(1), error);
            if (!error) {
                if (n) {
                  ostringstream_content << &response;
                }
            } 

            if (error == asio::error::eof) {
                break;
            }
            if (error) {
                return -1; //throw boost::system::system_error(error);
            }
          }
          auto str_response = ostringstream_content.str();
          std::cout << str_response << std::endl;
    }

    //}

    return 0;
}

And my python server for test:

Python 2.7.10 (default, Jul 14 2015, 19:46:27)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket as s
>>> sd = s.socket(s.AF_INET, s.SOCK_STREAM)
>>> sd.bind(("127.0.0.1", 8000))
>>> sd.listen(1)
>>> c, a = sd.accept()
>>> resp = "HTTP/1.1 200 OK\nDate: Fri, 31 Dec 1999 23:59:59 GMT\nContent-Type: text/plain\nTransfer-Encoding: chunked\n\r\n\r\n{\"token\":\"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0Nzk5Mjk2NjcsIn...030\"}"
>>> c.send(resp)
173
>>> exit()

ASIO output:

MacBook-Pro:asio_test amuralid$ ./json_read_so
Request sent
V : HTTP/1.1
H: Date: Fri, 31 Dec 1999 23:59:59 GMT
H: Content-Type: text/plain
H: Transfer-Encoding: chunked

{"token":"eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0Nzk5Mjk2NjcsIn...030"}

Upvotes: 1

Related Questions