istream-timeout.c revision 8e0eb6a0cfc2cb66385bdf05a70a38d753bc1e24
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2014-2015 Dovecot authors, see the included COPYING file */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "lib.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "ioloop.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "time-util.h"
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen#include "istream-private.h"
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#include "istream-timeout.h"
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenstruct timeout_istream {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct istream_private istream;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct timeout *to;
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen struct timeval last_read_timestamp;
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen time_t created;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
d477acb83e14a776ece4ca94dcd1869e75d0c6eeTimo Sirainen unsigned int timeout_msecs;
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen bool update_timestamp;
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen};
d477acb83e14a776ece4ca94dcd1869e75d0c6eeTimo Sirainen
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainenstatic void i_stream_timeout_close(struct iostream_private *stream,
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen bool close_parent)
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen{
373492be949e159fda651807b3acda2c5c077027Timo Sirainen struct timeout_istream *tstream = (struct timeout_istream *)stream;
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen if (tstream->to != NULL)
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen timeout_remove(&tstream->to);
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen if (close_parent)
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen i_stream_close(tstream->istream.parent);
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen}
373492be949e159fda651807b3acda2c5c077027Timo Sirainen
373492be949e159fda651807b3acda2c5c077027Timo Sirainenstatic void i_stream_timeout_switch_ioloop(struct istream_private *stream)
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen{
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen struct timeout_istream *tstream = (struct timeout_istream *)stream;
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen if (tstream->to != NULL)
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen tstream->to = io_loop_move_timeout(&tstream->to);
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen}
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainenstatic void i_stream_timeout(struct timeout_istream *tstream)
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen{
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen unsigned int msecs;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen int diff;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen if (tstream->update_timestamp) {
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen /* we came here after a long-running code. timeouts are handled
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen before IOs, so wait for i_stream_read() to be called again
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen before assuming that we've timed out. */
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen return;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen }
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen timeout_remove(&tstream->to);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen diff = timeval_diff_msecs(&ioloop_timeval, &tstream->last_read_timestamp);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen if (diff < (int)tstream->timeout_msecs) {
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen /* we haven't reached the read timeout yet, update it */
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen if (diff < 0)
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen diff = 0;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen tstream->to = timeout_add(tstream->timeout_msecs - diff,
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen i_stream_timeout, tstream);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen return;
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen }
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen msecs = tstream->timeout_msecs % 1000;
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen io_stream_set_error(&tstream->istream.iostream,
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen "Read timeout in %u%s s after %"PRIuUOFF_T" bytes",
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen tstream->timeout_msecs/1000,
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen msecs == 0 ? "" : t_strdup_printf(".%u", msecs),
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen tstream->istream.istream.v_offset);
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen tstream->istream.istream.stream_errno = ETIMEDOUT;
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen i_stream_set_input_pending(tstream->istream.parent, TRUE);
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen}
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainenstatic void i_stream_timeout_set_pending(struct timeout_istream *tstream)
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen{
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen /* make sure we get called again on the next ioloop run. this updates
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen the timeout to the timestamp where we actually would have wanted to
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen start waiting for more data (so if there is long-running code
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen outside the ioloop it's not counted) */
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen tstream->update_timestamp = TRUE;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen tstream->last_read_timestamp = ioloop_timeval;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen i_stream_set_input_pending(&tstream->istream.istream, TRUE);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen}
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainenstatic ssize_t
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Siraineni_stream_timeout_read(struct istream_private *stream)
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen{
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen struct timeout_istream *tstream = (struct timeout_istream *)stream;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen ssize_t ret;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen i_stream_seek(stream->parent, stream->parent_start_offset +
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen stream->istream.v_offset);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen ret = i_stream_read_copy_from_parent(&stream->istream);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen if (ret < 0) {
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen /* failed */
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen if (errno == ECONNRESET || errno == EPIPE) {
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen int diff = ioloop_time - tstream->created;
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen io_stream_set_error(&tstream->istream.iostream,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen "%s (opened %d secs ago)",
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_stream_get_error(stream->parent), diff);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen } else if (tstream->to == NULL && tstream->timeout_msecs > 0) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen /* first read. add the timeout here instead of in init
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen in case the stream is created long before it's actually
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen read from. */
380dbb60ae291cbe39d1f710284562ca9167150bTimo Sirainen tstream->to = timeout_add(tstream->timeout_msecs,
380dbb60ae291cbe39d1f710284562ca9167150bTimo Sirainen i_stream_timeout, tstream);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_stream_timeout_set_pending(tstream);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen } else if (ret > 0 && tstream->to != NULL) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen /* we read something, reset the timeout */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen timeout_reset(tstream->to);
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen i_stream_timeout_set_pending(tstream);
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen } else if (tstream->update_timestamp) {
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen tstream->update_timestamp = FALSE;
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen tstream->last_read_timestamp = ioloop_timeval;
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen }
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen return ret;
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen}
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainenstruct istream *
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Siraineni_stream_create_timeout(struct istream *input, unsigned int timeout_msecs)
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen{
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct timeout_istream *tstream;
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen tstream = i_new(struct timeout_istream, 1);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen tstream->timeout_msecs = timeout_msecs;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen tstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen tstream->istream.stream_size_passthrough = TRUE;
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen tstream->created = ioloop_time;
9dd1c256910f1fb42823116a641e7edb3ad11970Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen tstream->istream.read = i_stream_timeout_read;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen tstream->istream.switch_ioloop = i_stream_timeout_switch_ioloop;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen tstream->istream.iostream.close = i_stream_timeout_close;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen tstream->istream.istream.readable_fd = input->readable_fd;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen tstream->istream.istream.blocking = input->blocking;
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen tstream->istream.istream.seekable = input->seekable;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return i_stream_create(&tstream->istream, input,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_stream_get_fd(input));
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen