]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added istream-timeout, which triggers I/O event and fails with ETIMEDOUT after the...
authorTimo Sirainen <tss@iki.fi>
Thu, 3 Apr 2014 16:53:13 +0000 (19:53 +0300)
committerTimo Sirainen <tss@iki.fi>
Thu, 3 Apr 2014 16:53:13 +0000 (19:53 +0300)
src/lib/Makefile.am
src/lib/istream-timeout.c [new file with mode: 0644]
src/lib/istream-timeout.h [new file with mode: 0644]

index 054996b6089d580c4b78685ea4b027adcc8ef775..a6e9e53f2a6665e9d3e920a3b13715e74b083d09 100644 (file)
@@ -70,6 +70,7 @@ liblib_la_SOURCES = \
        istream-seekable.c \
        istream-sized.c \
        istream-tee.c \
+       istream-timeout.c \
        ioloop.c \
        ioloop-iolist.c \
        ioloop-notify-none.c \
@@ -197,6 +198,7 @@ headers = \
        istream-seekable.h \
        istream-sized.h \
        istream-tee.h \
+       istream-timeout.h \
        ioloop.h \
        ioloop-iolist.h \
        ioloop-private.h \
diff --git a/src/lib/istream-timeout.c b/src/lib/istream-timeout.c
new file mode 100644 (file)
index 0000000..874ac7c
--- /dev/null
@@ -0,0 +1,123 @@
+/* Copyright (c) 2014 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "time-util.h"
+#include "istream-private.h"
+#include "istream-timeout.h"
+
+struct timeout_istream {
+       struct istream_private istream;
+
+       struct timeout *to;
+       struct timeval last_read_timestamp;
+
+       unsigned int timeout_msecs;
+       bool update_timestamp;
+};
+
+static void i_stream_timeout_close(struct iostream_private *stream,
+                                  bool close_parent)
+{
+       struct timeout_istream *tstream = (struct timeout_istream *)stream;
+
+       if (tstream->to != NULL)
+               timeout_remove(&tstream->to);
+       if (close_parent)
+               i_stream_close(tstream->istream.parent);
+}
+
+static void i_stream_timeout_switch_ioloop(struct istream_private *stream)
+{
+       struct timeout_istream *tstream = (struct timeout_istream *)stream;
+
+       if (tstream->to != NULL)
+               tstream->to = io_loop_move_timeout(&tstream->to);
+}
+
+static void i_stream_timeout(struct timeout_istream *tstream)
+{
+       unsigned int msecs;
+       int diff;
+
+       timeout_remove(&tstream->to);
+
+       diff = timeval_diff_msecs(&ioloop_timeval, &tstream->last_read_timestamp);
+       if (diff < (int)tstream->timeout_msecs) {
+               /* we haven't reached the read timeout yet, update it */
+               if (diff < 0)
+                       diff = 0;
+               tstream->to = timeout_add(tstream->timeout_msecs - diff,
+                                         i_stream_timeout, tstream);
+               return;
+       }
+
+       msecs = tstream->timeout_msecs % 1000;
+       io_stream_set_error(&tstream->istream.iostream,
+                           "Read timeout in %u%s s after %"PRIuUOFF_T" bytes",
+                           tstream->timeout_msecs/1000,
+                           msecs == 0 ? "" : t_strdup_printf(".%u", msecs),
+                           tstream->istream.istream.v_offset);
+       tstream->istream.istream.stream_errno = ETIMEDOUT;
+
+       i_stream_set_input_pending(tstream->istream.parent, TRUE);
+}
+
+static ssize_t
+i_stream_timeout_read(struct istream_private *stream)
+{
+       struct timeout_istream *tstream = (struct timeout_istream *)stream;
+       ssize_t ret;
+
+       i_stream_seek(stream->parent, stream->parent_start_offset +
+                     stream->istream.v_offset);
+
+       ret = i_stream_read_copy_from_parent(&stream->istream);
+       if (ret < 0) {
+               /* failed */
+       } else if (tstream->to == NULL) {
+               /* first read. add the timeout here instead of in init
+                  in case the stream is created long before it's actually
+                  read from. */
+               tstream->to = tstream->timeout_msecs == 0 ? NULL :
+                       timeout_add(tstream->timeout_msecs,
+                                   i_stream_timeout, tstream);
+               tstream->update_timestamp = TRUE;
+               tstream->last_read_timestamp = ioloop_timeval;
+       } else if (ret > 0 && tstream->to != NULL) {
+               /* we read something, reset the timeout */
+               timeout_reset(tstream->to);
+               /* make sure we get called again on the next ioloop run.
+                  this updates the timeout to the timestamp where we actually
+                  would have wanted to start waiting for more data (so if
+                  there is long-running code outside the ioloop it's not
+                  counted) */
+               tstream->update_timestamp = TRUE;
+               tstream->last_read_timestamp = ioloop_timeval;
+               i_stream_set_input_pending(&stream->istream, TRUE);
+       } else if (tstream->update_timestamp) {
+               tstream->update_timestamp = FALSE;
+               tstream->last_read_timestamp = ioloop_timeval;
+       }
+       return ret;
+}
+
+struct istream *
+i_stream_create_timeout(struct istream *input, unsigned int timeout_msecs)
+{
+       struct timeout_istream *tstream;
+
+       tstream = i_new(struct timeout_istream, 1);
+       tstream->timeout_msecs = timeout_msecs;
+       tstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
+       tstream->istream.stream_size_passthrough = TRUE;
+
+       tstream->istream.read = i_stream_timeout_read;
+       tstream->istream.switch_ioloop = i_stream_timeout_switch_ioloop;
+       tstream->istream.iostream.close = i_stream_timeout_close;
+
+       tstream->istream.istream.blocking = input->blocking;
+       tstream->istream.istream.seekable = input->seekable;
+       return i_stream_create(&tstream->istream, input,
+                              i_stream_get_fd(input));
+}
diff --git a/src/lib/istream-timeout.h b/src/lib/istream-timeout.h
new file mode 100644 (file)
index 0000000..a26318a
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef ISTREAM_TIMEOUT_H
+#define ISTREAM_TIMEOUT_H
+
+/* Return ETIMEDOUT error if read() doesn't return anything for timeout_msecs.
+   If timeout_msecs=0, there is no timeout. */
+struct istream *
+i_stream_create_timeout(struct istream *input, unsigned int timeout_msecs);
+
+#endif