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
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))
PROP_SSL_CREDS,
PROP_ASYNC_CONTEXT,
PROP_TIMEOUT,
+ PROP_IDLE_TIMEOUT,
LAST_PROP
};
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
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);
"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));
}
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);
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);
}
}
+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;
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;
}
priv->connected = TRUE;
+ start_idle_timer (conn);
done:
g_signal_emit (conn, signals[CONNECT_RESULT], 0,
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);
}
}
- 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);
#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;
SoupURI *proxy_uri;
SoupAuth *proxy_auth;
- guint max_conns, max_conns_per_host;
-
char *ssl_ca_file;
SoupSSLCredentials *ssl_creds;
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.
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))
PROP_ASYNC_CONTEXT,
PROP_TIMEOUT,
PROP_USER_AGENT,
+ PROP_IDLE_TIMEOUT,
LAST_PROP
};
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);
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",
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);
} 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;
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;
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),
#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);