socket_connect_result (SoupSocket *sock, guint status, gpointer user_data)
{
SoupConnectionAsyncConnectData *data = user_data;
- SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
+ SoupConnectionPrivate *priv;
+
+ if (!data->conn) {
+ if (data->callback) {
+ data->callback (NULL, SOUP_STATUS_CANCELLED,
+ data->callback_data);
+ }
+ g_slice_free (SoupConnectionAsyncConnectData, data);
+ return;
+ }
+
+ g_object_remove_weak_pointer (G_OBJECT (data->conn),
+ (gpointer *)&data->conn);
+
+ priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
if (!SOUP_STATUS_IS_SUCCESSFUL (status))
goto done;
data->conn = conn;
data->callback = callback;
data->callback_data = user_data;
+ g_object_add_weak_pointer (G_OBJECT (conn), (gpointer *)&data->conn);
priv->socket =
soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, priv->remote_addr,
g_return_if_fail (SOUP_IS_CONNECTION (conn));
priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+ soup_connection_set_state (conn, SOUP_CONNECTION_DISCONNECTED);
if (!priv->socket)
return;
g_object_unref (priv->socket);
priv->socket = NULL;
- /* Don't emit "disconnected" if we aren't yet connected */
- if (priv->state < SOUP_CONNECTION_IDLE)
- return;
-
- soup_connection_set_state (conn, SOUP_CONNECTION_DISCONNECTED);
/* NB: this might cause conn to be destroyed. */
g_signal_emit (conn, signals[DISCONNECTED], 0);
}
}
static void
-got_connection (SoupConnection *conn, guint status, gpointer session)
+got_connection (SoupConnection *conn, guint status, gpointer user_data)
{
+ gpointer *session_p = user_data;
+ SoupSession *session = *session_p;
SoupAddress *tunnel_addr;
+ if (!session) {
+ g_slice_free (gpointer, session_p);
+ return;
+ }
+ g_object_remove_weak_pointer (G_OBJECT (session), session_p);
+ g_slice_free (gpointer, session_p);
+
if (status != SOUP_STATUS_OK) {
/* There may have been messages waiting for the
* connection count to go down, so queue a run_queue.
continue;
if (soup_connection_get_state (conn) == SOUP_CONNECTION_NEW) {
+ gpointer *session_p;
+
+ session_p = g_slice_new (gpointer);
+ *session_p = session;
+ g_object_add_weak_pointer (G_OBJECT (session), session_p);
soup_connection_connect_async (conn, got_connection,
- session);
+ session_p);
} else
soup_session_send_queue_item (session, item, conn);
}
GMutex *iolock, *addrlock;
guint timeout;
+
+ GCancellable *connect_cancel;
} SoupSocketPrivate;
#define SOUP_SOCKET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SOCKET, SoupSocketPrivate))
{
SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (object);
+ if (priv->connect_cancel)
+ g_object_unref (priv->connect_cancel);
if (priv->iochannel)
disconnect_internal (priv);
gpointer user_data;
} SoupSocketAsyncConnectData;
+static void
+free_sacd (SoupSocketAsyncConnectData *sacd)
+{
+ if (sacd->cancel_id)
+ g_signal_handler_disconnect (sacd->cancellable, sacd->cancel_id);
+ g_object_unref (sacd->cancellable);
+ g_object_unref (sacd->sock);
+ g_slice_free (SoupSocketAsyncConnectData, sacd);
+}
+
static gboolean
idle_connect_result (gpointer user_data)
{
SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sacd->sock);
guint status;
+ priv->connect_cancel = NULL;
priv->watch_src = NULL;
- if (sacd->cancel_id)
- g_signal_handler_disconnect (sacd->cancellable, sacd->cancel_id);
if (priv->sockfd == -1) {
if (g_cancellable_is_cancelled (sacd->cancellable))
status = SOUP_STATUS_OK;
sacd->callback (sacd->sock, status, sacd->user_data);
- g_slice_free (SoupSocketAsyncConnectData, sacd);
+ free_sacd (sacd);
return FALSE;
}
got_address (SoupAddress *addr, guint status, gpointer user_data)
{
SoupSocketAsyncConnectData *sacd = user_data;
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sacd->sock);
+
+ priv->connect_cancel = NULL;
- if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ soup_socket_connect_async (sacd->sock, sacd->cancellable,
+ sacd->callback, sacd->user_data);
+ } else
sacd->callback (sacd->sock, status, sacd->user_data);
- g_slice_free (SoupSocketAsyncConnectData, sacd);
- return;
- }
- soup_socket_connect_async (sacd->sock, sacd->cancellable,
- sacd->callback, sacd->user_data);
- g_slice_free (SoupSocketAsyncConnectData, sacd);
+ free_sacd (sacd);
}
static void
g_return_if_fail (priv->remote_addr != NULL);
sacd = g_slice_new0 (SoupSocketAsyncConnectData);
- sacd->sock = sock;
- sacd->cancellable = cancellable;
+ sacd->sock = g_object_ref (sock);
+ sacd->cancellable = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
sacd->callback = callback;
sacd->user_data = user_data;
+ priv->connect_cancel = sacd->cancellable;
+
+ if (g_cancellable_is_cancelled (priv->connect_cancel)) {
+ priv->watch_src = soup_add_completion (priv->async_context,
+ idle_connect_result, sacd);
+ return;
+ }
+
if (!soup_address_get_sockaddr (priv->remote_addr, NULL)) {
soup_address_resolve_async (priv->remote_addr,
priv->async_context,
- cancellable,
+ sacd->cancellable,
got_address, sacd);
return;
}
priv->timeout * 1000,
connect_timeout, sacd);
}
- if (cancellable) {
- sacd->cancel_id =
- g_signal_connect (cancellable, "cancelled",
- G_CALLBACK (async_cancel),
- sacd);
- }
+ sacd->cancel_id =
+ g_signal_connect (sacd->cancellable, "cancelled",
+ G_CALLBACK (async_cancel),
+ sacd);
} else {
priv->watch_src = soup_add_completion (priv->async_context,
idle_connect_result, sacd);
g_return_val_if_fail (priv->sockfd == -1, SOUP_STATUS_MALFORMED);
g_return_val_if_fail (priv->remote_addr != NULL, SOUP_STATUS_MALFORMED);
+ if (cancellable)
+ g_object_ref (cancellable);
+ else
+ cancellable = g_cancellable_new ();
+ priv->connect_cancel = cancellable;
+
if (!soup_address_get_sockaddr (priv->remote_addr, NULL)) {
status = soup_address_resolve_sync (priv->remote_addr,
cancellable);
- if (!SOUP_STATUS_IS_SUCCESSFUL (status))
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ priv->connect_cancel = NULL;
+ g_object_unref (cancellable);
return status;
+ }
}
- if (cancellable) {
- cancel_id = g_signal_connect (cancellable, "cancelled",
- G_CALLBACK (sync_cancel), sock);
- }
+ cancel_id = g_signal_connect (cancellable, "cancelled",
+ G_CALLBACK (sync_cancel), sock);
status = socket_connect_internal (sock);
+ priv->connect_cancel = NULL;
- if (cancellable) {
- if (status != SOUP_STATUS_OK &&
- g_cancellable_is_cancelled (cancellable)) {
- status = SOUP_STATUS_CANCELLED;
- disconnect_internal (priv);
- }
- g_signal_handler_disconnect (cancellable, cancel_id);
+ if (status != SOUP_STATUS_OK &&
+ g_cancellable_is_cancelled (cancellable)) {
+ status = SOUP_STATUS_CANCELLED;
+ disconnect_internal (priv);
}
+ g_signal_handler_disconnect (cancellable, cancel_id);
+ g_object_unref (cancellable);
return status;
}
g_return_if_fail (SOUP_IS_SOCKET (sock));
priv = SOUP_SOCKET_GET_PRIVATE (sock);
- if (g_mutex_trylock (priv->iolock)) {
+ if (priv->connect_cancel) {
+ g_cancellable_cancel (priv->connect_cancel);
+ return;
+ } else if (g_mutex_trylock (priv->iolock)) {
if (priv->iochannel)
disconnect_internal (priv);
else
soup_address_resolve_sync (addr, NULL);
bad_server = soup_server_new (SOUP_SERVER_INTERFACE, addr,
NULL);
+ g_object_unref (addr);
bad_uri = g_strdup_printf ("http://127.0.0.1:%u/",
soup_server_get_port (bad_server));
g_free (signal_ids);
}
+/* Server handlers for "*" work but are separate from handlers for
+ * all other URIs. #590751
+ */
static void
do_star_test (void)
{
soup_uri_free (star_uri);
}
+/* Handle unexpectedly-early aborts. #596074, #618641 */
+static void
+ea_msg_completed_one (SoupSession *session, SoupMessage *msg, gpointer loop)
+{
+ debug_printf (2, " Message 1 completed\n");
+ if (msg->status_code != SOUP_STATUS_CANCELLED) {
+ debug_printf (1, " Unexpected status on Message 1: %d %s\n",
+ msg->status_code, msg->reason_phrase);
+ errors++;
+ }
+ g_main_loop_quit (loop);
+}
+
static gboolean
ea_abort_session (gpointer session)
{
{
SoupSession *session;
SoupMessage *msg;
+ GMainContext *context;
+ GMainLoop *loop;
debug_printf (1, "\nAbort with pending connection\n");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ msg = soup_message_new_from_uri ("GET", base_uri);
+ context = g_main_context_default ();
+ loop = g_main_loop_new (context, TRUE);
+ soup_session_queue_message (session, msg, ea_msg_completed_one, loop);
+ g_main_context_iteration (context, FALSE);
+
+ soup_session_abort (session);
+ while (g_main_context_pending (context))
+ g_main_context_iteration (context, FALSE);
+ g_main_loop_unref (loop);
+ soup_test_session_abort_unref (session);
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
msg = soup_message_new_from_uri ("GET", base_uri);
g_signal_connect (session, "connection-created",
G_CALLBACK (ea_connection_created), NULL);
soup_session_send_message (session, msg);
+ debug_printf (2, " Message 2 completed\n");
if (msg->status_code != SOUP_STATUS_CANCELLED) {
debug_printf (1, " Unexpected response: %d %s\n",
}
g_object_unref (msg);
+ while (g_main_context_pending (context))
+ g_main_context_iteration (context, FALSE);
+
soup_test_session_abort_unref (session);
}