add a "timeout" property, which gets passed from server to socket, and
authorVeerapuram Varadhan <vvaradan@src.gnome.org>
Fri, 24 Feb 2006 20:13:17 +0000 (20:13 +0000)
committerVeerapuram Varadhan <vvaradan@src.gnome.org>
Fri, 24 Feb 2006 20:13:17 +0000 (20:13 +0000)
        * libsoup/soup-connection.c:
        * libsoup/soup-session.c:
        * libsoup/soup-socket.c: add a "timeout" property,
        which gets passed from server to socket, and session to connection
        to socket, allowing blocking non-responsive sync connections to
        return.  Combination of "EAGAIN" && "Blocking" connection is treated
        as error and the connection will be terminated and the control
        is returned to the caller immediately.

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

index 4791b18..d08d0a6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2006-02-25  Veerapuram Varadhan <vvaradhan@novell.com>
+
+       * libsoup/soup-connection.c:
+       * libsoup/soup-session.c:
+       * libsoup/soup-socket.c: add a "timeout" property,
+       which gets passed from server to socket, and session to connection
+       to socket, allowing blocking non-responsive sync connections to 
+       return.  Combination of "EAGAIN" && "Blocking" connection is treated
+       as error and the connection will be terminated and the control 
+       is returned to the caller immediately.
+               
 2006-02-02  Tor Lillqvist  <tml@novell.com>
 
        * configure.in: Don't use getaddrinfo() etc or try to support IPv6
index 0169e13..c6622d4 100644 (file)
@@ -46,6 +46,7 @@ typedef struct {
        SoupMessage *cur_req;
        time_t       last_used;
        gboolean     connected, in_use;
+       guint        timeout;
 } SoupConnectionPrivate;
 #define SOUP_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_CONNECTION, SoupConnectionPrivate))
 
@@ -69,6 +70,7 @@ enum {
        PROP_SSL_CREDS,
        PROP_MESSAGE_FILTER,
        PROP_ASYNC_CONTEXT,
+       PROP_TIMEOUT,
 
        LAST_PROP
 };
@@ -84,6 +86,8 @@ 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
@@ -254,6 +258,13 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
                                      "Async GMainContext",
                                      "GMainContext to dispatch this connection's async I/O in",
                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+       g_object_class_install_property (
+               object_class, PROP_TIMEOUT,
+               g_param_spec_uint (SOUP_CONNECTION_TIMEOUT,
+                                  "Timeout value",
+                                  "Value in seconds to timeout a blocking I/O",
+                                  0, G_MAXUINT, 0,
+                                  G_PARAM_READWRITE));
 }
 
 
@@ -325,6 +336,9 @@ set_property (GObject *object, guint prop_id,
                if (priv->async_context)
                        g_main_context_ref (priv->async_context);
                break;
+       case PROP_TIMEOUT:
+               priv->timeout = g_value_get_uint (value);
+               break;
        default:
                break;
        }
@@ -355,6 +369,9 @@ get_property (GObject *object, guint prop_id,
        case PROP_ASYNC_CONTEXT:
                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);
+               break;
        default:
                break;
        }
@@ -558,6 +575,7 @@ guint
 soup_connection_connect_sync (SoupConnection *conn)
 {
        SoupConnectionPrivate *priv;
+       SoupAddress* addr;
        guint status;
 
        g_return_val_if_fail (SOUP_IS_CONNECTION (conn), SOUP_STATUS_MALFORMED);
@@ -565,14 +583,21 @@ soup_connection_connect_sync (SoupConnection *conn)
        g_return_val_if_fail (priv->socket == NULL, SOUP_STATUS_MALFORMED);
 
        priv->socket =
-               soup_socket_client_new_sync (priv->conn_uri->host,
-                                            priv->conn_uri->port,
-                                            priv->ssl_creds,
-                                            &status);
+               soup_socket_new (SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
+                                SOUP_SOCKET_FLAG_NONBLOCKING, FALSE,
+                                SOUP_SOCKET_TIMEOUT, priv->timeout,
+                                NULL);
+
+       addr = soup_address_new (priv->conn_uri->host,
+                                priv->conn_uri->port);
+
+       status = soup_socket_connect (priv->socket, addr);
+       g_object_unref (addr);
 
        if (!SOUP_STATUS_IS_SUCCESSFUL (status))
                goto fail;
 
+               
        g_signal_connect (priv->socket, "disconnected",
                          G_CALLBACK (socket_disconnected), conn);
 
index 5bfb6aa..098b93a 100644 (file)
@@ -61,6 +61,7 @@ typedef void  (*SoupConnectionCallback)        (SoupConnection   *conn,
 #define SOUP_CONNECTION_SSL_CREDENTIALS "ssl-creds"
 #define SOUP_CONNECTION_MESSAGE_FILTER  "message-filter"
 #define SOUP_CONNECTION_ASYNC_CONTEXT   "async-context"
+#define SOUP_CONNECTION_TIMEOUT                "timeout"
 
 SoupConnection *soup_connection_new            (const char       *propname1,
                                                ...) G_GNUC_NULL_TERMINATED;
index 6381939..95594b8 100644 (file)
@@ -56,6 +56,11 @@ 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))
 
@@ -97,6 +102,7 @@ enum {
        PROP_USE_NTLM,
        PROP_SSL_CA_FILE,
        PROP_ASYNC_CONTEXT,
+       PROP_TIMEOUT,
 
        LAST_PROP
 };
@@ -119,6 +125,8 @@ 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;
 }
 
 static gboolean
@@ -319,6 +327,13 @@ soup_session_class_init (SoupSessionClass *session_class)
                                      "Async GMainContext",
                                      "The GMainContext to dispatch async I/O in",
                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+       g_object_class_install_property (
+               object_class, PROP_TIMEOUT,
+               g_param_spec_uint (SOUP_SESSION_TIMEOUT,
+                                  "Timeout value",
+                                  "Value in seconds to timeout a blocking I/O",
+                                  0, G_MAXUINT, 0,
+                                  G_PARAM_READWRITE));
 }
 
 static void
@@ -415,6 +430,9 @@ set_property (GObject *object, guint prop_id,
                if (priv->async_context)
                        g_main_context_ref (priv->async_context);
                break;
+       case PROP_TIMEOUT:
+               priv->timeout = g_value_get_uint (value);
+               break;
        default:
                break;
        }
@@ -448,6 +466,9 @@ get_property (GObject *object, guint prop_id,
        case PROP_ASYNC_CONTEXT:
                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);
+               break;
        default:
                break;
        }
@@ -1163,6 +1184,7 @@ soup_session_get_connection (SoupSession *session, SoupMessage *msg,
                SOUP_CONNECTION_SSL_CREDENTIALS, priv->ssl_creds,
                SOUP_CONNECTION_MESSAGE_FILTER, session,
                SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
+               SOUP_CONNECTION_TIMEOUT, priv->timeout,
                NULL);
        g_signal_connect (conn, "connect_result",
                          G_CALLBACK (connect_result),
index b19f783..4814deb 100644 (file)
@@ -54,6 +54,7 @@ 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"
 
 void            soup_session_add_filter       (SoupSession           *session,
                                               SoupMessageFilter     *filter);
index a958af9..3b0bc68 100644 (file)
@@ -46,6 +46,7 @@ enum {
        PROP_IS_SERVER,
        PROP_SSL_CREDENTIALS,
        PROP_ASYNC_CONTEXT,
+       PROP_TIMEOUT,
 
        LAST_PROP
 };
@@ -68,6 +69,7 @@ typedef struct {
        GByteArray     *read_buf;
 
        GMutex *iolock, *addrlock;
+       guint timeout;
 } SoupSocketPrivate;
 #define SOUP_SOCKET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SOCKET, SoupSocketPrivate))
 
@@ -105,6 +107,7 @@ soup_socket_init (SoupSocket *sock)
        priv->cloexec = FALSE;
        priv->addrlock = g_mutex_new ();
        priv->iolock = g_mutex_new ();
+       priv->timeout = 0;
 }
 
 static void
@@ -299,6 +302,14 @@ soup_socket_class_init (SoupSocketClass *socket_class)
                                      "The GMainContext to dispatch this socket's async I/O in",
                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
+       g_object_class_install_property (
+               object_class, PROP_TIMEOUT,
+               g_param_spec_uint (SOUP_SOCKET_TIMEOUT,
+                                  "Timeout value",
+                                  "Value in seconds to timeout a blocking I/O",
+                                  0, G_MAXUINT, 0,
+                                  G_PARAM_READWRITE));
+
 #ifdef G_OS_WIN32
        /* Make sure WSAStartup() gets called. */
        soup_address_get_type ();
@@ -310,6 +321,7 @@ static void
 update_fdflags (SoupSocketPrivate *priv)
 {
        int opt;
+       struct timeval timeout;
 #ifndef G_OS_WIN32
        int flags;
 #endif
@@ -349,6 +361,16 @@ update_fdflags (SoupSocketPrivate *priv)
        setsockopt (priv->sockfd, IPPROTO_TCP,
                    TCP_NODELAY, (void *) &opt, sizeof (opt));
 
+       timeout.tv_sec = priv->timeout;
+       timeout.tv_usec = 0;
+       setsockopt (priv->sockfd, SOL_SOCKET,
+                   SO_RCVTIMEO, (void *) &timeout, sizeof (timeout));
+
+       timeout.tv_sec = priv->timeout;
+       timeout.tv_usec = 0;
+       setsockopt (priv->sockfd, SOL_SOCKET,
+                   SO_SNDTIMEO, (void *) &timeout, sizeof (timeout));
+
        opt = (priv->reuseaddr != 0);
        setsockopt (priv->sockfd, SOL_SOCKET,
                    SO_REUSEADDR, (void *) &opt, sizeof (opt));
@@ -385,6 +407,9 @@ set_property (GObject *object, guint prop_id,
                if (priv->async_context)
                        g_main_context_ref (priv->async_context);
                break;
+       case PROP_TIMEOUT:
+               priv->timeout = g_value_get_uint (value);
+               break;
        default:
                break;
        }
@@ -418,6 +443,9 @@ get_property (GObject *object, guint prop_id,
        case PROP_ASYNC_CONTEXT:
                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);
+               break;
        default:
                break;
        }
@@ -1074,6 +1102,13 @@ read_from_network (SoupSocket *sock, gpointer buffer, gsize len, gsize *nread)
                if (*nread > 0)
                        return SOUP_SOCKET_OK;
 
+               /* If the connection/session is sync and  we get
+                  EAGAIN or EWOULDBLOCK, then it will be socket timeout
+                  and should be treated as an error condition.
+               */
+               if (!priv->non_blocking)
+                       return SOUP_SOCKET_ERROR;
+
                if (!priv->read_src) {
                        priv->read_src =
                                soup_add_io_watch (priv->async_context,
@@ -1314,6 +1349,15 @@ soup_socket_write (SoupSocket *sock, gconstpointer buffer,
                                   NULL);
        }
 
+       /* If the connection/session is sync and  we get
+          EAGAIN or EWOULDBLOCK, then it will be socket timeout
+          and should be treated as an error condition.
+       */
+       if (!priv->non_blocking && status == G_IO_STATUS_AGAIN) {
+               g_mutex_unlock (priv->iolock);
+               return SOUP_SOCKET_ERROR;
+       }
+
        if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN) {
                g_mutex_unlock (priv->iolock);
                return SOUP_SOCKET_ERROR;
index 0f95dc6..72766ff 100644 (file)
@@ -39,6 +39,7 @@ typedef struct {
 #define SOUP_SOCKET_IS_SERVER        "is-server"
 #define SOUP_SOCKET_SSL_CREDENTIALS  "ssl-creds"
 #define SOUP_SOCKET_ASYNC_CONTEXT    "async-context"
+#define SOUP_SOCKET_TIMEOUT         "timeout"
 
 /**
  * SoupSocketCallback: