valgrind --leak-check=full for http_client_test now reports: ==30442== ==30442== HEAP SUMMARY: ==30442== in use at exit: 304 bytes in 10 blocks ==30442== total heap usage: 54,976 allocs, 54,966 frees, 4,334,461 bytes allocated ==30442== ==30442== LEAK SUMMARY: ==30442== definitely lost: 0 bytes in 0 blocks ==30442== indirectly lost: 0 bytes in 0 blocks ==30442== possibly lost: 0 bytes in 0 blocks ==30442== still reachable: 304 bytes in 10 blocks ==30442== suppressed: 0 bytes in 0 blocks ==30442== Reachable blocks (those to which a pointer was found) are not shown. ==30442== To see them, rerun with: --leak-check=full --show-leak-kinds=all ==30442== ==30442== For counts of detected and suppressed errors, rerun with: -v ==30442== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) valgrind --leak-check=full for http_client_load_test now reports: ==30468== ==30468== HEAP SUMMARY: ==30468== in use at exit: 139,615 bytes in 1,125 blocks ==30468== total heap usage: 315,910 allocs, 314,785 frees, 54,715,917 bytes allocated ==30468== ==30468== 139,311 (152 direct, 139,159 indirect) bytes in 1 blocks are definitely lost in loss record 97 of 97 ==30468== at 0x4C2B100: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==30468== by 0x4E8FDDD: boost::asio::detail::epoll_reactor::descriptor_state* boost::asio::detail::object_pool_access::create() (object_pool.hpp:35) ==30468== by 0x4E8D6AA: boost::asio::detail::object_pool::alloc() (object_pool.hpp:89) ==30468== by 0x4E8985A: boost::asio::detail::epoll_reactor::allocate_descriptor_state() (epoll_reactor.ipp:512) ==30468== by 0x4E88D99: boost::asio::detail::epoll_reactor::register_descriptor(int, boost::asio::detail::epoll_reactor::descriptor_state*&) (epoll_reactor.ipp:151) ==30468== by 0x4E8BE6C: boost::asio::detail::reactive_descriptor_service::assign(boost::asio::detail::reactive_descriptor_service::implementation_type&, int const&, boost::system::error_code&) (reactive_descriptor_service.ipp:108) ==30468== by 0x4E8C1B6: boost::asio::posix::stream_descriptor_service::assign(boost::asio::detail::reactive_descriptor_service::implementation_type&, int const&, boost::system::error_code&) (stream_descriptor_service.hpp:116) ==30468== by 0x4E922D2: boost::asio::posix::basic_descriptor::basic_descriptor(boost::asio::io_service&, int const&) (basic_descriptor.hpp:90) ==30468== by 0x4E8F84E: boost::asio::posix::basic_stream_descriptor::basic_stream_descriptor(boost::asio::io_service&, int const&) (basic_stream_descriptor.hpp:91) ==30468== by 0x4E83FC9: curl::multi::Handle::Private::Socket::Private::Private(boost::asio::io_service&, int) (multi.cpp:463) ==30468== by 0x4E83E4E: curl::multi::Handle::Private::Socket::Socket(boost::asio::io_service&, int) (multi.cpp:441) ==30468== by 0x4E84741: curl::multi::Handle::Private::socket_callback(void*, int, int, void*, void*) (multi.cpp:577) ==30468== ==30468== LEAK SUMMARY: ==30468== definitely lost: 152 bytes in 1 blocks ==30468== indirectly lost: 139,159 bytes in 1,114 blocks ==30468== possibly lost: 0 bytes in 0 blocks ==30468== still reachable: 304 bytes in 10 blocks ==30468== suppressed: 0 bytes in 0 blocks ==30468== Reachable blocks (those to which a pointer was found) are not shown. ==30468== To see them, rerun with: --leak-check=full --show-leak-kinds=all ==30468== ==30468== For counts of detected and suppressed errors, rerun with: -v ==30468== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) The stream descriptor is leaked "on purpose" in the test, so I would call this issue fixed.