istream-timeout.c revision 25fb397382c6f7d39bfeee85774e7675f02bfb3c
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2014-2015 Dovecot authors, see the included COPYING file */
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen#include "lib.h"
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen#include "ioloop.h"
70ead6466f9baa8294e71fc2fba0a4f54f488b5eTimo Sirainen#include "time-util.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "istream-private.h"
dd7cbb32412c2f4d2d223af66672535bc1237246Timo Sirainen#include "istream-timeout.h"
57f5683fd9dc9bc79816c418bb30fdbc33b68a8cTimo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenstruct timeout_istream {
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen struct istream_private istream;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct timeout *to;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen struct timeval last_read_timestamp;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen time_t created;
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen unsigned int timeout_msecs;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen bool update_timestamp;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen};
70ead6466f9baa8294e71fc2fba0a4f54f488b5eTimo Sirainen
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainenstatic void i_stream_timeout_close(struct iostream_private *stream,
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen bool close_parent)
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen{
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen struct timeout_istream *tstream = (struct timeout_istream *)stream;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (tstream->to != NULL)
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen timeout_remove(&tstream->to);
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen if (close_parent)
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen i_stream_close(tstream->istream.parent);
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen}
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainen
318ef3683d67683173f1b552cf5f9af4375b3017Timo Sirainenstatic void i_stream_timeout_switch_ioloop(struct istream_private *stream)
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen{
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen struct timeout_istream *tstream = (struct timeout_istream *)stream;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen if (tstream->to != NULL)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen tstream->to = io_loop_move_timeout(&tstream->to);
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen}
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainenstatic void i_stream_timeout(struct timeout_istream *tstream)
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen{
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen unsigned int msecs;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen int diff;
4c892b0d94c5b1d6853dbe8e0b38059ea5b08ecaTimo Sirainen
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen if (tstream->update_timestamp) {
abc79eec93e58e0152cd1d483f37be66c26811b9Timo Sirainen /* we came here after a long-running code. timeouts are handled
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen before IOs, so wait for i_stream_read() to be called again
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen before assuming that we've timed out. */
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen return;
4c892b0d94c5b1d6853dbe8e0b38059ea5b08ecaTimo Sirainen }
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen timeout_remove(&tstream->to);
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen diff = timeval_diff_msecs(&ioloop_timeval, &tstream->last_read_timestamp);
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen if (diff < (int)tstream->timeout_msecs) {
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen /* we haven't reached the read timeout yet, update it */
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen if (diff < 0)
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen diff = 0;
a8d47e2427558d5011dfc75694b704760c1ef8baTimo Sirainen tstream->to = timeout_add(tstream->timeout_msecs - diff,
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen i_stream_timeout, tstream);
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen return;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen msecs = tstream->timeout_msecs % 1000;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen io_stream_set_error(&tstream->istream.iostream,
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen "Read timeout in %u%s s after %"PRIuUOFF_T" bytes",
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen tstream->timeout_msecs/1000,
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen msecs == 0 ? "" : t_strdup_printf(".%u", msecs),
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen tstream->istream.istream.v_offset);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen tstream->istream.istream.stream_errno = ETIMEDOUT;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_stream_set_input_pending(tstream->istream.parent, TRUE);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen}
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenstatic void i_stream_timeout_set_pending(struct timeout_istream *tstream)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen /* make sure we get called again on the next ioloop run. this updates
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen the timeout to the timestamp where we actually would have wanted to
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen start waiting for more data (so if there is long-running code
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen outside the ioloop it's not counted) */
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen tstream->update_timestamp = TRUE;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen tstream->last_read_timestamp = ioloop_timeval;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_stream_set_input_pending(&tstream->istream.istream, TRUE);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen}
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenstatic ssize_t
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Siraineni_stream_timeout_read(struct istream_private *stream)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct timeout_istream *tstream = (struct timeout_istream *)stream;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen ssize_t ret;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_stream_seek(stream->parent, stream->parent_start_offset +
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen stream->istream.v_offset);
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen ret = i_stream_read_copy_from_parent(&stream->istream);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (ret < 0) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen /* failed */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (errno == ECONNRESET || errno == EPIPE) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen int diff = ioloop_time - tstream->created;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo 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 }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen } else if (tstream->to == NULL) {
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. */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen tstream->to = tstream->timeout_msecs == 0 ? NULL :
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen timeout_add(tstream->timeout_msecs,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_stream_timeout, tstream);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_stream_timeout_set_pending(tstream);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen } else if (ret > 0 && tstream->to != NULL) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen /* we read something, reset the timeout */
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen timeout_reset(tstream->to);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_stream_timeout_set_pending(tstream);
65889a7d8c059e2feb159aee1633b847aba84831Timo Sirainen } else if (tstream->update_timestamp) {
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen tstream->update_timestamp = FALSE;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen tstream->last_read_timestamp = ioloop_timeval;
5666a3d6a7ea89362b8d9e8b39b15424cd9d6388Timo Sirainen }
a26b7e87b4157cfa800f9bcd8c4c044462d21268Timo Sirainen return ret;
a26b7e87b4157cfa800f9bcd8c4c044462d21268Timo Sirainen}
a26b7e87b4157cfa800f9bcd8c4c044462d21268Timo Sirainen
a26b7e87b4157cfa800f9bcd8c4c044462d21268Timo Sirainenstruct istream *
a26b7e87b4157cfa800f9bcd8c4c044462d21268Timo Siraineni_stream_create_timeout(struct istream *input, unsigned int timeout_msecs)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen struct timeout_istream *tstream;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen tstream = i_new(struct timeout_istream, 1);
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen tstream->timeout_msecs = timeout_msecs;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen tstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen tstream->istream.stream_size_passthrough = TRUE;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen tstream->created = ioloop_time;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen tstream->istream.read = i_stream_timeout_read;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen tstream->istream.switch_ioloop = i_stream_timeout_switch_ioloop;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen tstream->istream.iostream.close = i_stream_timeout_close;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen tstream->istream.istream.readable_fd = input->readable_fd;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen tstream->istream.istream.blocking = input->blocking;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen tstream->istream.istream.seekable = input->seekable;
d80f37f025593d959bdfa9c378915e4322f4f504Timo Sirainen return i_stream_create(&tstream->istream, input,
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen i_stream_get_fd(input));
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen}
e51cfb5506de764499cb5b81a098b23cf46f90f1Timo Sirainen