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