/* This is a test program to reproduce a bug which appears in the implementation of boost::asio::async_read_some() somewhere between boost 1.42.0 and 1.46.1. From the async_read_some() documentation: (http://www.boost.org/doc/libs/1_46_1/doc/html/boost_asio/reference/async_read_until/overload2.html) "If the streambuf's get area already contains the delimiter, this asynchronous operation completes immediately." This condition appears to be met under boost 1.42.0, and not under boost 1.46.1, boost 1.49.0, or boost trunk from subversion. Output on ubuntu 11.10, using boost 1.42.0: % ./boost_async_read_test Calling readline, streambuf: async_read_some, invocations: 1 HandleLine: test line Calling readline, streambuf: more async_read_some, invocations: 2 HandleLine: more lines Calling readline, streambuf: another line HandleLine: another line Output on ubuntu 11.10, using boost 1.46.1 (1.49.0 and trunk produce identical output): % ./boost_async_read_test Calling readline, streambuf: async_read_some, invocations: 1 HandleLine: test line Calling readline, streambuf: more async_read_some, invocations: 2 HandleLine: more lines Calling readline, streambuf: another line async_read_some, invocations: 3 */ #include #include #include #include #include #include #include #include const std::string line1 = "test line\nmore "; const std::string line2 = "lines\nanother line\n"; namespace asio = boost::asio; namespace { class StreamStub : boost::noncopyable { public: explicit StreamStub(asio::io_service& service) : service_(service), invocations_(0) {} asio::io_service& get_io_service() { return service_; } template void async_read_some(MutableBufferSequence mb, Handler h) { ++invocations_; std::cout << "async_read_some, invocations: " << invocations_ << "\n"; const std::string* line; if (invocations_ == 1) { line = &line1; } else if (invocations_ == 2) { line = &line2; } else { // don't call the handler on later reads return; } size_t bufsize = asio::buffer_size(*mb.begin()); BOOST_ASSERT(bufsize >= line->size()); memcpy(asio::buffer_cast(*mb.begin()), line->data(), line->size()); h(boost::system::error_code(), line->size()); } private: asio::io_service& service_; int invocations_; }; class Reader : boost::noncopyable { public: Reader() : stream_(service_) {} void Readline() { std::cout << "Calling readline, streambuf: " << std::string( asio::buffer_cast(*streambuf_.data().begin()), asio::buffer_size(*streambuf_.data().begin())) << "\n"; asio::async_read_until( stream_, streambuf_, "\n", boost::bind(&Reader::HandleLine, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } asio::io_service service_; private: void HandleLine(boost::system::error_code ec, size_t len) { BOOST_ASSERT(!ec); std::istream is(&streambuf_); std::string line; std::getline(is, line); std::cout << "HandleLine: " << line << "\n"; } StreamStub stream_; std::list lines_; asio::streambuf streambuf_; }; } int main(int ac, char *av[]) { Reader reader; reader.Readline(); reader.service_.poll(); reader.service_.reset(); reader.Readline(); reader.service_.poll(); reader.service_.reset(); reader.Readline(); reader.service_.poll(); reader.service_.reset(); }