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