]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-auth-client: auth-master - Make request reply handling asynchronous
authorStephan Bosch <stephan@dovecot.fi>
Sat, 7 May 2016 12:53:29 +0000 (14:53 +0200)
committerStephan Bosch <stephan.bosch@open-xchange.com>
Wed, 27 Aug 2025 14:17:02 +0000 (16:17 +0200)
src/lib-auth-client/auth-master-private.h
src/lib-auth-client/auth-master-request.c
src/lib-auth-client/auth-master.c
src/lib-auth-client/auth-master.h
src/lib-auth-client/test-auth-master.c

index 9e2f86012348186029498a5c538b11890f4a13f1..d8f0fb04a1072ac1d2ce5bc6fdb208da20c8df9d 100644 (file)
 #define MAX_INBUF_SIZE 8192
 #define MAX_OUTBUF_SIZE 1024
 
+enum auth_master_request_state {
+       AUTH_MASTER_REQUEST_STATE_SENT = 0,
+       AUTH_MASTER_REQUEST_STATE_REPLIED,
+       AUTH_MASTER_REQUEST_STATE_FINISHED,
+       AUTH_MASTER_REQUEST_STATE_ABORTED,
+};
+
 struct auth_master_request {
+       int refcount;
        pool_t pool;
        struct event *event;
 
@@ -24,10 +32,14 @@ struct auth_master_request {
        size_t args_size;
 
        unsigned int id;
+       struct timeval create_stamp;
+
+       enum auth_master_request_state state;
 
        auth_master_request_callback_t *callback;
        void *context;
 
+       bool sent:1;
        bool aborted:1;
        bool removed:1;
        bool in_callback:1;
@@ -42,11 +54,11 @@ struct auth_master_connection {
        const char *auth_socket_path;
        enum auth_master_flags flags;
 
+       struct timeout *to_connect, *to_idle, *to_request;
        struct ioloop *ioloop, *prev_ioloop;
-       struct timeout *to;
 
        unsigned int id_counter;
-
+       HASH_TABLE(void *, struct auth_master_request *) requests;
        struct auth_master_request *requests_head, *requests_tail;
        unsigned int requests_count;
 
@@ -54,6 +66,7 @@ struct auth_master_connection {
 
        pid_t auth_server_pid;
 
+       bool in_timeout:1;
        bool connected:1;
        bool sent_handshake:1;
        bool waiting:1;
@@ -63,6 +76,9 @@ struct auth_master_connection {
  * Request
  */
 
+unsigned int
+auth_master_request_get_timeout_msecs(struct auth_master_request *req);
+
 int auth_master_request_got_reply(struct auth_master_request **_req,
                                  const char *reply, const char *const *args);
 void auth_master_request_fail(struct auth_master_request **_req,
@@ -72,7 +88,10 @@ void auth_master_request_fail(struct auth_master_request **_req,
  * Connection
  */
 
-void auth_master_set_io(struct auth_master_connection *conn);
-void auth_master_unset_io(struct auth_master_connection *conn);
+void auth_master_check_idle(struct auth_master_connection *conn);
+void auth_master_stop_idle(struct auth_master_connection *conn);
+
+void auth_master_connection_update_timeout(struct auth_master_connection *conn);
+void auth_master_connection_start_timeout(struct auth_master_connection *conn);
 
 #endif
index a3c16784a8084a6fbfc14d2a6975ff4957b258ef..7b80a58e8d4fead081aef4c72a8bcf116bd94256 100644 (file)
@@ -1,10 +1,13 @@
 /* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "array.h"
+#include "hash.h"
 #include "llist.h"
 #include "ioloop.h"
 #include "istream.h"
 #include "ostream.h"
+#include "time-util.h"
 #include "master-service.h"
 
 #include "auth-master-private.h"
@@ -16,6 +19,18 @@ static void auth_master_request_update_event(struct auth_master_request *req)
                                    t_strdup_printf("request [%u]: ", req->id));
 }
 
+unsigned int
+auth_master_request_get_timeout_msecs(struct auth_master_request *req)
+{
+       struct timeval expires = req->create_stamp;
+       int msecs;
+
+       timeval_add_msecs(&expires, req->conn->timeout_msecs);
+
+       msecs = timeval_diff_msecs(&expires, &ioloop_timeval);
+       return (unsigned int)(msecs < 0 ? 0 : msecs);
+}
+
 static void auth_master_request_remove(struct auth_master_request *req)
 {
        struct auth_master_connection *conn = req->conn;
@@ -24,29 +39,42 @@ static void auth_master_request_remove(struct auth_master_request *req)
                return;
        req->removed = TRUE;
 
+       if (req->sent)
+               hash_table_remove(conn->requests, POINTER_CAST(req->id));
        DLLIST2_REMOVE(&conn->requests_head, &conn->requests_tail, req);
        conn->requests_count--;
 
+       auth_master_connection_update_timeout(conn);
+       auth_master_check_idle(conn);
+
        if (conn->waiting) {
                i_assert(conn->ioloop != NULL);
                io_loop_stop(conn->ioloop);
-       } else if (conn->requests_head == NULL) {
-               auth_master_unset_io(conn);
        }
 }
 
-static void auth_master_request_free(struct auth_master_request **_req)
+static void auth_master_request_ref(struct auth_master_request *req)
+{
+       req->refcount++;
+}
+
+static bool auth_master_request_unref(struct auth_master_request **_req)
 {
        struct auth_master_request *req = *_req;
 
        *_req = NULL;
 
        if (req == NULL)
-               return;
+               return TRUE;
+
+       i_assert(req->refcount > 0);
+       if (--req->refcount > 0)
+               return TRUE;
 
        auth_master_request_remove(req);
        event_unref(&req->event);
        pool_unref(&req->pool);
+       return FALSE;
 }
 
 void auth_master_request_set_event(struct auth_master_request *req,
@@ -64,7 +92,9 @@ static int
 auth_master_request_callback(struct auth_master_request *req,
                             const struct auth_master_reply *mreply)
 {
+       struct auth_master_connection *conn = req->conn;
        auth_master_request_callback_t *callback = req->callback;
+       struct auth_master_request *tmp_req = req;
        int ret;
 
        req->callback = NULL;
@@ -79,9 +109,21 @@ auth_master_request_callback(struct auth_master_request *req,
        if (callback == NULL)
                return 1;
 
+       if (conn->prev_ioloop != NULL) {
+               /* Don't let callback see that we've created our
+                  internal ioloop in case it wants to add some ios
+                  or timeouts. */
+               current_ioloop = conn->prev_ioloop;
+       }
+
+       auth_master_request_ref(tmp_req);
        req->in_callback = TRUE;
        ret = callback(mreply, req->context);
        req->in_callback = FALSE;
+       auth_master_request_unref(&tmp_req);
+
+       if (conn->prev_ioloop != NULL)
+               current_ioloop = conn->ioloop;
 
        if (ret == 0) {
                /* Application expects more replies for this request. */
@@ -101,6 +143,9 @@ int auth_master_request_got_reply(struct auth_master_request **_req,
 
        i_assert(!req->in_callback);
 
+       if (req->state < AUTH_MASTER_REQUEST_STATE_FINISHED)
+               req->state = AUTH_MASTER_REQUEST_STATE_REPLIED;
+
        e_debug(req->event, "Got reply: %s %s",
                reply, t_strarray_join(args, " "));
 
@@ -116,8 +161,10 @@ int auth_master_request_got_reply(struct auth_master_request **_req,
                        io_loop_stop(conn->ioloop);
                }
        } else {
+               if (req->state < AUTH_MASTER_REQUEST_STATE_FINISHED)
+                       req->state = AUTH_MASTER_REQUEST_STATE_FINISHED;
                auth_master_request_remove(req);
-               auth_master_request_free(&req);
+               auth_master_request_unref(&req);
        }
        return ret;
 }
@@ -133,10 +180,14 @@ void auth_master_request_abort(struct auth_master_request **_req)
        if (req->in_callback)
                return;
 
+       if (req->state >= AUTH_MASTER_REQUEST_STATE_FINISHED)
+               return;
+       req->state = AUTH_MASTER_REQUEST_STATE_ABORTED;
+
        e_debug(req->event, "Aborted");
 
        auth_master_request_remove(req);
-       auth_master_request_free(&req);
+       auth_master_request_unref(&req);
 }
 
 void auth_master_request_fail(struct auth_master_request **_req,
@@ -190,7 +241,9 @@ auth_master_request(struct auth_master_connection *conn, const char *cmd,
        pool = pool_alloconly_create("auth_master_request", 256 + args_size);
        req = p_new(pool, struct auth_master_request, 1);
        req->pool = pool;
+       req->refcount = 1;
        req->conn = conn;
+       req->create_stamp = ioloop_timeval;
 
        if (++conn->id_counter == 0) {
                /* avoid zero */
@@ -224,14 +277,15 @@ int auth_master_request_submit(struct auth_master_request **_req)
        if (req == NULL)
                return -1;
 
-       auth_master_set_io(conn);
-
        if (!conn->connected) {
                if (auth_master_connect(conn) < 0) {
-                       auth_master_unset_io(conn);
-                       auth_master_request_free(_req);
+                       // FIXME: handle
+                       /* we couldn't connect to auth now,
+                          so we probably can't in future either. */
+                       auth_master_request_unref(_req);
                        return -1;
                }
+               // FIXME: allow asynchronous connection
                i_assert(conn->connected);
                connection_input_resume(&conn->conn);
        }
@@ -253,11 +307,17 @@ int auth_master_request_submit(struct auth_master_request **_req)
        if (o_stream_flush(conn->conn.output) < 0) {
                e_error(conn->conn.event, "write(auth socket) failed: %s",
                        o_stream_get_error(conn->conn.output));
-               auth_master_unset_io(conn);
                auth_master_disconnect(conn);
-               auth_master_request_free(_req);
+               auth_master_request_unref(_req);
                return -1;
        }
+
+       hash_table_insert(conn->requests, POINTER_CAST(req->id), req);
+       req->sent = TRUE;
+
+       auth_master_connection_start_timeout(conn);
+       auth_master_stop_idle(conn);
+
        return 0;
 }
 
@@ -274,8 +334,25 @@ static void auth_master_request_stop(struct auth_master_request *req)
 bool auth_master_request_wait(struct auth_master_request *req)
 {
        struct auth_master_connection *conn = req->conn;
+       struct ioloop *ioloop, *prev_ioloop;
+       enum auth_master_request_state last_state;
        struct timeout *to;
-       bool was_corked = FALSE;
+       bool waiting = conn->waiting, was_corked = FALSE, freed;
+
+       if (req->state >= AUTH_MASTER_REQUEST_STATE_FINISHED)
+               return TRUE;
+
+       i_assert(auth_master_request_count(conn) > 0);
+
+       if ((conn->flags & AUTH_MASTER_FLAG_NO_INNER_IOLOOP) != 0)
+               ioloop = conn->ioloop;
+       else {
+               prev_ioloop = conn->ioloop;
+               if (!waiting)
+                       conn->prev_ioloop = prev_ioloop;
+               ioloop = io_loop_create();
+               auth_master_switch_ioloop_to(conn, ioloop);
+       }
 
        if (conn->conn.input != NULL &&
            i_stream_get_data_size(conn->conn.input) > 0)
@@ -285,21 +362,41 @@ bool auth_master_request_wait(struct auth_master_request *req)
                o_stream_uncork(conn->conn.output);
        }
 
+       /* either we're waiting for network I/O or we're getting out of a
+          callback using timeout_add_short(0) */
+       i_assert(io_loop_have_ios(ioloop) ||
+                io_loop_have_immediate_timeouts(ioloop));
+
+       auth_master_request_ref(req);
+       req->state = AUTH_MASTER_REQUEST_STATE_SENT;
+
        /* add stop handler */
        to = timeout_add_short(100, auth_master_request_stop, req);
 
        conn->waiting = TRUE;
-       io_loop_run(conn->ioloop);
-       conn->waiting = FALSE;
+       while (req->state < AUTH_MASTER_REQUEST_STATE_REPLIED)
+               io_loop_run(conn->ioloop);
+       conn->waiting = waiting;
 
        timeout_remove(&to);
 
        if (conn->conn.output != NULL && was_corked)
                o_stream_cork(conn->conn.output);
 
-       if (conn->requests_head != NULL)
-               return FALSE;
+       last_state = req->state;
+       freed = !auth_master_request_unref(&req);
+
+       if ((conn->flags & AUTH_MASTER_FLAG_NO_INNER_IOLOOP) == 0) {
+               auth_master_switch_ioloop_to(conn, prev_ioloop);
+               io_loop_destroy(&ioloop);
+               if (!waiting)
+                       conn->prev_ioloop = NULL;
+       }
+
+       return (freed || last_state >= AUTH_MASTER_REQUEST_STATE_FINISHED);
+}
 
-       auth_master_unset_io(conn);
-       return TRUE;
+unsigned int auth_master_request_count(struct auth_master_connection *conn)
+{
+       return conn->requests_count;
 }
index d6579738f40d045c13904ee81b4513d990995958..9cb9b2c5fe699557e67acc6b61f37353198bba31 100644 (file)
@@ -3,6 +3,7 @@
 #include "lib.h"
 #include "lib-signals.h"
 #include "array.h"
+#include "hash.h"
 #include "ioloop.h"
 #include "eacces-error.h"
 #include "net.h"
@@ -10,6 +11,7 @@
 #include "ostream.h"
 #include "str.h"
 #include "strescape.h"
+#include "time-util.h"
 
 #include "auth-master-private.h"
 
@@ -60,6 +62,7 @@ auth_master_init(const char *auth_socket_path, enum auth_master_flags flags)
        conn = p_new(pool, struct auth_master_connection, 1);
        conn->pool = pool;
        conn->refcount = 1;
+       conn->ioloop = current_ioloop;
        conn->auth_socket_path = p_strdup(pool, auth_socket_path);
        conn->flags = flags;
        conn->timeout_msecs = 1000*MASTER_AUTH_LOOKUP_TIMEOUT_SECS;
@@ -79,6 +82,13 @@ auth_master_init(const char *auth_socket_path, enum auth_master_flags flags)
 
        if ((flags & AUTH_MASTER_FLAG_NO_INNER_IOLOOP) != 0)
                conn->ioloop = current_ioloop;
+
+       hash_table_create_direct(&conn->requests, pool, 0);
+
+       /* Try to use auth request ID numbers from wider range to ease
+          debugging. */
+       conn->id_counter = i_rand_limit(32767) * 131072U;
+
        return conn;
 }
 
@@ -97,13 +107,16 @@ auth_master_connection_failure(struct auth_master_connection *conn,
        conn->connected = FALSE;
        conn->sent_handshake = FALSE;
 
-       timeout_remove(&conn->to);
+       timeout_remove(&conn->to_connect);
+       timeout_remove(&conn->to_request);
+       timeout_remove(&conn->to_idle);
 
        while (conn->requests_head != NULL) {
                req = conn->requests_head;
 
                auth_master_request_fail(&req, reason);
        }
+       i_assert(hash_table_count(conn->requests) == 0);
 
        if (conn->ioloop != NULL && conn->waiting)
                io_loop_stop(conn->ioloop);
@@ -136,6 +149,7 @@ auth_master_unref(struct auth_master_connection **_conn)
        auth_master_disconnect(conn);
        connection_deinit(&conn->conn);
        connection_list_deinit(&clist);
+       hash_table_destroy(&conn->requests);
        pool_unref(&conn->pool);
 }
 
@@ -187,17 +201,68 @@ static void auth_master_destroy(struct connection *_conn)
        }
 }
 
-static void auth_request_timeout(struct auth_master_connection *conn)
+static void
+auth_master_connection_timeout(struct auth_master_connection *conn)
 {
-       if (!connection_handshake_received(&conn->conn)) {
-               e_error(conn->conn.event, "Connecting timed out");
-               auth_master_connection_failure(conn, "Connecting timed out");
+       struct auth_master_request *req;
+       const char *reason;
+
+       timeout_remove(&conn->to_request);
+
+       conn->in_timeout = TRUE;
+       req = conn->requests_head;
+       while (req != NULL && auth_master_request_get_timeout_msecs(req) == 0) {
+               struct auth_master_request *req_next = req->next;
+               int msecs;
+
+               if (req->in_callback) {
+                       req = req_next;
+                       continue;
+               }
+
+               msecs = timeval_diff_msecs(&ioloop_timeval, &req->create_stamp);
+               reason = t_strdup_printf(
+                       "Auth server request timed out after %u.%03u secs",
+                        msecs / 1000, msecs % 1000);
+               auth_master_request_fail(&req, reason);
+
+               req = req_next;
+       }
+       conn->in_timeout = FALSE;
+
+       auth_master_connection_update_timeout(conn);
+}
+
+void auth_master_connection_update_timeout(struct auth_master_connection *conn)
+{
+       struct auth_master_request *req;
+
+       if (conn->in_timeout)
+               return;
+       if (!conn->connected) {
+               i_assert(conn->to_request == NULL);
                return;
        }
 
-       e_error(conn->conn.event, "Request timed out");
-       struct auth_master_request *req = conn->requests_head;
-       auth_master_request_abort(&req);
+       req = conn->requests_head;
+       while (req != NULL && req->in_callback)
+               req = req->next;
+
+       timeout_remove(&conn->to_request);
+       if (req == NULL)
+               return;
+
+       conn->to_request = timeout_add_to(
+               conn->ioloop, auth_master_request_get_timeout_msecs(req),
+               auth_master_connection_timeout, conn);
+}
+
+void auth_master_connection_start_timeout(struct auth_master_connection *conn)
+{
+       if (conn->to_request != NULL || conn->to_connect != NULL)
+               return;
+
+       auth_master_connection_update_timeout(conn);
 }
 
 static int
@@ -233,6 +298,10 @@ auth_master_handshake_line(struct connection *_conn, const char *line)
                        "Authentication server sent invalid SPID: %s", line);
                return -1;
        }
+
+       /* Handshake complete */
+       timeout_remove(&conn->to_connect);
+       auth_master_connection_update_timeout(conn);
        return 1;
 }
 
@@ -256,10 +325,11 @@ auth_master_handle_input(struct auth_master_connection *conn,
                return -1;
        }
 
-       req = conn->requests_head;
-       if (req == NULL || id != req->id) {
-               e_error(conn->conn.event,
-                       "Auth server sent reply with unknown ID %u", id);
+       req = hash_table_lookup(conn->requests, POINTER_CAST(id));
+       if (req == NULL) {
+               e_debug(conn->conn.event,
+                       "Auth server sent reply with unknown ID %u "
+                       "(this request was probably aborted)", id);
                return -1;
        }
 
@@ -316,11 +386,30 @@ static void auth_master_connected(struct connection *_conn, bool success)
        conn->connected = TRUE;
 }
 
+static void auth_master_connect_timeout(struct auth_master_connection *conn)
+{
+       e_error(conn->conn.event, "Connecting timed out");
+       auth_master_connection_failure(conn, "Connecting timed out");
+}
+
+static void
+auth_master_delayed_connect_failure(struct auth_master_connection *conn)
+{
+       e_debug(conn->conn.event, "Delayed connect failure");
+
+       i_assert(conn->to_connect != NULL);
+       timeout_remove(&conn->to_connect);
+       auth_master_connection_failure(conn, "Connect failed");
+}
+
 int auth_master_connect(struct auth_master_connection *conn)
 {
        if (conn->connected)
                return 0;
 
+       i_assert(conn->to_connect == NULL);
+       i_assert(conn->to_request == NULL);
+
        if (conn->ioloop != NULL)
                connection_switch_ioloop_to(&conn->conn, conn->ioloop);
        if (connection_client_connect(&conn->conn) < 0) {
@@ -332,59 +421,61 @@ int auth_master_connect(struct auth_master_connection *conn)
                        e_error(conn->conn.event, "connect(%s) failed: %m",
                                conn->auth_socket_path);
                }
+               conn->to_connect = timeout_add_to(
+                       conn->ioloop, 0,
+                       auth_master_delayed_connect_failure, conn);
                return -1;
        }
 
-       connection_input_halt(&conn->conn);
+       conn->to_connect = timeout_add_to(conn->ioloop, conn->timeout_msecs,
+                                         auth_master_connect_timeout, conn);
        return 0;
 }
+void auth_master_switch_ioloop_to(struct auth_master_connection *conn,
+                                 struct ioloop *ioloop)
+{
+       conn->ioloop = ioloop;
+
+       if (conn->to_connect != NULL) {
+               conn->to_connect =
+                       io_loop_move_timeout_to(ioloop, &conn->to_connect);
+       }
+       if (conn->to_request != NULL) {
+               conn->to_request =
+                       io_loop_move_timeout_to(ioloop, &conn->to_request);
+       }
+       if (conn->to_idle != NULL)
+               conn->to_idle = io_loop_move_timeout_to(ioloop, &conn->to_idle);
+       connection_switch_ioloop_to(&conn->conn, conn->ioloop);
+}
+
+void auth_master_switch_ioloop(struct auth_master_connection *conn)
+{
+       auth_master_switch_ioloop_to(conn, current_ioloop);
+}
 
 static void auth_master_idle_timeout(struct auth_master_connection *conn)
 {
        auth_master_disconnect(conn);
 }
 
-void auth_master_set_io(struct auth_master_connection *conn)
+void auth_master_check_idle(struct auth_master_connection *conn)
 {
-       if (conn->ioloop != NULL)
+       if ((conn->flags & AUTH_MASTER_FLAG_NO_IDLE_TIMEOUT) != 0)
                return;
-
-       timeout_remove(&conn->to);
-
-       conn->prev_ioloop = current_ioloop;
-       conn->ioloop = io_loop_create();
-       connection_switch_ioloop_to(&conn->conn, conn->ioloop);
-       if (conn->connected)
-               connection_input_resume(&conn->conn);
-
-       conn->to = timeout_add_to(conn->ioloop, conn->timeout_msecs,
-                                 auth_request_timeout, conn);
+       if (current_ioloop == NULL)
+               return;
+       i_assert(conn->to_idle == NULL);
+       if (conn->requests_head != NULL)
+               return;
+       conn->to_idle = timeout_add_to(conn->ioloop,
+                                      1000 * AUTH_MASTER_IDLE_SECS,
+                                      auth_master_idle_timeout, conn);
 }
 
-void auth_master_unset_io(struct auth_master_connection *conn)
+void auth_master_stop_idle(struct auth_master_connection *conn)
 {
-       if (conn->ioloop == NULL)
-               return;
-
-       if (conn->prev_ioloop != NULL) {
-               io_loop_set_current(conn->prev_ioloop);
-       }
-       if ((conn->flags & AUTH_MASTER_FLAG_NO_INNER_IOLOOP) == 0) {
-               io_loop_set_current(conn->ioloop);
-               connection_switch_ioloop_to(&conn->conn, conn->ioloop);
-               connection_input_halt(&conn->conn);
-               timeout_remove(&conn->to);
-               io_loop_destroy(&conn->ioloop);
-       }
-
-       if ((conn->flags & AUTH_MASTER_FLAG_NO_IDLE_TIMEOUT) == 0) {
-               if (conn->prev_ioloop == NULL)
-                       auth_master_disconnect(conn);
-               else if (conn->to == NULL) {
-                       conn->to = timeout_add(1000*AUTH_MASTER_IDLE_SECS,
-                                              auth_master_idle_timeout, conn);
-               }
-       }
+       timeout_remove(&conn->to_idle);
 }
 
 /*
@@ -848,8 +939,6 @@ auth_user_list_reply_callback(const struct auth_master_reply *reply,
 {
        const char *const *args = reply->args;
 
-       timeout_reset(ctx->conn->to);
-
        if (reply->errormsg != NULL) {
                e_error(ctx->event, "User listing failed: %s", reply->errormsg);
                ctx->req = NULL;
@@ -892,6 +981,8 @@ auth_master_user_list_init(struct auth_master_connection *conn,
        struct auth_master_user_list_ctx *ctx;
        string_t *args;
 
+       i_assert(auth_master_request_count(conn) == 0);
+
        ctx = i_new(struct auth_master_user_list_ctx, 1);
        ctx->conn = conn;
        ctx->username = str_new(default_pool, 128);
@@ -917,8 +1008,8 @@ auth_master_user_list_init(struct auth_master_connection *conn,
                ctx->failed = TRUE;
        else
                auth_master_request_set_event(ctx->req, ctx->event);
-       if (conn->prev_ioloop != NULL)
-               io_loop_set_current(conn->prev_ioloop);
+
+       connection_input_halt(&conn->conn);
 
        return ctx;
 }
@@ -936,22 +1027,25 @@ auth_master_user_do_list_next(struct auth_master_user_list_ctx *ctx)
        str_truncate(ctx->username, 0);
 
        /* try to read already buffered input */
-       line = i_stream_next_line(conn->conn.input);
-       if (line != NULL) {
-               T_BEGIN {
-                       conn->conn.v.input_line(&conn->conn, line);
-               } T_END;
+       if (conn->to_connect == NULL) {
+               line = i_stream_next_line(conn->conn.input);
+               if (line != NULL) {
+                       T_BEGIN {
+                               conn->conn.v.input_line(&conn->conn, line);
+                       } T_END;
+               }
+               if (ctx->finished || ctx->failed)
+                       return NULL;
+               if (str_len(ctx->username) > 0)
+                       return str_c(ctx->username);
        }
-       if (ctx->finished || ctx->failed)
-               return NULL;
-       if (str_len(ctx->username) > 0)
-               return str_c(ctx->username);
 
        /* wait for more data */
-       io_loop_set_current(conn->ioloop);
+       if (!conn->conn.disconnected)
+               connection_input_resume(&conn->conn);
        if (auth_master_request_wait(ctx->req))
                ctx->req = NULL;
-       io_loop_set_current(conn->prev_ioloop);
+       connection_input_halt(&conn->conn);
 
        if (ctx->finished || ctx->failed)
                return NULL;
@@ -973,6 +1067,7 @@ const char *auth_master_user_list_next(struct auth_master_user_list_ctx *ctx)
 int auth_master_user_list_deinit(struct auth_master_user_list_ctx **_ctx)
 {
        struct auth_master_user_list_ctx *ctx = *_ctx;
+       struct auth_master_connection *conn = ctx->conn;
        int ret = ctx->failed ? -1 : 0;
 
        *_ctx = NULL;
@@ -991,6 +1086,9 @@ int auth_master_user_list_deinit(struct auth_master_user_list_ctx **_ctx)
        }
 
        auth_master_request_abort(&ctx->req);
+       if (!conn->conn.disconnected)
+               connection_input_resume(&conn->conn);
+
        str_free(&ctx->username);
        event_unref(&ctx->event);
        i_free(ctx);
index ed1cdb9fb2f03b9a432f424f6c43e8cb5bdc573b..bf7b93f76b848b4280b70acb8699b1c8eb66329b 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "net.h"
 
+struct ioloop;
 struct auth_master_request;
 struct auth_master_reply;
 struct auth_master_connection;
@@ -52,6 +53,8 @@ void auth_master_request_set_event(struct auth_master_request *req,
 void auth_master_request_abort(struct auth_master_request **_req);
 bool auth_master_request_wait(struct auth_master_request *req);
 
+unsigned int auth_master_request_count(struct auth_master_connection *conn);
+
 /*
  * Connection
  */
@@ -69,6 +72,10 @@ void auth_master_set_timeout(struct auth_master_connection *conn,
 /* Returns the auth_socket_path */
 const char *auth_master_get_socket_path(struct auth_master_connection *conn);
 
+void auth_master_switch_ioloop_to(struct auth_master_connection *conn,
+                                 struct ioloop *ioloop);
+void auth_master_switch_ioloop(struct auth_master_connection *conn);
+
 /*
  * Lookup common
  */
index 7b23ce7b8e847bb4e2681ec4d23a019881136cb8..7790cfe26c999c9440e8f1dc50f0aace4560390a 100644 (file)
@@ -454,7 +454,9 @@ static bool test_client_passdb_timeout(void)
 
        ret = test_client_passdb_lookup_simple("holger", FALSE, &error);
        test_out("run (ret == -1)", ret == -1);
-       test_assert(error == NULL);
+       test_assert(error != NULL &&
+                   str_begins_with(
+                       error, "Auth server request timed out after"));
 
        return FALSE;
 }
@@ -500,7 +502,6 @@ static void test_passdb_fail(void)
        test_end();
 
        test_begin("passdb timeout");
-       test_expect_error_string("Request timed out");
        test_run_client_server(test_client_passdb_timeout,
                               test_server_passdb_fail);
        test_end();
@@ -641,7 +642,9 @@ static bool test_client_userdb_timeout(void)
 
        ret = test_client_userdb_lookup_simple("holger", FALSE, &error);
        test_out("run (ret == -1)", ret == -1);
-       test_assert(error == NULL);
+       test_assert(error != NULL &&
+                   str_begins_with(
+                       error, "Auth server request timed out after"));
 
        return FALSE;
 }
@@ -687,7 +690,6 @@ static void test_userdb_fail(void)
        test_end();
 
        test_begin("userdb timeout");
-       test_expect_error_string("Request timed out");
        test_run_client_server(test_client_userdb_timeout,
                               test_server_userdb_fail);
        test_end();