SoupConnectionState soup_connection_get_state (SoupConnection *conn) { SoupConnectionPrivate *priv; g_return_val_if_fail (SOUP_IS_CONNECTION (conn), SOUP_CONNECTION_DISCONNECTED); priv = SOUP_CONNECTION_GET_PRIVATE (conn); #ifdef G_OS_UNIX if (priv->state == SOUP_CONNECTION_IDLE) { GPollFD pfd; pfd.fd = soup_socket_get_fd (priv->socket); pfd.events = G_IO_IN; pfd.revents = 0; if (g_poll (&pfd, 1, 0) == 1) soup_connection_set_state (conn, SOUP_CONNECTION_REMOTE_DISCONNECTED); } #endif if (priv->state == SOUP_CONNECTION_IDLE && priv->unused_timeout && priv->unused_timeout < time (NULL)) soup_connection_set_state (conn, SOUP_CONNECTION_REMOTE_DISCONNECTED); return priv->state; }
static void tunnel_message_completed (SoupMessage *msg, gpointer user_data) { SoupMessageQueueItem *item = user_data; SoupSession *session = item->session; if (item->state == SOUP_MESSAGE_RESTARTING) { soup_message_restarted (msg); if (item->conn) { soup_session_send_queue_item (session, item, tunnel_message_completed); return; } soup_message_set_status (msg, SOUP_STATUS_TRY_AGAIN); } item->state = SOUP_MESSAGE_FINISHED; if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { if (item->conn) 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; } else soup_message_set_status (item->related->msg, msg->status_code); goto done; } if (!soup_connection_start_ssl (item->conn)) { if (item->conn) soup_connection_disconnect (item->conn); soup_message_set_status (item->related->msg, SOUP_STATUS_SSL_FAILED); goto done; } g_signal_connect (item->conn, "disconnected", G_CALLBACK (connection_closed), item->session); soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE); soup_connection_set_state (item->conn, SOUP_CONNECTION_IN_USE); item->related->state = SOUP_MESSAGE_READY; done: soup_message_finished (msg); if (item->related->msg->status_code) item->related->state = SOUP_MESSAGE_FINISHING; do_idle_run_queue (item->session); soup_message_queue_item_unref (item->related); soup_session_unqueue_item (session, item); soup_message_queue_item_unref (item); g_object_unref (session); }
gboolean soup_connection_connect_sync (SoupConnection *conn, GCancellable *cancellable, GError **error) { SoupConnectionPrivate *priv; SoupAddress *remote_addr; g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE); priv = SOUP_CONNECTION_GET_PRIVATE (conn); g_return_val_if_fail (priv->socket == NULL, FALSE); soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING); /* Set the protocol to ensure correct proxy resolution. */ remote_addr = g_object_new (SOUP_TYPE_ADDRESS, SOUP_ADDRESS_NAME, priv->remote_uri->host, SOUP_ADDRESS_PORT, priv->remote_uri->port, SOUP_ADDRESS_PROTOCOL, priv->remote_uri->scheme, NULL); priv->socket = soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, remote_addr, SOUP_SOCKET_SSL_FALLBACK, priv->ssl_fallback, SOUP_SOCKET_SOCKET_PROPERTIES, priv->socket_props, SOUP_SOCKET_FLAG_NONBLOCKING, FALSE, NULL); g_object_unref (remote_addr); g_signal_connect (priv->socket, "event", G_CALLBACK (re_emit_socket_event), conn); if (!soup_socket_connect_sync_internal (priv->socket, cancellable, error)) return FALSE; priv->proxy_uri = soup_socket_get_http_proxy_uri (priv->socket); if (priv->ssl && !priv->proxy_uri) { if (!soup_socket_handshake_sync (priv->socket, priv->remote_uri->host, cancellable, error)) return FALSE; } if (!priv->ssl || !priv->proxy_uri) { 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); return TRUE; }
/** * soup_connection_connect_sync: * @conn: the connection * * Synchronously connects @conn. * * Return value: the soup status **/ guint soup_connection_connect_sync (SoupConnection *conn) { SoupConnectionPrivate *priv; guint status; g_return_val_if_fail (SOUP_IS_CONNECTION (conn), SOUP_STATUS_MALFORMED); priv = SOUP_CONNECTION_GET_PRIVATE (conn); g_return_val_if_fail (priv->socket == NULL, SOUP_STATUS_MALFORMED); soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING); priv->socket = soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, priv->remote_addr, SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds, SOUP_SOCKET_FLAG_NONBLOCKING, FALSE, SOUP_SOCKET_TIMEOUT, priv->io_timeout, NULL); status = soup_socket_connect_sync (priv->socket, NULL); if (!SOUP_STATUS_IS_SUCCESSFUL (status)) goto fail; g_signal_connect (priv->socket, "disconnected", G_CALLBACK (socket_disconnected), conn); if (priv->ssl_creds && !priv->tunnel_addr) { if (!soup_socket_start_ssl (priv->socket, NULL)) { status = SOUP_STATUS_SSL_FAILED; goto fail; } } if (SOUP_STATUS_IS_SUCCESSFUL (status)) { soup_connection_set_state (conn, SOUP_CONNECTION_IDLE); priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT; start_idle_timer (conn); } else { fail: if (priv->socket) { g_object_unref (priv->socket); priv->socket = NULL; } } if (priv->proxy_uri != NULL) status = soup_status_proxify (status); return status; }
static void queue_message_restarted (SoupMessage *msg, gpointer user_data) { SoupMessageQueueItem *item = user_data; if (item->proxy_addr) { g_object_unref (item->proxy_addr); item->proxy_addr = NULL; } if (item->proxy_uri) { soup_uri_free (item->proxy_uri); item->proxy_uri = NULL; } if (item->conn && (!soup_message_is_keepalive (msg) || 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; } g_cancellable_reset (item->cancellable); item->state = SOUP_MESSAGE_STARTING; }
static void soup_connection_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object); switch (prop_id) { case PROP_REMOTE_URI: priv->remote_uri = g_value_dup_boxed (value); if (priv->remote_uri) priv->ssl = (priv->remote_uri->scheme == SOUP_URI_SCHEME_HTTPS); else priv->ssl = FALSE; break; break; case PROP_SSL_FALLBACK: priv->ssl_fallback = g_value_get_boolean (value); break; case PROP_SOCKET_PROPERTIES: priv->socket_props = g_value_dup_boxed (value); break; case PROP_STATE: soup_connection_set_state (SOUP_CONNECTION (object), g_value_get_uint (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
/** * soup_connection_disconnect: * @conn: a connection * * Disconnects @conn's socket and emits a %disconnected signal. * After calling this, @conn will be essentially useless. **/ void soup_connection_disconnect (SoupConnection *conn) { SoupConnectionPrivate *priv; g_return_if_fail (SOUP_IS_CONNECTION (conn)); priv = SOUP_CONNECTION_GET_PRIVATE (conn); if (!priv->socket) return; g_signal_handlers_disconnect_by_func (priv->socket, socket_disconnected, conn); soup_socket_disconnect (priv->socket); 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 tunnel_connected (SoupMessage *msg, gpointer user_data) { SoupSessionAsyncTunnelData *data = user_data; if (SOUP_MESSAGE_IS_STARTING (msg)) { soup_session_send_queue_item (data->session, data->item, data->conn); return; } if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { soup_session_connection_failed (data->session, data->conn, msg->status_code); goto done; } if (!soup_connection_start_ssl (data->conn)) { soup_session_connection_failed (data->session, data->conn, SOUP_STATUS_SSL_FAILED); goto done; } g_signal_connect (data->conn, "disconnected", G_CALLBACK (connection_closed), data->session); soup_connection_set_state (data->conn, SOUP_CONNECTION_IDLE); do_idle_run_queue (data->session); done: soup_message_queue_item_unref (data->item); g_slice_free (SoupSessionAsyncTunnelData, data); }
/** * soup_connection_disconnect: * @conn: a connection * * Disconnects @conn's socket and emits a %disconnected signal. * After calling this, @conn will be essentially useless. **/ void soup_connection_disconnect (SoupConnection *conn) { SoupConnectionPrivate *priv; SoupConnectionState old_state; g_return_if_fail (SOUP_IS_CONNECTION (conn)); priv = SOUP_CONNECTION_GET_PRIVATE (conn); old_state = priv->state; if (old_state != SOUP_CONNECTION_DISCONNECTED) soup_connection_set_state (conn, SOUP_CONNECTION_DISCONNECTED); if (priv->socket) { SoupSocket *socket = priv->socket; g_signal_handlers_disconnect_by_func (socket, G_CALLBACK (re_emit_socket_event), conn); priv->socket = NULL; soup_socket_disconnect (socket); g_object_unref (socket); } if (old_state != SOUP_CONNECTION_DISCONNECTED) g_signal_emit (conn, signals[DISCONNECTED], 0); }
/** * soup_connection_connect_async: * @conn: the connection * @callback: callback to call when the connection succeeds or fails * @user_data: data for @callback * * Asynchronously connects @conn. **/ void soup_connection_connect_async (SoupConnection *conn, SoupConnectionCallback callback, gpointer user_data) { SoupConnectionAsyncConnectData *data; SoupConnectionPrivate *priv; g_return_if_fail (SOUP_IS_CONNECTION (conn)); priv = SOUP_CONNECTION_GET_PRIVATE (conn); g_return_if_fail (priv->socket == NULL); soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING); data = g_slice_new (SoupConnectionAsyncConnectData); data->conn = conn; data->callback = callback; data->callback_data = user_data; priv->socket = soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, priv->remote_addr, SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds, SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context, SOUP_SOCKET_TIMEOUT, priv->io_timeout, NULL); soup_socket_connect_async (priv->socket, NULL, socket_connect_result, data); }
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)) goto done; if (priv->ssl_creds && !priv->tunnel_addr) { if (!soup_socket_start_ssl (sock, NULL)) { status = SOUP_STATUS_SSL_FAILED; goto done; } } g_signal_connect (priv->socket, "disconnected", G_CALLBACK (socket_disconnected), data->conn); soup_connection_set_state (data->conn, SOUP_CONNECTION_IDLE); priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT; start_idle_timer (data->conn); done: if (data->callback) { if (priv->proxy_uri != NULL) status = soup_status_proxify (status); data->callback (data->conn, status, data->callback_data); } g_slice_free (SoupConnectionAsyncConnectData, data); }
SoupConnectionState soup_connection_get_state (SoupConnection *conn) { SoupConnectionPrivate *priv; g_return_val_if_fail (SOUP_IS_CONNECTION (conn), SOUP_CONNECTION_DISCONNECTED); priv = SOUP_CONNECTION_GET_PRIVATE (conn); if (priv->state == SOUP_CONNECTION_IDLE && (!soup_socket_is_connected (priv->socket) || soup_socket_is_readable (priv->socket))) soup_connection_set_state (conn, SOUP_CONNECTION_REMOTE_DISCONNECTED); if (priv->state == SOUP_CONNECTION_IDLE && priv->unused_timeout && priv->unused_timeout < time (NULL)) soup_connection_set_state (conn, SOUP_CONNECTION_REMOTE_DISCONNECTED); return priv->state; }
static void got_connection (SoupConnection *conn, guint status, gpointer session) { SoupAddress *tunnel_addr; if (status != SOUP_STATUS_OK) { /* There may have been messages waiting for the * connection count to go down, so queue a run_queue. */ do_idle_run_queue (session); soup_session_connection_failed (session, conn, status); /* session may be destroyed at this point */ return; } tunnel_addr = soup_connection_get_tunnel_addr (conn); if (tunnel_addr) { SoupSessionAsyncTunnelData *data; data = g_slice_new (SoupSessionAsyncTunnelData); data->session = session; data->conn = conn; data->item = soup_session_make_connect_message (session, tunnel_addr); g_signal_emit_by_name (session, "tunneling", conn); g_signal_connect (data->item->msg, "finished", G_CALLBACK (tunnel_connected), data); g_signal_connect (data->item->msg, "restarted", G_CALLBACK (tunnel_connected), data); soup_session_send_queue_item (session, data->item, conn); return; } g_signal_connect (conn, "disconnected", G_CALLBACK (connection_closed), session); /* @conn has been marked reserved by SoupSession, but * we don't actually have any specific message in mind * for it. (In particular, the message we were * originally planning to queue on it may have already * been queued on some other connection that became * available while we were waiting for this one to * connect.) So we release the connection into the * idle pool and then just run the queue and see what * happens. */ soup_connection_set_state (conn, SOUP_CONNECTION_IDLE); do_idle_run_queue (session); }
void soup_connection_connect_async (SoupConnection *conn, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { SoupConnectionPrivate *priv; SoupAddress *remote_addr; GTask *task; g_return_if_fail (SOUP_IS_CONNECTION (conn)); priv = SOUP_CONNECTION_GET_PRIVATE (conn); g_return_if_fail (priv->socket == NULL); soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING); /* Set the protocol to ensure correct proxy resolution. */ remote_addr = g_object_new (SOUP_TYPE_ADDRESS, SOUP_ADDRESS_NAME, priv->remote_uri->host, SOUP_ADDRESS_PORT, priv->remote_uri->port, SOUP_ADDRESS_PROTOCOL, priv->remote_uri->scheme, NULL); priv->socket = soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, remote_addr, SOUP_SOCKET_SSL_FALLBACK, priv->ssl_fallback, SOUP_SOCKET_SOCKET_PROPERTIES, priv->socket_props, NULL); g_object_unref (remote_addr); g_signal_connect (priv->socket, "event", G_CALLBACK (re_emit_socket_event), conn); soup_socket_properties_push_async_context (priv->socket_props); task = g_task_new (conn, cancellable, callback, user_data); soup_socket_connect_async_internal (priv->socket, cancellable, socket_connect_complete, task); soup_socket_properties_pop_async_context (priv->socket_props); }
static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object); switch (prop_id) { case PROP_REMOTE_ADDRESS: priv->remote_addr = g_value_dup_object (value); break; case PROP_TUNNEL_ADDRESS: priv->tunnel_addr = g_value_dup_object (value); break; case PROP_PROXY_URI: if (priv->proxy_uri) soup_uri_free (priv->proxy_uri); priv->proxy_uri = g_value_dup_boxed (value); break; case PROP_SSL_CREDS: priv->ssl_creds = g_value_get_pointer (value); break; case PROP_ASYNC_CONTEXT: priv->async_context = g_value_get_pointer (value); if (priv->async_context) g_main_context_ref (priv->async_context); break; case PROP_TIMEOUT: priv->io_timeout = g_value_get_uint (value); break; case PROP_IDLE_TIMEOUT: priv->idle_timeout = g_value_get_uint (value); break; case PROP_STATE: soup_connection_set_state (SOUP_CONNECTION (object), g_value_get_uint (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static void socket_connect_finished (GTask *task, SoupSocket *sock, GError *error) { SoupConnection *conn = g_task_get_source_object (task); SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn); if (!error) { if (!priv->ssl || !priv->proxy_uri) { 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); g_task_return_boolean (task, TRUE); } else g_task_return_error (task, error); g_object_unref (task); }
/** * soup_message_io_stop: * @msg: a #SoupMessage * * Immediately stops I/O on msg; if the connection would be left in an * inconsistent state, it will be closed. * * Note: this is a low-level function that does not cause any signals * to be emitted on @msg; it is up to the caller to make sure that * @msg doesn't get "stranded". **/ void soup_message_io_stop (SoupMessage *msg) { SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg); SoupMessageIOData *io = priv->io_data; if (!io) return; if (io->read_tag) { g_signal_handler_disconnect (io->sock, io->read_tag); io->read_tag = 0; } if (io->write_tag) { g_signal_handler_disconnect (io->sock, io->write_tag); io->write_tag = 0; } if (io->err_tag) { g_signal_handler_disconnect (io->sock, io->err_tag); io->err_tag = 0; } if (io->unpause_source) { g_source_destroy (io->unpause_source); io->unpause_source = NULL; } if (io->read_state < SOUP_MESSAGE_IO_STATE_FINISHING) soup_socket_disconnect (io->sock); else if (io->conn) { SoupConnection *conn = io->conn; io->conn = NULL; soup_connection_set_state (conn, SOUP_CONNECTION_IDLE); g_object_unref (conn); } }
static void set_current_request (SoupConnection *conn, SoupMessage *req) { SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn); g_return_if_fail (priv->cur_req == NULL); g_object_freeze_notify (G_OBJECT (conn)); stop_idle_timer (priv); priv->unused_timeout = 0; soup_message_set_io_status (req, SOUP_MESSAGE_IO_STATUS_RUNNING); priv->cur_req = req; g_object_notify (G_OBJECT (conn), "message"); if (priv->state == SOUP_CONNECTION_IDLE || req->method != SOUP_METHOD_CONNECT) soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE); g_object_add_weak_pointer (G_OBJECT (req), (gpointer)&priv->cur_req); g_object_thaw_notify (G_OBJECT (conn)); }
/** * soup_session_get_connection: * @session: a #SoupSession * @item: a #SoupMessageQueueItem * @try_pruning: on return, whether or not to try pruning a connection * * Tries to find or create a connection for @item; this is an internal * method for #SoupSession subclasses. * * If there is an idle connection to the relevant host available, then * that connection will be returned. The connection will be set to * %SOUP_CONNECTION_IN_USE, so the caller must call * soup_connection_set_state() to set it to %SOUP_CONNECTION_IDLE if * it ends up not using the connection right away. * * If there is no idle connection available, but it is possible to * create a new connection, then one will be created and returned * (with state %SOUP_CONNECTION_NEW). The caller MUST then call * soup_connection_connect_sync() or soup_connection_connect_async() * to connect it. If the connection attempt fails, the caller must * call soup_session_connection_failed() to tell the session to free * the connection. * * If no connection is available and a new connection cannot be made, * soup_session_get_connection() will return %NULL. If @session has * the maximum number of open connections open, but does not have the * maximum number of per-host connections open to the relevant host, * then *@try_pruning will be set to %TRUE. In this case, the caller * can call soup_session_try_prune_connection() to close an idle * connection, and then try soup_session_get_connection() again. (If * calling soup_session_try_prune_connection() wouldn't help, then * *@try_pruning is left untouched; it is NOT set to %FALSE.) * * Return value: a #SoupConnection, or %NULL **/ SoupConnection * soup_session_get_connection (SoupSession *session, SoupMessageQueueItem *item, gboolean *try_pruning) { SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session); SoupConnection *conn; SoupSessionHost *host; SoupAddress *remote_addr, *tunnel_addr; SoupSSLCredentials *ssl_creds; GSList *conns; gboolean has_pending = FALSE; SoupURI *uri; g_mutex_lock (priv->host_lock); host = get_host_for_message (session, item->msg); for (conns = host->connections; conns; conns = conns->next) { if (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); return conns->data; } else if (soup_connection_get_state (conns->data) == SOUP_CONNECTION_CONNECTING) has_pending = TRUE; } if (has_pending) { /* We've already started one connection to this * address, so don't start another one until it's * done. */ g_mutex_unlock (priv->host_lock); return NULL; } if (host->num_conns >= priv->max_conns_per_host) { g_mutex_unlock (priv->host_lock); return NULL; } if (priv->num_conns >= priv->max_conns) { *try_pruning = TRUE; g_mutex_unlock (priv->host_lock); return NULL; } if (item->proxy_addr) { remote_addr = item->proxy_addr; tunnel_addr = NULL; } else { remote_addr = host->addr; tunnel_addr = NULL; } uri = soup_message_get_uri (item->msg); if (uri->scheme == SOUP_URI_SCHEME_HTTPS) { if (!priv->ssl_creds) priv->ssl_creds = soup_ssl_get_client_credentials (priv->ssl_ca_file); ssl_creds = priv->ssl_creds; if (item->proxy_addr) tunnel_addr = host->addr; } else ssl_creds = NULL; conn = soup_connection_new ( SOUP_CONNECTION_REMOTE_ADDRESS, remote_addr, SOUP_CONNECTION_TUNNEL_ADDRESS, tunnel_addr, SOUP_CONNECTION_PROXY_URI, item->proxy_uri, SOUP_CONNECTION_SSL_CREDENTIALS, ssl_creds, SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context, SOUP_CONNECTION_TIMEOUT, priv->io_timeout, SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout, NULL); g_signal_connect (conn, "disconnected", G_CALLBACK (connection_disconnected), session); g_hash_table_insert (priv->conns, conn, host); priv->num_conns++; host->num_conns++; host->connections = g_slist_prepend (host->connections, conn); g_mutex_unlock (priv->host_lock); return conn; }