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