1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
5 * Copyright (C) 2000-2003, Ximian, Inc.
12 #include <glib/gi18n-lib.h>
14 #include "soup-session.h"
16 #include "soup-auth-manager.h"
17 #include "soup-cache-private.h"
18 #include "soup-connection.h"
19 #include "soup-message-private.h"
20 #include "soup-misc-private.h"
21 #include "soup-message-queue.h"
22 #include "soup-proxy-resolver-wrapper.h"
23 #include "soup-session-private.h"
24 #include "soup-socket-private.h"
25 #include "soup-websocket.h"
26 #include "soup-websocket-connection.h"
27 #include "soup-websocket-extension-manager-private.h"
29 #define HOST_KEEP_ALIVE 5 * 60 * 1000 /* 5 min in msecs */
32 * SECTION:soup-session
33 * @short_description: Soup session state object
35 * #SoupSession is the object that controls client-side HTTP. A
36 * #SoupSession encapsulates all of the state that libsoup is keeping
37 * on behalf of your program; cached HTTP connections, authentication
38 * information, etc. It also keeps track of various global options
39 * and features that you are using.
41 * Most applications will only need a single #SoupSession; the primary
42 * reason you might need multiple sessions is if you need to have
43 * multiple independent authentication contexts. (Eg, you are
44 * connecting to a server and authenticating as two different users at
45 * different times; the easiest way to ensure that each #SoupMessage
46 * is sent with the authentication information you intended is to use
47 * one session for the first user, and a second session for the other
50 * In the past, #SoupSession was an abstract class, and users needed
51 * to choose between #SoupSessionAsync (which always uses
52 * #GMainLoop<!-- -->-based I/O), or #SoupSessionSync (which always uses
53 * blocking I/O and can be used from multiple threads simultaneously).
54 * This is no longer necessary; you can (and should) use a plain
55 * #SoupSession, which supports both synchronous and asynchronous use.
56 * (When using a plain #SoupSession, soup_session_queue_message()
57 * behaves like it traditionally did on a #SoupSessionAsync, and
58 * soup_session_send_message() behaves like it traditionally did on a
61 * Additional #SoupSession functionality is provided by
62 * #SoupSessionFeature objects, which can be added to a session with
63 * soup_session_add_feature() or soup_session_add_feature_by_type()
64 * (or at construct time with the %SOUP_SESSION_ADD_FEATURE_BY_TYPE
65 * pseudo-property). For example, #SoupLogger provides support for
66 * logging HTTP traffic, #SoupContentDecoder provides support for
67 * compressed response handling, and #SoupContentSniffer provides
68 * support for HTML5-style response body content sniffing.
69 * Additionally, subtypes of #SoupAuth and #SoupRequest can be added
70 * as features, to add support for additional authentication and URI
73 * All #SoupSessions are created with a #SoupAuthManager, and support
74 * for %SOUP_TYPE_AUTH_BASIC and %SOUP_TYPE_AUTH_DIGEST. For
75 * #SoupRequest types, #SoupRequestHTTP, #SoupRequestFile, and
76 * #SoupRequestData are supported. Additionally, sessions using the
77 * plain #SoupSession class (rather than one of its deprecated
78 * subtypes) have a #SoupContentDecoder by default.
85 GSList *connections; /* CONTAINS: SoupConnection */
90 GSource *keep_alive_src;
93 static guint soup_host_uri_hash (gconstpointer key);
94 static gboolean soup_host_uri_equal (gconstpointer v1, gconstpointer v2);
100 GTlsInteraction *tls_interaction;
103 gboolean tlsdb_use_default;
105 guint io_timeout, idle_timeout;
106 SoupAddress *local_addr;
109 GProxyResolver *proxy_resolver;
110 gboolean proxy_use_default;
113 SoupSocketProperties *socket_props;
115 SoupMessageQueue *queue;
118 char *accept_language;
119 gboolean accept_language_auto;
122 GHashTable *features_cache;
124 GHashTable *http_hosts, *https_hosts; /* char* -> SoupSessionHost */
125 GHashTable *conns; /* SoupConnection -> SoupSessionHost */
127 guint max_conns, max_conns_per_host;
129 /* Must hold the conn_lock before potentially creating a new
130 * SoupSessionHost, adding/removing a connection,
131 * disconnecting a connection, moving a connection from
132 * IDLE to IN_USE, or when updating socket properties.
133 * Must not emit signals or destroy objects while holding it.
134 * The conn_cond is signaled when it may be possible for
135 * a previously-blocked message to continue.
140 GMainContext *async_context;
141 gboolean use_thread_context;
143 char **http_aliases, **https_aliases;
145 GHashTable *request_types;
146 } SoupSessionPrivate;
148 #define SOUP_IS_PLAIN_SESSION(o) (G_TYPE_FROM_INSTANCE (o) == SOUP_TYPE_SESSION)
150 static void free_host (SoupSessionHost *host);
151 static void connection_state_changed (GObject *object, GParamSpec *param,
153 static void connection_disconnected (SoupConnection *conn, gpointer user_data);
154 static void drop_connection (SoupSession *session, SoupSessionHost *host,
155 SoupConnection *conn);
157 static void auth_manager_authenticate (SoupAuthManager *manager,
158 SoupMessage *msg, SoupAuth *auth,
159 gboolean retrying, gpointer user_data);
161 static void async_run_queue (SoupSession *session);
163 static void async_send_request_running (SoupSession *session, SoupMessageQueueItem *item);
165 #define SOUP_SESSION_MAX_CONNS_DEFAULT 10
166 #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2
168 #define SOUP_SESSION_MAX_RESEND_COUNT 20
170 #define SOUP_SESSION_USER_AGENT_BASE "libsoup/" PACKAGE_VERSION
172 G_DEFINE_TYPE_WITH_PRIVATE (SoupSession, soup_session, G_TYPE_OBJECT)
184 static guint signals[LAST_SIGNAL] = { 0 };
192 PROP_MAX_CONNS_PER_HOST,
195 PROP_SSL_USE_SYSTEM_CA_FILE,
199 PROP_USE_THREAD_CONTEXT,
202 PROP_ACCEPT_LANGUAGE,
203 PROP_ACCEPT_LANGUAGE_AUTO,
206 PROP_ADD_FEATURE_BY_TYPE,
207 PROP_REMOVE_FEATURE_BY_TYPE,
211 PROP_TLS_INTERACTION,
217 soup_session_init (SoupSession *session)
219 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
220 SoupAuthManager *auth_manager;
222 priv->queue = soup_message_queue_new (session);
224 g_mutex_init (&priv->conn_lock);
225 g_cond_init (&priv->conn_cond);
226 priv->http_hosts = g_hash_table_new_full (soup_host_uri_hash,
228 NULL, (GDestroyNotify)free_host);
229 priv->https_hosts = g_hash_table_new_full (soup_host_uri_hash,
231 NULL, (GDestroyNotify)free_host);
232 priv->conns = g_hash_table_new (NULL, NULL);
234 priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
235 priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
237 priv->features_cache = g_hash_table_new (NULL, NULL);
239 auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER, NULL);
240 g_signal_connect (auth_manager, "authenticate",
241 G_CALLBACK (auth_manager_authenticate), session);
242 soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
243 SOUP_TYPE_AUTH_BASIC);
244 soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
245 SOUP_TYPE_AUTH_DIGEST);
246 soup_session_add_feature (session, SOUP_SESSION_FEATURE (auth_manager));
247 g_object_unref (auth_manager);
249 /* We'll be doing DNS continuously-ish while the session is active,
250 * so hold a ref on the default GResolver.
252 priv->resolver = g_resolver_get_default ();
254 priv->ssl_strict = TRUE;
256 priv->http_aliases = g_new (char *, 2);
257 priv->http_aliases[0] = (char *)g_intern_string ("*");
258 priv->http_aliases[1] = NULL;
260 priv->request_types = g_hash_table_new (soup_str_case_hash,
261 soup_str_case_equal);
262 soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_HTTP);
263 soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_FILE);
264 soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_DATA);
268 soup_session_constructor (GType type,
269 guint n_construct_properties,
270 GObjectConstructParam *construct_params)
274 object = G_OBJECT_CLASS (soup_session_parent_class)->constructor (type, n_construct_properties, construct_params);
276 /* If this is a "plain" SoupSession, fix up the default
277 * properties values, etc.
279 if (type == SOUP_TYPE_SESSION) {
280 SoupSession *session = SOUP_SESSION (object);
281 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
283 g_clear_pointer (&priv->async_context, g_main_context_unref);
284 priv->async_context = g_main_context_ref_thread_default ();
285 priv->use_thread_context = TRUE;
287 priv->io_timeout = priv->idle_timeout = 60;
289 priv->http_aliases[0] = NULL;
291 /* If the user overrides the proxy or tlsdb during construction,
292 * we don't want to needlessly resolve the extension point. So
293 * we just set flags saying to do it later.
295 priv->proxy_use_default = TRUE;
296 priv->tlsdb_use_default = TRUE;
298 soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_DECODER);
305 soup_session_dispose (GObject *object)
307 SoupSession *session = SOUP_SESSION (object);
308 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
310 priv->disposed = TRUE;
311 soup_session_abort (session);
312 g_warn_if_fail (g_hash_table_size (priv->conns) == 0);
314 while (priv->features)
315 soup_session_remove_feature (session, priv->features->data);
317 G_OBJECT_CLASS (soup_session_parent_class)->dispose (object);
321 soup_session_finalize (GObject *object)
323 SoupSession *session = SOUP_SESSION (object);
324 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
326 soup_message_queue_destroy (priv->queue);
328 g_mutex_clear (&priv->conn_lock);
329 g_cond_clear (&priv->conn_cond);
330 g_hash_table_destroy (priv->http_hosts);
331 g_hash_table_destroy (priv->https_hosts);
332 g_hash_table_destroy (priv->conns);
334 g_free (priv->user_agent);
335 g_free (priv->accept_language);
337 g_clear_object (&priv->tlsdb);
338 g_clear_object (&priv->tls_interaction);
339 g_free (priv->ssl_ca_file);
341 g_clear_pointer (&priv->async_context, g_main_context_unref);
342 g_clear_object (&priv->local_addr);
344 g_hash_table_destroy (priv->features_cache);
346 g_object_unref (priv->resolver);
347 g_clear_object (&priv->proxy_resolver);
348 g_clear_pointer (&priv->proxy_uri, soup_uri_free);
350 g_free (priv->http_aliases);
351 g_free (priv->https_aliases);
353 g_hash_table_destroy (priv->request_types);
355 g_clear_pointer (&priv->socket_props, soup_socket_properties_unref);
357 G_OBJECT_CLASS (soup_session_parent_class)->finalize (object);
360 /* requires conn_lock */
362 ensure_socket_props (SoupSession *session)
364 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
367 if (priv->socket_props)
370 if (priv->proxy_use_default) {
371 priv->proxy_resolver = g_object_ref (g_proxy_resolver_get_default ());
372 priv->proxy_use_default = FALSE;
374 if (priv->tlsdb_use_default) {
375 priv->tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
376 priv->tlsdb_use_default = FALSE;
379 ssl_strict = priv->ssl_strict && (priv->tlsdb != NULL ||
380 SOUP_IS_PLAIN_SESSION (session));
382 priv->socket_props = soup_socket_properties_new (priv->async_context,
383 priv->use_thread_context,
384 priv->proxy_resolver,
387 priv->tls_interaction,
393 /* Converts a language in POSIX format and to be RFC2616 compliant */
394 /* Based on code from epiphany-webkit (ephy_langs_append_languages()) */
396 posix_lang_to_rfc2616 (const gchar *language)
398 /* Don't include charset variants, etc */
399 if (strchr (language, '.') || strchr (language, '@'))
402 /* Ignore "C" locale, which g_get_language_names() always
403 * includes as a fallback.
405 if (!strcmp (language, "C"))
408 return g_strdelimit (g_ascii_strdown (language, -1), "_", '-');
411 /* Converts @quality from 0-100 to 0.0-1.0 and appends to @str */
413 add_quality_value (const gchar *str, int quality)
415 g_return_val_if_fail (str != NULL, NULL);
417 if (quality >= 0 && quality < 100) {
418 /* We don't use %.02g because of "." vs "," locale issues */
420 return g_strdup_printf ("%s;q=0.%02d", str, quality);
422 return g_strdup_printf ("%s;q=0.%d", str, quality / 10);
424 return g_strdup (str);
427 /* Returns a RFC2616 compliant languages list from system locales */
429 accept_languages_from_system (void)
431 const char * const * lang_names;
432 GPtrArray *langs = NULL;
433 char *lang, *langs_str;
437 lang_names = g_get_language_names ();
438 g_return_val_if_fail (lang_names != NULL, NULL);
440 /* Build the array of languages */
441 langs = g_ptr_array_new_with_free_func (g_free);
442 for (i = 0; lang_names[i] != NULL; i++) {
443 lang = posix_lang_to_rfc2616 (lang_names[i]);
445 g_ptr_array_add (langs, lang);
448 /* Add quality values */
451 else if (langs->len < 20)
456 for (i = 0; i < langs->len; i++) {
457 lang = langs->pdata[i];
458 langs->pdata[i] = add_quality_value (lang, 100 - i * delta);
462 /* Fallback: add "en" if list is empty */
464 g_ptr_array_add (langs, g_strdup ("en"));
466 g_ptr_array_add (langs, NULL);
467 langs_str = g_strjoinv (", ", (char **)langs->pdata);
468 g_ptr_array_free (langs, TRUE);
474 set_tlsdb (SoupSession *session, GTlsDatabase *tlsdb)
476 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
477 GTlsDatabase *system_default;
479 priv->tlsdb_use_default = FALSE;
480 if (tlsdb == priv->tlsdb)
483 g_object_freeze_notify (G_OBJECT (session));
485 system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
486 if (system_default) {
487 if (priv->tlsdb == system_default || tlsdb == system_default) {
488 g_object_notify (G_OBJECT (session), "ssl-use-system-ca-file");
490 g_object_unref (system_default);
493 if (priv->ssl_ca_file) {
494 g_free (priv->ssl_ca_file);
495 priv->ssl_ca_file = NULL;
496 g_object_notify (G_OBJECT (session), "ssl-ca-file");
500 g_object_unref (priv->tlsdb);
503 g_object_ref (priv->tlsdb);
505 g_object_notify (G_OBJECT (session), "tls-database");
506 g_object_thaw_notify (G_OBJECT (session));
510 set_use_system_ca_file (SoupSession *session, gboolean use_system_ca_file)
512 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
513 GTlsDatabase *system_default;
515 priv->tlsdb_use_default = FALSE;
517 system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
519 if (use_system_ca_file)
520 set_tlsdb (session, system_default);
521 else if (priv->tlsdb == system_default)
522 set_tlsdb (session, NULL);
524 g_clear_object (&system_default);
528 set_ssl_ca_file (SoupSession *session, const char *ssl_ca_file)
530 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
532 GError *error = NULL;
534 priv->tlsdb_use_default = FALSE;
535 if (!g_strcmp0 (priv->ssl_ca_file, ssl_ca_file))
538 g_object_freeze_notify (G_OBJECT (session));
540 if (g_path_is_absolute (ssl_ca_file))
541 tlsdb = g_tls_file_database_new (ssl_ca_file, &error);
545 cwd = g_get_current_dir ();
546 path = g_build_filename (cwd, ssl_ca_file, NULL);
547 tlsdb = g_tls_file_database_new (path, &error);
553 if (!g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_UNAVAILABLE)) {
554 g_warning ("Could not set SSL credentials from '%s': %s",
555 ssl_ca_file, error->message);
557 tlsdb = g_tls_file_database_new ("/dev/null", NULL);
559 g_error_free (error);
562 set_tlsdb (session, tlsdb);
564 g_object_unref (tlsdb);
566 priv->ssl_ca_file = g_strdup (ssl_ca_file);
567 g_object_notify (G_OBJECT (session), "ssl-ca-file");
568 } else if (priv->ssl_ca_file) {
569 g_clear_pointer (&priv->ssl_ca_file, g_free);
570 g_object_notify (G_OBJECT (session), "ssl-ca-file");
573 g_object_thaw_notify (G_OBJECT (session));
576 /* priv->http_aliases and priv->https_aliases are stored as arrays of
577 * *interned* strings, so we can't just use g_strdupv() to set them.
580 set_aliases (char ***variable, char **value)
592 len = g_strv_length (value);
593 *variable = g_new (char *, len + 1);
594 for (i = 0; i < len; i++)
595 (*variable)[i] = (char *)g_intern_string (value[i]);
596 (*variable)[i] = NULL;
600 set_proxy_resolver (SoupSession *session, SoupURI *uri,
601 SoupProxyURIResolver *soup_resolver,
602 GProxyResolver *g_resolver)
604 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
606 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
607 soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_URI_RESOLVER);
608 G_GNUC_END_IGNORE_DEPRECATIONS;
609 g_clear_object (&priv->proxy_resolver);
610 g_clear_pointer (&priv->proxy_uri, soup_uri_free);
611 priv->proxy_use_default = FALSE;
616 priv->proxy_uri = soup_uri_copy (uri);
617 uri_string = soup_uri_to_string_internal (uri, FALSE, TRUE, TRUE);
618 priv->proxy_resolver = g_simple_proxy_resolver_new (uri_string, NULL);
620 } else if (soup_resolver) {
621 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
622 if (SOUP_IS_PROXY_RESOLVER_DEFAULT (soup_resolver))
623 priv->proxy_resolver = g_object_ref (g_proxy_resolver_get_default ());
625 priv->proxy_resolver = soup_proxy_resolver_wrapper_new (soup_resolver);
626 G_GNUC_END_IGNORE_DEPRECATIONS;
627 } else if (g_resolver)
628 priv->proxy_resolver = g_object_ref (g_resolver);
632 soup_session_set_property (GObject *object, guint prop_id,
633 const GValue *value, GParamSpec *pspec)
635 SoupSession *session = SOUP_SESSION (object);
636 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
637 const char *user_agent;
638 SoupSessionFeature *feature;
639 GMainContext *async_context;
640 gboolean socket_props_changed = FALSE;
643 case PROP_LOCAL_ADDRESS:
644 priv->local_addr = g_value_dup_object (value);
645 socket_props_changed = TRUE;
648 set_proxy_resolver (session, g_value_get_boxed (value),
650 soup_session_abort (session);
651 socket_props_changed = TRUE;
653 case PROP_PROXY_RESOLVER:
654 set_proxy_resolver (session, NULL, NULL,
655 g_value_get_object (value));
656 socket_props_changed = TRUE;
659 priv->max_conns = g_value_get_int (value);
661 case PROP_MAX_CONNS_PER_HOST:
662 priv->max_conns_per_host = g_value_get_int (value);
665 g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
666 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
668 if (g_value_get_boolean (value))
669 soup_session_feature_add_feature (feature, SOUP_TYPE_AUTH_NTLM);
671 soup_session_feature_remove_feature (feature, SOUP_TYPE_AUTH_NTLM);
673 g_warning ("Trying to set use-ntlm on session with no auth-manager");
675 case PROP_SSL_CA_FILE:
676 set_ssl_ca_file (session, g_value_get_string (value));
677 socket_props_changed = TRUE;
679 case PROP_SSL_USE_SYSTEM_CA_FILE:
680 set_use_system_ca_file (session, g_value_get_boolean (value));
681 socket_props_changed = TRUE;
683 case PROP_TLS_DATABASE:
684 set_tlsdb (session, g_value_get_object (value));
685 socket_props_changed = TRUE;
687 case PROP_TLS_INTERACTION:
688 g_clear_object(&priv->tls_interaction);
689 priv->tls_interaction = g_value_dup_object (value);
690 socket_props_changed = TRUE;
692 case PROP_SSL_STRICT:
693 priv->ssl_strict = g_value_get_boolean (value);
694 socket_props_changed = TRUE;
696 case PROP_ASYNC_CONTEXT:
697 async_context = g_value_get_pointer (value);
698 if (async_context && async_context != g_main_context_get_thread_default ())
699 g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
700 priv->async_context = async_context;
701 if (priv->async_context)
702 g_main_context_ref (priv->async_context);
703 socket_props_changed = TRUE;
705 case PROP_USE_THREAD_CONTEXT:
706 if (!g_value_get_boolean (value))
707 g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
708 priv->use_thread_context = g_value_get_boolean (value);
709 if (priv->use_thread_context) {
710 if (priv->async_context)
711 g_main_context_unref (priv->async_context);
712 priv->async_context = g_main_context_get_thread_default ();
713 if (priv->async_context)
714 g_main_context_ref (priv->async_context);
716 socket_props_changed = TRUE;
719 priv->io_timeout = g_value_get_uint (value);
720 socket_props_changed = TRUE;
722 case PROP_USER_AGENT:
723 g_free (priv->user_agent);
724 user_agent = g_value_get_string (value);
726 priv->user_agent = NULL;
727 else if (!*user_agent) {
729 g_strdup (SOUP_SESSION_USER_AGENT_BASE);
730 } else if (g_str_has_suffix (user_agent, " ")) {
732 g_strdup_printf ("%s%s", user_agent,
733 SOUP_SESSION_USER_AGENT_BASE);
735 priv->user_agent = g_strdup (user_agent);
737 case PROP_ACCEPT_LANGUAGE:
738 g_free (priv->accept_language);
739 priv->accept_language = g_strdup (g_value_get_string (value));
740 priv->accept_language_auto = FALSE;
742 case PROP_ACCEPT_LANGUAGE_AUTO:
743 priv->accept_language_auto = g_value_get_boolean (value);
744 if (priv->accept_language) {
745 g_free (priv->accept_language);
746 priv->accept_language = NULL;
749 /* Get languages from system if needed */
750 if (priv->accept_language_auto)
751 priv->accept_language = accept_languages_from_system ();
753 case PROP_IDLE_TIMEOUT:
754 priv->idle_timeout = g_value_get_uint (value);
755 socket_props_changed = TRUE;
757 case PROP_ADD_FEATURE:
758 soup_session_add_feature (session, g_value_get_object (value));
760 case PROP_ADD_FEATURE_BY_TYPE:
761 soup_session_add_feature_by_type (session, g_value_get_gtype (value));
763 case PROP_REMOVE_FEATURE_BY_TYPE:
764 soup_session_remove_feature_by_type (session, g_value_get_gtype (value));
766 case PROP_HTTP_ALIASES:
767 set_aliases (&priv->http_aliases, g_value_get_boxed (value));
769 case PROP_HTTPS_ALIASES:
770 set_aliases (&priv->https_aliases, g_value_get_boxed (value));
773 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
777 g_mutex_lock (&priv->conn_lock);
778 if (priv->socket_props && socket_props_changed) {
779 soup_socket_properties_unref (priv->socket_props);
780 priv->socket_props = NULL;
781 ensure_socket_props (session);
783 g_mutex_unlock (&priv->conn_lock);
787 soup_session_get_property (GObject *object, guint prop_id,
788 GValue *value, GParamSpec *pspec)
790 SoupSession *session = SOUP_SESSION (object);
791 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
792 SoupSessionFeature *feature;
796 case PROP_LOCAL_ADDRESS:
797 g_value_set_object (value, priv->local_addr);
800 g_value_set_boxed (value, priv->proxy_uri);
802 case PROP_PROXY_RESOLVER:
803 g_mutex_lock (&priv->conn_lock);
804 ensure_socket_props (session);
805 g_mutex_unlock (&priv->conn_lock);
806 g_value_set_object (value, priv->proxy_resolver);
809 g_value_set_int (value, priv->max_conns);
811 case PROP_MAX_CONNS_PER_HOST:
812 g_value_set_int (value, priv->max_conns_per_host);
815 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
817 g_value_set_boolean (value, soup_session_feature_has_feature (feature, SOUP_TYPE_AUTH_NTLM));
819 g_value_set_boolean (value, FALSE);
821 case PROP_SSL_CA_FILE:
822 g_value_set_string (value, priv->ssl_ca_file);
824 case PROP_SSL_USE_SYSTEM_CA_FILE:
825 tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
826 g_mutex_lock (&priv->conn_lock);
827 ensure_socket_props (session);
828 g_mutex_unlock (&priv->conn_lock);
829 g_value_set_boolean (value, priv->tlsdb == tlsdb);
830 g_clear_object (&tlsdb);
832 case PROP_TLS_DATABASE:
833 g_mutex_lock (&priv->conn_lock);
834 ensure_socket_props (session);
835 g_mutex_unlock (&priv->conn_lock);
836 g_value_set_object (value, priv->tlsdb);
838 case PROP_TLS_INTERACTION:
839 g_value_set_object (value, priv->tls_interaction);
841 case PROP_SSL_STRICT:
842 g_value_set_boolean (value, priv->ssl_strict);
844 case PROP_ASYNC_CONTEXT:
845 g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
847 case PROP_USE_THREAD_CONTEXT:
848 g_value_set_boolean (value, priv->use_thread_context);
851 g_value_set_uint (value, priv->io_timeout);
853 case PROP_USER_AGENT:
854 g_value_set_string (value, priv->user_agent);
856 case PROP_ACCEPT_LANGUAGE:
857 g_value_set_string (value, priv->accept_language);
859 case PROP_ACCEPT_LANGUAGE_AUTO:
860 g_value_set_boolean (value, priv->accept_language_auto);
862 case PROP_IDLE_TIMEOUT:
863 g_value_set_uint (value, priv->idle_timeout);
865 case PROP_HTTP_ALIASES:
866 g_value_set_boxed (value, priv->http_aliases);
868 case PROP_HTTPS_ALIASES:
869 g_value_set_boxed (value, priv->https_aliases);
872 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
880 * Creates a #SoupSession with the default options.
882 * Return value: the new session.
887 soup_session_new (void)
889 return g_object_new (SOUP_TYPE_SESSION, NULL);
893 * soup_session_new_with_options:
894 * @optname1: name of first property to set
895 * @...: value of @optname1, followed by additional property/value pairs
897 * Creates a #SoupSession with the specified options.
899 * Return value: the new session.
904 soup_session_new_with_options (const char *optname1,
907 SoupSession *session;
910 va_start (ap, optname1);
911 session = (SoupSession *)g_object_new_valist (SOUP_TYPE_SESSION,
919 * soup_session_get_async_context:
920 * @session: a #SoupSession
922 * Gets @session's #SoupSession:async-context. This does not add a ref
923 * to the context, so you will need to ref it yourself if you want it
924 * to outlive its session.
926 * For a modern #SoupSession, this will always just return the
927 * thread-default #GMainContext, and so is not especially useful.
929 * Return value: (nullable) (transfer none): @session's #GMainContext,
933 soup_session_get_async_context (SoupSession *session)
935 SoupSessionPrivate *priv;
937 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
938 priv = soup_session_get_instance_private (session);
940 if (priv->use_thread_context)
941 return g_main_context_get_thread_default ();
943 return priv->async_context;
948 /* Note that we can't use soup_uri_host_hash() and soup_uri_host_equal()
949 * because we want to ignore the protocol; http://example.com and
950 * webcal://example.com are the same host.
953 soup_host_uri_hash (gconstpointer key)
955 const SoupURI *uri = key;
957 g_return_val_if_fail (uri != NULL && uri->host != NULL, 0);
959 return uri->port + soup_str_case_hash (uri->host);
963 soup_host_uri_equal (gconstpointer v1, gconstpointer v2)
965 const SoupURI *one = v1;
966 const SoupURI *two = v2;
968 g_return_val_if_fail (one != NULL && two != NULL, one == two);
969 g_return_val_if_fail (one->host != NULL && two->host != NULL, one->host == two->host);
971 if (one->port != two->port)
974 return g_ascii_strcasecmp (one->host, two->host) == 0;
978 static SoupSessionHost *
979 soup_session_host_new (SoupSession *session, SoupURI *uri)
981 SoupSessionHost *host;
983 host = g_slice_new0 (SoupSessionHost);
984 host->uri = soup_uri_copy_host (uri);
985 if (host->uri->scheme != SOUP_URI_SCHEME_HTTP &&
986 host->uri->scheme != SOUP_URI_SCHEME_HTTPS) {
987 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
989 if (soup_uri_is_https (host->uri, priv->https_aliases))
990 host->uri->scheme = SOUP_URI_SCHEME_HTTPS;
992 host->uri->scheme = SOUP_URI_SCHEME_HTTP;
995 host->addr = g_object_new (SOUP_TYPE_ADDRESS,
996 SOUP_ADDRESS_NAME, host->uri->host,
997 SOUP_ADDRESS_PORT, host->uri->port,
998 SOUP_ADDRESS_PROTOCOL, host->uri->scheme,
1000 host->keep_alive_src = NULL;
1001 host->session = session;
1006 /* Requires conn_lock to be locked */
1007 static SoupSessionHost *
1008 get_host_for_uri (SoupSession *session, SoupURI *uri)
1010 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1011 SoupSessionHost *host;
1013 SoupURI *uri_tmp = NULL;
1015 https = soup_uri_is_https (uri, priv->https_aliases);
1017 host = g_hash_table_lookup (priv->https_hosts, uri);
1019 host = g_hash_table_lookup (priv->http_hosts, uri);
1023 if (uri->scheme != SOUP_URI_SCHEME_HTTP &&
1024 uri->scheme != SOUP_URI_SCHEME_HTTPS) {
1025 uri = uri_tmp = soup_uri_copy (uri);
1026 uri->scheme = https ? SOUP_URI_SCHEME_HTTPS : SOUP_URI_SCHEME_HTTP;
1028 host = soup_session_host_new (session, uri);
1030 soup_uri_free (uri_tmp);
1033 g_hash_table_insert (priv->https_hosts, host->uri, host);
1035 g_hash_table_insert (priv->http_hosts, host->uri, host);
1040 /* Requires conn_lock to be locked */
1041 static SoupSessionHost *
1042 get_host_for_message (SoupSession *session, SoupMessage *msg)
1044 return get_host_for_uri (session, soup_message_get_uri (msg));
1048 free_host (SoupSessionHost *host)
1050 g_warn_if_fail (host->connections == NULL);
1052 if (host->keep_alive_src) {
1053 g_source_destroy (host->keep_alive_src);
1054 g_source_unref (host->keep_alive_src);
1057 soup_uri_free (host->uri);
1058 g_object_unref (host->addr);
1059 g_slice_free (SoupSessionHost, host);
1063 auth_manager_authenticate (SoupAuthManager *manager, SoupMessage *msg,
1064 SoupAuth *auth, gboolean retrying,
1067 g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
1070 #define SOUP_SESSION_WOULD_REDIRECT_AS_GET(session, msg) \
1071 ((msg)->status_code == SOUP_STATUS_SEE_OTHER || \
1072 ((msg)->status_code == SOUP_STATUS_FOUND && \
1073 !SOUP_METHOD_IS_SAFE ((msg)->method)) || \
1074 ((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY && \
1075 (msg)->method == SOUP_METHOD_POST))
1077 #define SOUP_SESSION_WOULD_REDIRECT_AS_SAFE(session, msg) \
1078 (((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY || \
1079 (msg)->status_code == SOUP_STATUS_PERMANENT_REDIRECT || \
1080 (msg)->status_code == SOUP_STATUS_TEMPORARY_REDIRECT || \
1081 (msg)->status_code == SOUP_STATUS_FOUND) && \
1082 SOUP_METHOD_IS_SAFE ((msg)->method))
1084 static inline SoupURI *
1085 redirection_uri (SoupMessage *msg)
1087 const char *new_loc;
1090 new_loc = soup_message_headers_get_one (msg->response_headers,
1094 new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
1095 if (!new_uri || !new_uri->host) {
1097 soup_uri_free (new_uri);
1105 * soup_session_would_redirect:
1106 * @session: a #SoupSession
1107 * @msg: a #SoupMessage that has response headers
1109 * Checks if @msg contains a response that would cause @session to
1110 * redirect it to a new URL (ignoring @msg's %SOUP_MESSAGE_NO_REDIRECT
1111 * flag, and the number of times it has already been redirected).
1113 * Return value: whether @msg would be redirected
1118 soup_session_would_redirect (SoupSession *session, SoupMessage *msg)
1120 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1123 /* It must have an appropriate status code and method */
1124 if (!SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg) &&
1125 !SOUP_SESSION_WOULD_REDIRECT_AS_SAFE (session, msg))
1128 /* and a Location header that parses to an http URI */
1129 if (!soup_message_headers_get_one (msg->response_headers, "Location"))
1131 new_uri = redirection_uri (msg);
1134 if (!new_uri->host || !*new_uri->host ||
1135 (!soup_uri_is_http (new_uri, priv->http_aliases) &&
1136 !soup_uri_is_https (new_uri, priv->https_aliases))) {
1137 soup_uri_free (new_uri);
1141 soup_uri_free (new_uri);
1146 * soup_session_redirect_message:
1147 * @session: the session
1148 * @msg: a #SoupMessage that has received a 3xx response
1150 * Updates @msg's URI according to its status code and "Location"
1151 * header, and requeues it on @session. Use this when you have set
1152 * %SOUP_MESSAGE_NO_REDIRECT on a message, but have decided to allow a
1153 * particular redirection to occur, or if you want to allow a
1154 * redirection that #SoupSession will not perform automatically (eg,
1155 * redirecting a non-safe method such as DELETE).
1157 * If @msg's status code indicates that it should be retried as a GET
1158 * request, then @msg will be modified accordingly.
1160 * If @msg has already been redirected too many times, this will
1161 * cause it to fail with %SOUP_STATUS_TOO_MANY_REDIRECTS.
1163 * Return value: %TRUE if a redirection was applied, %FALSE if not
1164 * (eg, because there was no Location header, or it could not be
1170 soup_session_redirect_message (SoupSession *session, SoupMessage *msg)
1174 new_uri = redirection_uri (msg);
1178 if (SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg)) {
1179 if (msg->method != SOUP_METHOD_HEAD) {
1181 SOUP_MESSAGE_METHOD, SOUP_METHOD_GET,
1184 soup_message_set_request (msg, NULL,
1185 SOUP_MEMORY_STATIC, NULL, 0);
1186 soup_message_headers_set_encoding (msg->request_headers,
1187 SOUP_ENCODING_NONE);
1190 soup_message_set_uri (msg, new_uri);
1191 soup_uri_free (new_uri);
1193 soup_session_requeue_message (session, msg);
1198 redirect_handler (SoupMessage *msg, gpointer user_data)
1200 SoupMessageQueueItem *item = user_data;
1201 SoupSession *session = item->session;
1203 if (!soup_session_would_redirect (session, msg)) {
1204 SoupURI *new_uri = redirection_uri (msg);
1205 gboolean invalid = !new_uri || !new_uri->host;
1208 soup_uri_free (new_uri);
1209 if (invalid && !item->new_api) {
1210 soup_message_set_status_full (msg,
1211 SOUP_STATUS_MALFORMED,
1212 "Invalid Redirect URL");
1217 soup_session_redirect_message (session, msg);
1221 re_emit_connection_event (SoupConnection *conn,
1222 GSocketClientEvent event,
1223 GIOStream *connection,
1226 SoupMessageQueueItem *item = user_data;
1228 soup_message_network_event (item->msg, event, connection);
1232 soup_session_set_item_connection (SoupSession *session,
1233 SoupMessageQueueItem *item,
1234 SoupConnection *conn)
1237 g_signal_handlers_disconnect_by_func (item->conn, re_emit_connection_event, item);
1238 g_object_unref (item->conn);
1242 item->conn_is_dedicated = FALSE;
1243 soup_message_set_connection (item->msg, conn);
1246 g_object_ref (item->conn);
1247 g_signal_connect (item->conn, "event",
1248 G_CALLBACK (re_emit_connection_event), item);
1253 message_restarted (SoupMessage *msg, gpointer user_data)
1255 SoupMessageQueueItem *item = user_data;
1258 (!soup_message_is_keepalive (msg) ||
1259 SOUP_STATUS_IS_REDIRECTION (msg->status_code))) {
1260 if (soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE)
1261 soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
1262 soup_session_set_item_connection (item->session, item, NULL);
1265 soup_message_cleanup_response (msg);
1268 SoupMessageQueueItem *
1269 soup_session_append_queue_item (SoupSession *session, SoupMessage *msg,
1270 gboolean async, gboolean new_api,
1271 SoupSessionCallback callback, gpointer user_data)
1273 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1274 SoupMessageQueueItem *item;
1275 SoupSessionHost *host;
1277 soup_message_cleanup_response (msg);
1279 item = soup_message_queue_append (priv->queue, msg, callback, user_data);
1280 item->async = async;
1281 item->new_api = new_api;
1283 g_mutex_lock (&priv->conn_lock);
1284 host = get_host_for_message (session, item->msg);
1285 host->num_messages++;
1286 g_mutex_unlock (&priv->conn_lock);
1288 if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
1289 soup_message_add_header_handler (
1290 msg, "got_body", "Location",
1291 G_CALLBACK (redirect_handler), item);
1293 g_signal_connect (msg, "restarted",
1294 G_CALLBACK (message_restarted), item);
1296 g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
1298 soup_message_queue_item_ref (item);
1303 soup_session_send_queue_item (SoupSession *session,
1304 SoupMessageQueueItem *item,
1305 SoupMessageCompletionFn completion_cb)
1307 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1309 if (priv->user_agent) {
1310 soup_message_headers_replace (item->msg->request_headers,
1311 "User-Agent", priv->user_agent);
1314 if (priv->accept_language &&
1315 !soup_message_headers_get_list (item->msg->request_headers,
1316 "Accept-Language")) {
1317 soup_message_headers_append (item->msg->request_headers,
1319 priv->accept_language);
1322 /* Force keep alive connections for HTTP 1.0. Performance will
1323 * improve when issuing multiple requests to the same host in
1324 * a short period of time, as we wouldn't need to establish
1325 * new connections. Keep alive is implicit for HTTP 1.1.
1327 if (!soup_message_headers_header_contains (item->msg->request_headers,
1328 "Connection", "Keep-Alive") &&
1329 !soup_message_headers_header_contains (item->msg->request_headers,
1330 "Connection", "close") &&
1331 !soup_message_headers_header_contains (item->msg->request_headers,
1332 "Connection", "Upgrade")) {
1333 soup_message_headers_append (item->msg->request_headers,
1334 "Connection", "Keep-Alive");
1337 g_signal_emit (session, signals[REQUEST_STARTED], 0,
1338 item->msg, soup_connection_get_socket (item->conn));
1339 soup_message_starting (item->msg);
1340 if (item->state == SOUP_MESSAGE_RUNNING)
1341 soup_connection_send_request (item->conn, item, completion_cb, item);
1345 soup_session_cleanup_connections (SoupSession *session,
1346 gboolean cleanup_idle)
1348 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1349 GSList *conns = NULL, *c;
1350 GHashTableIter iter;
1351 gpointer conn, host;
1352 SoupConnectionState state;
1354 g_mutex_lock (&priv->conn_lock);
1355 g_hash_table_iter_init (&iter, priv->conns);
1356 while (g_hash_table_iter_next (&iter, &conn, &host)) {
1357 state = soup_connection_get_state (conn);
1358 if (state == SOUP_CONNECTION_REMOTE_DISCONNECTED ||
1359 (cleanup_idle && state == SOUP_CONNECTION_IDLE)) {
1360 conns = g_slist_prepend (conns, g_object_ref (conn));
1361 g_hash_table_iter_remove (&iter);
1362 drop_connection (session, host, conn);
1365 g_mutex_unlock (&priv->conn_lock);
1370 for (c = conns; c; c = c->next) {
1372 soup_connection_disconnect (conn);
1373 g_object_unref (conn);
1375 g_slist_free (conns);
1381 free_unused_host (gpointer user_data)
1383 SoupSessionHost *host = (SoupSessionHost *) user_data;
1384 SoupSessionPrivate *priv = soup_session_get_instance_private (host->session);
1386 g_mutex_lock (&priv->conn_lock);
1388 /* In a multithreaded session, a connection might have been
1389 * added while we were waiting for conn_lock.
1391 if (host->connections) {
1392 g_mutex_unlock (&priv->conn_lock);
1396 /* This will free the host in addition to removing it from the
1399 if (host->uri->scheme == SOUP_URI_SCHEME_HTTPS)
1400 g_hash_table_remove (priv->https_hosts, host->uri);
1402 g_hash_table_remove (priv->http_hosts, host->uri);
1403 g_mutex_unlock (&priv->conn_lock);
1409 drop_connection (SoupSession *session, SoupSessionHost *host, SoupConnection *conn)
1411 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1413 /* Note: caller must hold conn_lock, and must remove @conn
1414 * from priv->conns itself.
1418 host->connections = g_slist_remove (host->connections, conn);
1421 /* Free the SoupHost (and its SoupAddress) if there
1422 * has not been any new connection to the host during
1423 * the last HOST_KEEP_ALIVE msecs.
1425 if (host->num_conns == 0) {
1426 g_assert (host->keep_alive_src == NULL);
1427 host->keep_alive_src = soup_add_timeout (priv->async_context,
1431 host->keep_alive_src = g_source_ref (host->keep_alive_src);
1435 g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
1436 g_signal_handlers_disconnect_by_func (conn, connection_state_changed, session);
1439 g_object_unref (conn);
1443 connection_disconnected (SoupConnection *conn, gpointer user_data)
1445 SoupSession *session = user_data;
1446 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1447 SoupSessionHost *host;
1449 g_mutex_lock (&priv->conn_lock);
1451 host = g_hash_table_lookup (priv->conns, conn);
1453 g_hash_table_remove (priv->conns, conn);
1454 drop_connection (session, host, conn);
1456 g_mutex_unlock (&priv->conn_lock);
1458 soup_session_kick_queue (session);
1462 connection_state_changed (GObject *object, GParamSpec *param, gpointer user_data)
1464 SoupSession *session = user_data;
1465 SoupConnection *conn = SOUP_CONNECTION (object);
1467 if (soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE)
1468 soup_session_kick_queue (session);
1472 soup_session_get_queue (SoupSession *session)
1474 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1480 soup_session_unqueue_item (SoupSession *session,
1481 SoupMessageQueueItem *item)
1483 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1484 SoupSessionHost *host;
1485 SoupConnection *dedicated_conn = NULL;
1488 if (item->conn_is_dedicated)
1489 dedicated_conn = g_object_ref (item->conn);
1490 else if (item->msg->method != SOUP_METHOD_CONNECT ||
1491 !SOUP_STATUS_IS_SUCCESSFUL (item->msg->status_code))
1492 soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
1493 soup_session_set_item_connection (session, item, NULL);
1496 if (item->state != SOUP_MESSAGE_FINISHED) {
1497 g_warning ("finished an item with state %d", item->state);
1501 soup_message_queue_remove (priv->queue, item);
1503 g_mutex_lock (&priv->conn_lock);
1504 host = get_host_for_message (session, item->msg);
1505 host->num_messages--;
1506 if (dedicated_conn) {
1507 /* FIXME: Do not drop the connection if current number of connections
1508 * is no longer over the limits, just mark it as IDLE so it can be reused.
1510 g_hash_table_remove (priv->conns, dedicated_conn);
1511 drop_connection (session, host, dedicated_conn);
1513 g_cond_broadcast (&priv->conn_cond);
1514 g_mutex_unlock (&priv->conn_lock);
1516 if (dedicated_conn) {
1517 soup_connection_disconnect (dedicated_conn);
1518 g_object_unref (dedicated_conn);
1521 /* g_signal_handlers_disconnect_by_func doesn't work if you
1522 * have a metamarshal, meaning it doesn't work with
1523 * soup_message_add_header_handler()
1525 g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
1526 0, 0, NULL, NULL, item);
1527 g_signal_emit (session, signals[REQUEST_UNQUEUED], 0, item->msg);
1528 soup_message_queue_item_unref (item);
1532 soup_session_set_item_status (SoupSession *session,
1533 SoupMessageQueueItem *item,
1537 SoupURI *uri = NULL;
1539 switch (status_code) {
1540 case SOUP_STATUS_CANT_RESOLVE:
1541 case SOUP_STATUS_CANT_CONNECT:
1542 uri = soup_message_get_uri (item->msg);
1545 case SOUP_STATUS_CANT_RESOLVE_PROXY:
1546 case SOUP_STATUS_CANT_CONNECT_PROXY:
1548 uri = soup_connection_get_proxy_uri (item->conn);
1551 case SOUP_STATUS_SSL_FAILED:
1552 if (!g_tls_backend_supports_tls (g_tls_backend_get_default ())) {
1553 soup_message_set_status_full (item->msg, status_code,
1554 "TLS/SSL support not available; install glib-networking");
1564 soup_message_set_status_full (item->msg, status_code, error->message);
1565 else if (uri && uri->host) {
1566 char *msg = g_strdup_printf ("%s (%s)",
1567 soup_status_get_phrase (status_code),
1569 soup_message_set_status_full (item->msg, status_code, msg);
1572 soup_message_set_status (item->msg, status_code);
1577 message_completed (SoupMessage *msg, SoupMessageIOCompletion completion, gpointer user_data)
1579 SoupMessageQueueItem *item = user_data;
1582 soup_session_kick_queue (item->session);
1584 if (completion == SOUP_MESSAGE_IO_STOLEN) {
1585 item->state = SOUP_MESSAGE_FINISHED;
1586 soup_session_unqueue_item (item->session, item);
1590 if (item->state != SOUP_MESSAGE_RESTARTING) {
1591 item->state = SOUP_MESSAGE_FINISHING;
1593 if (item->new_api && !item->async)
1594 soup_session_process_queue_item (item->session, item, NULL, TRUE);
1599 status_from_connect_error (SoupMessageQueueItem *item, GError *error)
1604 return SOUP_STATUS_OK;
1606 if (error->domain == G_TLS_ERROR)
1607 status = SOUP_STATUS_SSL_FAILED;
1608 else if (error->domain == G_RESOLVER_ERROR)
1609 status = SOUP_STATUS_CANT_RESOLVE;
1610 else if (error->domain == G_IO_ERROR) {
1611 if (error->code == G_IO_ERROR_CANCELLED)
1612 status = SOUP_STATUS_CANCELLED;
1613 else if (error->code == G_IO_ERROR_HOST_UNREACHABLE ||
1614 error->code == G_IO_ERROR_NETWORK_UNREACHABLE ||
1615 error->code == G_IO_ERROR_CONNECTION_REFUSED)
1616 status = SOUP_STATUS_CANT_CONNECT;
1617 else if (error->code == G_IO_ERROR_PROXY_FAILED ||
1618 error->code == G_IO_ERROR_PROXY_AUTH_FAILED ||
1619 error->code == G_IO_ERROR_PROXY_NEED_AUTH ||
1620 error->code == G_IO_ERROR_PROXY_NOT_ALLOWED)
1621 status = SOUP_STATUS_CANT_CONNECT_PROXY;
1623 status = SOUP_STATUS_IO_ERROR;
1625 status = SOUP_STATUS_IO_ERROR;
1627 if (item->conn && soup_connection_is_via_proxy (item->conn))
1628 return soup_status_proxify (status);
1634 tunnel_complete (SoupMessageQueueItem *tunnel_item,
1635 guint status, GError *error)
1637 SoupMessageQueueItem *item = tunnel_item->related;
1638 SoupSession *session = tunnel_item->session;
1640 soup_message_finished (tunnel_item->msg);
1641 soup_message_queue_item_unref (tunnel_item);
1643 if (item->msg->status_code)
1644 item->state = SOUP_MESSAGE_FINISHING;
1645 soup_message_set_https_status (item->msg, item->conn);
1647 item->error = error;
1649 status = status_from_connect_error (item, error);
1650 if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
1651 soup_connection_disconnect (item->conn);
1652 soup_session_set_item_connection (session, item, NULL);
1653 if (!item->new_api || item->msg->status_code == 0)
1654 soup_session_set_item_status (session, item, status, error);
1657 item->state = SOUP_MESSAGE_READY;
1659 soup_session_kick_queue (session);
1660 soup_message_queue_item_unref (item);
1664 tunnel_handshake_complete (GObject *object,
1665 GAsyncResult *result,
1668 SoupConnection *conn = SOUP_CONNECTION (object);
1669 SoupMessageQueueItem *tunnel_item = user_data;
1670 GError *error = NULL;
1672 soup_connection_start_ssl_finish (conn, result, &error);
1673 tunnel_complete (tunnel_item, 0, error);
1677 tunnel_message_completed (SoupMessage *msg, SoupMessageIOCompletion completion,
1680 SoupMessageQueueItem *tunnel_item = user_data;
1681 SoupMessageQueueItem *item = tunnel_item->related;
1682 SoupSession *session = tunnel_item->session;
1685 if (tunnel_item->state == SOUP_MESSAGE_RESTARTING) {
1686 soup_message_restarted (msg);
1687 if (tunnel_item->conn) {
1688 tunnel_item->state = SOUP_MESSAGE_RUNNING;
1689 soup_session_send_queue_item (session, tunnel_item,
1690 tunnel_message_completed);
1694 soup_message_set_status (msg, SOUP_STATUS_TRY_AGAIN);
1697 tunnel_item->state = SOUP_MESSAGE_FINISHED;
1698 soup_session_unqueue_item (session, tunnel_item);
1700 status = tunnel_item->msg->status_code;
1701 if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
1702 tunnel_complete (tunnel_item, status, NULL);
1706 if (tunnel_item->async) {
1707 soup_connection_start_ssl_async (item->conn, item->cancellable,
1708 tunnel_handshake_complete,
1711 GError *error = NULL;
1713 soup_connection_start_ssl_sync (item->conn, item->cancellable, &error);
1714 tunnel_complete (tunnel_item, 0, error);
1719 tunnel_connect (SoupMessageQueueItem *item)
1721 SoupSession *session = item->session;
1722 SoupMessageQueueItem *tunnel_item;
1726 item->state = SOUP_MESSAGE_TUNNELING;
1728 uri = soup_connection_get_remote_uri (item->conn);
1729 msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT, uri);
1730 soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
1732 tunnel_item = soup_session_append_queue_item (session, msg,
1735 g_object_unref (msg);
1736 tunnel_item->related = item;
1737 soup_message_queue_item_ref (item);
1738 soup_session_set_item_connection (session, tunnel_item, item->conn);
1739 tunnel_item->state = SOUP_MESSAGE_RUNNING;
1741 g_signal_emit (session, signals[TUNNELING], 0, tunnel_item->conn);
1743 soup_session_send_queue_item (session, tunnel_item,
1744 tunnel_message_completed);
1748 connect_complete (SoupMessageQueueItem *item, SoupConnection *conn, GError *error)
1750 SoupSession *session = item->session;
1753 soup_message_set_https_status (item->msg, item->conn);
1756 item->state = SOUP_MESSAGE_CONNECTED;
1760 item->error = error;
1761 status = status_from_connect_error (item, error);
1762 soup_connection_disconnect (conn);
1763 if (item->state == SOUP_MESSAGE_CONNECTING) {
1764 if (!item->new_api || item->msg->status_code == 0)
1765 soup_session_set_item_status (session, item, status, error);
1766 soup_session_set_item_connection (session, item, NULL);
1767 item->state = SOUP_MESSAGE_READY;
1772 connect_async_complete (GObject *object,
1773 GAsyncResult *result,
1776 SoupConnection *conn = SOUP_CONNECTION (object);
1777 SoupMessageQueueItem *item = user_data;
1778 GError *error = NULL;
1780 soup_connection_connect_finish (conn, result, &error);
1781 connect_complete (item, conn, error);
1783 if (item->state == SOUP_MESSAGE_CONNECTED ||
1784 item->state == SOUP_MESSAGE_READY)
1785 async_run_queue (item->session);
1787 soup_session_kick_queue (item->session);
1789 soup_message_queue_item_unref (item);
1792 /* requires conn_lock */
1793 static SoupConnection *
1794 get_connection_for_host (SoupSession *session,
1795 SoupMessageQueueItem *item,
1796 SoupSessionHost *host,
1797 gboolean need_new_connection,
1798 gboolean ignore_connection_limits,
1799 gboolean *try_cleanup,
1800 gboolean *is_dedicated_connection)
1802 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1803 SoupConnection *conn;
1805 guint num_pending = 0;
1811 g_return_val_if_fail (soup_connection_get_state (item->conn) != SOUP_CONNECTION_DISCONNECTED, FALSE);
1815 for (conns = host->connections; conns; conns = conns->next) {
1818 if (!need_new_connection && soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE) {
1819 soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
1821 } else if (soup_connection_get_state (conn) == SOUP_CONNECTION_CONNECTING)
1825 /* Limit the number of pending connections; num_messages / 2
1826 * is somewhat arbitrary...
1828 if (num_pending > host->num_messages / 2) {
1829 if (!ignore_connection_limits)
1832 *is_dedicated_connection = TRUE;
1835 if (host->num_conns >= priv->max_conns_per_host) {
1836 if (!ignore_connection_limits) {
1837 if (need_new_connection)
1838 *try_cleanup = TRUE;
1842 *is_dedicated_connection = TRUE;
1845 if (priv->num_conns >= priv->max_conns) {
1846 if (!ignore_connection_limits) {
1847 *try_cleanup = TRUE;
1851 *is_dedicated_connection = TRUE;
1854 ensure_socket_props (session);
1855 conn = g_object_new (SOUP_TYPE_CONNECTION,
1856 SOUP_CONNECTION_REMOTE_URI, host->uri,
1857 SOUP_CONNECTION_SSL, soup_uri_is_https (host->uri, priv->https_aliases),
1858 SOUP_CONNECTION_SOCKET_PROPERTIES, priv->socket_props,
1861 g_signal_connect (conn, "disconnected",
1862 G_CALLBACK (connection_disconnected),
1864 g_signal_connect (conn, "notify::state",
1865 G_CALLBACK (connection_state_changed),
1868 /* This is a debugging-related signal, and so can ignore the
1869 * usual rule about not emitting signals while holding
1872 g_signal_emit (session, signals[CONNECTION_CREATED], 0, conn);
1874 g_hash_table_insert (priv->conns, conn, host);
1878 host->connections = g_slist_prepend (host->connections, conn);
1880 if (host->keep_alive_src) {
1881 g_source_destroy (host->keep_alive_src);
1882 g_source_unref (host->keep_alive_src);
1883 host->keep_alive_src = NULL;
1890 get_connection (SoupMessageQueueItem *item, gboolean *should_cleanup)
1892 SoupSession *session = item->session;
1893 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1894 SoupSessionHost *host;
1895 SoupConnection *conn = NULL;
1896 gboolean my_should_cleanup = FALSE;
1897 gboolean need_new_connection;
1898 gboolean ignore_connection_limits;
1899 gboolean is_dedicated_connection = FALSE;
1901 soup_session_cleanup_connections (session, FALSE);
1903 need_new_connection =
1904 (soup_message_get_flags (item->msg) & SOUP_MESSAGE_NEW_CONNECTION) ||
1905 (!(soup_message_get_flags (item->msg) & SOUP_MESSAGE_IDEMPOTENT) &&
1906 !SOUP_METHOD_IS_IDEMPOTENT (item->msg->method));
1907 ignore_connection_limits =
1908 (soup_message_get_flags (item->msg) & SOUP_MESSAGE_IGNORE_CONNECTION_LIMITS);
1910 g_mutex_lock (&priv->conn_lock);
1911 host = get_host_for_message (session, item->msg);
1913 conn = get_connection_for_host (session, item, host,
1914 need_new_connection,
1915 ignore_connection_limits,
1917 &is_dedicated_connection);
1918 if (conn || item->async)
1921 if (my_should_cleanup) {
1922 g_mutex_unlock (&priv->conn_lock);
1923 soup_session_cleanup_connections (session, TRUE);
1924 g_mutex_lock (&priv->conn_lock);
1926 my_should_cleanup = FALSE;
1930 g_cond_wait (&priv->conn_cond, &priv->conn_lock);
1932 g_mutex_unlock (&priv->conn_lock);
1936 *should_cleanup = my_should_cleanup;
1940 soup_session_set_item_connection (session, item, conn);
1941 item->conn_is_dedicated = is_dedicated_connection;
1943 if (soup_connection_get_state (item->conn) != SOUP_CONNECTION_NEW) {
1944 item->state = SOUP_MESSAGE_READY;
1945 soup_message_set_https_status (item->msg, item->conn);
1949 item->state = SOUP_MESSAGE_CONNECTING;
1952 soup_message_queue_item_ref (item);
1953 soup_connection_connect_async (item->conn, item->cancellable,
1954 connect_async_complete, item);
1957 GError *error = NULL;
1959 soup_connection_connect_sync (item->conn, item->cancellable, &error);
1960 connect_complete (item, conn, error);
1967 soup_session_process_queue_item (SoupSession *session,
1968 SoupMessageQueueItem *item,
1969 gboolean *should_cleanup,
1972 g_assert (item->session == session);
1978 switch (item->state) {
1979 case SOUP_MESSAGE_STARTING:
1980 if (!get_connection (item, should_cleanup))
1984 case SOUP_MESSAGE_CONNECTED:
1985 if (soup_connection_is_tunnelled (item->conn))
1986 tunnel_connect (item);
1988 item->state = SOUP_MESSAGE_READY;
1991 case SOUP_MESSAGE_READY:
1992 if (item->connect_only) {
1993 item->state = SOUP_MESSAGE_FINISHING;
1997 if (item->msg->status_code) {
1998 if (item->msg->status_code == SOUP_STATUS_TRY_AGAIN) {
1999 soup_message_cleanup_response (item->msg);
2000 item->state = SOUP_MESSAGE_STARTING;
2002 item->state = SOUP_MESSAGE_FINISHING;
2006 item->state = SOUP_MESSAGE_RUNNING;
2008 soup_session_send_queue_item (session, item, message_completed);
2010 if (item->new_api) {
2012 async_send_request_running (session, item);
2017 case SOUP_MESSAGE_RUNNING:
2021 g_warn_if_fail (item->new_api);
2022 item->state = SOUP_MESSAGE_FINISHING;
2025 case SOUP_MESSAGE_CACHED:
2026 /* Will be handled elsewhere */
2029 case SOUP_MESSAGE_RESTARTING:
2030 item->state = SOUP_MESSAGE_STARTING;
2031 soup_message_restarted (item->msg);
2034 case SOUP_MESSAGE_FINISHING:
2035 item->state = SOUP_MESSAGE_FINISHED;
2036 soup_message_finished (item->msg);
2037 if (item->state != SOUP_MESSAGE_FINISHED) {
2038 g_return_if_fail (!item->new_api);
2042 soup_message_queue_item_ref (item);
2043 soup_session_unqueue_item (session, item);
2044 if (item->async && item->callback)
2045 item->callback (session, item->msg, item->callback_data);
2046 soup_message_queue_item_unref (item);
2050 /* Nothing to do with this message in any
2053 g_warn_if_fail (item->async);
2056 } while (loop && item->state != SOUP_MESSAGE_FINISHED);
2060 async_run_queue (SoupSession *session)
2062 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
2063 SoupMessageQueueItem *item;
2065 gboolean try_cleanup = TRUE, should_cleanup = FALSE;
2067 g_object_ref (session);
2068 soup_session_cleanup_connections (session, FALSE);
2071 for (item = soup_message_queue_first (priv->queue);
2073 item = soup_message_queue_next (priv->queue, item)) {
2076 /* CONNECT messages are handled specially */
2077 if (msg->method == SOUP_METHOD_CONNECT)
2081 item->async_context != soup_session_get_async_context (session))
2084 item->async_pending = FALSE;
2085 soup_session_process_queue_item (session, item, &should_cleanup, TRUE);
2088 if (try_cleanup && should_cleanup) {
2089 /* There is at least one message in the queue that
2090 * could be sent if we cleanupd an idle connection from
2091 * some other server.
2093 if (soup_session_cleanup_connections (session, TRUE)) {
2094 try_cleanup = should_cleanup = FALSE;
2099 g_object_unref (session);
2103 idle_run_queue (gpointer user_data)
2105 GWeakRef *wref = user_data;
2106 SoupSession *session;
2108 session = g_weak_ref_get (wref);
2112 async_run_queue (session);
2113 g_object_unref (session);
2118 idle_run_queue_dnotify (gpointer user_data)
2120 GWeakRef *wref = user_data;
2122 g_weak_ref_clear (wref);
2123 g_slice_free (GWeakRef, wref);
2127 * SoupSessionCallback:
2128 * @session: the session
2129 * @msg: the message that has finished
2130 * @user_data: the data passed to soup_session_queue_message
2132 * Prototype for the callback passed to soup_session_queue_message(),
2137 soup_session_real_queue_message (SoupSession *session, SoupMessage *msg,
2138 SoupSessionCallback callback, gpointer user_data)
2140 SoupMessageQueueItem *item;
2142 item = soup_session_append_queue_item (session, msg, TRUE, FALSE,
2143 callback, user_data);
2144 soup_session_kick_queue (session);
2145 soup_message_queue_item_unref (item);
2149 * soup_session_queue_message:
2150 * @session: a #SoupSession
2151 * @msg: (transfer full): the message to queue
2152 * @callback: (allow-none) (scope async): a #SoupSessionCallback which will
2153 * be called after the message completes or when an unrecoverable error occurs.
2154 * @user_data: (allow-none): a pointer passed to @callback.
2156 * Queues the message @msg for asynchronously sending the request and
2157 * receiving a response in the current thread-default #GMainContext.
2158 * If @msg has been processed before, any resources related to the
2159 * time it was last sent are freed.
2161 * Upon message completion, the callback specified in @callback will
2162 * be invoked. If after returning from this callback the message has not
2163 * been requeued, @msg will be unreffed.
2165 * (The behavior above applies to a plain #SoupSession; if you are
2166 * using #SoupSessionAsync or #SoupSessionSync, then the #GMainContext
2167 * that is used depends on the settings of #SoupSession:async-context
2168 * and #SoupSession:use-thread-context, and for #SoupSessionSync, the
2169 * message will actually be sent and processed in another thread, with
2170 * only the final callback occurring in the indicated #GMainContext.)
2172 * Contrast this method with soup_session_send_async(), which also
2173 * asynchronously sends a message, but returns before reading the
2174 * response body, and allows you to read the response via a
2178 soup_session_queue_message (SoupSession *session, SoupMessage *msg,
2179 SoupSessionCallback callback, gpointer user_data)
2181 g_return_if_fail (SOUP_IS_SESSION (session));
2182 g_return_if_fail (SOUP_IS_MESSAGE (msg));
2184 SOUP_SESSION_GET_CLASS (session)->queue_message (session, msg,
2185 callback, user_data);
2186 /* The SoupMessageQueueItem will hold a ref on @msg until it is
2187 * finished, so we can drop the ref adopted from the caller now.
2189 g_object_unref (msg);
2193 soup_session_real_requeue_message (SoupSession *session, SoupMessage *msg)
2195 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
2196 SoupMessageQueueItem *item;
2198 item = soup_message_queue_lookup (priv->queue, msg);
2199 g_return_if_fail (item != NULL);
2201 if (item->resend_count >= SOUP_SESSION_MAX_RESEND_COUNT) {
2202 if (SOUP_STATUS_IS_REDIRECTION (msg->status_code))
2203 soup_message_set_status (msg, SOUP_STATUS_TOO_MANY_REDIRECTS);
2205 g_warning ("SoupMessage %p stuck in infinite loop?", msg);
2207 item->resend_count++;
2208 item->state = SOUP_MESSAGE_RESTARTING;
2211 soup_message_queue_item_unref (item);
2215 * soup_session_requeue_message:
2216 * @session: a #SoupSession
2217 * @msg: the message to requeue
2219 * This causes @msg to be placed back on the queue to be attempted
2223 soup_session_requeue_message (SoupSession *session, SoupMessage *msg)
2225 g_return_if_fail (SOUP_IS_SESSION (session));
2226 g_return_if_fail (SOUP_IS_MESSAGE (msg));
2228 SOUP_SESSION_GET_CLASS (session)->requeue_message (session, msg);
2232 soup_session_real_send_message (SoupSession *session, SoupMessage *msg)
2234 SoupMessageQueueItem *item;
2237 item = soup_session_append_queue_item (session, msg, FALSE, FALSE,
2239 soup_session_process_queue_item (session, item, NULL, TRUE);
2240 status = msg->status_code;
2241 soup_message_queue_item_unref (item);
2246 * soup_session_send_message:
2247 * @session: a #SoupSession
2248 * @msg: the message to send
2250 * Synchronously send @msg. This call will not return until the
2251 * transfer is finished successfully or there is an unrecoverable
2254 * Unlike with soup_session_queue_message(), @msg is not freed upon
2257 * (Note that if you call this method on a #SoupSessionAsync, it will
2258 * still use asynchronous I/O internally, running the glib main loop
2259 * to process the message, which may also cause other events to be
2262 * Contrast this method with soup_session_send(), which also
2263 * synchronously sends a message, but returns before reading the
2264 * response body, and allows you to read the response via a
2267 * Return value: the HTTP status code of the response
2270 soup_session_send_message (SoupSession *session, SoupMessage *msg)
2272 g_return_val_if_fail (SOUP_IS_SESSION (session), SOUP_STATUS_MALFORMED);
2273 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_STATUS_MALFORMED);
2275 return SOUP_SESSION_GET_CLASS (session)->send_message (session, msg);
2280 * soup_session_pause_message:
2281 * @session: a #SoupSession
2282 * @msg: a #SoupMessage currently running on @session
2284 * Pauses HTTP I/O on @msg. Call soup_session_unpause_message() to
2287 * This may only be called for asynchronous messages (those sent on a
2288 * #SoupSessionAsync or using soup_session_queue_message()).
2291 soup_session_pause_message (SoupSession *session,
2294 SoupSessionPrivate *priv;
2295 SoupMessageQueueItem *item;
2297 g_return_if_fail (SOUP_IS_SESSION (session));
2298 g_return_if_fail (SOUP_IS_MESSAGE (msg));
2300 priv = soup_session_get_instance_private (session);
2301 item = soup_message_queue_lookup (priv->queue, msg);
2302 g_return_if_fail (item != NULL);
2303 g_return_if_fail (item->async);
2305 item->paused = TRUE;
2306 if (item->state == SOUP_MESSAGE_RUNNING)
2307 soup_message_io_pause (msg);
2308 soup_message_queue_item_unref (item);
2312 soup_session_real_kick_queue (SoupSession *session)
2314 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
2315 SoupMessageQueueItem *item;
2316 GHashTable *async_pending;
2317 gboolean have_sync_items = FALSE;
2322 async_pending = g_hash_table_new (NULL, NULL);
2323 for (item = soup_message_queue_first (priv->queue);
2325 item = soup_message_queue_next (priv->queue, item)) {
2327 GMainContext *context = item->async_context ? item->async_context : g_main_context_default ();
2329 if (!g_hash_table_contains (async_pending, context)) {
2330 if (!item->async_pending) {
2331 GWeakRef *wref = g_slice_new (GWeakRef);
2334 g_weak_ref_init (wref, session);
2335 source = soup_add_completion_reffed (context, idle_run_queue, wref, idle_run_queue_dnotify);
2336 g_source_unref (source);
2338 g_hash_table_add (async_pending, context);
2340 item->async_pending = TRUE;
2342 have_sync_items = TRUE;
2344 g_hash_table_unref (async_pending);
2346 if (have_sync_items) {
2347 g_mutex_lock (&priv->conn_lock);
2348 g_cond_broadcast (&priv->conn_cond);
2349 g_mutex_unlock (&priv->conn_lock);
2354 soup_session_kick_queue (SoupSession *session)
2356 SOUP_SESSION_GET_CLASS (session)->kick (session);
2360 * soup_session_unpause_message:
2361 * @session: a #SoupSession
2362 * @msg: a #SoupMessage currently running on @session
2364 * Resumes HTTP I/O on @msg. Use this to resume after calling
2365 * soup_session_pause_message().
2367 * If @msg is being sent via blocking I/O, this will resume reading or
2368 * writing immediately. If @msg is using non-blocking I/O, then
2369 * reading or writing won't resume until you return to the main loop.
2371 * This may only be called for asynchronous messages (those sent on a
2372 * #SoupSessionAsync or using soup_session_queue_message()).
2375 soup_session_unpause_message (SoupSession *session,
2378 SoupSessionPrivate *priv;
2379 SoupMessageQueueItem *item;
2381 g_return_if_fail (SOUP_IS_SESSION (session));
2382 g_return_if_fail (SOUP_IS_MESSAGE (msg));
2384 priv = soup_session_get_instance_private (session);
2385 item = soup_message_queue_lookup (priv->queue, msg);
2386 g_return_if_fail (item != NULL);
2387 g_return_if_fail (item->async);
2389 item->paused = FALSE;
2390 if (item->state == SOUP_MESSAGE_RUNNING)
2391 soup_message_io_unpause (msg);
2392 soup_message_queue_item_unref (item);
2394 soup_session_kick_queue (session);
2399 soup_session_real_cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
2401 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
2402 SoupMessageQueueItem *item;
2404 item = soup_message_queue_lookup (priv->queue, msg);
2405 g_return_if_fail (item != NULL);
2408 item->paused = FALSE;
2410 if (soup_message_io_in_progress (msg))
2411 soup_message_io_unpause (msg);
2414 soup_message_set_status (msg, status_code);
2415 g_cancellable_cancel (item->cancellable);
2417 soup_session_kick_queue (item->session);
2418 soup_message_queue_item_unref (item);
2422 * soup_session_cancel_message:
2423 * @session: a #SoupSession
2424 * @msg: the message to cancel
2425 * @status_code: status code to set on @msg (generally
2426 * %SOUP_STATUS_CANCELLED)
2428 * Causes @session to immediately finish processing @msg (regardless
2429 * of its current state) with a final status_code of @status_code. You
2430 * may call this at any time after handing @msg off to @session; if
2431 * @session has started sending the request but has not yet received
2432 * the complete response, then it will close the request's connection.
2433 * Note that with requests that have side effects (eg,
2434 * <literal>POST</literal>, <literal>PUT</literal>,
2435 * <literal>DELETE</literal>) it is possible that you might cancel the
2436 * request after the server acts on it, but before it returns a
2437 * response, leaving the remote resource in an unknown state.
2439 * If the message is cancelled while its response body is being read,
2440 * then the response body in @msg will be left partially-filled-in.
2441 * The response headers, on the other hand, will always be either
2442 * empty or complete.
2444 * Beware that with the deprecated #SoupSessionAsync, messages queued
2445 * with soup_session_queue_message() will have their callbacks invoked
2446 * before soup_session_cancel_message() returns. The plain
2447 * #SoupSession does not have this behavior; cancelling an
2448 * asynchronous message will merely queue its callback to be run after
2449 * returning to the main loop.
2452 soup_session_cancel_message (SoupSession *session, SoupMessage *msg,
2455 SoupSessionPrivate *priv;
2456 SoupMessageQueueItem *item;
2458 g_return_if_fail (SOUP_IS_SESSION (session));
2459 g_return_if_fail (SOUP_IS_MESSAGE (msg));
2461 priv = soup_session_get_instance_private (session);
2462 item = soup_message_queue_lookup (priv->queue, msg);
2463 /* If the message is already ending, don't do anything */
2466 if (item->state == SOUP_MESSAGE_FINISHED) {
2467 soup_message_queue_item_unref (item);
2471 SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg, status_code);
2472 soup_message_queue_item_unref (item);
2476 soup_session_real_flush_queue (SoupSession *session)
2478 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
2479 SoupMessageQueueItem *item;
2480 GHashTable *current = NULL;
2481 gboolean done = FALSE;
2483 if (SOUP_IS_SESSION_SYNC (session)) {
2484 /* Record the current contents of the queue */
2485 current = g_hash_table_new (NULL, NULL);
2486 for (item = soup_message_queue_first (priv->queue);
2488 item = soup_message_queue_next (priv->queue, item))
2489 g_hash_table_insert (current, item, item);
2492 /* Cancel everything */
2493 for (item = soup_message_queue_first (priv->queue);
2495 item = soup_message_queue_next (priv->queue, item)) {
2496 soup_session_cancel_message (session, item->msg,
2497 SOUP_STATUS_CANCELLED);
2500 if (SOUP_IS_SESSION_SYNC (session)) {
2501 /* Wait until all of the items in @current have been
2502 * removed from the queue. (This is not the same as
2503 * "wait for the queue to be empty", because the app
2504 * may queue new requests in response to the
2505 * cancellation of the old ones. We don't try to
2506 * cancel those requests as well, since we'd likely
2507 * just end up looping forever.)
2509 g_mutex_lock (&priv->conn_lock);
2512 for (item = soup_message_queue_first (priv->queue);
2514 item = soup_message_queue_next (priv->queue, item)) {
2515 if (g_hash_table_lookup (current, item))
2520 g_cond_wait (&priv->conn_cond, &priv->conn_lock);
2522 g_mutex_unlock (&priv->conn_lock);
2524 g_hash_table_destroy (current);
2529 * soup_session_abort:
2530 * @session: the session
2532 * Cancels all pending requests in @session and closes all idle
2533 * persistent connections.
2535 * The message cancellation has the same semantics as with
2536 * soup_session_cancel_message(); asynchronous requests on a
2537 * #SoupSessionAsync will have their callback called before
2538 * soup_session_abort() returns. Requests on a plain #SoupSession will
2542 soup_session_abort (SoupSession *session)
2544 SoupSessionPrivate *priv;
2546 GHashTableIter iter;
2547 gpointer conn, host;
2549 g_return_if_fail (SOUP_IS_SESSION (session));
2550 priv = soup_session_get_instance_private (session);
2552 SOUP_SESSION_GET_CLASS (session)->flush_queue (session);
2554 /* Close all idle connections */
2555 g_mutex_lock (&priv->conn_lock);
2557 g_hash_table_iter_init (&iter, priv->conns);
2558 while (g_hash_table_iter_next (&iter, &conn, &host)) {
2559 SoupConnectionState state;
2561 state = soup_connection_get_state (conn);
2562 if (state == SOUP_CONNECTION_IDLE ||
2563 state == SOUP_CONNECTION_REMOTE_DISCONNECTED) {
2564 conns = g_slist_prepend (conns, g_object_ref (conn));
2565 g_hash_table_iter_remove (&iter);
2566 drop_connection (session, host, conn);
2569 g_mutex_unlock (&priv->conn_lock);
2571 for (c = conns; c; c = c->next) {
2572 soup_connection_disconnect (c->data);
2573 g_object_unref (c->data);
2576 g_slist_free (conns);
2580 prefetch_uri (SoupSession *session, SoupURI *uri,
2581 GCancellable *cancellable,
2582 SoupAddressCallback callback, gpointer user_data)
2584 SoupSessionPrivate *priv;
2585 SoupSessionHost *host;
2588 priv = soup_session_get_instance_private (session);
2590 g_mutex_lock (&priv->conn_lock);
2591 host = get_host_for_uri (session, uri);
2592 addr = g_object_ref (host->addr);
2593 g_mutex_unlock (&priv->conn_lock);
2595 soup_address_resolve_async (addr,
2596 soup_session_get_async_context (session),
2597 cancellable, callback, user_data);
2598 g_object_unref (addr);
2602 * soup_session_prepare_for_uri:
2603 * @session: a #SoupSession
2604 * @uri: a #SoupURI which may be required
2606 * Tells @session that @uri may be requested shortly, and so the
2607 * session can try to prepare (resolving the domain name, obtaining
2608 * proxy address, etc.) in order to work more quickly once the URI is
2609 * actually requested.
2613 * Deprecated: 2.38: use soup_session_prefetch_dns() instead
2616 soup_session_prepare_for_uri (SoupSession *session, SoupURI *uri)
2618 g_return_if_fail (SOUP_IS_SESSION (session));
2619 g_return_if_fail (uri != NULL);
2624 prefetch_uri (session, uri, NULL, NULL, NULL);
2628 * soup_session_prefetch_dns:
2629 * @session: a #SoupSession
2630 * @hostname: a hostname to be resolved
2631 * @cancellable: (allow-none): a #GCancellable object, or %NULL
2632 * @callback: (scope async) (allow-none): callback to call with the
2634 * @user_data: data for @callback
2636 * Tells @session that an URI from the given @hostname may be requested
2637 * shortly, and so the session can try to prepare by resolving the
2638 * domain name in advance, in order to work more quickly once the URI
2639 * is actually requested.
2641 * If @cancellable is non-%NULL, it can be used to cancel the
2642 * resolution. @callback will still be invoked in this case, with a
2643 * status of %SOUP_STATUS_CANCELLED.
2648 soup_session_prefetch_dns (SoupSession *session, const char *hostname,
2649 GCancellable *cancellable,
2650 SoupAddressCallback callback, gpointer user_data)
2654 g_return_if_fail (SOUP_IS_SESSION (session));
2655 g_return_if_fail (hostname != NULL);
2657 /* FIXME: Prefetching should work for both HTTP and HTTPS */
2658 uri = soup_uri_new (NULL);
2659 soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP);
2660 soup_uri_set_host (uri, hostname);
2661 soup_uri_set_path (uri, "");
2663 prefetch_uri (session, uri, cancellable, callback, user_data);
2664 soup_uri_free (uri);
2668 * soup_session_add_feature:
2669 * @session: a #SoupSession
2670 * @feature: an object that implements #SoupSessionFeature
2672 * Adds @feature's functionality to @session. You can also add a
2673 * feature to the session at construct time by using the
2674 * %SOUP_SESSION_ADD_FEATURE property.
2676 * See the main #SoupSession documentation for information on what
2677 * features are present in sessions by default.
2682 soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature)
2684 SoupSessionPrivate *priv;
2686 g_return_if_fail (SOUP_IS_SESSION (session));
2687 g_return_if_fail (SOUP_IS_SESSION_FEATURE (feature));
2689 priv = soup_session_get_instance_private (session);
2691 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2692 if (SOUP_IS_PROXY_URI_RESOLVER (feature)) {
2693 set_proxy_resolver (session, NULL,
2694 SOUP_PROXY_URI_RESOLVER (feature),
2697 G_GNUC_END_IGNORE_DEPRECATIONS;
2699 priv->features = g_slist_prepend (priv->features, g_object_ref (feature));
2700 g_hash_table_remove_all (priv->features_cache);
2701 soup_session_feature_attach (feature, session);
2705 * soup_session_add_feature_by_type:
2706 * @session: a #SoupSession
2707 * @feature_type: a #GType
2709 * If @feature_type is the type of a class that implements
2710 * #SoupSessionFeature, this creates a new feature of that type and
2711 * adds it to @session as with soup_session_add_feature(). You can use
2712 * this when you don't need to customize the new feature in any way.
2714 * If @feature_type is not a #SoupSessionFeature type, this gives each
2715 * existing feature on @session the chance to accept @feature_type as
2716 * a "subfeature". This can be used to add new #SoupAuth or
2717 * #SoupRequest types, for instance.
2719 * You can also add a feature to the session at construct time by
2720 * using the %SOUP_SESSION_ADD_FEATURE_BY_TYPE property.
2722 * See the main #SoupSession documentation for information on what
2723 * features are present in sessions by default.
2728 soup_session_add_feature_by_type (SoupSession *session, GType feature_type)
2730 SoupSessionPrivate *priv;
2732 g_return_if_fail (SOUP_IS_SESSION (session));
2734 priv = soup_session_get_instance_private (session);
2736 if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2737 SoupSessionFeature *feature;
2739 feature = g_object_new (feature_type, NULL);
2740 soup_session_add_feature (session, feature);
2741 g_object_unref (feature);
2742 } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
2743 SoupRequestClass *request_class;
2746 request_class = g_type_class_ref (feature_type);
2747 for (i = 0; request_class->schemes[i]; i++) {
2748 g_hash_table_insert (priv->request_types,
2749 (char *)request_class->schemes[i],
2750 GSIZE_TO_POINTER (feature_type));
2755 for (f = priv->features; f; f = f->next) {
2756 if (soup_session_feature_add_feature (f->data, feature_type))
2759 g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
2764 * soup_session_remove_feature:
2765 * @session: a #SoupSession
2766 * @feature: a feature that has previously been added to @session
2768 * Removes @feature's functionality from @session.
2773 soup_session_remove_feature (SoupSession *session, SoupSessionFeature *feature)
2775 SoupSessionPrivate *priv;
2777 g_return_if_fail (SOUP_IS_SESSION (session));
2779 priv = soup_session_get_instance_private (session);
2780 if (g_slist_find (priv->features, feature)) {
2781 priv->features = g_slist_remove (priv->features, feature);
2782 g_hash_table_remove_all (priv->features_cache);
2783 soup_session_feature_detach (feature, session);
2785 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2786 if (SOUP_IS_PROXY_URI_RESOLVER (feature)) {
2787 if (SOUP_IS_PROXY_RESOLVER_WRAPPER (priv->proxy_resolver) &&
2788 SOUP_PROXY_RESOLVER_WRAPPER (priv->proxy_resolver)->soup_resolver == SOUP_PROXY_URI_RESOLVER (feature))
2789 g_clear_object (&priv->proxy_resolver);
2791 G_GNUC_END_IGNORE_DEPRECATIONS;
2793 g_object_unref (feature);
2798 * soup_session_remove_feature_by_type:
2799 * @session: a #SoupSession
2800 * @feature_type: a #GType
2802 * Removes all features of type @feature_type (or any subclass of
2803 * @feature_type) from @session. You can also remove standard features
2804 * from the session at construct time by using the
2805 * %SOUP_SESSION_REMOVE_FEATURE_BY_TYPE property.
2810 soup_session_remove_feature_by_type (SoupSession *session, GType feature_type)
2812 SoupSessionPrivate *priv;
2815 g_return_if_fail (SOUP_IS_SESSION (session));
2817 priv = soup_session_get_instance_private (session);
2819 if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2821 for (f = priv->features; f; f = f->next) {
2822 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type)) {
2823 soup_session_remove_feature (session, f->data);
2827 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2828 if (g_type_is_a (feature_type, SOUP_TYPE_PROXY_URI_RESOLVER))
2829 priv->proxy_use_default = FALSE;
2830 G_GNUC_END_IGNORE_DEPRECATIONS;
2831 } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
2832 SoupRequestClass *request_class;
2835 request_class = g_type_class_peek (feature_type);
2838 for (i = 0; request_class->schemes[i]; i++) {
2839 g_hash_table_remove (priv->request_types,
2840 request_class->schemes[i]);
2843 for (f = priv->features; f; f = f->next) {
2844 if (soup_session_feature_remove_feature (f->data, feature_type))
2847 g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
2852 * soup_session_has_feature:
2853 * @session: a #SoupSession
2854 * @feature_type: the #GType of the class of features to check for
2856 * Tests if @session has at a feature of type @feature_type (which can
2857 * be the type of either a #SoupSessionFeature, or else a subtype of
2858 * some class managed by another feature, such as #SoupAuth or
2861 * Return value: %TRUE or %FALSE
2866 soup_session_has_feature (SoupSession *session,
2869 SoupSessionPrivate *priv;
2872 g_return_val_if_fail (SOUP_IS_SESSION (session), FALSE);
2874 priv = soup_session_get_instance_private (session);
2876 if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2877 for (f = priv->features; f; f = f->next) {
2878 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
2881 } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
2882 SoupRequestClass *request_class;
2885 request_class = g_type_class_peek (feature_type);
2889 for (i = 0; request_class->schemes[i]; i++) {
2892 type = g_hash_table_lookup (priv->request_types,
2893 request_class->schemes[i]);
2894 if (type && g_type_is_a (GPOINTER_TO_SIZE (type), feature_type))
2898 for (f = priv->features; f; f = f->next) {
2899 if (soup_session_feature_has_feature (f->data, feature_type))
2908 * soup_session_get_features:
2909 * @session: a #SoupSession
2910 * @feature_type: the #GType of the class of features to get
2912 * Generates a list of @session's features of type @feature_type. (If
2913 * you want to see all features, you can pass %SOUP_TYPE_SESSION_FEATURE
2914 * for @feature_type.)
2916 * Return value: (transfer container) (element-type Soup.SessionFeature):
2917 * a list of features. You must free the list, but not its contents
2922 soup_session_get_features (SoupSession *session, GType feature_type)
2924 SoupSessionPrivate *priv;
2927 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
2929 priv = soup_session_get_instance_private (session);
2930 for (f = priv->features, ret = NULL; f; f = f->next) {
2931 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
2932 ret = g_slist_prepend (ret, f->data);
2934 return g_slist_reverse (ret);
2938 * soup_session_get_feature:
2939 * @session: a #SoupSession
2940 * @feature_type: the #GType of the feature to get
2942 * Gets the first feature in @session of type @feature_type. For
2943 * features where there may be more than one feature of a given type,
2944 * use soup_session_get_features().
2946 * Return value: (nullable) (transfer none): a #SoupSessionFeature, or
2947 * %NULL. The feature is owned by @session.
2951 SoupSessionFeature *
2952 soup_session_get_feature (SoupSession *session, GType feature_type)
2954 SoupSessionPrivate *priv;
2955 SoupSessionFeature *feature;
2958 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
2960 priv = soup_session_get_instance_private (session);
2962 feature = g_hash_table_lookup (priv->features_cache,
2963 GSIZE_TO_POINTER (feature_type));
2967 for (f = priv->features; f; f = f->next) {
2969 if (G_TYPE_CHECK_INSTANCE_TYPE (feature, feature_type)) {
2970 g_hash_table_insert (priv->features_cache,
2971 GSIZE_TO_POINTER (feature_type),
2980 * soup_session_get_feature_for_message:
2981 * @session: a #SoupSession
2982 * @feature_type: the #GType of the feature to get
2983 * @msg: a #SoupMessage
2985 * Gets the first feature in @session of type @feature_type, provided
2986 * that it is not disabled for @msg. As with
2987 * soup_session_get_feature(), this should only be used for features
2988 * where @feature_type is only expected to match a single feature. In
2989 * particular, if there are two matching features, and the first is
2990 * disabled on @msg, and the second is not, then this will return
2991 * %NULL, not the second feature.
2993 * Return value: (nullable) (transfer none): a #SoupSessionFeature, or %NULL. The
2994 * feature is owned by @session.
2998 SoupSessionFeature *
2999 soup_session_get_feature_for_message (SoupSession *session, GType feature_type,
3002 SoupSessionFeature *feature;
3004 feature = soup_session_get_feature (session, feature_type);
3005 if (feature && soup_message_disables_feature (msg, feature))
3011 soup_session_class_init (SoupSessionClass *session_class)
3013 GObjectClass *object_class = G_OBJECT_CLASS (session_class);
3015 /* virtual method definition */
3016 session_class->queue_message = soup_session_real_queue_message;
3017 session_class->send_message = soup_session_real_send_message;
3018 session_class->requeue_message = soup_session_real_requeue_message;
3019 session_class->cancel_message = soup_session_real_cancel_message;
3020 session_class->flush_queue = soup_session_real_flush_queue;
3021 session_class->kick = soup_session_real_kick_queue;
3023 /* virtual method override */
3024 object_class->constructor = soup_session_constructor;
3025 object_class->dispose = soup_session_dispose;
3026 object_class->finalize = soup_session_finalize;
3027 object_class->set_property = soup_session_set_property;
3028 object_class->get_property = soup_session_get_property;
3033 * SoupSession::request-queued:
3034 * @session: the session
3035 * @msg: the request that was queued
3037 * Emitted when a request is queued on @session. (Note that
3038 * "queued" doesn't just mean soup_session_queue_message();
3039 * soup_session_send_message() implicitly queues the message
3042 * When sending a request, first #SoupSession::request_queued
3043 * is emitted, indicating that the session has become aware of
3046 * Once a connection is available to send the request on, the
3047 * session emits #SoupSession::request_started. Then, various
3048 * #SoupMessage signals are emitted as the message is
3049 * processed. If the message is requeued, it will emit
3050 * #SoupMessage::restarted, which will then be followed by
3051 * another #SoupSession::request_started and another set of
3052 * #SoupMessage signals when the message is re-sent.
3054 * Eventually, the message will emit #SoupMessage::finished.
3055 * Normally, this signals the completion of message
3056 * processing. However, it is possible that the application
3057 * will requeue the message from the "finished" handler (or
3058 * equivalently, from the soup_session_queue_message()
3059 * callback). In that case, the process will loop back to
3060 * #SoupSession::request_started.
3062 * Eventually, a message will reach "finished" and not be
3063 * requeued. At that point, the session will emit
3064 * #SoupSession::request_unqueued to indicate that it is done
3067 * To sum up: #SoupSession::request_queued and
3068 * #SoupSession::request_unqueued are guaranteed to be emitted
3069 * exactly once, but #SoupSession::request_started and
3070 * #SoupMessage::finished (and all of the other #SoupMessage
3071 * signals) may be invoked multiple times for a given message.
3075 signals[REQUEST_QUEUED] =
3076 g_signal_new ("request-queued",
3077 G_OBJECT_CLASS_TYPE (object_class),
3086 * SoupSession::request-started:
3087 * @session: the session
3088 * @msg: the request being sent
3089 * @socket: the socket the request is being sent on
3091 * Emitted just before a request is sent. See
3092 * #SoupSession::request_queued for a detailed description of
3093 * the message lifecycle within a session.
3095 * Deprecated: 2.50. Use #SoupMessage::starting instead.
3097 signals[REQUEST_STARTED] =
3098 g_signal_new ("request-started",
3099 G_OBJECT_CLASS_TYPE (object_class),
3101 G_STRUCT_OFFSET (SoupSessionClass, request_started),
3109 * SoupSession::request-unqueued:
3110 * @session: the session
3111 * @msg: the request that was unqueued
3113 * Emitted when a request is removed from @session's queue,
3114 * indicating that @session is done with it. See
3115 * #SoupSession::request_queued for a detailed description of the
3116 * message lifecycle within a session.
3120 signals[REQUEST_UNQUEUED] =
3121 g_signal_new ("request-unqueued",
3122 G_OBJECT_CLASS_TYPE (object_class),
3131 * SoupSession::authenticate:
3132 * @session: the session
3133 * @msg: the #SoupMessage being sent
3134 * @auth: the #SoupAuth to authenticate
3135 * @retrying: %TRUE if this is the second (or later) attempt
3137 * Emitted when the session requires authentication. If
3138 * credentials are available call soup_auth_authenticate() on
3139 * @auth. If these credentials fail, the signal will be
3140 * emitted again, with @retrying set to %TRUE, which will
3141 * continue until you return without calling
3142 * soup_auth_authenticate() on @auth.
3144 * Note that this may be emitted before @msg's body has been
3147 * If you call soup_session_pause_message() on @msg before
3148 * returning, then you can authenticate @auth asynchronously
3149 * (as long as you g_object_ref() it to make sure it doesn't
3150 * get destroyed), and then unpause @msg when you are ready
3151 * for it to continue.
3153 signals[AUTHENTICATE] =
3154 g_signal_new ("authenticate",
3155 G_OBJECT_CLASS_TYPE (object_class),
3157 G_STRUCT_OFFSET (SoupSessionClass, authenticate),
3166 * SoupSession::connection-created:
3167 * @session: the #SoupSession
3168 * @connection: the connection
3170 * Emitted when a new connection is created. This is an
3171 * internal signal intended only to be used for debugging
3172 * purposes, and may go away in the future.
3176 signals[CONNECTION_CREATED] =
3177 g_signal_new ("connection-created",
3178 G_OBJECT_CLASS_TYPE (object_class),
3184 /* SoupConnection is private, so we can't use
3185 * SOUP_TYPE_CONNECTION here.
3190 * SoupSession::tunneling:
3191 * @session: the #SoupSession
3192 * @connection: the connection
3194 * Emitted when an SSL tunnel is being created on a proxy
3195 * connection. This is an internal signal intended only to be
3196 * used for debugging purposes, and may go away in the future.
3200 signals[TUNNELING] =
3201 g_signal_new ("tunneling",
3202 G_OBJECT_CLASS_TYPE (object_class),
3208 /* SoupConnection is private, so we can't use
3209 * SOUP_TYPE_CONNECTION here.
3216 * SoupSession:proxy-uri:
3218 * A proxy to use for all http and https requests in this
3219 * session. Setting this will clear the
3220 * #SoupSession:proxy-resolver property, and remove any
3221 * <type>SoupProxyURIResolver</type> features that have been
3222 * added to the session. Setting this property will also
3223 * cancel all currently pending messages.
3225 * Note that #SoupSession will normally handle looking up the
3226 * user's proxy settings for you; you should only use
3227 * #SoupSession:proxy-uri if you need to override the user's
3228 * normal proxy settings.
3230 * Also note that this proxy will be used for
3231 * <emphasis>all</emphasis> requests; even requests to
3232 * <literal>localhost</literal>. If you need more control over
3233 * proxies, you can create a #GSimpleProxyResolver and set the
3234 * #SoupSession:proxy-resolver property.
3236 * Deprecated: 2.70: Use SoupSession:proxy-resolver along with #GSimpleProxyResolver.
3239 * SOUP_SESSION_PROXY_URI:
3241 * Alias for the #SoupSession:proxy-uri property, qv.
3243 g_object_class_install_property (
3244 object_class, PROP_PROXY_URI,
3245 g_param_spec_boxed (SOUP_SESSION_PROXY_URI,
3247 "The HTTP Proxy to use for this session",
3250 G_PARAM_STATIC_STRINGS |
3251 G_PARAM_DEPRECATED));
3253 * SoupSession:proxy-resolver:
3255 * A #GProxyResolver to use with this session. Setting this
3256 * will clear the #SoupSession:proxy-uri property, and remove
3257 * any <type>SoupProxyURIResolver</type> features that have
3258 * been added to the session.
3260 * By default, in a plain #SoupSession, this is set to the
3261 * default #GProxyResolver, but you can set it to %NULL if you
3262 * don't want to use proxies, or set it to your own
3263 * #GProxyResolver if you want to control what proxies get
3269 * SOUP_SESSION_PROXY_RESOLVER:
3271 * Alias for the #SoupSession:proxy-resolver property, qv.
3273 g_object_class_install_property (
3274 object_class, PROP_PROXY_RESOLVER,
3275 g_param_spec_object (SOUP_SESSION_PROXY_RESOLVER,
3277 "The GProxyResolver to use for this session",
3278 G_TYPE_PROXY_RESOLVER,
3280 G_PARAM_STATIC_STRINGS));
3282 * SOUP_SESSION_MAX_CONNS:
3284 * Alias for the #SoupSession:max-conns property, qv.
3286 g_object_class_install_property (
3287 object_class, PROP_MAX_CONNS,
3288 g_param_spec_int (SOUP_SESSION_MAX_CONNS,
3289 "Max Connection Count",
3290 "The maximum number of connections that the session can open at once",
3293 SOUP_SESSION_MAX_CONNS_DEFAULT,
3295 G_PARAM_STATIC_STRINGS));
3297 * SOUP_SESSION_MAX_CONNS_PER_HOST:
3299 * Alias for the #SoupSession:max-conns-per-host property, qv.
3301 g_object_class_install_property (
3302 object_class, PROP_MAX_CONNS_PER_HOST,
3303 g_param_spec_int (SOUP_SESSION_MAX_CONNS_PER_HOST,
3304 "Max Per-Host Connection Count",
3305 "The maximum number of connections that the session can open at once to a given host",
3308 SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT,
3310 G_PARAM_STATIC_STRINGS));
3312 * SoupSession:idle-timeout:
3314 * Connection lifetime (in seconds) when idle. Any connection
3315 * left idle longer than this will be closed.
3317 * Although you can change this property at any time, it will
3318 * only affect newly-created connections, not currently-open
3319 * ones. You can call soup_session_abort() after setting this
3320 * if you want to ensure that all future connections will have
3321 * this timeout value.
3323 * Note that the default value of 60 seconds only applies to
3324 * plain #SoupSessions. If you are using #SoupSessionAsync or
3325 * #SoupSessionSync, the default value is 0 (meaning idle
3326 * connections will never time out).
3331 * SOUP_SESSION_IDLE_TIMEOUT:
3333 * Alias for the #SoupSession:idle-timeout property, qv.
3337 g_object_class_install_property (
3338 object_class, PROP_IDLE_TIMEOUT,
3339 g_param_spec_uint (SOUP_SESSION_IDLE_TIMEOUT,
3341 "Connection lifetime when idle",
3344 G_PARAM_STATIC_STRINGS));
3346 * SoupSession:use-ntlm:
3348 * Whether or not to use NTLM authentication.
3350 * Deprecated: use soup_session_add_feature_by_type() with
3351 * #SOUP_TYPE_AUTH_NTLM.
3354 * SOUP_SESSION_USE_NTLM:
3356 * Alias for the #SoupSession:use-ntlm property, qv.
3358 g_object_class_install_property (
3359 object_class, PROP_USE_NTLM,
3360 g_param_spec_boolean (SOUP_SESSION_USE_NTLM,
3362 "Whether or not to use NTLM authentication",
3364 G_PARAM_READWRITE | G_PARAM_DEPRECATED |
3365 G_PARAM_STATIC_STRINGS));
3367 * SoupSession:ssl-ca-file:
3369 * File containing SSL CA certificates.
3371 * If the specified file does not exist or cannot be read,
3372 * then libsoup will print a warning, and then behave as
3373 * though it had read in a empty CA file, meaning that all SSL
3374 * certificates will be considered invalid.
3376 * Deprecated: use #SoupSession:ssl-use-system-ca-file, or
3377 * else #SoupSession:tls-database with a #GTlsFileDatabase
3378 * (which allows you to do explicit error handling).
3381 * SOUP_SESSION_SSL_CA_FILE:
3383 * Alias for the #SoupSession:ssl-ca-file property, qv.
3385 g_object_class_install_property (
3386 object_class, PROP_SSL_CA_FILE,
3387 g_param_spec_string (SOUP_SESSION_SSL_CA_FILE,
3389 "File containing SSL CA certificates",
3391 G_PARAM_READWRITE | G_PARAM_DEPRECATED |
3392 G_PARAM_STATIC_STRINGS));
3394 * SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE:
3396 * Alias for the #SoupSession:ssl-use-system-ca-file property,
3402 * SoupSession:ssl-use-system-ca-file:
3404 * Setting this to %TRUE is equivalent to setting
3405 * #SoupSession:tls-database to the default system CA database.
3406 * (and likewise, setting #SoupSession:tls-database to the
3407 * default database by hand will cause this property to
3410 * Setting this to %FALSE (when it was previously %TRUE) will
3411 * clear the #SoupSession:tls-database field.
3413 * See #SoupSession:ssl-strict for more information on how
3414 * https certificate validation is handled.
3416 * Note that the default value of %TRUE only applies to plain
3417 * #SoupSessions. If you are using #SoupSessionAsync or
3418 * #SoupSessionSync, the default value is %FALSE, for backward
3423 g_object_class_install_property (
3424 object_class, PROP_SSL_USE_SYSTEM_CA_FILE,
3425 g_param_spec_boolean (SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE,
3426 "Use system CA file",
3427 "Use the system certificate database",
3430 G_PARAM_STATIC_STRINGS));
3432 * SOUP_SESSION_TLS_DATABASE:
3434 * Alias for the #SoupSession:tls-database property, qv.
3439 * SoupSession:tls-database:
3441 * Sets the #GTlsDatabase to use for validating SSL/TLS
3444 * Note that setting the #SoupSession:ssl-ca-file or
3445 * #SoupSession:ssl-use-system-ca-file property will cause
3446 * this property to be set to a #GTlsDatabase corresponding to
3447 * the indicated file or system default.
3449 * See #SoupSession:ssl-strict for more information on how
3450 * https certificate validation is handled.
3452 * If you are using a plain #SoupSession then
3453 * #SoupSession:ssl-use-system-ca-file will be %TRUE by
3454 * default, and so this property will be a copy of the system
3455 * CA database. If you are using #SoupSessionAsync or
3456 * #SoupSessionSync, this property will be %NULL by default.
3460 g_object_class_install_property (
3461 object_class, PROP_TLS_DATABASE,
3462 g_param_spec_object (SOUP_SESSION_TLS_DATABASE,
3464 "TLS database to use",
3465 G_TYPE_TLS_DATABASE,
3467 G_PARAM_STATIC_STRINGS));
3469 * SOUP_SESSION_SSL_STRICT:
3471 * Alias for the #SoupSession:ssl-strict property, qv.
3476 * SoupSession:ssl-strict:
3478 * Normally, if #SoupSession:tls-database is set (including if
3479 * it was set via #SoupSession:ssl-use-system-ca-file or
3480 * #SoupSession:ssl-ca-file), then libsoup will reject any
3481 * certificate that is invalid (ie, expired) or that is not
3482 * signed by one of the given CA certificates, and the
3483 * #SoupMessage will fail with the status
3484 * %SOUP_STATUS_SSL_FAILED.
3486 * If you set #SoupSession:ssl-strict to %FALSE, then all
3487 * certificates will be accepted, and you will need to call
3488 * soup_message_get_https_status() to distinguish valid from
3489 * invalid certificates. (This can be used, eg, if you want to
3490 * accept invalid certificates after giving some sort of
3493 * For a plain #SoupSession, if the session has no CA file or
3494 * TLS database, and this property is %TRUE, then all
3495 * certificates will be rejected. However, beware that the
3496 * deprecated #SoupSession subclasses (#SoupSessionAsync and
3497 * #SoupSessionSync) have the opposite behavior: if there is
3498 * no CA file or TLS database, then all certificates are always
3499 * accepted, and this property has no effect.
3503 g_object_class_install_property (
3504 object_class, PROP_SSL_STRICT,
3505 g_param_spec_boolean (SOUP_SESSION_SSL_STRICT,
3506 "Strictly validate SSL certificates",
3507 "Whether certificate errors should be considered a connection error",
3510 G_PARAM_STATIC_STRINGS));
3512 * SoupSession:async-context:
3514 * The #GMainContext that miscellaneous session-related
3515 * asynchronous callbacks are invoked on. (Eg, setting
3516 * #SoupSession:idle-timeout will add a timeout source on this
3519 * For a plain #SoupSession, this property is always set to
3520 * the #GMainContext that is the thread-default at the time
3521 * the session was created, and cannot be overridden. For the
3522 * deprecated #SoupSession subclasses, the default value is
3523 * %NULL, meaning to use the global default #GMainContext.
3525 * If #SoupSession:use-thread-context is %FALSE, this context
3526 * will also be used for asynchronous HTTP I/O.
3529 * SOUP_SESSION_ASYNC_CONTEXT:
3531 * Alias for the #SoupSession:async-context property, qv.
3533 g_object_class_install_property (
3534 object_class, PROP_ASYNC_CONTEXT,
3535 g_param_spec_pointer (SOUP_SESSION_ASYNC_CONTEXT,
3536 "Async GMainContext",
3537 "The GMainContext to dispatch async I/O in",
3538 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
3539 G_PARAM_STATIC_STRINGS));
3541 * SOUP_SESSION_USE_THREAD_CONTEXT:
3543 * Alias for the #SoupSession:use-thread-context property, qv.
3548 * SoupSession:use-thread-context:
3550 * If %TRUE (which it always is on a plain #SoupSession),
3551 * asynchronous HTTP requests in this session will run in
3552 * whatever the thread-default #GMainContext is at the time
3553 * they are started, rather than always occurring in
3554 * #SoupSession:async-context.
3558 g_object_class_install_property (
3559 object_class, PROP_USE_THREAD_CONTEXT,
3560 g_param_spec_boolean (SOUP_SESSION_USE_THREAD_CONTEXT,
3561 "Use thread-default GMainContext",
3562 "Whether to use thread-default main contexts",
3565 G_PARAM_STATIC_STRINGS));
3567 * SoupSession:timeout:
3569 * The timeout (in seconds) for socket I/O operations
3570 * (including connecting to a server, and waiting for a reply
3571 * to an HTTP request).
3573 * Although you can change this property at any time, it will
3574 * only affect newly-created connections, not currently-open
3575 * ones. You can call soup_session_abort() after setting this
3576 * if you want to ensure that all future connections will have
3577 * this timeout value.
3579 * Note that the default value of 60 seconds only applies to
3580 * plain #SoupSessions. If you are using #SoupSessionAsync or
3581 * #SoupSessionSync, the default value is 0 (meaning socket I/O
3582 * will not time out).
3584 * Not to be confused with #SoupSession:idle-timeout (which is
3585 * the length of time that idle persistent connections will be
3589 * SOUP_SESSION_TIMEOUT:
3591 * Alias for the #SoupSession:timeout property, qv.
3593 g_object_class_install_property (
3594 object_class, PROP_TIMEOUT,
3595 g_param_spec_uint (SOUP_SESSION_TIMEOUT,
3597 "Value in seconds to timeout a blocking I/O",
3600 G_PARAM_STATIC_STRINGS));
3603 * SoupSession:user-agent:
3605 * If non-%NULL, the value to use for the "User-Agent" header
3606 * on #SoupMessage<!-- -->s sent from this session.
3608 * RFC 2616 says: "The User-Agent request-header field
3609 * contains information about the user agent originating the
3610 * request. This is for statistical purposes, the tracing of
3611 * protocol violations, and automated recognition of user
3612 * agents for the sake of tailoring responses to avoid
3613 * particular user agent limitations. User agents SHOULD
3614 * include this field with requests."
3616 * The User-Agent header contains a list of one or more
3617 * product tokens, separated by whitespace, with the most
3618 * significant product token coming first. The tokens must be
3619 * brief, ASCII, and mostly alphanumeric (although "-", "_",
3620 * and "." are also allowed), and may optionally include a "/"
3621 * followed by a version string. You may also put comments,
3622 * enclosed in parentheses, between or after the tokens.
3624 * If you set a #SoupSession:user_agent property that has trailing
3625 * whitespace, #SoupSession will append its own product token
3626 * (eg, "<literal>libsoup/2.3.2</literal>") to the end of the
3630 * SOUP_SESSION_USER_AGENT:
3632 * Alias for the #SoupSession:user-agent property, qv.
3634 g_object_class_install_property (
3635 object_class, PROP_USER_AGENT,
3636 g_param_spec_string (SOUP_SESSION_USER_AGENT,
3637 "User-Agent string",
3638 "User-Agent string",
3641 G_PARAM_STATIC_STRINGS));
3644 * SoupSession:accept-language:
3646 * If non-%NULL, the value to use for the "Accept-Language" header
3647 * on #SoupMessage<!-- -->s sent from this session.
3649 * Setting this will disable
3650 * #SoupSession:accept-language-auto.
3655 * SOUP_SESSION_ACCEPT_LANGUAGE:
3657 * Alias for the #SoupSession:accept-language property, qv.
3661 g_object_class_install_property (
3662 object_class, PROP_ACCEPT_LANGUAGE,
3663 g_param_spec_string (SOUP_SESSION_ACCEPT_LANGUAGE,
3664 "Accept-Language string",
3665 "Accept-Language string",
3668 G_PARAM_STATIC_STRINGS));
3671 * SoupSession:accept-language-auto:
3673 * If %TRUE, #SoupSession will automatically set the string
3674 * for the "Accept-Language" header on every #SoupMessage
3675 * sent, based on the return value of g_get_language_names().
3677 * Setting this will override any previous value of
3678 * #SoupSession:accept-language.
3683 * SOUP_SESSION_ACCEPT_LANGUAGE_AUTO:
3685 * Alias for the #SoupSession:accept-language-auto property, qv.
3689 g_object_class_install_property (
3690 object_class, PROP_ACCEPT_LANGUAGE_AUTO,
3691 g_param_spec_boolean (SOUP_SESSION_ACCEPT_LANGUAGE_AUTO,
3692 "Accept-Language automatic mode",
3693 "Accept-Language automatic mode",
3696 G_PARAM_STATIC_STRINGS));
3699 * SoupSession:add-feature: (skip)
3701 * Add a feature object to the session. (Shortcut for calling
3702 * soup_session_add_feature().)
3707 * SOUP_SESSION_ADD_FEATURE: (skip)
3709 * Alias for the #SoupSession:add-feature property, qv.
3713 g_object_class_install_property (
3714 object_class, PROP_ADD_FEATURE,
3715 g_param_spec_object (SOUP_SESSION_ADD_FEATURE,
3717 "Add a feature object to the session",
3718 SOUP_TYPE_SESSION_FEATURE,
3720 G_PARAM_STATIC_STRINGS));
3722 * SoupSession:add-feature-by-type: (skip)
3724 * Add a feature object of the given type to the session.
3725 * (Shortcut for calling soup_session_add_feature_by_type().)
3730 * SOUP_SESSION_ADD_FEATURE_BY_TYPE: (skip)
3732 * Alias for the #SoupSession:add-feature-by-type property, qv.
3736 g_object_class_install_property (
3737 object_class, PROP_ADD_FEATURE_BY_TYPE,
3738 g_param_spec_gtype (SOUP_SESSION_ADD_FEATURE_BY_TYPE,
3739 "Add Feature By Type",
3740 "Add a feature object of the given type to the session",
3743 G_PARAM_STATIC_STRINGS));
3745 * SoupSession:remove-feature-by-type: (skip)
3747 * Remove feature objects from the session. (Shortcut for
3748 * calling soup_session_remove_feature_by_type().)
3753 * SOUP_SESSION_REMOVE_FEATURE_BY_TYPE: (skip)
3755 * Alias for the #SoupSession:remove-feature-by-type property,
3760 g_object_class_install_property (
3761 object_class, PROP_REMOVE_FEATURE_BY_TYPE,
3762 g_param_spec_gtype (SOUP_SESSION_REMOVE_FEATURE_BY_TYPE,
3763 "Remove Feature By Type",
3764 "Remove features of the given type from the session",
3767 G_PARAM_STATIC_STRINGS));
3769 * SoupSession:http-aliases:
3771 * A %NULL-terminated array of URI schemes that should be
3772 * considered to be aliases for "http". Eg, if this included
3773 * <literal>"dav"</literal>, than a URI of
3774 * <literal>dav://example.com/path</literal> would be treated
3775 * identically to <literal>http://example.com/path</literal>.
3777 * In a plain #SoupSession, the default value is %NULL,
3778 * meaning that only "http" is recognized as meaning "http".
3779 * In #SoupSessionAsync and #SoupSessionSync, for backward
3780 * compatibility, the default value is an array containing the
3781 * single element <literal>"*"</literal>, a special value
3782 * which means that any scheme except "https" is considered to
3783 * be an alias for "http".
3785 * See also #SoupSession:https-aliases.
3790 * SOUP_SESSION_HTTP_ALIASES:
3792 * Alias for the #SoupSession:http-aliases property, qv.
3796 g_object_class_install_property (
3797 object_class, PROP_HTTP_ALIASES,
3798 g_param_spec_boxed (SOUP_SESSION_HTTP_ALIASES,
3800 "URI schemes that are considered aliases for 'http'",
3803 G_PARAM_STATIC_STRINGS));
3805 * SoupSession:https-aliases:
3807 * A comma-delimited list of URI schemes that should be
3808 * considered to be aliases for "https". See
3809 * #SoupSession:http-aliases for more information.
3811 * The default value is %NULL, meaning that no URI schemes
3812 * are considered aliases for "https".
3817 * SOUP_SESSION_HTTPS_ALIASES:
3819 * Alias for the #SoupSession:https-aliases property, qv.
3823 g_object_class_install_property (
3824 object_class, PROP_HTTPS_ALIASES,
3825 g_param_spec_boxed (SOUP_SESSION_HTTPS_ALIASES,
3827 "URI schemes that are considered aliases for 'https'",
3830 G_PARAM_STATIC_STRINGS));
3833 * SOUP_SESSION_LOCAL_ADDRESS:
3835 * Alias for the #SoupSession:local-address property, qv.
3840 * SoupSession:local-address:
3842 * Sets the #SoupAddress to use for the client side of
3845 * Use this property if you want for instance to bind the
3846 * local socket to a specific IP address.
3850 g_object_class_install_property (
3851 object_class, PROP_LOCAL_ADDRESS,
3852 g_param_spec_object (SOUP_SESSION_LOCAL_ADDRESS,
3854 "Address of local end of socket",
3856 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
3857 G_PARAM_STATIC_STRINGS));
3860 * SOUP_SESSION_TLS_INTERACTION:
3862 * Alias for the #SoupSession:tls-interaction property, qv.
3867 * SoupSession:tls-interaction:
3869 * A #GTlsInteraction object that will be passed on to any
3870 * #GTlsConnections created by the session. (This can be used to
3871 * provide client-side certificates, for example.)
3875 g_object_class_install_property (
3876 object_class, PROP_TLS_INTERACTION,
3877 g_param_spec_object (SOUP_SESSION_TLS_INTERACTION,
3879 "TLS interaction to use",
3880 G_TYPE_TLS_INTERACTION,
3882 G_PARAM_STATIC_STRINGS));
3887 expected_to_be_requeued (SoupSession *session, SoupMessage *msg)
3889 if (msg->status_code == SOUP_STATUS_UNAUTHORIZED ||
3890 msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED) {
3891 SoupSessionFeature *feature =
3892 soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
3893 return !feature || !soup_message_disables_feature (msg, feature);
3896 if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT))
3897 return soup_session_would_redirect (session, msg);
3902 /* send_request_async */
3905 async_send_request_return_result (SoupMessageQueueItem *item,
3906 gpointer stream, GError *error)
3910 g_return_if_fail (item->task != NULL);
3912 g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
3913 0, 0, NULL, NULL, item);
3918 if (item->io_source) {
3919 g_source_destroy (item->io_source);
3920 g_clear_pointer (&item->io_source, g_source_unref);
3924 g_task_return_error (task, error);
3925 else if (item->error) {
3927 g_object_unref (stream);
3928 g_task_return_error (task, g_error_copy (item->error));
3929 } else if (SOUP_STATUS_IS_TRANSPORT_ERROR (item->msg->status_code)) {
3931 g_object_unref (stream);
3932 g_task_return_new_error (task, SOUP_HTTP_ERROR,
3933 item->msg->status_code,
3935 item->msg->reason_phrase);
3937 g_task_return_pointer (task, stream, g_object_unref);
3938 g_object_unref (task);
3942 async_send_request_restarted (SoupMessage *msg, gpointer user_data)
3944 SoupMessageQueueItem *item = user_data;
3946 /* We won't be needing this, then. */
3948 g_object_set_data (G_OBJECT (item->task), "SoupSession:ostream", NULL);
3949 item->io_started = FALSE;
3953 async_send_request_finished (SoupMessage *msg, gpointer user_data)
3955 SoupMessageQueueItem *item = user_data;
3956 GMemoryOutputStream *mostream;
3957 GInputStream *istream = NULL;
3958 GError *error = NULL;
3961 /* Something else already took care of it. */
3965 mostream = g_object_get_data (G_OBJECT (item->task), "SoupSession:ostream");
3970 /* We thought it would be requeued, but it wasn't, so
3971 * return the original body.
3973 size = g_memory_output_stream_get_data_size (mostream);
3974 data = size ? g_memory_output_stream_steal_data (mostream) : g_strdup ("");
3975 istream = g_memory_input_stream_new_from_data (data, size, g_free);
3976 } else if (item->io_started) {
3977 /* The message finished before becoming readable. This
3978 * will happen, eg, if it's cancelled from got-headers.
3979 * Do nothing; the op will complete via read_ready_cb()
3984 /* The message finished before even being started;
3985 * probably a tunnel connect failure.
3987 istream = g_memory_input_stream_new ();
3990 async_send_request_return_result (item, istream, error);
3994 send_async_spliced (GObject *source, GAsyncResult *result, gpointer user_data)
3996 SoupMessageQueueItem *item = user_data;
3997 GInputStream *istream = g_object_get_data (source, "istream");
3998 GError *error = NULL;
4000 /* It should be safe to call the sync close() method here since
4001 * the message body has already been written.
4003 g_input_stream_close (istream, NULL, NULL);
4004 g_object_unref (istream);
4006 /* If the message was cancelled, it will be completed via other means */
4007 if (g_cancellable_is_cancelled (item->cancellable) ||
4009 soup_message_queue_item_unref (item);
4013 if (g_output_stream_splice_finish (G_OUTPUT_STREAM (source),
4014 result, &error) == -1) {
4015 async_send_request_return_result (item, NULL, error);
4016 soup_message_queue_item_unref (item);
4020 /* Otherwise either restarted or finished will eventually be called. */
4021 soup_session_kick_queue (item->session);
4022 soup_message_queue_item_unref (item);
4026 send_async_maybe_complete (SoupMessageQueueItem *item,
4027 GInputStream *stream)
4029 if (expected_to_be_requeued (item->session, item->msg)) {
4030 GOutputStream *ostream;
4032 /* Gather the current message body... */
4033 ostream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
4034 g_object_set_data_full (G_OBJECT (item->task), "SoupSession:ostream",
4035 ostream, g_object_unref);
4037 g_object_set_data (G_OBJECT (ostream), "istream", stream);
4039 /* Give the splice op its own ref on item */
4040 soup_message_queue_item_ref (item);
4041 /* We don't use CLOSE_SOURCE because we need to control when the
4042 * side effects of closing the SoupClientInputStream happen.
4044 g_output_stream_splice_async (ostream, stream,
4045 G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
4048 send_async_spliced, item);
4052 async_send_request_return_result (item, stream, NULL);
4055 static void try_run_until_read (SoupMessageQueueItem *item);
4058 read_ready_cb (SoupMessage *msg, gpointer user_data)
4060 SoupMessageQueueItem *item = user_data;
4062 g_clear_pointer (&item->io_source, g_source_unref);
4063 try_run_until_read (item);
4068 try_run_until_read (SoupMessageQueueItem *item)
4070 GError *error = NULL;
4071 GInputStream *stream = NULL;
4073 if (soup_message_io_run_until_read (item->msg, FALSE, item->cancellable, &error))
4074 stream = soup_message_io_get_response_istream (item->msg, &error);
4076 send_async_maybe_complete (item, stream);
4080 if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) {
4081 item->state = SOUP_MESSAGE_RESTARTING;
4082 soup_message_io_finished (item->msg);
4083 g_error_free (error);
4087 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
4088 if (item->state != SOUP_MESSAGE_FINISHED) {
4089 if (soup_message_io_in_progress (item->msg))
4090 soup_message_io_finished (item->msg);
4091 item->state = SOUP_MESSAGE_FINISHING;
4092 soup_session_process_queue_item (item->session, item, NULL, FALSE);
4094 async_send_request_return_result (item, NULL, error);
4098 g_clear_error (&error);
4099 item->io_source = soup_message_io_get_source (item->msg, item->cancellable,
4100 read_ready_cb, item);
4101 g_source_attach (item->io_source, soup_session_get_async_context (item->session));
4105 async_send_request_running (SoupSession *session, SoupMessageQueueItem *item)
4107 item->io_started = TRUE;
4108 try_run_until_read (item);
4112 cache_stream_finished (GInputStream *stream,
4113 SoupMessageQueueItem *item)
4115 g_signal_handlers_disconnect_matched (stream, G_SIGNAL_MATCH_DATA,
4116 0, 0, NULL, NULL, item);
4117 item->state = SOUP_MESSAGE_FINISHING;
4118 soup_session_kick_queue (item->session);
4119 soup_message_queue_item_unref (item);
4123 async_return_from_cache (SoupMessageQueueItem *item,
4124 GInputStream *stream)
4126 const char *content_type;
4127 GHashTable *params = NULL;
4129 soup_message_got_headers (item->msg);
4131 content_type = soup_message_headers_get_content_type (item->msg->response_headers, ¶ms);
4133 soup_message_content_sniffed (item->msg, content_type, params);
4134 g_hash_table_unref (params);
4137 soup_message_queue_item_ref (item);
4138 g_signal_connect (stream, "eof", G_CALLBACK (cache_stream_finished), item);
4139 g_signal_connect (stream, "closed", G_CALLBACK (cache_stream_finished), item);
4141 async_send_request_return_result (item, g_object_ref (stream), NULL);
4146 SoupMessage *conditional_msg;
4147 } AsyncCacheCancelData;
4151 free_async_cache_cancel_data (AsyncCacheCancelData *data)
4153 g_object_unref (data->conditional_msg);
4154 g_object_unref (data->cache);
4155 g_slice_free (AsyncCacheCancelData, data);
4159 cancel_cache_response (SoupMessageQueueItem *item)
4161 item->paused = FALSE;
4162 item->state = SOUP_MESSAGE_FINISHING;
4163 soup_message_set_status (item->msg, SOUP_STATUS_CANCELLED);
4164 soup_session_kick_queue (item->session);
4168 conditional_request_cancelled_cb (GCancellable *cancellable, AsyncCacheCancelData *data)
4170 soup_cache_cancel_conditional_request (data->cache, data->conditional_msg);
4174 conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
4176 SoupMessageQueueItem *item = user_data;
4177 GInputStream *stream;
4180 if (g_cancellable_is_cancelled (item->cancellable)) {
4181 cancel_cache_response (item);
4184 gulong handler_id = GPOINTER_TO_SIZE (g_object_get_data (G_OBJECT (msg), "SoupSession:handler-id"));
4185 g_cancellable_disconnect (item->cancellable, handler_id);
4188 cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
4189 soup_cache_update_from_conditional_request (cache, msg);
4191 if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) {
4192 stream = soup_cache_send_response (cache, item->msg);
4194 async_return_from_cache (item, stream);
4195 g_object_unref (stream);
4200 /* The resource was modified or the server returned a 200
4201 * OK. Either way we reload it. FIXME.
4203 item->state = SOUP_MESSAGE_STARTING;
4204 soup_session_kick_queue (session);
4208 idle_return_from_cache_cb (gpointer data)
4211 SoupMessageQueueItem *item = g_task_get_task_data (task);
4212 GInputStream *istream;
4214 if (item->state == SOUP_MESSAGE_FINISHED) {
4215 /* The original request was cancelled using
4216 * soup_session_cancel_message () so it has been
4217 * already handled by the cancellation code path.
4220 } else if (g_cancellable_is_cancelled (item->cancellable)) {
4221 /* Cancel original msg after g_cancellable_cancel(). */
4222 cancel_cache_response (item);
4226 istream = g_object_get_data (G_OBJECT (task), "SoupSession:istream");
4227 async_return_from_cache (item, istream);
4234 async_respond_from_cache (SoupSession *session,
4235 SoupMessageQueueItem *item)
4238 SoupCacheResponse response;
4240 cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
4244 response = soup_cache_has_response (cache, item->msg);
4245 if (response == SOUP_CACHE_RESPONSE_FRESH) {
4246 GInputStream *stream;
4249 stream = soup_cache_send_response (cache, item->msg);
4251 /* Cached file was deleted? */
4254 g_object_set_data_full (G_OBJECT (item->task), "SoupSession:istream",
4255 stream, g_object_unref);
4257 source = g_timeout_source_new (0);
4258 g_task_attach_source (item->task, source,
4259 (GSourceFunc) idle_return_from_cache_cb);
4260 g_source_unref (source);
4262 } else if (response == SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) {
4263 SoupMessage *conditional_msg;
4264 AsyncCacheCancelData *data;
4267 conditional_msg = soup_cache_generate_conditional_request (cache, item->msg);
4268 if (!conditional_msg)
4271 /* Detect any quick cancellation before the cache is able to return data. */
4272 data = g_slice_new0 (AsyncCacheCancelData);
4273 data->cache = g_object_ref (cache);
4274 data->conditional_msg = g_object_ref (conditional_msg);
4275 handler_id = g_cancellable_connect (item->cancellable, G_CALLBACK (conditional_request_cancelled_cb),
4276 data, (GDestroyNotify) free_async_cache_cancel_data);
4278 g_object_set_data (G_OBJECT (conditional_msg), "SoupSession:handler-id",
4279 GSIZE_TO_POINTER (handler_id));
4280 soup_session_queue_message (session, conditional_msg,
4281 conditional_get_ready_cb,
4291 cancel_cancellable (G_GNUC_UNUSED GCancellable *cancellable, GCancellable *chained_cancellable)
4293 g_cancellable_cancel (chained_cancellable);
4297 * soup_session_send_async:
4298 * @session: a #SoupSession
4299 * @msg: a #SoupMessage
4300 * @cancellable: a #GCancellable
4301 * @callback: the callback to invoke
4302 * @user_data: data for @callback
4304 * Asynchronously sends @msg and waits for the beginning of a
4305 * response. When @callback is called, then either @msg has been sent,
4306 * and its response headers received, or else an error has occurred.
4307 * Call soup_session_send_finish() to get a #GInputStream for reading
4308 * the response body.
4310 * See soup_session_send() for more details on the general semantics.
4312 * Contrast this method with soup_session_queue_message(), which also
4313 * asynchronously sends a #SoupMessage, but doesn't invoke its
4314 * callback until the response has been completely read.
4316 * (Note that this method cannot be called on the deprecated
4317 * #SoupSessionSync subclass, and can only be called on
4318 * #SoupSessionAsync if you have set the
4319 * #SoupSession:use-thread-context property.)
4324 soup_session_send_async (SoupSession *session,
4326 GCancellable *cancellable,
4327 GAsyncReadyCallback callback,
4330 SoupMessageQueueItem *item;
4331 gboolean use_thread_context;
4333 g_return_if_fail (SOUP_IS_SESSION (session));
4334 g_return_if_fail (!SOUP_IS_SESSION_SYNC (session));
4336 g_object_get (G_OBJECT (session),
4337 SOUP_SESSION_USE_THREAD_CONTEXT, &use_thread_context,
4339 g_return_if_fail (use_thread_context);
4341 item = soup_session_append_queue_item (session, msg, TRUE, TRUE,
4343 g_signal_connect (msg, "restarted",
4344 G_CALLBACK (async_send_request_restarted), item);
4345 g_signal_connect (msg, "finished",
4346 G_CALLBACK (async_send_request_finished), item);
4349 g_cancellable_connect (cancellable, G_CALLBACK (cancel_cancellable),
4350 g_object_ref (item->cancellable),
4351 (GDestroyNotify) g_object_unref);
4354 item->new_api = TRUE;
4355 item->task = g_task_new (session, item->cancellable, callback, user_data);
4356 g_task_set_task_data (item->task, item, (GDestroyNotify) soup_message_queue_item_unref);
4358 /* Do not check for cancellations as we do not want to
4359 * overwrite custom error messages set during cancellations
4360 * (for example SOUP_HTTP_ERROR is set for cancelled messages
4361 * in async_send_request_return_result() (status_code==1
4362 * means CANCEL and is considered a TRANSPORT_ERROR)).
4364 g_task_set_check_cancellable (item->task, FALSE);
4366 if (async_respond_from_cache (session, item))
4367 item->state = SOUP_MESSAGE_CACHED;
4369 soup_session_kick_queue (session);
4373 * soup_session_send_finish:
4374 * @session: a #SoupSession
4375 * @result: the #GAsyncResult passed to your callback
4376 * @error: return location for a #GError, or %NULL
4378 * Gets the response to a soup_session_send_async() call and (if
4379 * successful), returns a #GInputStream that can be used to read the
4382 * Return value: (transfer full): a #GInputStream for reading the
4383 * response body, or %NULL on error.
4388 soup_session_send_finish (SoupSession *session,
4389 GAsyncResult *result,
4394 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
4395 g_return_val_if_fail (!SOUP_IS_SESSION_SYNC (session), NULL);
4396 g_return_val_if_fail (g_task_is_valid (result, session), NULL);
4398 task = G_TASK (result);
4399 if (g_task_had_error (task)) {
4400 SoupMessageQueueItem *item = g_task_get_task_data (task);
4402 if (soup_message_io_in_progress (item->msg))
4403 soup_message_io_finished (item->msg);
4404 else if (item->state != SOUP_MESSAGE_FINISHED)
4405 item->state = SOUP_MESSAGE_FINISHING;
4407 if (item->state != SOUP_MESSAGE_FINISHED)
4408 soup_session_process_queue_item (session, item, NULL, FALSE);
4411 return g_task_propagate_pointer (task, error);
4415 * soup_session_send:
4416 * @session: a #SoupSession
4417 * @msg: a #SoupMessage
4418 * @cancellable: a #GCancellable
4419 * @error: return location for a #GError, or %NULL
4421 * Synchronously sends @msg and waits for the beginning of a response.
4422 * On success, a #GInputStream will be returned which you can use to
4423 * read the response body. ("Success" here means only that an HTTP
4424 * response was received and understood; it does not necessarily mean
4425 * that a 2xx class status code was received.)
4427 * If non-%NULL, @cancellable can be used to cancel the request;
4428 * soup_session_send() will return a %G_IO_ERROR_CANCELLED error. Note
4429 * that with requests that have side effects (eg,
4430 * <literal>POST</literal>, <literal>PUT</literal>,
4431 * <literal>DELETE</literal>) it is possible that you might cancel the
4432 * request after the server acts on it, but before it returns a
4433 * response, leaving the remote resource in an unknown state.
4435 * If @msg is requeued due to a redirect or authentication, the
4436 * initial (3xx/401/407) response body will be suppressed, and
4437 * soup_session_send() will only return once a final response has been
4440 * Contrast this method with soup_session_send_message(), which also
4441 * synchronously sends a #SoupMessage, but doesn't return until the
4442 * response has been completely read.
4444 * (Note that this method cannot be called on the deprecated
4445 * #SoupSessionAsync subclass.)
4447 * Return value: (transfer full): a #GInputStream for reading the
4448 * response body, or %NULL on error.
4453 soup_session_send (SoupSession *session,
4455 GCancellable *cancellable,
4458 SoupMessageQueueItem *item;
4459 GInputStream *stream = NULL;
4460 GOutputStream *ostream;
4461 GMemoryOutputStream *mostream;
4463 GError *my_error = NULL;
4465 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
4466 g_return_val_if_fail (!SOUP_IS_SESSION_ASYNC (session), NULL);
4468 item = soup_session_append_queue_item (session, msg, FALSE, TRUE,
4471 item->new_api = TRUE;
4473 g_cancellable_connect (cancellable, G_CALLBACK (cancel_cancellable),
4474 g_object_ref (item->cancellable),
4475 (GDestroyNotify) g_object_unref);
4479 /* Get a connection, etc */
4480 soup_session_process_queue_item (session, item, NULL, TRUE);
4481 if (item->state != SOUP_MESSAGE_RUNNING)
4484 /* Send request, read headers */
4485 if (!soup_message_io_run_until_read (msg, TRUE, item->cancellable, &my_error)) {
4486 if (g_error_matches (my_error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) {
4487 item->state = SOUP_MESSAGE_RESTARTING;
4488 soup_message_io_finished (item->msg);
4489 g_clear_error (&my_error);
4495 stream = soup_message_io_get_response_istream (msg, &my_error);
4499 if (!expected_to_be_requeued (session, msg))
4502 /* Gather the current message body... */
4503 ostream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
4504 if (g_output_stream_splice (ostream, stream,
4505 G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
4506 G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
4507 item->cancellable, &my_error) == -1) {
4508 g_object_unref (stream);
4509 g_object_unref (ostream);
4513 g_object_unref (stream);
4516 /* If the message was requeued, loop */
4517 if (item->state == SOUP_MESSAGE_RESTARTING) {
4518 g_object_unref (ostream);
4522 /* Not requeued, so return the original body */
4523 mostream = G_MEMORY_OUTPUT_STREAM (ostream);
4524 size = g_memory_output_stream_get_data_size (mostream);
4525 stream = g_memory_input_stream_new ();
4527 g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (stream),
4528 g_memory_output_stream_steal_data (mostream),
4531 g_object_unref (ostream);
4535 g_propagate_error (error, my_error);
4536 else if (item->error) {
4537 g_clear_object (&stream);
4539 *error = g_error_copy (item->error);
4540 } else if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
4541 g_clear_object (&stream);
4542 g_set_error_literal (error, SOUP_HTTP_ERROR, msg->status_code,
4543 msg->reason_phrase);
4545 stream = g_memory_input_stream_new ();
4548 if (soup_message_io_in_progress (msg))
4549 soup_message_io_finished (msg);
4550 else if (item->state != SOUP_MESSAGE_FINISHED)
4551 item->state = SOUP_MESSAGE_FINISHING;
4553 if (item->state != SOUP_MESSAGE_FINISHED)
4554 soup_session_process_queue_item (session, item, NULL, TRUE);
4557 soup_message_queue_item_unref (item);
4562 * soup_session_request:
4563 * @session: a #SoupSession
4564 * @uri_string: a URI, in string form
4565 * @error: return location for a #GError, or %NULL
4567 * Creates a #SoupRequest for retrieving @uri_string.
4569 * Return value: (transfer full): a new #SoupRequest, or
4575 soup_session_request (SoupSession *session, const char *uri_string,
4581 uri = soup_uri_new (uri_string);
4583 g_set_error (error, SOUP_REQUEST_ERROR,
4584 SOUP_REQUEST_ERROR_BAD_URI,
4585 _("Could not parse URI “%s”"), uri_string);
4589 req = soup_session_request_uri (session, uri, error);
4590 soup_uri_free (uri);
4595 * soup_session_request_uri:
4596 * @session: a #SoupSession
4597 * @uri: a #SoupURI representing the URI to retrieve
4598 * @error: return location for a #GError, or %NULL
4600 * Creates a #SoupRequest for retrieving @uri.
4602 * Return value: (transfer full): a new #SoupRequest, or
4608 soup_session_request_uri (SoupSession *session, SoupURI *uri,
4611 SoupSessionPrivate *priv;
4614 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
4616 priv = soup_session_get_instance_private (session);
4618 request_type = (GType)GPOINTER_TO_SIZE (g_hash_table_lookup (priv->request_types, uri->scheme));
4619 if (!request_type) {
4620 g_set_error (error, SOUP_REQUEST_ERROR,
4621 SOUP_REQUEST_ERROR_UNSUPPORTED_URI_SCHEME,
4622 _("Unsupported URI scheme “%s”"), uri->scheme);
4626 return g_initable_new (request_type, NULL, error,
4632 static SoupRequestHTTP *
4633 initialize_http_request (SoupRequest *req,
4637 SoupRequestHTTP *http;
4640 if (!SOUP_IS_REQUEST_HTTP (req)) {
4641 g_object_unref (req);
4642 g_set_error (error, SOUP_REQUEST_ERROR,
4643 SOUP_REQUEST_ERROR_BAD_URI,
4644 _("Not an HTTP URI"));
4648 http = SOUP_REQUEST_HTTP (req);
4649 msg = soup_request_http_get_message (http);
4650 g_object_set (G_OBJECT (msg),
4651 SOUP_MESSAGE_METHOD, method,
4653 g_object_unref (msg);
4659 * soup_session_request_http:
4660 * @session: a #SoupSession
4661 * @method: an HTTP method
4662 * @uri_string: a URI, in string form
4663 * @error: return location for a #GError, or %NULL
4665 * Creates a #SoupRequest for retrieving @uri_string, which must be an
4666 * "http" or "https" URI (or another protocol listed in @session's
4667 * #SoupSession:http-aliases or #SoupSession:https-aliases).
4669 * Return value: (transfer full): a new #SoupRequestHTTP, or
4675 soup_session_request_http (SoupSession *session,
4677 const char *uri_string,
4682 req = soup_session_request (session, uri_string, error);
4686 return initialize_http_request (req, method, error);
4690 * soup_session_request_http_uri:
4691 * @session: a #SoupSession
4692 * @method: an HTTP method
4693 * @uri: a #SoupURI representing the URI to retrieve
4694 * @error: return location for a #GError, or %NULL
4696 * Creates a #SoupRequest for retrieving @uri, which must be an
4697 * "http" or "https" URI (or another protocol listed in @session's
4698 * #SoupSession:http-aliases or #SoupSession:https-aliases).
4700 * Return value: (transfer full): a new #SoupRequestHTTP, or
4706 soup_session_request_http_uri (SoupSession *session,
4713 req = soup_session_request_uri (session, uri, error);
4717 return initialize_http_request (req, method, error);
4721 * SOUP_REQUEST_ERROR:
4723 * A #GError domain for #SoupRequest<!-- -->-related errors. Used with
4724 * #SoupRequestError.
4730 * @SOUP_REQUEST_ERROR_BAD_URI: the URI could not be parsed
4731 * @SOUP_REQUEST_ERROR_UNSUPPORTED_URI_SCHEME: the URI scheme is not
4732 * supported by this #SoupSession
4733 * @SOUP_REQUEST_ERROR_PARSING: the server's response could not
4735 * @SOUP_REQUEST_ERROR_ENCODING: the server's response was in an
4736 * unsupported format
4738 * A #SoupRequest error.
4744 soup_request_error_quark (void)
4746 static GQuark error;
4748 error = g_quark_from_static_string ("soup_request_error_quark");
4753 steal_connection (SoupSession *session,
4754 SoupMessageQueueItem *item)
4756 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
4757 SoupConnection *conn;
4759 SoupSessionHost *host;
4762 conn = g_object_ref (item->conn);
4763 soup_session_set_item_connection (session, item, NULL);
4765 g_mutex_lock (&priv->conn_lock);
4766 host = get_host_for_message (session, item->msg);
4767 g_hash_table_remove (priv->conns, conn);
4768 drop_connection (session, host, conn);
4769 g_mutex_unlock (&priv->conn_lock);
4771 sock = soup_connection_get_socket (conn);
4773 SOUP_SOCKET_TIMEOUT, 0,
4776 if (item->connect_only)
4777 stream = g_object_ref (soup_socket_get_connection (sock));
4779 stream = soup_message_io_steal (item->msg);
4780 g_object_set_data_full (G_OBJECT (stream), "GSocket",
4781 soup_socket_steal_gsocket (sock),
4783 g_object_unref (conn);
4789 * soup_session_steal_connection:
4790 * @session: a #SoupSession
4791 * @msg: the message whose connection is to be stolen
4793 * "Steals" the HTTP connection associated with @msg from @session.
4794 * This happens immediately, regardless of the current state of the
4795 * connection, and @msg's callback will not be called. You can steal
4796 * the connection from a #SoupMessage signal handler if you need to
4797 * wait for part or all of the response to be received first.
4799 * Calling this function may cause @msg to be freed if you are not
4800 * holding any other reference to it.
4802 * Return value: (transfer full): the #GIOStream formerly associated
4803 * with @msg (or %NULL if @msg was no longer associated with a
4804 * connection). No guarantees are made about what kind of #GIOStream
4810 soup_session_steal_connection (SoupSession *session,
4813 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
4814 SoupMessageQueueItem *item;
4815 GIOStream *stream = NULL;
4817 item = soup_message_queue_lookup (priv->queue, msg);
4821 if (item->conn && soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE)
4822 stream = steal_connection (session, item);
4824 soup_message_queue_item_unref (item);
4830 soup_session_get_supported_websocket_extensions_for_message (SoupSession *session,
4833 SoupSessionFeature *extension_manager;
4835 extension_manager = soup_session_get_feature_for_message (session, SOUP_TYPE_WEBSOCKET_EXTENSION_MANAGER, msg);
4836 if (!extension_manager)
4839 return soup_websocket_extension_manager_get_supported_extensions (SOUP_WEBSOCKET_EXTENSION_MANAGER (extension_manager));
4842 static void websocket_connect_async_stop (SoupMessage *msg, gpointer user_data);
4845 websocket_connect_async_complete (SoupSession *session, SoupMessage *msg, gpointer user_data)
4847 GTask *task = user_data;
4849 /* Disconnect websocket_connect_async_stop() handler. */
4850 g_signal_handlers_disconnect_matched (msg, G_SIGNAL_MATCH_DATA,
4851 0, 0, NULL, NULL, task);
4853 g_task_return_new_error (task,
4854 SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_NOT_WEBSOCKET,
4855 "%s", _("The server did not accept the WebSocket handshake."));
4856 g_object_unref (task);
4860 websocket_connect_async_stop (SoupMessage *msg, gpointer user_data)
4862 GTask *task = user_data;
4863 SoupMessageQueueItem *item = g_task_get_task_data (task);
4865 SoupWebsocketConnection *client;
4866 SoupSession *session = g_task_get_source_object (task);
4867 GPtrArray *supported_extensions;
4868 GList *accepted_extensions = NULL;
4869 GError *error = NULL;
4871 /* Disconnect websocket_connect_async_stop() handler. */
4872 g_signal_handlers_disconnect_matched (msg, G_SIGNAL_MATCH_DATA,
4873 0, 0, NULL, NULL, task);
4874 /* Ensure websocket_connect_async_complete is not called either. */
4875 item->callback = NULL;
4877 supported_extensions = soup_session_get_supported_websocket_extensions_for_message (session, msg);
4878 if (soup_websocket_client_verify_handshake_with_extensions (item->msg, supported_extensions, &accepted_extensions, &error)) {
4879 stream = soup_session_steal_connection (item->session, item->msg);
4880 client = soup_websocket_connection_new_with_extensions (stream,
4881 soup_message_get_uri (item->msg),
4882 SOUP_WEBSOCKET_CONNECTION_CLIENT,
4883 soup_message_headers_get_one (msg->request_headers, "Origin"),
4884 soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol"),
4885 accepted_extensions);
4886 g_object_unref (stream);
4887 g_task_return_pointer (task, client, g_object_unref);
4888 g_object_unref (task);
4893 soup_message_io_finished (item->msg);
4894 g_task_return_error (task, error);
4895 g_object_unref (task);
4899 * soup_session_websocket_connect_async:
4900 * @session: a #SoupSession
4901 * @msg: #SoupMessage indicating the WebSocket server to connect to
4902 * @origin: (allow-none): origin of the connection
4903 * @protocols: (allow-none) (array zero-terminated=1): a
4904 * %NULL-terminated array of protocols supported
4905 * @cancellable: a #GCancellable
4906 * @callback: the callback to invoke
4907 * @user_data: data for @callback
4909 * Asynchronously creates a #SoupWebsocketConnection to communicate
4910 * with a remote server.
4912 * All necessary WebSocket-related headers will be added to @msg, and
4913 * it will then be sent and asynchronously processed normally
4914 * (including handling of redirection and HTTP authentication).
4916 * If the server returns "101 Switching Protocols", then @msg's status
4917 * code and response headers will be updated, and then the WebSocket
4918 * handshake will be completed. On success,
4919 * soup_session_websocket_connect_finish() will return a new
4920 * #SoupWebsocketConnection. On failure it will return a #GError.
4922 * If the server returns a status other than "101 Switching
4923 * Protocols", then @msg will contain the complete response headers
4924 * and body from the server's response, and
4925 * soup_session_websocket_connect_finish() will return
4926 * %SOUP_WEBSOCKET_ERROR_NOT_WEBSOCKET.
4931 soup_session_websocket_connect_async (SoupSession *session,
4935 GCancellable *cancellable,
4936 GAsyncReadyCallback callback,
4939 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
4940 SoupMessageQueueItem *item;
4942 GPtrArray *supported_extensions;
4943 SoupMessageFlags flags;
4945 g_return_if_fail (SOUP_IS_SESSION (session));
4946 g_return_if_fail (priv->use_thread_context);
4947 g_return_if_fail (SOUP_IS_MESSAGE (msg));
4949 supported_extensions = soup_session_get_supported_websocket_extensions_for_message (session, msg);
4950 soup_websocket_client_prepare_handshake_with_extensions (msg, origin, protocols, supported_extensions);
4952 /* When the client is to _Establish a WebSocket Connection_ given a set
4953 * of (/host/, /port/, /resource name/, and /secure/ flag), along with a
4954 * list of /protocols/ and /extensions/ to be used, and an /origin/ in
4955 * the case of web browsers, it MUST open a connection, send an opening
4956 * handshake, and read the server's handshake in response.
4958 flags = soup_message_get_flags (msg);
4959 soup_message_set_flags (msg, flags | SOUP_MESSAGE_NEW_CONNECTION);
4961 task = g_task_new (session, cancellable, callback, user_data);
4962 item = soup_session_append_queue_item (session, msg, TRUE, FALSE,
4963 websocket_connect_async_complete, task);
4964 g_task_set_task_data (task, item, (GDestroyNotify) soup_message_queue_item_unref);
4966 soup_message_add_status_code_handler (msg, "got-informational",
4967 SOUP_STATUS_SWITCHING_PROTOCOLS,
4968 G_CALLBACK (websocket_connect_async_stop), task);
4969 soup_session_kick_queue (session);
4973 * soup_session_websocket_connect_finish:
4974 * @session: a #SoupSession
4975 * @result: the #GAsyncResult passed to your callback
4976 * @error: return location for a #GError, or %NULL
4978 * Gets the #SoupWebsocketConnection response to a
4979 * soup_session_websocket_connect_async() call and (if successful),
4980 * returns a #SoupWebsocketConnection that can be used to communicate
4983 * Return value: (transfer full): a new #SoupWebsocketConnection, or
4988 SoupWebsocketConnection *
4989 soup_session_websocket_connect_finish (SoupSession *session,
4990 GAsyncResult *result,
4993 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
4994 g_return_val_if_fail (g_task_is_valid (result, session), NULL);
4996 return g_task_propagate_pointer (G_TASK (result), error);
5000 * SoupSessionConnectProgressCallback:
5001 * @session: the #SoupSession
5002 * @event: a #GSocketClientEvent
5003 * @connection: the current state of the network connection
5004 * @user_data: the data passed to soup_session_connect_async().
5006 * Prototype for the progress callback passed to soup_session_connect_async().
5012 SoupMessageQueueItem *item;
5013 SoupSessionConnectProgressCallback progress_callback;
5017 static ConnectAsyncData *
5018 connect_async_data_new (SoupMessageQueueItem *item,
5019 SoupSessionConnectProgressCallback progress_callback,
5022 ConnectAsyncData *data;
5024 soup_message_queue_item_ref (item);
5026 data = g_slice_new (ConnectAsyncData);
5028 data->progress_callback = progress_callback;
5029 data->user_data = user_data;
5035 connect_async_data_free (ConnectAsyncData *data)
5037 soup_message_queue_item_unref (data->item);
5039 g_slice_free (ConnectAsyncData, data);
5043 connect_async_message_network_event (SoupMessage *msg,
5044 GSocketClientEvent event,
5045 GIOStream *connection,
5048 ConnectAsyncData *data = g_task_get_task_data (task);
5050 if (data->progress_callback)
5051 data->progress_callback (data->item->session, event, connection, data->user_data);
5055 connect_async_message_finished (SoupMessage *msg,
5058 ConnectAsyncData *data = g_task_get_task_data (task);
5059 SoupMessageQueueItem *item = data->item;
5061 if (!item->conn || item->error) {
5062 g_task_return_error (task, g_error_copy (item->error));
5064 g_task_return_pointer (task,
5065 steal_connection (item->session, item),
5068 g_object_unref (task);
5072 * soup_session_connect_async:
5073 * @session: a #SoupSession
5074 * @uri: a #SoupURI to connect to
5075 * @cancellable: a #GCancellable
5076 * @progress_callback: (allow-none) (scope async): a #SoupSessionConnectProgressCallback which
5077 * will be called for every network event that occurs during the connection.
5078 * @callback: (allow-none) (scope async): the callback to invoke when the operation finishes
5079 * @user_data: data for @progress_callback and @callback
5081 * Start a connection to @uri. The operation can be monitored by providing a @progress_callback
5082 * and finishes when the connection is done or an error ocurred.
5084 * Call soup_session_connect_finish() to get the #GIOStream to communicate with the server.
5089 soup_session_connect_async (SoupSession *session,
5091 GCancellable *cancellable,
5092 SoupSessionConnectProgressCallback progress_callback,
5093 GAsyncReadyCallback callback,
5096 SoupSessionPrivate *priv;
5098 SoupMessageQueueItem *item;
5099 ConnectAsyncData *data;
5102 g_return_if_fail (SOUP_IS_SESSION (session));
5103 g_return_if_fail (!SOUP_IS_SESSION_SYNC (session));
5104 priv = soup_session_get_instance_private (session);
5105 g_return_if_fail (priv->use_thread_context);
5106 g_return_if_fail (uri != NULL);
5108 task = g_task_new (session, cancellable, callback, user_data);
5110 msg = soup_message_new_from_uri (SOUP_METHOD_HEAD, uri);
5111 soup_message_set_flags (msg, SOUP_MESSAGE_NEW_CONNECTION);
5112 g_signal_connect_object (msg, "finished",
5113 G_CALLBACK (connect_async_message_finished),
5115 if (progress_callback) {
5116 g_signal_connect_object (msg, "network-event",
5117 G_CALLBACK (connect_async_message_network_event),
5121 item = soup_session_append_queue_item (session, msg, TRUE, FALSE, NULL, NULL);
5122 item->connect_only = TRUE;
5123 data = connect_async_data_new (item, progress_callback, user_data);
5124 g_task_set_task_data (task, data, (GDestroyNotify) connect_async_data_free);
5125 soup_session_kick_queue (session);
5126 soup_message_queue_item_unref (item);
5127 g_object_unref (msg);
5131 * soup_session_connect_finish:
5132 * @session: a #SoupSession
5133 * @result: the #GAsyncResult passed to your callback
5134 * @error: return location for a #GError, or %NULL
5136 * Gets the #GIOStream created for the connection to communicate with the server.
5138 * Return value: (transfer full): a new #GIOStream, or %NULL on error.
5143 soup_session_connect_finish (SoupSession *session,
5144 GAsyncResult *result,
5147 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
5148 g_return_val_if_fail (g_task_is_valid (result, session), NULL);
5150 return g_task_propagate_pointer (G_TASK (result), error);