[evolution-data-server/evolution-data-server-3-12] [IMAPx] Automatically limit maximum number of concurrent connections



commit ddfe83e7d68912faa0f8c21f78cc9e9037d3c0e6
Author: Milan Crha <mcrha redhat com>
Date:   Thu Apr 17 18:34:00 2014 +0200

    [IMAPx] Automatically limit maximum number of concurrent connections
    
    In case a user has set more connections than the server has allowed,
    the IMAPx may limit the connection count automatically, instead of
    panic and close already established connections.

 camel/providers/imapx/camel-imapx-conn-manager.c |   43 +++++++++++++++++++---
 camel/providers/imapx/camel-imapx-server.c       |   25 ++++++++++++-
 camel/providers/imapx/camel-imapx-server.h       |    8 ++++
 camel/providers/imapx/camel-imapx-store.c        |   22 +++++++++++-
 camel/providers/imapx/camel-imapx-store.h        |    5 ++-
 5 files changed, 94 insertions(+), 9 deletions(-)
---
diff --git a/camel/providers/imapx/camel-imapx-conn-manager.c 
b/camel/providers/imapx/camel-imapx-conn-manager.c
index 0980c2e..a83cacf 100644
--- a/camel/providers/imapx/camel-imapx-conn-manager.c
+++ b/camel/providers/imapx/camel-imapx-conn-manager.c
@@ -46,6 +46,7 @@ struct _CamelIMAPXConnManagerPrivate {
        GList *connections;
        GWeakRef store;
        GRWLock rw_lock;
+       guint limit_max_connections;
 };
 
 struct _ConnectionInfo {
@@ -577,6 +578,10 @@ imapx_find_connection_unlocked (CamelIMAPXConnManager *con_man,
                camel_imapx_settings_get_concurrent_connections (
                CAMEL_IMAPX_SETTINGS (settings));
 
+       if (con_man->priv->limit_max_connections > 0 &&
+           con_man->priv->limit_max_connections < concurrent_connections)
+               concurrent_connections = con_man->priv->limit_max_connections;
+
        g_object_unref (settings);
 
        /* XXX Have a dedicated connection for INBOX ? */
@@ -757,9 +762,9 @@ imapx_create_new_connection_unlocked (CamelIMAPXConnManager *con_man,
         *     we should not have multiple IMAPX connections trying to
         *     authenticate at once, so this should be thread-safe.
         */
-       camel_imapx_store_set_connecting_server (imapx_store, is);
+       camel_imapx_store_set_connecting_server (imapx_store, is, con_man->priv->connections != NULL);
        success = camel_imapx_server_connect (is, cancellable, error);
-       camel_imapx_store_set_connecting_server (imapx_store, NULL);
+       camel_imapx_store_set_connecting_server (imapx_store, NULL, FALSE);
 
        if (!success) {
                g_clear_object (&is);
@@ -843,9 +848,35 @@ camel_imapx_conn_manager_get_connection (CamelIMAPXConnManager *con_man,
        /* Check if we got cancelled while waiting for the lock. */
        if (!g_cancellable_set_error_if_cancelled (cancellable, error)) {
                is = imapx_find_connection_unlocked (con_man, folder_name, for_expensive_job);
-               if (is == NULL)
-                       is = imapx_create_new_connection_unlocked (
-                               con_man, folder_name, cancellable, error);
+               if (is == NULL) {
+                       GError *local_error = NULL;
+
+                       is = imapx_create_new_connection_unlocked (con_man, folder_name, cancellable, 
&local_error);
+
+                       if (!is) {
+                               gboolean limit_connections =
+                                       g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR,
+                                       CAMEL_IMAPX_SERVER_ERROR_CONCURRENT_CONNECT_FAILED) &&
+                                       con_man->priv->connections;
+
+                               c ('*', "Failed to open a new connection, while having %d opened, with error: 
%s; will limit connections: %s\n",
+                                       g_list_length (con_man->priv->connections),
+                                       local_error ? local_error->message : "Unknown error",
+                                       limit_connections ? "yes" : "no");
+
+                               if (limit_connections) {
+                                       /* limit to one-less than current connection count - be nice to the 
server */
+                                       con_man->priv->limit_max_connections = g_list_length 
(con_man->priv->connections) - 1;
+                                       if (!con_man->priv->limit_max_connections)
+                                               con_man->priv->limit_max_connections = 1;
+
+                                       g_clear_error (&local_error);
+                                       is = imapx_find_connection_unlocked (con_man, folder_name, 
for_expensive_job);
+                               } else {
+                                       g_propagate_error (error, local_error);
+                               }
+                       }
+               }
        }
 
        CON_WRITE_UNLOCK (con_man);
@@ -930,6 +961,8 @@ camel_imapx_conn_manager_close_connections (CamelIMAPXConnManager *con_man)
 
        CON_WRITE_LOCK (con_man);
 
+       c('*', "Closing all %d connections\n", g_list_length (con_man->priv->connections));
+
        g_list_free_full (
                con_man->priv->connections,
                (GDestroyNotify) connection_info_cancel_and_unref);
diff --git a/camel/providers/imapx/camel-imapx-server.c b/camel/providers/imapx/camel-imapx-server.c
index 45e3e77..2c39f26 100644
--- a/camel/providers/imapx/camel-imapx-server.c
+++ b/camel/providers/imapx/camel-imapx-server.c
@@ -82,6 +82,8 @@
 #define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
 #endif
 
+G_DEFINE_QUARK (camel-imapx-server-error-quark, camel_imapx_server_error)
+
 extern gint camel_application_is_exiting;
 
 /* Job-specific structs */
@@ -5015,8 +5017,27 @@ camel_imapx_server_authenticate (CamelIMAPXServer *is,
                result = CAMEL_AUTHENTICATION_ERROR;
        else if (ic->status->result == IMAPX_OK)
                result = CAMEL_AUTHENTICATION_ACCEPTED;
-       else
-               result = CAMEL_AUTHENTICATION_REJECTED;
+       else if (ic->status->result == IMAPX_NO) {
+               if (camel_imapx_store_is_connecting_concurrent_connection (store)) {
+                       /* At least one connection succeeded, probably max connection limit
+                          set on the server had been reached, thus use special error code
+                          for it, to instruct the connection manager to decrease the limit
+                          and use already created connection. */
+                       g_set_error_literal (
+                               error, CAMEL_IMAPX_SERVER_ERROR,
+                               CAMEL_IMAPX_SERVER_ERROR_CONCURRENT_CONNECT_FAILED,
+                               ic->status->text ? ic->status->text : _("Unknown error"));
+                       result = CAMEL_AUTHENTICATION_ERROR;
+               } else {
+                       result = CAMEL_AUTHENTICATION_REJECTED;
+               }
+       } else {
+               g_set_error_literal (
+                       error, CAMEL_SERVICE_ERROR,
+                       CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
+                       ic->status->text ? ic->status->text : _("Unknown error"));
+               result = CAMEL_AUTHENTICATION_ERROR;
+       }
 
        /* Forget old capabilities after login. */
        if (result == CAMEL_AUTHENTICATION_ACCEPTED) {
diff --git a/camel/providers/imapx/camel-imapx-server.h b/camel/providers/imapx/camel-imapx-server.h
index 3842b33..1bc4c22 100644
--- a/camel/providers/imapx/camel-imapx-server.h
+++ b/camel/providers/imapx/camel-imapx-server.h
@@ -44,8 +44,16 @@
        (G_TYPE_INSTANCE_GET_CLASS \
        ((obj), CAMEL_TYPE_IMAPX_SERVER, CamelIMAPXServerClass))
 
+#define CAMEL_IMAPX_SERVER_ERROR (camel_imapx_server_error_quark ())
+
 G_BEGIN_DECLS
 
+typedef enum {
+       CAMEL_IMAPX_SERVER_ERROR_CONCURRENT_CONNECT_FAILED
+} CamelIMAPXServerError;
+
+GQuark         camel_imapx_server_error_quark          (void) G_GNUC_CONST;
+
 /* Avoid a circular reference. */
 struct _CamelIMAPXStore;
 struct _CamelIMAPXSettings;
diff --git a/camel/providers/imapx/camel-imapx-store.c b/camel/providers/imapx/camel-imapx-store.c
index b69ed0f..eb7c25f 100644
--- a/camel/providers/imapx/camel-imapx-store.c
+++ b/camel/providers/imapx/camel-imapx-store.c
@@ -57,7 +57,10 @@
 
 struct _CamelIMAPXStorePrivate {
        CamelIMAPXConnManager *con_man;
+
        CamelIMAPXServer *connecting_server;
+       gboolean is_concurrent_connection;
+
        gulong mailbox_created_handler_id;
        gulong mailbox_renamed_handler_id;
        gulong mailbox_updated_handler_id;
@@ -2496,7 +2499,8 @@ camel_imapx_store_ref_server (CamelIMAPXStore *store,
 /* The caller should hold the store->priv->server_lock already, when calling this */
 void
 camel_imapx_store_set_connecting_server (CamelIMAPXStore *store,
-                                        CamelIMAPXServer *server)
+                                        CamelIMAPXServer *server,
+                                        gboolean is_concurrent_connection)
 {
        g_return_if_fail (CAMEL_IS_IMAPX_STORE (store));
 
@@ -2511,9 +2515,25 @@ camel_imapx_store_set_connecting_server (CamelIMAPXStore *store,
                        store->priv->connecting_server = g_object_ref (server);
        }
 
+       store->priv->is_concurrent_connection = is_concurrent_connection;
+
        g_mutex_unlock (&store->priv->server_lock);
 }
 
+gboolean
+camel_imapx_store_is_connecting_concurrent_connection (CamelIMAPXStore *imapx_store)
+{
+       gboolean res;
+
+       g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), FALSE);
+
+       g_mutex_lock (&imapx_store->priv->server_lock);
+       res = imapx_store->priv->is_concurrent_connection;
+       g_mutex_unlock (&imapx_store->priv->server_lock);
+
+       return res;
+}
+
 void
 camel_imapx_store_folder_op_done (CamelIMAPXStore *store,
                                  CamelIMAPXServer *server,
diff --git a/camel/providers/imapx/camel-imapx-store.h b/camel/providers/imapx/camel-imapx-store.h
index 8ef34ba..80cde3c 100644
--- a/camel/providers/imapx/camel-imapx-store.h
+++ b/camel/providers/imapx/camel-imapx-store.h
@@ -71,7 +71,10 @@ CamelIMAPXServer *
                                                 GError **error);
 void           camel_imapx_store_set_connecting_server
                                                (CamelIMAPXStore *store,
-                                                CamelIMAPXServer *server);
+                                                CamelIMAPXServer *server,
+                                                gboolean is_concurrent_connection);
+gboolean       camel_imapx_store_is_connecting_concurrent_connection
+                                               (CamelIMAPXStore *imapx_store);
 void           camel_imapx_store_folder_op_done
                                                (CamelIMAPXStore *store,
                                                 CamelIMAPXServer *server,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]