Add a new property, SOUP_SESSION_IDLE_TIMEOUT, to specify a timeout after
authorDan Winship <danw@src.gnome.org>
Tue, 18 Mar 2008 23:38:00 +0000 (23:38 +0000)
committerDan Winship <danw@src.gnome.org>
Tue, 18 Mar 2008 23:38:00 +0000 (23:38 +0000)
* libsoup/soup-session.c (soup_session_class_init): Add a new
property, SOUP_SESSION_IDLE_TIMEOUT, to specify a timeout after
which idle connections should be closed.
(soup_session_get_connection): pass the idle_timeout value on to
the connection.

* libsoup/soup-connection.c (soup_connection_class_init): Add
SOUP_CONNECTION_IDLE_TIMEOUT.
(start_idle_timer, stop_idle_timer): add/remove a timeout to call
soup_connection_disconnect().
(socket_connect_result, soup_connection_connect_sync): start the
idle timer after connection is complete
(set_current_request): call stop_idle_timer() when starting a new
request
(clear_current_request): call start_idle_timer() when finishing a
request
(dispose): call stop_idle_timer() when destroying the connection

#518214, based on a patch from Jorn Baayen.

svn path=/trunk/; revision=1112

ChangeLog
libsoup/soup-connection.c
libsoup/soup-connection.h
libsoup/soup-session.c
libsoup/soup-session.h

index 431cf66..b7dce6c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,27 @@
 2008-03-18  Dan Winship  <danw@gnome.org>
 
+       * libsoup/soup-session.c (soup_session_class_init): Add a new
+       property, SOUP_SESSION_IDLE_TIMEOUT, to specify a timeout after
+       which idle connections should be closed.
+       (soup_session_get_connection): pass the idle_timeout value on to
+       the connection.
+
+       * libsoup/soup-connection.c (soup_connection_class_init): Add
+       SOUP_CONNECTION_IDLE_TIMEOUT.
+       (start_idle_timer, stop_idle_timer): add/remove a timeout to call
+       soup_connection_disconnect().
+       (socket_connect_result, soup_connection_connect_sync): start the
+       idle timer after connection is complete
+       (set_current_request): call stop_idle_timer() when starting a new
+       request
+       (clear_current_request): call start_idle_timer() when finishing a
+       request
+       (dispose): call stop_idle_timer() when destroying the connection
+
+       #518214, based on a patch from Jorn Baayen.
+
+2008-03-18  Dan Winship  <danw@gnome.org>
+
        * libsoup/soup-message-io.c (soup_message_io_unpause): if delaying
        the unpause to idle time, we need to keep track of the idle source
        (soup_message_io_stop): if the message is waiting to unpause
index 4539902..cdc9ea9 100644 (file)
@@ -52,7 +52,8 @@ typedef struct {
        SoupMessage *cur_req;
        time_t       last_used;
        gboolean     connected, in_use;
-       guint        timeout;
+       guint        io_timeout, idle_timeout;
+       GSource     *idle_timeout_src;
 } SoupConnectionPrivate;
 #define SOUP_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_CONNECTION, SoupConnectionPrivate))
 
@@ -75,6 +76,7 @@ enum {
        PROP_SSL_CREDS,
        PROP_ASYNC_CONTEXT,
        PROP_TIMEOUT,
+       PROP_IDLE_TIMEOUT,
 
        LAST_PROP
 };
@@ -84,14 +86,14 @@ static void set_property (GObject *object, guint prop_id,
 static void get_property (GObject *object, guint prop_id,
                          GValue *value, GParamSpec *pspec);
 
+static void stop_idle_timer (SoupConnectionPrivate *priv);
 static void send_request (SoupConnection *conn, SoupMessage *req);
 static void clear_current_request (SoupConnection *conn);
 
 static void
 soup_connection_init (SoupConnection *conn)
 {
-       SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
-       priv->timeout = 0;
+       ;
 }
 
 static void
@@ -114,6 +116,11 @@ static void
 dispose (GObject *object)
 {
        SoupConnection *conn = SOUP_CONNECTION (object);
+       SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+
+       stop_idle_timer (priv);
+       /* Make sure clear_current_request doesn't re-establish the timeout */
+       priv->idle_timeout = 0;
 
        clear_current_request (conn);
        soup_connection_disconnect (conn);
@@ -198,6 +205,13 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
                                   "Value in seconds to timeout a blocking I/O",
                                   0, G_MAXUINT, 0,
                                   G_PARAM_READWRITE));
+       g_object_class_install_property (
+               object_class, PROP_IDLE_TIMEOUT,
+               g_param_spec_uint (SOUP_CONNECTION_IDLE_TIMEOUT,
+                                  "Idle Timeout",
+                                  "Connection lifetime when idle",
+                                  0, G_MAXUINT, 0,
+                                  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 }
 
 
@@ -272,7 +286,10 @@ set_property (GObject *object, guint prop_id,
                        g_main_context_ref (priv->async_context);
                break;
        case PROP_TIMEOUT:
-               priv->timeout = g_value_get_uint (value);
+               priv->io_timeout = g_value_get_uint (value);
+               break;
+       case PROP_IDLE_TIMEOUT:
+               priv->idle_timeout = g_value_get_uint (value);
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -303,7 +320,10 @@ get_property (GObject *object, guint prop_id,
                g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
                break;
        case PROP_TIMEOUT:
-               g_value_set_uint (value, priv->timeout);
+               g_value_set_uint (value, priv->io_timeout);
+               break;
+       case PROP_IDLE_TIMEOUT:
+               g_value_set_uint (value, priv->idle_timeout);
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -311,11 +331,42 @@ get_property (GObject *object, guint prop_id,
        }
 }
 
+static gboolean
+idle_timeout (gpointer conn)
+{
+       soup_connection_disconnect (conn);
+       return FALSE;
+}
+
+static void
+start_idle_timer (SoupConnection *conn)
+{
+       SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+
+       if (priv->idle_timeout > 0 && !priv->idle_timeout_src) {
+               priv->idle_timeout_src =
+                       soup_add_timeout (priv->async_context,
+                                         priv->idle_timeout * 1000,
+                                         idle_timeout, conn);
+       }
+}
+
+static void
+stop_idle_timer (SoupConnectionPrivate *priv)
+{
+       if (priv->idle_timeout_src) {
+               g_source_destroy (priv->idle_timeout_src);
+               priv->idle_timeout_src = NULL;
+       }
+}
+
 static void
 set_current_request (SoupConnectionPrivate *priv, SoupMessage *req)
 {
        g_return_if_fail (priv->cur_req == NULL);
 
+       stop_idle_timer (priv);
+
        soup_message_set_io_status (req, SOUP_MESSAGE_IO_STATUS_RUNNING);
        priv->cur_req = req;
        priv->in_use = TRUE;
@@ -328,6 +379,7 @@ clear_current_request (SoupConnection *conn)
        SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
 
        priv->in_use = FALSE;
+       start_idle_timer (conn);
        if (priv->cur_req) {
                SoupMessage *cur_req = priv->cur_req;
 
@@ -453,6 +505,7 @@ socket_connect_result (SoupSocket *sock, guint status, gpointer user_data)
        }
 
        priv->connected = TRUE;
+       start_idle_timer (conn);
 
  done:
        g_signal_emit (conn, signals[CONNECT_RESULT], 0,
@@ -527,7 +580,7 @@ soup_connection_connect_sync (SoupConnection *conn)
                soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, addr,
                                 SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
                                 SOUP_SOCKET_FLAG_NONBLOCKING, FALSE,
-                                SOUP_SOCKET_TIMEOUT, priv->timeout,
+                                SOUP_SOCKET_TIMEOUT, priv->io_timeout,
                                 NULL);
 
        status = soup_socket_connect_sync (priv->socket, NULL);
@@ -574,9 +627,10 @@ soup_connection_connect_sync (SoupConnection *conn)
                }
        }
 
-       if (SOUP_STATUS_IS_SUCCESSFUL (status))
+       if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
                priv->connected = TRUE;
-       else {
+               start_idle_timer (conn);
+       } else {
        fail:
                if (priv->socket) {
                        g_object_unref (priv->socket);
index 08c21be..548cb32 100644 (file)
@@ -49,7 +49,8 @@ typedef void  (*SoupConnectionCallback)        (SoupConnection   *conn,
 #define SOUP_CONNECTION_PROXY_URI       "proxy-uri"
 #define SOUP_CONNECTION_SSL_CREDENTIALS "ssl-creds"
 #define SOUP_CONNECTION_ASYNC_CONTEXT   "async-context"
-#define SOUP_CONNECTION_TIMEOUT                "timeout"
+#define SOUP_CONNECTION_TIMEOUT         "timeout"
+#define SOUP_CONNECTION_IDLE_TIMEOUT    "idle-timeout"
 
 SoupConnection *soup_connection_new            (const char       *propname1,
                                                ...) G_GNUC_NULL_TERMINATED;
index b32ff13..d200b3c 100644 (file)
@@ -68,8 +68,6 @@ typedef struct {
        SoupURI *proxy_uri;
        SoupAuth *proxy_auth;
 
-       guint max_conns, max_conns_per_host;
-
        char *ssl_ca_file;
        SoupSSLCredentials *ssl_creds;
 
@@ -83,6 +81,8 @@ typedef struct {
        GHashTable *hosts; /* SoupURI -> SoupSessionHost */
        GHashTable *conns; /* SoupConnection -> SoupSessionHost */
        guint num_conns;
+       guint max_conns, max_conns_per_host;
+       guint io_timeout, idle_timeout;
 
        /* Must hold the host_lock before potentially creating a
         * new SoupSessionHost, or adding/removing a connection.
@@ -91,11 +91,6 @@ typedef struct {
        GMutex *host_lock;
 
        GMainContext *async_context;
-
-       /* Holds the timeout value for the connection, when
-          no response is received.
-       */
-       guint timeout;
 } SoupSessionPrivate;
 #define SOUP_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION, SoupSessionPrivate))
 
@@ -141,6 +136,7 @@ enum {
        PROP_ASYNC_CONTEXT,
        PROP_TIMEOUT,
        PROP_USER_AGENT,
+       PROP_IDLE_TIMEOUT,
 
        LAST_PROP
 };
@@ -165,8 +161,6 @@ soup_session_init (SoupSession *session)
        priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
        priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
 
-       priv->timeout = 0;
-
        priv->auth_manager = soup_auth_manager_new (session);
        soup_auth_manager_add_type (priv->auth_manager, SOUP_TYPE_AUTH_BASIC);
        soup_auth_manager_add_type (priv->auth_manager, SOUP_TYPE_AUTH_DIGEST);
@@ -410,6 +404,13 @@ soup_session_class_init (SoupSessionClass *session_class)
                                  SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT,
                                  G_PARAM_READWRITE));
        g_object_class_install_property (
+               object_class, PROP_IDLE_TIMEOUT,
+               g_param_spec_uint (SOUP_SESSION_IDLE_TIMEOUT,
+                                  "Idle Timeout",
+                                  "Connection lifetime when idle",
+                                  0, G_MAXUINT, 0,
+                                  G_PARAM_READWRITE));
+       g_object_class_install_property (
                object_class, PROP_USE_NTLM,
                g_param_spec_boolean (SOUP_SESSION_USE_NTLM,
                                      "Use NTLM",
@@ -568,7 +569,7 @@ set_property (GObject *object, guint prop_id,
                        g_main_context_ref (priv->async_context);
                break;
        case PROP_TIMEOUT:
-               priv->timeout = g_value_get_uint (value);
+               priv->io_timeout = g_value_get_uint (value);
                break;
        case PROP_USER_AGENT:
                g_free (priv->user_agent);
@@ -585,6 +586,9 @@ set_property (GObject *object, guint prop_id,
                } else
                        priv->user_agent = g_strdup (user_agent);
                break;
+       case PROP_IDLE_TIMEOUT:
+               priv->idle_timeout = g_value_get_uint (value);
+               break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
@@ -618,11 +622,14 @@ get_property (GObject *object, guint prop_id,
                g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
                break;
        case PROP_TIMEOUT:
-               g_value_set_uint (value, priv->timeout);
+               g_value_set_uint (value, priv->io_timeout);
                break;
        case PROP_USER_AGENT:
                g_value_set_string (value, priv->user_agent);
                break;
+       case PROP_IDLE_TIMEOUT:
+               g_value_set_uint (value, priv->idle_timeout);
+               break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
@@ -1017,7 +1024,8 @@ soup_session_get_connection (SoupSession *session, SoupMessage *msg,
                SOUP_CONNECTION_PROXY_URI, priv->proxy_uri,
                SOUP_CONNECTION_SSL_CREDENTIALS, priv->ssl_creds,
                SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
-               SOUP_CONNECTION_TIMEOUT, priv->timeout,
+               SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
+               SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
                NULL);
        g_signal_connect (conn, "connect_result",
                          G_CALLBACK (connect_result),
index 495c077..fcbd30a 100644 (file)
@@ -61,8 +61,9 @@ GType soup_session_get_type (void);
 #define SOUP_SESSION_USE_NTLM           "use-ntlm"
 #define SOUP_SESSION_SSL_CA_FILE        "ssl-ca-file"
 #define SOUP_SESSION_ASYNC_CONTEXT      "async-context"
-#define SOUP_SESSION_TIMEOUT           "timeout"
+#define SOUP_SESSION_TIMEOUT            "timeout"
 #define SOUP_SESSION_USER_AGENT         "user-agent"
+#define SOUP_SESSION_IDLE_TIMEOUT       "idle-timeout"
 
 GMainContext   *soup_session_get_async_context(SoupSession           *session);