From 666d532354a71aa69ea84b5e76f0839bd59e900f Mon Sep 17 00:00:00 2001 From: Veerapuram Varadhan Date: Fri, 24 Feb 2006 20:13:17 +0000 Subject: [PATCH] add a "timeout" property, which gets passed from server to socket, and * 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 | 11 +++++++++++ libsoup/soup-connection.c | 33 +++++++++++++++++++++++++++++---- libsoup/soup-connection.h | 1 + libsoup/soup-session.c | 22 ++++++++++++++++++++++ libsoup/soup-session.h | 1 + libsoup/soup-socket.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ libsoup/soup-socket.h | 1 + 7 files changed, 109 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4791b18..d08d0a6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2006-02-25 Veerapuram Varadhan + + * 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 * configure.in: Don't use getaddrinfo() etc or try to support IPv6 diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c index 0169e13..c6622d4 100644 --- a/libsoup/soup-connection.c +++ b/libsoup/soup-connection.c @@ -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); diff --git a/libsoup/soup-connection.h b/libsoup/soup-connection.h index 5bfb6aa..098b93a 100644 --- a/libsoup/soup-connection.h +++ b/libsoup/soup-connection.h @@ -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; diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c index 6381939..95594b8 100644 --- a/libsoup/soup-session.c +++ b/libsoup/soup-session.c @@ -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), diff --git a/libsoup/soup-session.h b/libsoup/soup-session.h index b19f783..4814deb 100644 --- a/libsoup/soup-session.h +++ b/libsoup/soup-session.h @@ -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); diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c index a958af9..3b0bc68 100644 --- a/libsoup/soup-socket.c +++ b/libsoup/soup-socket.c @@ -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; diff --git a/libsoup/soup-socket.h b/libsoup/soup-socket.h index 0f95dc6..72766ff 100644 --- a/libsoup/soup-socket.h +++ b/libsoup/soup-socket.h @@ -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: -- 2.7.4