4

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.

Haatschii
  • 9,021
  • 10
  • 58
  • 95
  • 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 Jul 01 '19 at 03:31

2 Answers2

5

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

Vinnie Falco
  • 5,173
  • 28
  • 43
0

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.

RaGa__M
  • 2,550
  • 1
  • 23
  • 44
  • This would refute the idea of using async IO in the first place, right? – sehe Jul 02 '19 at 16:26
  • 1
    @sehe Hmmm...for sure blocking IO isn't a good thing( (in-case of scale-ability), but it looks like, the OP is stuck experimenting synchronous client, so I gave a work-around. – RaGa__M Jul 03 '19 at 05:04
  • @Explorer_N Yes, there's no need for futures or non-portable socket options since Beast provides an asynchronous connect function that has a timeout (see https://www.boost.org/doc/libs/1_70_0/libs/beast/doc/html/beast/using_io/timeouts.html#beast.using_io.timeouts.connecting) I have updated my answer to reflect this. – Vinnie Falco Jul 04 '19 at 17:15
  • @VinnieFalco, thanks for responding, you know, the OP is aware of timeout operation provided by `async_*` , but he said he is experimenting synchronous client which means he wants to block till the `connect` happens and it looks like there is no `timeout` for `sync connect` and that is the reason I suggested `std::future`... Sure `co_await` works too, but at the end, it is just an another asynchronous model like `std::future`..About `setsockopt`-- I leave it up-to the OP's discretion by linking an answer which talks about portability. you still think my answer is incorrect ? – RaGa__M Jul 05 '19 at 05:13
  • @VinnieFalco and you think `std::future` won't do the Job which OP is looking for? – RaGa__M Jul 05 '19 at 05:14
  • Well `std::future` uses the asynchronous API (e.g. `async_connect` with `net::use_future`). In that case, you might as well use `beast::tcp_stream` since that will actually cancel the connect for you on a timeout, instead of leaving the operation outstanding which is what happens with the future using the code above. – Vinnie Falco Jul 06 '19 at 01:19
  • @VinnieFalco "instead of leaving the operation outstanding which is what happens with the future " not necessary, OP can call `cancel` on timeout which indeed cancel the pending operation, moreover, the whole reason for `future` is OP wants to block, using timeout on `beast::tcp_stream`-- will it give a blocking behaviour? – RaGa__M Jul 08 '19 at 11:09
  • @VinnieFalco, Even from your response what I can see is (correct me if I am wrong) My answer isn't incorrect, yet, it is a plausible one. – RaGa__M Jul 08 '19 at 11:12