class session : public std::enable_shared_from_this<session>
{
    ...
    beast::flat_buffer buffer_; // (Must persist between reads)
    http::response<http::string_body> res_;
    ...
}
void on_write(beast::error_code ec, std::size_t bytes_transferred) {
  if (ec)
  {
    fail(ec, "write");
    return try_again();    
  }    
  // Receive the HTTP response
  http::async_read(
      stream_, buffer_, res_,
      beast::bind_front_handler(&session::on_read, shared_from_this()));
}
void on_read(beast::error_code ec, std::size_t bytes_transferred) {
  if (ec)
  {
    fail(ec, "read");
    return try_again();  
  }    
  // Step 1: process response
  //
  const auto &body_data = res_.body().data();
  user_parse_data(net::buffers_begin(body_data), net::buffers_end(body_data));
  
  // Step 2: clean up buffer_
  //
  buffer_.consume(buffer_.size()); // clean up buffer_ after finishing reading it
  // Step 3: continue to write
  ...
}
In the above implementation, I ONLY clean up the buffer_ when I finish parsing the data successfully.
Question> Should I clean up the buffer_ when I experience an error on the on_read too?
void on_read(beast::error_code ec, std::size_t bytes_transferred) {
  if (ec)
  {
    // clean up buffer_
    buffer_.consume(buffer_.size()); // Should we do the cleanup here too?
    
    fail(ec, "read");
    return try_again();    
  }    
  // Step 1: process response
  //
  const auto &body_data = res_.body().data();
  user_parse_data(net::buffers_begin(body_data), net::buffers_end(body_data));
  
  // Step 2: clean up buffer_
  //
  buffer_.consume(buffer_.size());
  // Step 3: continue to write
  ...
}