From: Dan Winship Date: Thu, 8 Dec 2011 16:34:20 +0000 (-0500) Subject: SoupMessage: add network-event signal X-Git-Tag: LIBSOUP_2_37_4~4 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f4478b4fbab0e94bef11042ed5ca3ab3e3145e8a;p=platform%2Fupstream%2Flibsoup.git SoupMessage: add network-event signal Proxy the new GSocketClient::event signal and emit it on SoupMessage, when relevant, to allow (a) debugging, (b) status messages, (c) request timing, (d) fiddling with sockets --- diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c index af54e88..199be73 100644 --- a/libsoup/soup-connection.c +++ b/libsoup/soup-connection.c @@ -51,6 +51,7 @@ typedef struct { G_DEFINE_TYPE (SoupConnection, soup_connection, G_TYPE_OBJECT) enum { + EVENT, DISCONNECTED, LAST_SIGNAL }; @@ -150,6 +151,16 @@ soup_connection_class_init (SoupConnectionClass *connection_class) object_class->get_property = get_property; /* signals */ + signals[EVENT] = + g_signal_new ("event", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 2, + G_TYPE_SOCKET_CLIENT_EVENT, + G_TYPE_IO_STREAM); signals[DISCONNECTED] = g_signal_new ("disconnected", G_OBJECT_CLASS_TYPE (object_class), @@ -433,8 +444,11 @@ set_current_item (SoupConnection *conn, SoupMessageQueueItem *item) g_signal_connect (item->msg, "restarted", G_CALLBACK (current_item_restarted), conn); - if (priv->state == SOUP_CONNECTION_IDLE || - item->msg->method != SOUP_METHOD_CONNECT) + if (item->msg->method == SOUP_METHOD_CONNECT) { + g_signal_emit (conn, signals[EVENT], 0, + G_SOCKET_CLIENT_PROXY_NEGOTIATING, + soup_socket_get_iostream (priv->socket)); + } else if (priv->state == SOUP_CONNECTION_IDLE) soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE); g_object_thaw_notify (G_OBJECT (conn)); @@ -461,6 +475,10 @@ clear_current_item (SoupConnection *conn) if (item->msg->method == SOUP_METHOD_CONNECT && SOUP_STATUS_IS_SUCCESSFUL (item->msg->status_code)) { + g_signal_emit (conn, signals[EVENT], 0, + G_SOCKET_CLIENT_PROXY_NEGOTIATED, + soup_socket_get_iostream (priv->socket)); + /* We're now effectively no longer proxying */ soup_uri_free (priv->proxy_uri); priv->proxy_uri = NULL; @@ -474,6 +492,33 @@ clear_current_item (SoupConnection *conn) } static void +soup_connection_event (SoupConnection *conn, + GSocketClientEvent event, + GIOStream *connection) +{ + SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn); + + if (!connection && priv->socket) + connection = soup_socket_get_iostream (priv->socket); + + g_signal_emit (conn, signals[EVENT], 0, + event, connection); +} + +static void +proxy_socket_event (SoupSocket *socket, + GSocketClientEvent event, + GIOStream *connection, + gpointer user_data) +{ + SoupConnection *conn = user_data; + + /* We handle COMPLETE ourselves */ + if (event != G_SOCKET_CLIENT_COMPLETE) + soup_connection_event (conn, event, connection); +} + +static void socket_disconnected (SoupSocket *sock, gpointer conn) { soup_connection_disconnect (conn); @@ -484,6 +529,8 @@ typedef struct { SoupConnectionCallback callback; gpointer callback_data; GCancellable *cancellable; + guint event_id; + gboolean tls_handshake; } SoupConnectionAsyncConnectData; static void @@ -492,10 +539,23 @@ socket_connect_finished (SoupSocket *socket, guint status, gpointer user_data) SoupConnectionAsyncConnectData *data = user_data; SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn); + g_signal_handler_disconnect (socket, data->event_id); + if (SOUP_STATUS_IS_SUCCESSFUL (status)) { g_signal_connect (priv->socket, "disconnected", G_CALLBACK (socket_disconnected), data->conn); + if (data->tls_handshake) { + soup_connection_event (data->conn, + G_SOCKET_CLIENT_TLS_HANDSHAKED, + NULL); + } + if (!priv->ssl || !priv->tunnel_addr) { + soup_connection_event (data->conn, + G_SOCKET_CLIENT_COMPLETE, + NULL); + } + soup_connection_set_state (data->conn, SOUP_CONNECTION_IN_USE); priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT; start_idle_timer (data->conn); @@ -519,11 +579,13 @@ static void socket_connect_result (SoupSocket *sock, guint status, gpointer user_data) { SoupConnectionAsyncConnectData *data = user_data; - SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn); if (SOUP_STATUS_IS_SUCCESSFUL (status) && - priv->ssl && !priv->tunnel_addr) { + data->tls_handshake) { if (soup_socket_start_ssl (sock, data->cancellable)) { + soup_connection_event (data->conn, + G_SOCKET_CLIENT_TLS_HANDSHAKING, + NULL); soup_socket_handshake_async (sock, data->cancellable, socket_connect_finished, data); return; @@ -555,6 +617,7 @@ soup_connection_connect_async (SoupConnection *conn, data->callback = callback; data->callback_data = user_data; data->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + data->tls_handshake = (priv->ssl && !priv->tunnel_addr); priv->socket = soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, priv->remote_addr, @@ -566,6 +629,9 @@ soup_connection_connect_async (SoupConnection *conn, SOUP_SOCKET_TIMEOUT, priv->io_timeout, "clean-dispose", TRUE, NULL); + data->event_id = g_signal_connect (priv->socket, "event", + G_CALLBACK (proxy_socket_event), + conn); soup_socket_connect_async (priv->socket, cancellable, socket_connect_result, data); } @@ -574,7 +640,7 @@ guint soup_connection_connect_sync (SoupConnection *conn, GCancellable *cancellable) { SoupConnectionPrivate *priv; - guint status; + guint status, event_id; g_return_val_if_fail (SOUP_IS_CONNECTION (conn), SOUP_STATUS_MALFORMED); priv = SOUP_CONNECTION_GET_PRIVATE (conn); @@ -592,6 +658,8 @@ soup_connection_connect_sync (SoupConnection *conn, GCancellable *cancellable) "clean-dispose", TRUE, NULL); + event_id = g_signal_connect (priv->socket, "event", + G_CALLBACK (proxy_socket_event), conn); status = soup_socket_connect_sync (priv->socket, cancellable); if (!SOUP_STATUS_IS_SUCCESSFUL (status)) @@ -601,8 +669,15 @@ soup_connection_connect_sync (SoupConnection *conn, GCancellable *cancellable) if (!soup_socket_start_ssl (priv->socket, cancellable)) status = SOUP_STATUS_SSL_FAILED; else { + soup_connection_event (conn, + G_SOCKET_CLIENT_TLS_HANDSHAKING, + NULL); status = soup_socket_handshake_sync (priv->socket, cancellable); - if (status == SOUP_STATUS_TLS_FAILED) { + if (status == SOUP_STATUS_OK) { + soup_connection_event (conn, + G_SOCKET_CLIENT_TLS_HANDSHAKED, + NULL); + } else if (status == SOUP_STATUS_TLS_FAILED) { priv->ssl_fallback = TRUE; status = SOUP_STATUS_TRY_AGAIN; } @@ -613,6 +688,11 @@ soup_connection_connect_sync (SoupConnection *conn, GCancellable *cancellable) g_signal_connect (priv->socket, "disconnected", G_CALLBACK (socket_disconnected), conn); + if (!priv->ssl || !priv->tunnel_addr) { + soup_connection_event (conn, + G_SOCKET_CLIENT_COMPLETE, + NULL); + } soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE); priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT; start_idle_timer (conn); @@ -625,6 +705,9 @@ soup_connection_connect_sync (SoupConnection *conn, GCancellable *cancellable) } } + if (priv->socket) + g_signal_handler_disconnect (priv->socket, event_id); + if (priv->proxy_uri != NULL) status = soup_status_proxify (status); return status; @@ -659,8 +742,11 @@ soup_connection_start_ssl_sync (SoupConnection *conn, cancellable)) return SOUP_STATUS_SSL_FAILED; + soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKING, NULL); status = soup_socket_handshake_sync (priv->socket, cancellable); - if (status == SOUP_STATUS_TLS_FAILED) { + if (status == SOUP_STATUS_OK) + soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKED, NULL); + else if (status == SOUP_STATUS_TLS_FAILED) { priv->ssl_fallback = TRUE; status = SOUP_STATUS_TRY_AGAIN; } @@ -674,7 +760,9 @@ start_ssl_completed (SoupSocket *socket, guint status, gpointer user_data) SoupConnectionAsyncConnectData *data = user_data; SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn); - if (status == SOUP_STATUS_TLS_FAILED) { + if (status == SOUP_STATUS_OK) + soup_connection_event (data->conn, G_SOCKET_CLIENT_TLS_HANDSHAKED, NULL); + else if (status == SOUP_STATUS_TLS_FAILED) { priv->ssl_fallback = TRUE; status = SOUP_STATUS_TRY_AGAIN; } @@ -727,6 +815,7 @@ soup_connection_start_ssl_async (SoupConnection *conn, return; } + soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKING, NULL); soup_socket_handshake_async (priv->socket, cancellable, start_ssl_completed, data); } diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h index 1bb2aab..5625354 100644 --- a/libsoup/soup-message-private.h +++ b/libsoup/soup-message-private.h @@ -100,4 +100,8 @@ gboolean soup_message_disables_feature (SoupMessage *msg, void soup_message_set_https_status (SoupMessage *msg, SoupConnection *conn); +void soup_message_network_event (SoupMessage *msg, + GSocketClientEvent event, + GIOStream *connection); + #endif /* SOUP_MESSAGE_PRIVATE_H */ diff --git a/libsoup/soup-message-queue.c b/libsoup/soup-message-queue.c index 0fc655f..7b1e5dd 100644 --- a/libsoup/soup-message-queue.c +++ b/libsoup/soup-message-queue.c @@ -72,8 +72,7 @@ queue_message_restarted (SoupMessage *msg, gpointer user_data) SOUP_STATUS_IS_REDIRECTION (msg->status_code))) { if (soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE) soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE); - g_object_unref (item->conn); - item->conn = NULL; + soup_message_queue_item_set_connection (item, NULL); } soup_message_cleanup_response (msg); @@ -185,11 +184,39 @@ soup_message_queue_item_unref (SoupMessageQueueItem *item) g_object_unref (item->proxy_addr); if (item->proxy_uri) soup_uri_free (item->proxy_uri); - if (item->conn) - g_object_unref (item->conn); + soup_message_queue_item_set_connection (item, NULL); g_slice_free (SoupMessageQueueItem, item); } +static void +proxy_connection_event (SoupConnection *conn, + GSocketClientEvent event, + GIOStream *connection, + gpointer user_data) +{ + SoupMessageQueueItem *item = user_data; + + soup_message_network_event (item->msg, event, connection); +} + +void +soup_message_queue_item_set_connection (SoupMessageQueueItem *item, + SoupConnection *conn) +{ + if (item->conn) { + g_signal_handlers_disconnect_by_func (item->conn, proxy_connection_event, item); + g_object_unref (item->conn); + } + + item->conn = conn; + + if (item->conn) { + g_object_ref (item->conn); + g_signal_connect (item->conn, "event", + G_CALLBACK (proxy_connection_event), item); + } +} + /** * soup_message_queue_lookup: * @queue: a #SoupMessageQueue diff --git a/libsoup/soup-message-queue.h b/libsoup/soup-message-queue.h index 5fb14c4..a1ae663 100644 --- a/libsoup/soup-message-queue.h +++ b/libsoup/soup-message-queue.h @@ -74,11 +74,12 @@ SoupMessageQueueItem *soup_message_queue_next (SoupMessageQueue *queue void soup_message_queue_remove (SoupMessageQueue *queue, SoupMessageQueueItem *item); -void soup_message_queue_item_ref (SoupMessageQueueItem *item); -void soup_message_queue_item_unref (SoupMessageQueueItem *item); - void soup_message_queue_destroy (SoupMessageQueue *queue); +void soup_message_queue_item_ref (SoupMessageQueueItem *item); +void soup_message_queue_item_unref (SoupMessageQueueItem *item); +void soup_message_queue_item_set_connection (SoupMessageQueueItem *item, + SoupConnection *conn); G_END_DECLS diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c index 6186087..cdc4392 100644 --- a/libsoup/soup-message.c +++ b/libsoup/soup-message.c @@ -107,6 +107,8 @@ enum { RESTARTED, FINISHED, + NETWORK_EVENT, + LAST_SIGNAL }; @@ -493,6 +495,38 @@ soup_message_class_init (SoupMessageClass *message_class) soup_marshal_NONE__NONE, G_TYPE_NONE, 0); + /** + * SoupMessage::network-event: + * @msg: the message + * @event: the network event + * @connection: the current state of the network connection + + * Emitted to indicate that some network-related event + * related to @msg has occurred. This essentially proxies the + * #GSocketClient::event signal, but only for events that + * occur while @msg "owns" the connection; if @msg is sent on + * an existing persistent connection, then this signal will + * not be emitted. (If you want to force the message to be + * sent on a new connection, set the + * %SOUP_MESSAGE_NEW_CONNECTION flag on it.) + * + * See #GSocketClient::event for more information on what + * the different values of @event correspond to, and what + * @connection will be in each case. + * + * Since: 2.38 + **/ + signals[NETWORK_EVENT] = + g_signal_new ("network_event", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 2, + G_TYPE_SOCKET_CLIENT_EVENT, + G_TYPE_IO_STREAM); + /* properties */ /** * SOUP_MESSAGE_METHOD: @@ -1125,6 +1159,15 @@ soup_message_finished (SoupMessage *msg) g_signal_emit (msg, signals[FINISHED], 0); } +void +soup_message_network_event (SoupMessage *msg, + GSocketClientEvent event, + GIOStream *connection) +{ + g_signal_emit (msg, signals[NETWORK_EVENT], 0, + event, connection); +} + static void header_handler_free (gpointer header_name, GClosure *closure) { diff --git a/libsoup/soup-misc-private.h b/libsoup/soup-misc-private.h index 1d472f9..e935168 100644 --- a/libsoup/soup-misc-private.h +++ b/libsoup/soup-misc-private.h @@ -18,7 +18,8 @@ void soup_socket_handshake_async (SoupSocket *sock, SoupSocketCallback callback, gpointer user_data); -GSocket *soup_socket_get_gsocket (SoupSocket *sock); +GSocket *soup_socket_get_gsocket (SoupSocket *sock); +GIOStream *soup_socket_get_iostream (SoupSocket *sock); #endif /* SOUP_URI_PRIVATE_H */ diff --git a/libsoup/soup-session-async.c b/libsoup/soup-session-async.c index edb6239..e872102 100644 --- a/libsoup/soup-session-async.c +++ b/libsoup/soup-session-async.c @@ -299,8 +299,7 @@ tunnel_message_completed (SoupMessage *msg, gpointer user_data) soup_connection_disconnect (item->conn); if (msg->status_code == SOUP_STATUS_TRY_AGAIN) { item->related->state = SOUP_MESSAGE_AWAITING_CONNECTION; - g_object_unref (item->related->conn); - item->related->conn = NULL; + soup_message_queue_item_set_connection (item->related, NULL); } else soup_message_set_status (item->related->msg, msg->status_code); @@ -333,8 +332,7 @@ got_connection (SoupConnection *conn, guint status, gpointer user_data) soup_connection_disconnect (conn); if (status == SOUP_STATUS_TRY_AGAIN) { - g_object_unref (item->conn); - item->conn = NULL; + soup_message_queue_item_set_connection (item, NULL); item->state = SOUP_MESSAGE_AWAITING_CONNECTION; } else { soup_session_set_item_status (session, item, status); diff --git a/libsoup/soup-session-sync.c b/libsoup/soup-session-sync.c index e0d023e..c54975c 100644 --- a/libsoup/soup-session-sync.c +++ b/libsoup/soup-session-sync.c @@ -209,8 +209,7 @@ try_again: status = soup_connection_connect_sync (item->conn, item->cancellable); if (status == SOUP_STATUS_TRY_AGAIN) { soup_connection_disconnect (item->conn); - g_object_unref (item->conn); - item->conn = NULL; + soup_message_queue_item_set_connection (item, NULL); goto try_again; } @@ -221,8 +220,7 @@ try_again: soup_session_set_item_status (session, item, status); item->state = SOUP_MESSAGE_FINISHING; soup_connection_disconnect (item->conn); - g_object_unref (item->conn); - item->conn = NULL; + soup_message_queue_item_set_connection (item, NULL); return; } @@ -230,8 +228,7 @@ try_again: status = tunnel_connect (session, item); if (!SOUP_STATUS_IS_SUCCESSFUL (status)) { soup_connection_disconnect (item->conn); - g_object_unref (item->conn); - item->conn = NULL; + soup_message_queue_item_set_connection (item, NULL); if (status == SOUP_STATUS_TRY_AGAIN) goto try_again; soup_session_set_item_status (session, item, status); diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c index e0d540c..eb5dd6e 100644 --- a/libsoup/soup-session.c +++ b/libsoup/soup-session.c @@ -1842,7 +1842,7 @@ soup_session_make_connect_message (SoupSession *session, */ queue_message (session, msg, NULL, NULL); item = soup_message_queue_lookup (priv->queue, msg); - item->conn = g_object_ref (conn); + soup_message_queue_item_set_connection (item, conn); g_object_unref (msg); g_signal_emit (session, signals[TUNNELING], 0, conn); @@ -1879,7 +1879,7 @@ soup_session_get_connection (SoupSession *session, if (!need_new_connection && soup_connection_get_state (conns->data) == SOUP_CONNECTION_IDLE) { soup_connection_set_state (conns->data, SOUP_CONNECTION_IN_USE); g_mutex_unlock (&priv->host_lock); - item->conn = g_object_ref (conns->data); + soup_message_queue_item_set_connection (item, conns->data); soup_message_set_https_status (item->msg, item->conn); return TRUE; } else if (soup_connection_get_state (conns->data) == SOUP_CONNECTION_CONNECTING) @@ -1951,7 +1951,7 @@ soup_session_get_connection (SoupSession *session, } g_mutex_unlock (&priv->host_lock); - item->conn = g_object_ref (conn); + soup_message_queue_item_set_connection (item, conn); return TRUE; } @@ -1972,8 +1972,7 @@ soup_session_unqueue_item (SoupSession *session, if (item->conn) { soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE); - g_object_unref (item->conn); - item->conn = NULL; + soup_message_queue_item_set_connection (item, NULL); } if (item->state != SOUP_MESSAGE_FINISHED) { diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c index b4396c5..452fada 100644 --- a/libsoup/soup-socket.c +++ b/libsoup/soup-socket.c @@ -39,6 +39,7 @@ enum { WRITABLE, DISCONNECTED, NEW_CONNECTION, + EVENT, LAST_SIGNAL }; @@ -263,6 +264,28 @@ soup_socket_class_init (SoupSocketClass *socket_class) soup_marshal_NONE__OBJECT, G_TYPE_NONE, 1, SOUP_TYPE_SOCKET); + /** + * SoupSocket::event: + * @sock: the socket + * @event: the event that occurred + * @connection: the current connection state + * + * Emitted when a network-related event occurs. See + * #GSocketClient::event for more details. + * + * Since: 2.38 + **/ + signals[EVENT] = + g_signal_new ("event", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 2, + G_TYPE_SOCKET_CLIENT_EVENT, + G_TYPE_IO_STREAM); + /* properties */ /** @@ -630,6 +653,19 @@ soup_socket_new (const char *optname1, ...) return sock; } +static void +proxy_socket_client_event (GSocketClient *client, + GSocketClientEvent event, + GSocketConnectable *connectable, + GIOStream *connection, + gpointer user_data) +{ + SoupSocket *sock = user_data; + + g_signal_emit (sock, signals[EVENT], 0, + event, connection); +} + static guint socket_connected (SoupSocket *sock, GSocketConnection *conn, GError *error) { @@ -729,6 +765,8 @@ soup_socket_connect_async (SoupSocket *sock, GCancellable *cancellable, g_main_context_push_thread_default (priv->async_context); client = g_socket_client_new (); + g_signal_connect (client, "event", + G_CALLBACK (proxy_socket_client_event), sock); if (priv->timeout) g_socket_client_set_timeout (client, priv->timeout); g_socket_client_connect_async (client, @@ -772,6 +810,8 @@ soup_socket_connect_sync (SoupSocket *sock, GCancellable *cancellable) priv->connect_cancel = cancellable; client = g_socket_client_new (); + g_signal_connect (client, "event", + G_CALLBACK (proxy_socket_client_event), sock); if (priv->timeout) g_socket_client_set_timeout (client, priv->timeout); conn = g_socket_client_connect (client, @@ -798,6 +838,14 @@ soup_socket_get_gsocket (SoupSocket *sock) return SOUP_SOCKET_GET_PRIVATE (sock)->gsock; } +GIOStream * +soup_socket_get_iostream (SoupSocket *sock) +{ + g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL); + + return SOUP_SOCKET_GET_PRIVATE (sock)->conn; +} + static GSource * soup_socket_create_watch (SoupSocketPrivate *priv, GIOCondition cond, GPollableSourceFunc callback, gpointer user_data,