+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
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))
PROP_SSL_CREDS,
PROP_MESSAGE_FILTER,
PROP_ASYNC_CONTEXT,
+ PROP_TIMEOUT,
LAST_PROP
};
static void
soup_connection_init (SoupConnection *conn)
{
+ SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+ priv->timeout = 0;
}
static void
"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));
}
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;
}
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;
}
soup_connection_connect_sync (SoupConnection *conn)
{
SoupConnectionPrivate *priv;
+ SoupAddress* addr;
guint status;
g_return_val_if_fail (SOUP_IS_CONNECTION (conn), SOUP_STATUS_MALFORMED);
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);
#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;
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_USE_NTLM,
PROP_SSL_CA_FILE,
PROP_ASYNC_CONTEXT,
+ PROP_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;
}
static gboolean
"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
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;
}
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;
}
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),
#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);
PROP_IS_SERVER,
PROP_SSL_CREDENTIALS,
PROP_ASYNC_CONTEXT,
+ PROP_TIMEOUT,
LAST_PROP
};
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))
priv->cloexec = FALSE;
priv->addrlock = g_mutex_new ();
priv->iolock = g_mutex_new ();
+ priv->timeout = 0;
}
static void
"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 ();
update_fdflags (SoupSocketPrivate *priv)
{
int opt;
+ struct timeval timeout;
#ifndef G_OS_WIN32
int flags;
#endif
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));
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;
}
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;
}
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,
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;
#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: