2
votes

I am adapting the synchronous HTTP client from the Boost Beast examples. Unfortunately the example client does not include timeout options and sometimes gets stuck in my workloads. I tried adding timeouts with

beast::get_lowest_layer(stream).expires_after(NetworkSettings::BASIC_TIMEOUT);

before calling write/read operations, but those only seem to work when using async_read/write. From what I found, it seems that basic boost asio supports timeouts only for async operations. So my question is whether beast has any capabilities to use a timeout on the blocking connect/read/write calls.

2
I don't have experience in Beast, but Have you tried using std::future and its wait_for member function? asio library (wrapped by beast) supports use_future which emulates blocking behaviour as synchronous clients. - RaGa__M

2 Answers

3
votes

Timeouts are not available for synchronous I/O in Asio. Since Beast is a layer above asio, it too does not support timeouts for synchronous I/O. If you want timeouts, you must use asynchronous APIs. You can use a stackful coroutine, or if you have a modern enough compiler you can experiment with stackless coroutines (co_await). These allow you to write code that appears synchronous but using asynchronous interfaces.

The Beast docs are clear on this: "For portability reasons, networking does not provide timeouts or cancellation features for synchronous stream operations."

https://www.boost.org/doc/libs/1_70_0/libs/beast/doc/html/beast/using_io/timeouts.html

If you want to have timeouts on connect operations, use an instance of beast::tcp_stream and call the async_connect member function: https://www.boost.org/doc/libs/1_70_0/libs/beast/doc/html/beast/using_io/timeouts.html#beast.using_io.timeouts.connecting

-1
votes

You can use something like this.

try change stream.connect(results) to

auto Future = stream.async_connect(endpoint, net::use_future);
if(Future.wait_for(std::chrono::seconds(1)) == std::future_status::timeout){

   std::cout<<"timed_out";
   ....
}else {

}

Bunch of things to note:

1)You may need below header files

#include<boost/asio/use_future.hpp>
#include<chrono>
#include<future>

2) Since you are asyc_* initiating; you need to call ioc.run();

3) You need an another thread to execute ioc.run(); as we are mocking synchronous via asynchronous-- someone have to run the event-loop.

Another approach: you can explicitly set the socket option using its native handle (I've never done it). But before doing that, read this answer https://stackoverflow.com/a/51850018/5198101

const int timeout = 200;
::setsockopt(socket.native_handle(), SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof timeout);//SO_SNDTIMEO for send ops

https://linux.die.net/man/7/socket

SO_RCVTIMEO and SO_SNDTIMEO Specify the receiving or sending timeouts until reporting an error. The argument is a struct timeval. If an input or output function blocks for this period of time, and data has been sent or received, the return value of that function will be the amount of data transferred; if no data has been transferred and the timeout has been reached then -1 is returned with errno set to EAGAIN or EWOULDBLOCK, or EINPROGRESS (for connect(2)) just as if the socket was specified to be nonblocking. If the timeout is set to zero (the default) then the operation will never timeout. Timeouts only have effect for system calls that perform socket I/O (e.g., read(2), recvmsg(2), send(2), sendmsg(2)); timeouts have no effect for select(2), poll(2), epoll_wait(2), and so on.