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-ntlm.h"
17 #include "soup-connection.h"
18 #include "soup-marshal.h"
19 #include "soup-message-private.h"
20 #include "soup-misc-private.h"
21 #include "soup-message-queue.h"
22 #include "soup-proxy-resolver-static.h"
23 #include "soup-session-private.h"
25 #define HOST_KEEP_ALIVE 5 * 60 * 1000 /* 5 min in msecs */
28 * SECTION:soup-session
29 * @short_description: Soup session state object
31 * #SoupSession is the object that controls client-side HTTP. A
32 * #SoupSession encapsulates all of the state that libsoup is keeping
33 * on behalf of your program; cached HTTP connections, authentication
36 * Most applications will only need a single #SoupSession; the primary
37 * reason you might need multiple sessions is if you need to have
38 * multiple independent authentication contexts. (Eg, you are
39 * connecting to a server and authenticating as two different users at
40 * different times; the easiest way to ensure that each #SoupMessage
41 * is sent with the authentication information you intended is to use
42 * one session for the first user, and a second session for the other
45 * #SoupSession itself is an abstract class, with two subclasses. If
46 * you are using the glib main loop, you will generally want to use
47 * #SoupSessionAsync, which uses non-blocking I/O and callbacks. On
48 * the other hand, if your application is threaded and you want to do
49 * synchronous I/O in a separate thread from the UI, use
56 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
57 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
58 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
66 GSList *connections; /* CONTAINS: SoupConnection */
71 gboolean ssl_fallback;
73 GSource *keep_alive_src;
76 static guint soup_host_uri_hash (gconstpointer key);
77 static gboolean soup_host_uri_equal (gconstpointer v1, gconstpointer v2);
87 SoupMessageQueue *queue;
90 char *accept_language;
91 gboolean accept_language_auto;
94 GHashTable *features_cache;
96 GHashTable *http_hosts, *https_hosts; /* char* -> SoupSessionHost */
97 GHashTable *conns; /* SoupConnection -> SoupSessionHost */
99 guint max_conns, max_conns_per_host;
100 guint io_timeout, idle_timeout;
102 /* Must hold the conn_lock before potentially creating a new
103 * SoupSessionHost, adding/removing a connection,
104 * disconnecting a connection, or moving a connection from
105 * IDLE to IN_USE. Must not emit signals or destroy objects
106 * while holding it. conn_cond is signaled when it may be
107 * possible for a previously-blocked message to continue.
112 GMainContext *async_context;
113 gboolean use_thread_context;
114 GSList *run_queue_sources;
118 char **http_aliases, **https_aliases;
120 GHashTable *request_types;
121 } SoupSessionPrivate;
122 #define SOUP_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION, SoupSessionPrivate))
124 #define SOUP_IS_PLAIN_SESSION(o) (G_TYPE_FROM_INSTANCE (o) == SOUP_TYPE_SESSION)
126 static void free_host (SoupSessionHost *host);
127 static void connection_state_changed (GObject *object, GParamSpec *param,
129 static void connection_disconnected (SoupConnection *conn, gpointer user_data);
130 static void drop_connection (SoupSession *session, SoupSessionHost *host,
131 SoupConnection *conn);
133 static void auth_manager_authenticate (SoupAuthManager *manager,
134 SoupMessage *msg, SoupAuth *auth,
135 gboolean retrying, gpointer user_data);
137 static void async_run_queue (SoupSession *session);
139 static void async_send_request_running (SoupSession *session, SoupMessageQueueItem *item);
141 #define SOUP_SESSION_MAX_CONNS_DEFAULT 10
142 #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2
144 #define SOUP_SESSION_MAX_REDIRECTION_COUNT 20
146 #define SOUP_SESSION_USER_AGENT_BASE "libsoup/" PACKAGE_VERSION
148 G_DEFINE_TYPE_WITH_CODE (SoupSession, soup_session, G_TYPE_OBJECT,
162 static guint signals[LAST_SIGNAL] = { 0 };
169 PROP_MAX_CONNS_PER_HOST,
172 PROP_SSL_USE_SYSTEM_CA_FILE,
176 PROP_USE_THREAD_CONTEXT,
179 PROP_ACCEPT_LANGUAGE,
180 PROP_ACCEPT_LANGUAGE_AUTO,
183 PROP_ADD_FEATURE_BY_TYPE,
184 PROP_REMOVE_FEATURE_BY_TYPE,
192 soup_session_init (SoupSession *session)
194 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
195 SoupAuthManager *auth_manager;
197 priv->session = session;
199 priv->queue = soup_message_queue_new (session);
201 g_mutex_init (&priv->conn_lock);
202 g_cond_init (&priv->conn_cond);
203 priv->http_hosts = g_hash_table_new_full (soup_host_uri_hash,
205 NULL, (GDestroyNotify)free_host);
206 priv->https_hosts = g_hash_table_new_full (soup_host_uri_hash,
208 NULL, (GDestroyNotify)free_host);
209 priv->conns = g_hash_table_new (NULL, NULL);
211 priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
212 priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
214 priv->features_cache = g_hash_table_new (NULL, NULL);
216 auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER_NTLM, NULL);
217 g_signal_connect (auth_manager, "authenticate",
218 G_CALLBACK (auth_manager_authenticate), session);
219 soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
220 SOUP_TYPE_AUTH_BASIC);
221 soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
222 SOUP_TYPE_AUTH_DIGEST);
223 soup_session_add_feature (session, SOUP_SESSION_FEATURE (auth_manager));
224 g_object_unref (auth_manager);
226 /* We'll be doing DNS continuously-ish while the session is active,
227 * so hold a ref on the default GResolver.
229 priv->resolver = g_resolver_get_default ();
231 priv->ssl_strict = TRUE;
233 priv->http_aliases = g_new (char *, 2);
234 priv->http_aliases[0] = (char *)g_intern_string ("*");
235 priv->http_aliases[1] = NULL;
237 priv->request_types = g_hash_table_new (soup_str_case_hash,
238 soup_str_case_equal);
239 soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_HTTP);
240 soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_FILE);
241 soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_DATA);
245 soup_session_constructor (GType type,
246 guint n_construct_properties,
247 GObjectConstructParam *construct_params)
251 object = G_OBJECT_CLASS (soup_session_parent_class)->constructor (type, n_construct_properties, construct_params);
253 /* If this is a "plain" SoupSession, fix up the default
254 * properties values, etc.
256 if (type == SOUP_TYPE_SESSION) {
257 SoupSession *session = SOUP_SESSION (object);
258 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
260 g_clear_object (&priv->tlsdb);
261 priv->tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
263 g_clear_pointer (&priv->async_context, g_main_context_unref);
264 priv->async_context = g_main_context_ref_thread_default ();
265 priv->use_thread_context = TRUE;
267 priv->io_timeout = priv->idle_timeout = 60;
269 priv->http_aliases[0] = NULL;
271 soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_DECODER);
272 soup_session_add_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_DEFAULT);
279 soup_session_dispose (GObject *object)
281 SoupSession *session = SOUP_SESSION (object);
282 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
285 priv->disposed = TRUE;
287 for (iter = priv->run_queue_sources; iter; iter = iter->next) {
288 g_source_destroy (iter->data);
289 g_source_unref (iter->data);
291 g_clear_pointer (&priv->run_queue_sources, g_slist_free);
293 priv->disposed = TRUE;
294 soup_session_abort (session);
295 g_warn_if_fail (g_hash_table_size (priv->conns) == 0);
297 while (priv->features)
298 soup_session_remove_feature (session, priv->features->data);
300 G_OBJECT_CLASS (soup_session_parent_class)->dispose (object);
304 soup_session_finalize (GObject *object)
306 SoupSession *session = SOUP_SESSION (object);
307 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
309 soup_message_queue_destroy (priv->queue);
311 g_mutex_clear (&priv->conn_lock);
312 g_cond_clear (&priv->conn_cond);
313 g_hash_table_destroy (priv->http_hosts);
314 g_hash_table_destroy (priv->https_hosts);
315 g_hash_table_destroy (priv->conns);
317 g_free (priv->user_agent);
318 g_free (priv->accept_language);
320 g_clear_object (&priv->tlsdb);
321 g_free (priv->ssl_ca_file);
323 g_clear_pointer (&priv->async_context, g_main_context_unref);
325 g_hash_table_destroy (priv->features_cache);
327 g_object_unref (priv->resolver);
329 g_free (priv->http_aliases);
330 g_free (priv->https_aliases);
332 g_hash_table_destroy (priv->request_types);
334 G_OBJECT_CLASS (soup_session_parent_class)->finalize (object);
337 /* Converts a language in POSIX format and to be RFC2616 compliant */
338 /* Based on code from epiphany-webkit (ephy_langs_append_languages()) */
340 posix_lang_to_rfc2616 (const gchar *language)
342 /* Don't include charset variants, etc */
343 if (strchr (language, '.') || strchr (language, '@'))
346 /* Ignore "C" locale, which g_get_language_names() always
347 * includes as a fallback.
349 if (!strcmp (language, "C"))
352 return g_strdelimit (g_ascii_strdown (language, -1), "_", '-');
355 /* Converts @quality from 0-100 to 0.0-1.0 and appends to @str */
357 add_quality_value (const gchar *str, int quality)
359 g_return_val_if_fail (str != NULL, NULL);
361 if (quality >= 0 && quality < 100) {
362 /* We don't use %.02g because of "." vs "," locale issues */
364 return g_strdup_printf ("%s;q=0.%02d", str, quality);
366 return g_strdup_printf ("%s;q=0.%d", str, quality / 10);
368 return g_strdup (str);
371 /* Returns a RFC2616 compliant languages list from system locales */
373 accept_languages_from_system (void)
375 const char * const * lang_names;
376 GPtrArray *langs = NULL;
377 char *lang, *langs_str;
381 lang_names = g_get_language_names ();
382 g_return_val_if_fail (lang_names != NULL, NULL);
384 /* Build the array of languages */
385 langs = g_ptr_array_new_with_free_func (g_free);
386 for (i = 0; lang_names[i] != NULL; i++) {
387 lang = posix_lang_to_rfc2616 (lang_names[i]);
389 g_ptr_array_add (langs, lang);
392 /* Add quality values */
395 else if (langs->len < 20)
400 for (i = 0; i < langs->len; i++) {
401 lang = langs->pdata[i];
402 langs->pdata[i] = add_quality_value (lang, 100 - i * delta);
406 /* Fallback: add "en" if list is empty */
408 g_ptr_array_add (langs, g_strdup ("en"));
410 g_ptr_array_add (langs, NULL);
411 langs_str = g_strjoinv (", ", (char **)langs->pdata);
412 g_ptr_array_free (langs, TRUE);
418 set_tlsdb (SoupSession *session, GTlsDatabase *tlsdb)
420 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
421 GTlsDatabase *system_default;
423 if (tlsdb == priv->tlsdb)
426 g_object_freeze_notify (G_OBJECT (session));
428 system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
429 if (priv->tlsdb == system_default || tlsdb == system_default) {
430 g_object_notify (G_OBJECT (session), "ssl-use-system-ca-file");
432 g_object_unref (system_default);
434 if (priv->ssl_ca_file) {
435 g_free (priv->ssl_ca_file);
436 priv->ssl_ca_file = NULL;
437 g_object_notify (G_OBJECT (session), "ssl-ca-file");
441 g_object_unref (priv->tlsdb);
444 g_object_ref (priv->tlsdb);
446 g_object_notify (G_OBJECT (session), "tls-database");
447 g_object_thaw_notify (G_OBJECT (session));
451 set_use_system_ca_file (SoupSession *session, gboolean use_system_ca_file)
453 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
454 GTlsDatabase *system_default;
456 system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
458 if (use_system_ca_file)
459 set_tlsdb (session, system_default);
460 else if (priv->tlsdb == system_default)
461 set_tlsdb (session, NULL);
463 g_object_unref (system_default);
467 set_ssl_ca_file (SoupSession *session, const char *ssl_ca_file)
469 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
471 GError *error = NULL;
473 if (!g_strcmp0 (priv->ssl_ca_file, ssl_ca_file))
476 g_object_freeze_notify (G_OBJECT (session));
478 if (g_path_is_absolute (ssl_ca_file))
479 tlsdb = g_tls_file_database_new (ssl_ca_file, &error);
483 cwd = g_get_current_dir ();
484 path = g_build_filename (cwd, ssl_ca_file, NULL);
485 tlsdb = g_tls_file_database_new (path, &error);
490 if (!g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_UNAVAILABLE)) {
491 g_warning ("Could not set SSL credentials from '%s': %s",
492 ssl_ca_file, error->message);
494 tlsdb = g_tls_file_database_new ("/dev/null", NULL);
496 g_error_free (error);
499 set_tlsdb (session, tlsdb);
500 g_object_unref (tlsdb);
502 priv->ssl_ca_file = g_strdup (ssl_ca_file);
503 g_object_notify (G_OBJECT (session), "ssl-ca-file");
505 g_object_thaw_notify (G_OBJECT (session));
508 /* priv->http_aliases and priv->https_aliases are stored as arrays of
509 * *interned* strings, so we can't just use g_strdupv() to set them.
512 set_aliases (char ***variable, char **value)
524 len = g_strv_length (value);
525 *variable = g_new (char *, len + 1);
526 for (i = 0; i < len; i++)
527 (*variable)[i] = (char *)g_intern_string (value[i]);
528 (*variable)[i] = NULL;
532 soup_session_set_property (GObject *object, guint prop_id,
533 const GValue *value, GParamSpec *pspec)
535 SoupSession *session = SOUP_SESSION (object);
536 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
538 const char *user_agent;
539 SoupSessionFeature *feature;
540 GMainContext *async_context;
544 uri = g_value_get_boxed (value);
547 #ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
548 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
550 soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER);
551 #ifdef G_GNUC_END_IGNORE_DEPRECATIONS
552 G_GNUC_END_IGNORE_DEPRECATIONS
554 feature = SOUP_SESSION_FEATURE (soup_proxy_resolver_static_new (uri));
555 soup_session_add_feature (session, feature);
556 g_object_unref (feature);
558 soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
560 soup_session_abort (session);
563 priv->max_conns = g_value_get_int (value);
565 case PROP_MAX_CONNS_PER_HOST:
566 priv->max_conns_per_host = g_value_get_int (value);
569 g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
570 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
572 if (g_value_get_boolean (value))
573 soup_session_feature_add_feature (feature, SOUP_TYPE_AUTH_NTLM);
575 soup_session_feature_remove_feature (feature, SOUP_TYPE_AUTH_NTLM);
577 g_warning ("Trying to set use-ntlm on session with no auth-manager");
579 case PROP_SSL_CA_FILE:
580 set_ssl_ca_file (session, g_value_get_string (value));
582 case PROP_SSL_USE_SYSTEM_CA_FILE:
583 set_use_system_ca_file (session, g_value_get_boolean (value));
585 case PROP_TLS_DATABASE:
586 set_tlsdb (session, g_value_get_object (value));
588 case PROP_SSL_STRICT:
589 priv->ssl_strict = g_value_get_boolean (value);
591 case PROP_ASYNC_CONTEXT:
592 async_context = g_value_get_pointer (value);
593 if (async_context && async_context != g_main_context_get_thread_default ())
594 g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
595 priv->async_context = async_context;
596 if (priv->async_context)
597 g_main_context_ref (priv->async_context);
599 case PROP_USE_THREAD_CONTEXT:
600 if (!g_value_get_boolean (value))
601 g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
602 priv->use_thread_context = g_value_get_boolean (value);
603 if (priv->use_thread_context) {
604 if (priv->async_context)
605 g_main_context_unref (priv->async_context);
606 priv->async_context = g_main_context_get_thread_default ();
607 if (priv->async_context)
608 g_main_context_ref (priv->async_context);
612 priv->io_timeout = g_value_get_uint (value);
614 case PROP_USER_AGENT:
615 g_free (priv->user_agent);
616 user_agent = g_value_get_string (value);
618 priv->user_agent = NULL;
619 else if (!*user_agent) {
621 g_strdup (SOUP_SESSION_USER_AGENT_BASE);
622 } else if (g_str_has_suffix (user_agent, " ")) {
624 g_strdup_printf ("%s%s", user_agent,
625 SOUP_SESSION_USER_AGENT_BASE);
627 priv->user_agent = g_strdup (user_agent);
629 case PROP_ACCEPT_LANGUAGE:
630 g_free (priv->accept_language);
631 priv->accept_language = g_strdup (g_value_get_string (value));
632 priv->accept_language_auto = FALSE;
634 case PROP_ACCEPT_LANGUAGE_AUTO:
635 priv->accept_language_auto = g_value_get_boolean (value);
636 if (priv->accept_language) {
637 g_free (priv->accept_language);
638 priv->accept_language = NULL;
641 /* Get languages from system if needed */
642 if (priv->accept_language_auto)
643 priv->accept_language = accept_languages_from_system ();
645 case PROP_IDLE_TIMEOUT:
646 priv->idle_timeout = g_value_get_uint (value);
648 case PROP_ADD_FEATURE:
649 soup_session_add_feature (session, g_value_get_object (value));
651 case PROP_ADD_FEATURE_BY_TYPE:
652 soup_session_add_feature_by_type (session, g_value_get_gtype (value));
654 case PROP_REMOVE_FEATURE_BY_TYPE:
655 soup_session_remove_feature_by_type (session, g_value_get_gtype (value));
657 case PROP_HTTP_ALIASES:
658 set_aliases (&priv->http_aliases, g_value_get_boxed (value));
660 case PROP_HTTPS_ALIASES:
661 set_aliases (&priv->https_aliases, g_value_get_boxed (value));
664 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
670 soup_session_get_property (GObject *object, guint prop_id,
671 GValue *value, GParamSpec *pspec)
673 SoupSession *session = SOUP_SESSION (object);
674 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
675 SoupSessionFeature *feature;
680 feature = soup_session_get_feature (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
682 g_object_get_property (G_OBJECT (feature),
683 SOUP_PROXY_RESOLVER_STATIC_PROXY_URI,
686 g_value_set_boxed (value, NULL);
689 g_value_set_int (value, priv->max_conns);
691 case PROP_MAX_CONNS_PER_HOST:
692 g_value_set_int (value, priv->max_conns_per_host);
695 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
697 g_value_set_boolean (value, soup_session_feature_has_feature (feature, SOUP_TYPE_AUTH_NTLM));
699 g_value_set_boolean (value, FALSE);
701 case PROP_SSL_CA_FILE:
702 g_value_set_string (value, priv->ssl_ca_file);
704 case PROP_SSL_USE_SYSTEM_CA_FILE:
705 tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
706 g_value_set_boolean (value, priv->tlsdb == tlsdb);
707 g_object_unref (tlsdb);
709 case PROP_TLS_DATABASE:
710 g_value_set_object (value, priv->tlsdb);
712 case PROP_SSL_STRICT:
713 g_value_set_boolean (value, priv->ssl_strict);
715 case PROP_ASYNC_CONTEXT:
716 g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
718 case PROP_USE_THREAD_CONTEXT:
719 g_value_set_boolean (value, priv->use_thread_context);
722 g_value_set_uint (value, priv->io_timeout);
724 case PROP_USER_AGENT:
725 g_value_set_string (value, priv->user_agent);
727 case PROP_ACCEPT_LANGUAGE:
728 g_value_set_string (value, priv->accept_language);
730 case PROP_ACCEPT_LANGUAGE_AUTO:
731 g_value_set_boolean (value, priv->accept_language_auto);
733 case PROP_IDLE_TIMEOUT:
734 g_value_set_uint (value, priv->idle_timeout);
736 case PROP_HTTP_ALIASES:
737 g_value_set_boxed (value, priv->http_aliases);
739 case PROP_HTTPS_ALIASES:
740 g_value_set_boxed (value, priv->https_aliases);
743 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
749 uri_is_http (SoupSessionPrivate *priv, SoupURI *uri)
753 if (uri->scheme == SOUP_URI_SCHEME_HTTP)
755 else if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
757 else if (!priv->http_aliases)
760 for (i = 0; priv->http_aliases[i]; i++) {
761 if (uri->scheme == priv->http_aliases[i])
765 if (!priv->http_aliases[1] && !strcmp (priv->http_aliases[0], "*"))
772 uri_is_https (SoupSessionPrivate *priv, SoupURI *uri)
776 if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
778 else if (uri->scheme == SOUP_URI_SCHEME_HTTP)
780 else if (!priv->https_aliases)
783 for (i = 0; priv->https_aliases[i]; i++) {
784 if (uri->scheme == priv->https_aliases[i])
792 * soup_session_get_async_context:
793 * @session: a #SoupSession
795 * Gets @session's async_context. This does not add a ref to the
796 * context, so you will need to ref it yourself if you want it to
797 * outlive its session.
799 * If #SoupSession:use-thread-context is true, this will return the
800 * current thread-default main context.
802 * Return value: (transfer none): @session's #GMainContext, which may
806 soup_session_get_async_context (SoupSession *session)
808 SoupSessionPrivate *priv;
810 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
811 priv = SOUP_SESSION_GET_PRIVATE (session);
813 if (priv->use_thread_context)
814 return g_main_context_get_thread_default ();
816 return priv->async_context;
821 /* Note that we can't use soup_uri_host_hash() and soup_uri_host_equal()
822 * because we want to ignore the protocol; http://example.com and
823 * webcal://example.com are the same host.
826 soup_host_uri_hash (gconstpointer key)
828 const SoupURI *uri = key;
830 g_return_val_if_fail (uri != NULL && uri->host != NULL, 0);
832 return uri->port + soup_str_case_hash (uri->host);
836 soup_host_uri_equal (gconstpointer v1, gconstpointer v2)
838 const SoupURI *one = v1;
839 const SoupURI *two = v2;
841 g_return_val_if_fail (one != NULL && two != NULL, one == two);
842 g_return_val_if_fail (one->host != NULL && two->host != NULL, one->host == two->host);
844 if (one->port != two->port)
847 return g_ascii_strcasecmp (one->host, two->host) == 0;
851 static SoupSessionHost *
852 soup_session_host_new (SoupSession *session, SoupURI *uri)
854 SoupSessionHost *host;
856 host = g_slice_new0 (SoupSessionHost);
857 host->uri = soup_uri_copy_host (uri);
858 if (host->uri->scheme != SOUP_URI_SCHEME_HTTP &&
859 host->uri->scheme != SOUP_URI_SCHEME_HTTPS) {
860 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
862 if (uri_is_https (priv, host->uri))
863 host->uri->scheme = SOUP_URI_SCHEME_HTTPS;
865 host->uri->scheme = SOUP_URI_SCHEME_HTTP;
868 host->addr = g_object_new (SOUP_TYPE_ADDRESS,
869 SOUP_ADDRESS_NAME, host->uri->host,
870 SOUP_ADDRESS_PORT, host->uri->port,
871 SOUP_ADDRESS_PROTOCOL, host->uri->scheme,
873 host->keep_alive_src = NULL;
874 host->session = session;
879 /* Requires conn_lock to be locked */
880 static SoupSessionHost *
881 get_host_for_uri (SoupSession *session, SoupURI *uri)
883 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
884 SoupSessionHost *host;
886 if (uri_is_https (priv, uri))
887 host = g_hash_table_lookup (priv->https_hosts, uri);
889 host = g_hash_table_lookup (priv->http_hosts, uri);
893 host = soup_session_host_new (session, uri);
895 if (uri_is_https (priv, uri))
896 g_hash_table_insert (priv->https_hosts, host->uri, host);
898 g_hash_table_insert (priv->http_hosts, host->uri, host);
903 /* Requires conn_lock to be locked */
904 static SoupSessionHost *
905 get_host_for_message (SoupSession *session, SoupMessage *msg)
907 return get_host_for_uri (session, soup_message_get_uri (msg));
911 free_host (SoupSessionHost *host)
913 g_warn_if_fail (host->connections == NULL);
915 if (host->keep_alive_src) {
916 g_source_destroy (host->keep_alive_src);
917 g_source_unref (host->keep_alive_src);
920 soup_uri_free (host->uri);
921 g_object_unref (host->addr);
922 g_slice_free (SoupSessionHost, host);
926 soup_session_real_auth_required (SoupSession *session, SoupMessage *msg,
927 SoupAuth *auth, gboolean retrying)
929 g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
933 auth_manager_authenticate (SoupAuthManager *manager, SoupMessage *msg,
934 SoupAuth *auth, gboolean retrying,
937 SOUP_SESSION_GET_CLASS (session)->auth_required (
938 session, msg, auth, retrying);
941 #define SOUP_SESSION_WOULD_REDIRECT_AS_GET(session, msg) \
942 ((msg)->status_code == SOUP_STATUS_SEE_OTHER || \
943 ((msg)->status_code == SOUP_STATUS_FOUND && \
944 !SOUP_METHOD_IS_SAFE ((msg)->method)) || \
945 ((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY && \
946 (msg)->method == SOUP_METHOD_POST))
948 #define SOUP_SESSION_WOULD_REDIRECT_AS_SAFE(session, msg) \
949 (((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY || \
950 (msg)->status_code == SOUP_STATUS_TEMPORARY_REDIRECT || \
951 (msg)->status_code == SOUP_STATUS_FOUND) && \
952 SOUP_METHOD_IS_SAFE ((msg)->method))
954 static inline SoupURI *
955 redirection_uri (SoupMessage *msg)
960 new_loc = soup_message_headers_get_one (msg->response_headers,
964 new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
965 if (!new_uri || !new_uri->host) {
967 soup_uri_free (new_uri);
975 * soup_session_would_redirect:
976 * @session: a #SoupSession
977 * @msg: a #SoupMessage that has response headers
979 * Checks if @msg contains a response that would cause @session to
980 * redirect it to a new URL (ignoring @msg's %SOUP_MESSAGE_NO_REDIRECT
981 * flag, and the number of times it has already been redirected).
983 * Return value: whether @msg would be redirected
988 soup_session_would_redirect (SoupSession *session, SoupMessage *msg)
990 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
993 /* It must have an appropriate status code and method */
994 if (!SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg) &&
995 !SOUP_SESSION_WOULD_REDIRECT_AS_SAFE (session, msg))
998 /* and a Location header that parses to an http URI */
999 if (!soup_message_headers_get_one (msg->response_headers, "Location"))
1001 new_uri = redirection_uri (msg);
1004 if (!new_uri->host || !*new_uri->host ||
1005 (!uri_is_http (priv, new_uri) && !uri_is_https (priv, new_uri))) {
1006 soup_uri_free (new_uri);
1010 soup_uri_free (new_uri);
1015 * soup_session_redirect_message:
1016 * @session: the session
1017 * @msg: a #SoupMessage that has received a 3xx response
1019 * Updates @msg's URI according to its status code and "Location"
1020 * header, and requeues it on @session. Use this when you have set
1021 * %SOUP_MESSAGE_NO_REDIRECT on a message, but have decided to allow a
1022 * particular redirection to occur, or if you want to allow a
1023 * redirection that #SoupSession will not perform automatically (eg,
1024 * redirecting a non-safe method such as DELETE).
1026 * If @msg's status code indicates that it should be retried as a GET
1027 * request, then @msg will be modified accordingly.
1029 * If @msg has already been redirected too many times, this will
1030 * cause it to fail with %SOUP_STATUS_TOO_MANY_REDIRECTS.
1032 * Return value: %TRUE if a redirection was applied, %FALSE if not
1033 * (eg, because there was no Location header, or it could not be
1039 soup_session_redirect_message (SoupSession *session, SoupMessage *msg)
1041 SoupMessageQueueItem *item;
1044 new_uri = redirection_uri (msg);
1048 item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
1050 soup_uri_free (new_uri);
1053 if (item->redirection_count >= SOUP_SESSION_MAX_REDIRECTION_COUNT) {
1054 soup_uri_free (new_uri);
1055 soup_session_cancel_message (session, msg, SOUP_STATUS_TOO_MANY_REDIRECTS);
1056 soup_message_queue_item_unref (item);
1059 item->redirection_count++;
1060 soup_message_queue_item_unref (item);
1062 if (SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg)) {
1063 if (msg->method != SOUP_METHOD_HEAD) {
1065 SOUP_MESSAGE_METHOD, SOUP_METHOD_GET,
1068 soup_message_set_request (msg, NULL,
1069 SOUP_MEMORY_STATIC, NULL, 0);
1070 soup_message_headers_set_encoding (msg->request_headers,
1071 SOUP_ENCODING_NONE);
1074 soup_message_set_uri (msg, new_uri);
1075 soup_uri_free (new_uri);
1077 soup_session_requeue_message (session, msg);
1082 redirect_handler (SoupMessage *msg, gpointer user_data)
1084 SoupMessageQueueItem *item = user_data;
1085 SoupSession *session = item->session;
1087 if (!soup_session_would_redirect (session, msg)) {
1088 SoupURI *new_uri = redirection_uri (msg);
1089 gboolean invalid = !new_uri || !new_uri->host;
1092 soup_uri_free (new_uri);
1093 if (invalid && !item->new_api) {
1094 soup_message_set_status_full (msg,
1095 SOUP_STATUS_MALFORMED,
1096 "Invalid Redirect URL");
1101 soup_session_redirect_message (session, msg);
1105 proxy_connection_event (SoupConnection *conn,
1106 GSocketClientEvent event,
1107 GIOStream *connection,
1110 SoupMessageQueueItem *item = user_data;
1112 soup_message_network_event (item->msg, event, connection);
1116 soup_session_set_item_connection (SoupSession *session,
1117 SoupMessageQueueItem *item,
1118 SoupConnection *conn)
1121 g_signal_handlers_disconnect_by_func (item->conn, proxy_connection_event, item);
1122 g_object_unref (item->conn);
1128 g_object_ref (item->conn);
1129 g_signal_connect (item->conn, "event",
1130 G_CALLBACK (proxy_connection_event), item);
1135 message_restarted (SoupMessage *msg, gpointer user_data)
1137 SoupMessageQueueItem *item = user_data;
1140 (!soup_message_is_keepalive (msg) ||
1141 SOUP_STATUS_IS_REDIRECTION (msg->status_code))) {
1142 if (soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE)
1143 soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
1144 soup_session_set_item_connection (item->session, item, NULL);
1147 soup_message_cleanup_response (msg);
1150 SoupMessageQueueItem *
1151 soup_session_append_queue_item (SoupSession *session, SoupMessage *msg,
1152 gboolean async, gboolean new_api,
1153 SoupSessionCallback callback, gpointer user_data)
1155 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1156 SoupMessageQueueItem *item;
1157 SoupSessionHost *host;
1159 soup_message_cleanup_response (msg);
1161 item = soup_message_queue_append (priv->queue, msg, callback, user_data);
1162 item->async = async;
1163 item->new_api = new_api;
1165 g_mutex_lock (&priv->conn_lock);
1166 host = get_host_for_message (session, item->msg);
1167 host->num_messages++;
1168 g_mutex_unlock (&priv->conn_lock);
1170 if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
1171 soup_message_add_header_handler (
1172 msg, "got_body", "Location",
1173 G_CALLBACK (redirect_handler), item);
1175 g_signal_connect (msg, "restarted",
1176 G_CALLBACK (message_restarted), item);
1178 g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
1180 soup_message_queue_item_ref (item);
1185 soup_session_send_queue_item (SoupSession *session,
1186 SoupMessageQueueItem *item,
1187 SoupMessageCompletionFn completion_cb)
1189 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1190 const char *conn_header;
1192 if (priv->user_agent) {
1193 soup_message_headers_replace (item->msg->request_headers,
1194 "User-Agent", priv->user_agent);
1197 if (priv->accept_language &&
1198 !soup_message_headers_get_list (item->msg->request_headers,
1199 "Accept-Language")) {
1200 soup_message_headers_append (item->msg->request_headers,
1202 priv->accept_language);
1205 /* Force keep alive connections for HTTP 1.0. Performance will
1206 * improve when issuing multiple requests to the same host in
1207 * a short period of time, as we wouldn't need to establish
1208 * new connections. Keep alive is implicit for HTTP 1.1.
1210 conn_header = soup_message_headers_get_list (item->msg->request_headers, "Connection");
1212 (!soup_header_contains (conn_header, "Keep-Alive") &&
1213 !soup_header_contains (conn_header, "close")))
1214 soup_message_headers_append (item->msg->request_headers,
1215 "Connection", "Keep-Alive");
1217 g_signal_emit (session, signals[REQUEST_STARTED], 0,
1218 item->msg, soup_connection_get_socket (item->conn));
1219 if (item->state == SOUP_MESSAGE_RUNNING)
1220 soup_connection_send_request (item->conn, item, completion_cb, item);
1224 soup_session_cleanup_connections (SoupSession *session,
1225 gboolean cleanup_idle)
1227 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1228 GSList *conns = NULL, *c;
1229 GHashTableIter iter;
1230 gpointer conn, host;
1231 SoupConnectionState state;
1233 g_mutex_lock (&priv->conn_lock);
1234 g_hash_table_iter_init (&iter, priv->conns);
1235 while (g_hash_table_iter_next (&iter, &conn, &host)) {
1236 state = soup_connection_get_state (conn);
1237 if (state == SOUP_CONNECTION_REMOTE_DISCONNECTED ||
1238 (cleanup_idle && state == SOUP_CONNECTION_IDLE)) {
1239 conns = g_slist_prepend (conns, g_object_ref (conn));
1240 g_hash_table_iter_remove (&iter);
1241 drop_connection (session, host, conn);
1244 g_mutex_unlock (&priv->conn_lock);
1249 for (c = conns; c; c = c->next) {
1251 soup_connection_disconnect (conn);
1252 g_object_unref (conn);
1254 g_slist_free (conns);
1260 free_unused_host (gpointer user_data)
1262 SoupSessionHost *host = (SoupSessionHost *) user_data;
1263 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (host->session);
1265 g_mutex_lock (&priv->conn_lock);
1267 /* In a multithreaded session, a connection might have been
1268 * added while we were waiting for conn_lock.
1270 if (host->connections) {
1271 g_mutex_unlock (&priv->conn_lock);
1275 /* This will free the host in addition to removing it from the
1278 if (host->uri->scheme == SOUP_URI_SCHEME_HTTPS)
1279 g_hash_table_remove (priv->https_hosts, host->uri);
1281 g_hash_table_remove (priv->http_hosts, host->uri);
1282 g_mutex_unlock (&priv->conn_lock);
1288 drop_connection (SoupSession *session, SoupSessionHost *host, SoupConnection *conn)
1290 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1292 /* Note: caller must hold conn_lock, and must remove @conn
1293 * from priv->conns itself.
1297 host->connections = g_slist_remove (host->connections, conn);
1300 /* Free the SoupHost (and its SoupAddress) if there
1301 * has not been any new connection to the host during
1302 * the last HOST_KEEP_ALIVE msecs.
1304 if (host->num_conns == 0) {
1305 g_assert (host->keep_alive_src == NULL);
1306 host->keep_alive_src = soup_add_timeout (priv->async_context,
1310 host->keep_alive_src = g_source_ref (host->keep_alive_src);
1313 if (soup_connection_get_ssl_fallback (conn))
1314 host->ssl_fallback = TRUE;
1317 g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
1318 g_signal_handlers_disconnect_by_func (conn, connection_state_changed, session);
1321 g_object_unref (conn);
1325 connection_disconnected (SoupConnection *conn, gpointer user_data)
1327 SoupSession *session = user_data;
1328 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1329 SoupSessionHost *host;
1331 g_mutex_lock (&priv->conn_lock);
1333 host = g_hash_table_lookup (priv->conns, conn);
1335 g_hash_table_remove (priv->conns, conn);
1336 drop_connection (session, host, conn);
1338 g_mutex_unlock (&priv->conn_lock);
1340 soup_session_kick_queue (session);
1344 connection_state_changed (GObject *object, GParamSpec *param, gpointer user_data)
1346 SoupSession *session = user_data;
1347 SoupConnection *conn = SOUP_CONNECTION (object);
1349 if (soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE)
1350 soup_session_kick_queue (session);
1354 soup_session_get_queue (SoupSession *session)
1356 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1362 soup_session_unqueue_item (SoupSession *session,
1363 SoupMessageQueueItem *item)
1365 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1366 SoupSessionHost *host;
1369 if (item->msg->method != SOUP_METHOD_CONNECT ||
1370 !SOUP_STATUS_IS_SUCCESSFUL (item->msg->status_code))
1371 soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
1372 soup_session_set_item_connection (session, item, NULL);
1375 if (item->state != SOUP_MESSAGE_FINISHED) {
1376 g_warning ("finished an item with state %d", item->state);
1380 soup_message_queue_remove (priv->queue, item);
1382 g_mutex_lock (&priv->conn_lock);
1383 host = get_host_for_message (session, item->msg);
1384 host->num_messages--;
1385 g_mutex_unlock (&priv->conn_lock);
1387 /* g_signal_handlers_disconnect_by_func doesn't work if you
1388 * have a metamarshal, meaning it doesn't work with
1389 * soup_message_add_header_handler()
1391 g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
1392 0, 0, NULL, NULL, item);
1393 g_signal_emit (session, signals[REQUEST_UNQUEUED], 0, item->msg);
1394 soup_message_queue_item_unref (item);
1398 soup_session_set_item_status (SoupSession *session,
1399 SoupMessageQueueItem *item,
1405 switch (status_code) {
1406 case SOUP_STATUS_CANT_RESOLVE:
1407 case SOUP_STATUS_CANT_CONNECT:
1408 uri = soup_message_get_uri (item->msg);
1409 msg = g_strdup_printf ("%s (%s)",
1410 soup_status_get_phrase (status_code),
1412 soup_message_set_status_full (item->msg, status_code, msg);
1416 case SOUP_STATUS_CANT_RESOLVE_PROXY:
1417 case SOUP_STATUS_CANT_CONNECT_PROXY:
1418 if (item->proxy_uri && item->proxy_uri->host) {
1419 msg = g_strdup_printf ("%s (%s)",
1420 soup_status_get_phrase (status_code),
1421 item->proxy_uri->host);
1422 soup_message_set_status_full (item->msg, status_code, msg);
1426 soup_message_set_status (item->msg, status_code);
1429 case SOUP_STATUS_SSL_FAILED:
1430 if (!g_tls_backend_supports_tls (g_tls_backend_get_default ())) {
1431 soup_message_set_status_full (item->msg, status_code,
1432 "TLS/SSL support not available; install glib-networking");
1434 soup_message_set_status (item->msg, status_code);
1438 soup_message_set_status (item->msg, status_code);
1445 message_completed (SoupMessage *msg, gpointer user_data)
1447 SoupMessageQueueItem *item = user_data;
1450 soup_session_kick_queue (item->session);
1452 if (item->state != SOUP_MESSAGE_RESTARTING) {
1453 item->state = SOUP_MESSAGE_FINISHING;
1455 if (item->new_api && !item->async)
1456 soup_session_process_queue_item (item->session, item, NULL, TRUE);
1461 tunnel_complete (SoupConnection *conn, guint status, gpointer user_data)
1463 SoupMessageQueueItem *tunnel_item = user_data;
1464 SoupMessageQueueItem *item = tunnel_item->related;
1465 SoupSession *session = tunnel_item->session;
1467 soup_message_finished (tunnel_item->msg);
1468 soup_message_queue_item_unref (tunnel_item);
1470 if (item->msg->status_code)
1471 item->state = SOUP_MESSAGE_FINISHING;
1473 soup_message_set_https_status (item->msg, item->conn);
1475 if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
1476 soup_session_set_item_connection (session, item, NULL);
1477 soup_session_set_item_status (session, item, status);
1480 item->state = SOUP_MESSAGE_READY;
1482 soup_session_kick_queue (session);
1483 soup_message_queue_item_unref (item);
1487 tunnel_message_completed (SoupMessage *msg, gpointer user_data)
1489 SoupMessageQueueItem *tunnel_item = user_data;
1490 SoupMessageQueueItem *item = tunnel_item->related;
1491 SoupSession *session = tunnel_item->session;
1494 if (tunnel_item->state == SOUP_MESSAGE_RESTARTING) {
1495 soup_message_restarted (msg);
1496 if (tunnel_item->conn) {
1497 tunnel_item->state = SOUP_MESSAGE_RUNNING;
1498 soup_session_send_queue_item (session, tunnel_item,
1499 tunnel_message_completed);
1503 soup_message_set_status (msg, SOUP_STATUS_TRY_AGAIN);
1506 tunnel_item->state = SOUP_MESSAGE_FINISHED;
1507 soup_session_unqueue_item (session, tunnel_item);
1509 status = tunnel_item->msg->status_code;
1510 if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
1511 tunnel_complete (item->conn, status, tunnel_item);
1515 if (tunnel_item->async) {
1516 soup_connection_start_ssl_async (item->conn, item->cancellable,
1517 tunnel_complete, tunnel_item);
1519 status = soup_connection_start_ssl_sync (item->conn, item->cancellable);
1520 tunnel_complete (item->conn, status, tunnel_item);
1525 tunnel_connect (SoupMessageQueueItem *item)
1527 SoupSession *session = item->session;
1528 SoupMessageQueueItem *tunnel_item;
1532 item->state = SOUP_MESSAGE_TUNNELING;
1534 uri = soup_connection_get_remote_uri (item->conn);
1535 msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT, uri);
1536 soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
1538 tunnel_item = soup_session_append_queue_item (session, msg,
1541 g_object_unref (msg);
1542 tunnel_item->related = item;
1543 soup_message_queue_item_ref (item);
1544 soup_session_set_item_connection (session, tunnel_item, item->conn);
1545 tunnel_item->state = SOUP_MESSAGE_RUNNING;
1547 g_signal_emit (session, signals[TUNNELING], 0, tunnel_item->conn);
1549 soup_session_send_queue_item (session, tunnel_item,
1550 tunnel_message_completed);
1554 got_connection (SoupConnection *conn, guint status, gpointer user_data)
1556 SoupMessageQueueItem *item = user_data;
1557 SoupSession *session = item->session;
1559 if (status != SOUP_STATUS_OK) {
1560 if (item->state == SOUP_MESSAGE_CONNECTING) {
1561 soup_session_set_item_status (session, item, status);
1562 soup_session_set_item_connection (session, item, NULL);
1563 item->state = SOUP_MESSAGE_READY;
1566 item->state = SOUP_MESSAGE_CONNECTED;
1569 if (item->state == SOUP_MESSAGE_CONNECTED ||
1570 item->state == SOUP_MESSAGE_READY)
1571 async_run_queue (item->session);
1573 soup_session_kick_queue (item->session);
1575 soup_message_queue_item_unref (item);
1579 /* requires conn_lock */
1580 static SoupConnection *
1581 get_connection_for_host (SoupSession *session,
1582 SoupMessageQueueItem *item,
1583 SoupSessionHost *host,
1584 gboolean need_new_connection,
1585 gboolean *try_cleanup)
1587 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1588 SoupConnection *conn;
1590 int num_pending = 0;
1596 g_return_val_if_fail (soup_connection_get_state (item->conn) != SOUP_CONNECTION_DISCONNECTED, FALSE);
1600 for (conns = host->connections; conns; conns = conns->next) {
1603 if (!need_new_connection && soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE) {
1604 soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
1606 } else if (soup_connection_get_state (conn) == SOUP_CONNECTION_CONNECTING)
1610 /* Limit the number of pending connections; num_messages / 2
1611 * is somewhat arbitrary...
1613 if (num_pending > host->num_messages / 2)
1616 if (host->num_conns >= priv->max_conns_per_host) {
1617 if (need_new_connection)
1618 *try_cleanup = TRUE;
1622 if (priv->num_conns >= priv->max_conns) {
1623 *try_cleanup = TRUE;
1627 conn = g_object_new (
1628 SOUP_TYPE_CONNECTION,
1629 SOUP_CONNECTION_REMOTE_URI, host->uri,
1630 SOUP_CONNECTION_PROXY_RESOLVER, soup_session_get_feature (session, SOUP_TYPE_PROXY_URI_RESOLVER),
1631 SOUP_CONNECTION_SSL, uri_is_https (priv, soup_message_get_uri (item->msg)),
1632 SOUP_CONNECTION_SSL_CREDENTIALS, priv->tlsdb,
1633 SOUP_CONNECTION_SSL_STRICT, (priv->tlsdb != NULL) && priv->ssl_strict,
1634 SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
1635 SOUP_CONNECTION_USE_THREAD_CONTEXT, priv->use_thread_context,
1636 SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
1637 SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
1638 SOUP_CONNECTION_SSL_FALLBACK, host->ssl_fallback,
1640 g_signal_connect (conn, "disconnected",
1641 G_CALLBACK (connection_disconnected),
1643 g_signal_connect (conn, "notify::state",
1644 G_CALLBACK (connection_state_changed),
1647 /* This is a debugging-related signal, and so can ignore the
1648 * usual rule about not emitting signals while holding
1651 g_signal_emit (session, signals[CONNECTION_CREATED], 0, conn);
1653 g_hash_table_insert (priv->conns, conn, host);
1657 host->connections = g_slist_prepend (host->connections, conn);
1659 if (host->keep_alive_src) {
1660 g_source_destroy (host->keep_alive_src);
1661 g_source_unref (host->keep_alive_src);
1662 host->keep_alive_src = NULL;
1669 get_connection (SoupMessageQueueItem *item, gboolean *should_cleanup)
1671 SoupSession *session = item->session;
1672 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1673 SoupSessionHost *host;
1674 SoupConnection *conn = NULL;
1675 gboolean my_should_cleanup = FALSE;
1676 gboolean need_new_connection;
1678 need_new_connection =
1679 (soup_message_get_flags (item->msg) & SOUP_MESSAGE_NEW_CONNECTION) ||
1680 (!(soup_message_get_flags (item->msg) & SOUP_MESSAGE_IDEMPOTENT) &&
1681 !SOUP_METHOD_IS_IDEMPOTENT (item->msg->method));
1683 g_mutex_lock (&priv->conn_lock);
1684 host = get_host_for_message (session, item->msg);
1686 conn = get_connection_for_host (session, item, host,
1687 need_new_connection,
1688 &my_should_cleanup);
1689 if (conn || item->async)
1692 if (my_should_cleanup) {
1693 g_mutex_unlock (&priv->conn_lock);
1694 soup_session_cleanup_connections (session, TRUE);
1695 g_mutex_lock (&priv->conn_lock);
1697 my_should_cleanup = FALSE;
1701 g_cond_wait (&priv->conn_cond, &priv->conn_lock);
1703 g_mutex_unlock (&priv->conn_lock);
1707 *should_cleanup = my_should_cleanup;
1711 soup_session_set_item_connection (session, item, conn);
1712 soup_message_set_https_status (item->msg, item->conn);
1714 if (soup_connection_get_state (item->conn) != SOUP_CONNECTION_NEW) {
1715 item->state = SOUP_MESSAGE_READY;
1719 item->state = SOUP_MESSAGE_CONNECTING;
1722 soup_message_queue_item_ref (item);
1723 soup_connection_connect_async (item->conn, item->cancellable,
1724 got_connection, item);
1729 status = soup_connection_connect_sync (item->conn, item->cancellable);
1730 got_connection (item->conn, status, item);
1737 soup_session_process_queue_item (SoupSession *session,
1738 SoupMessageQueueItem *item,
1739 gboolean *should_cleanup,
1742 g_assert (item->session == session);
1748 switch (item->state) {
1749 case SOUP_MESSAGE_STARTING:
1750 if (!get_connection (item, should_cleanup))
1754 case SOUP_MESSAGE_CONNECTED:
1755 if (soup_connection_is_tunnelled (item->conn))
1756 tunnel_connect (item);
1758 item->state = SOUP_MESSAGE_READY;
1761 case SOUP_MESSAGE_READY:
1762 soup_message_set_https_status (item->msg, item->conn);
1763 if (item->msg->status_code) {
1764 if (item->msg->status_code == SOUP_STATUS_TRY_AGAIN) {
1765 soup_message_cleanup_response (item->msg);
1766 item->state = SOUP_MESSAGE_STARTING;
1768 item->state = SOUP_MESSAGE_FINISHING;
1772 item->state = SOUP_MESSAGE_RUNNING;
1774 soup_session_send_queue_item (session, item, message_completed);
1776 if (item->new_api) {
1778 async_send_request_running (session, item);
1783 case SOUP_MESSAGE_RUNNING:
1787 g_warn_if_fail (item->new_api);
1788 item->state = SOUP_MESSAGE_FINISHING;
1791 case SOUP_MESSAGE_RESTARTING:
1792 item->state = SOUP_MESSAGE_STARTING;
1793 soup_message_restarted (item->msg);
1796 case SOUP_MESSAGE_FINISHING:
1797 item->state = SOUP_MESSAGE_FINISHED;
1798 soup_message_finished (item->msg);
1799 if (item->state != SOUP_MESSAGE_FINISHED) {
1800 g_return_if_fail (!item->new_api);
1804 soup_session_unqueue_item (session, item);
1805 if (item->async && item->callback)
1806 item->callback (session, item->msg, item->callback_data);
1810 /* Nothing to do with this message in any
1813 g_warn_if_fail (item->async);
1816 } while (loop && item->state != SOUP_MESSAGE_FINISHED);
1820 async_run_queue (SoupSession *session)
1822 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1823 SoupMessageQueueItem *item;
1825 gboolean try_cleanup = TRUE, should_cleanup = FALSE;
1827 g_object_ref (session);
1828 soup_session_cleanup_connections (session, FALSE);
1831 for (item = soup_message_queue_first (priv->queue);
1833 item = soup_message_queue_next (priv->queue, item)) {
1836 /* CONNECT messages are handled specially */
1837 if (msg->method == SOUP_METHOD_CONNECT)
1840 if (item->async_context != soup_session_get_async_context (session))
1843 soup_session_process_queue_item (session, item, &should_cleanup, TRUE);
1846 if (try_cleanup && should_cleanup) {
1847 /* There is at least one message in the queue that
1848 * could be sent if we cleanupd an idle connection from
1849 * some other server.
1851 if (soup_session_cleanup_connections (session, TRUE)) {
1852 try_cleanup = should_cleanup = FALSE;
1857 g_object_unref (session);
1861 idle_run_queue (gpointer user_data)
1863 SoupSessionPrivate *priv = user_data;
1869 source = g_main_current_source ();
1870 priv->run_queue_sources = g_slist_remove (priv->run_queue_sources, source);
1872 /* Ensure that the source is destroyed before running the queue */
1873 g_source_destroy (source);
1874 g_source_unref (source);
1876 g_assert (priv->session);
1877 async_run_queue (priv->session);
1882 * SoupSessionCallback:
1883 * @session: the session
1884 * @msg: the message that has finished
1885 * @user_data: the data passed to soup_session_queue_message
1887 * Prototype for the callback passed to soup_session_queue_message(),
1892 * soup_session_queue_message:
1893 * @session: a #SoupSession
1894 * @msg: (transfer full): the message to queue
1895 * @callback: (allow-none) (scope async): a #SoupSessionCallback which will
1896 * be called after the message completes or when an unrecoverable error occurs.
1897 * @user_data: (allow-none): a pointer passed to @callback.
1899 * Queues the message @msg for sending. All messages are processed
1900 * while the glib main loop runs. If @msg has been processed before,
1901 * any resources related to the time it was last sent are freed.
1903 * Upon message completion, the callback specified in @callback will
1904 * be invoked (in the thread associated with @session's async
1905 * context). If after returning from this callback the message has not
1906 * been requeued, @msg will be unreffed.
1909 soup_session_queue_message (SoupSession *session, SoupMessage *msg,
1910 SoupSessionCallback callback, gpointer user_data)
1912 g_return_if_fail (SOUP_IS_SESSION_ASYNC (session) || SOUP_IS_SESSION_SYNC (session));
1913 g_return_if_fail (SOUP_IS_MESSAGE (msg));
1915 SOUP_SESSION_GET_CLASS (session)->queue_message (session, msg,
1916 callback, user_data);
1917 /* The SoupMessageQueueItem will hold a ref on @msg until it is
1918 * finished, so we can drop the ref adopted from the caller now.
1920 g_object_unref (msg);
1924 soup_session_real_requeue_message (SoupSession *session, SoupMessage *msg)
1926 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1927 SoupMessageQueueItem *item;
1929 item = soup_message_queue_lookup (priv->queue, msg);
1930 g_return_if_fail (item != NULL);
1931 item->state = SOUP_MESSAGE_RESTARTING;
1932 soup_message_queue_item_unref (item);
1936 * soup_session_requeue_message:
1937 * @session: a #SoupSession
1938 * @msg: the message to requeue
1940 * This causes @msg to be placed back on the queue to be attempted
1944 soup_session_requeue_message (SoupSession *session, SoupMessage *msg)
1946 g_return_if_fail (SOUP_IS_SESSION (session));
1947 g_return_if_fail (SOUP_IS_MESSAGE (msg));
1949 SOUP_SESSION_GET_CLASS (session)->requeue_message (session, msg);
1953 * soup_session_send_message:
1954 * @session: a #SoupSession
1955 * @msg: the message to send
1957 * Synchronously send @msg. This call will not return until the
1958 * transfer is finished successfully or there is an unrecoverable
1961 * @msg is not freed upon return.
1963 * Return value: the HTTP status code of the response
1966 soup_session_send_message (SoupSession *session, SoupMessage *msg)
1968 g_return_val_if_fail (SOUP_IS_SESSION_ASYNC (session) || SOUP_IS_SESSION_SYNC (session), SOUP_STATUS_MALFORMED);
1969 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_STATUS_MALFORMED);
1971 return SOUP_SESSION_GET_CLASS (session)->send_message (session, msg);
1976 * soup_session_pause_message:
1977 * @session: a #SoupSession
1978 * @msg: a #SoupMessage currently running on @session
1980 * Pauses HTTP I/O on @msg. Call soup_session_unpause_message() to
1984 soup_session_pause_message (SoupSession *session,
1987 SoupSessionPrivate *priv;
1988 SoupMessageQueueItem *item;
1990 g_return_if_fail (SOUP_IS_SESSION (session));
1991 g_return_if_fail (SOUP_IS_MESSAGE (msg));
1993 priv = SOUP_SESSION_GET_PRIVATE (session);
1994 item = soup_message_queue_lookup (priv->queue, msg);
1995 g_return_if_fail (item != NULL);
1997 item->paused = TRUE;
1998 if (item->state == SOUP_MESSAGE_RUNNING)
1999 soup_message_io_pause (msg);
2000 soup_message_queue_item_unref (item);
2004 soup_session_real_kick_queue (SoupSession *session)
2006 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2007 SoupMessageQueueItem *item;
2008 gboolean have_sync_items = FALSE;
2013 for (item = soup_message_queue_first (priv->queue);
2015 item = soup_message_queue_next (priv->queue, item)) {
2019 /* We use priv rather than session as the
2020 * source data, because other parts of libsoup
2021 * (or the calling app) may have sources using
2022 * the session as the source data.
2024 source = g_main_context_find_source_by_user_data (item->async_context, priv);
2026 source = soup_add_completion_reffed (item->async_context,
2027 idle_run_queue, priv);
2028 priv->run_queue_sources = g_slist_prepend (priv->run_queue_sources,
2032 have_sync_items = TRUE;
2035 if (have_sync_items)
2036 g_cond_broadcast (&priv->conn_cond);
2040 soup_session_kick_queue (SoupSession *session)
2042 SOUP_SESSION_GET_CLASS (session)->kick (session);
2046 * soup_session_unpause_message:
2047 * @session: a #SoupSession
2048 * @msg: a #SoupMessage currently running on @session
2050 * Resumes HTTP I/O on @msg. Use this to resume after calling
2051 * soup_session_pause_message().
2053 * If @msg is being sent via blocking I/O, this will resume reading or
2054 * writing immediately. If @msg is using non-blocking I/O, then
2055 * reading or writing won't resume until you return to the main loop.
2058 soup_session_unpause_message (SoupSession *session,
2061 SoupSessionPrivate *priv;
2062 SoupMessageQueueItem *item;
2064 g_return_if_fail (SOUP_IS_SESSION (session));
2065 g_return_if_fail (SOUP_IS_MESSAGE (msg));
2067 priv = SOUP_SESSION_GET_PRIVATE (session);
2068 item = soup_message_queue_lookup (priv->queue, msg);
2069 g_return_if_fail (item != NULL);
2071 item->paused = FALSE;
2072 if (item->state == SOUP_MESSAGE_RUNNING)
2073 soup_message_io_unpause (msg);
2074 soup_message_queue_item_unref (item);
2076 soup_session_kick_queue (session);
2081 soup_session_real_cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
2083 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2084 SoupMessageQueueItem *item;
2086 item = soup_message_queue_lookup (priv->queue, msg);
2087 g_return_if_fail (item != NULL);
2089 item->paused = FALSE;
2090 soup_message_set_status (msg, status_code);
2091 g_cancellable_cancel (item->cancellable);
2093 soup_session_kick_queue (item->session);
2094 soup_message_queue_item_unref (item);
2098 * soup_session_cancel_message:
2099 * @session: a #SoupSession
2100 * @msg: the message to cancel
2101 * @status_code: status code to set on @msg (generally
2102 * %SOUP_STATUS_CANCELLED)
2104 * Causes @session to immediately finish processing @msg (regardless
2105 * of its current state) with a final status_code of @status_code. You
2106 * may call this at any time after handing @msg off to @session; if
2107 * @session has started sending the request but has not yet received
2108 * the complete response, then it will close the request's connection.
2109 * Note that with non-idempotent requests (eg,
2110 * <literal>POST</literal>, <literal>PUT</literal>,
2111 * <literal>DELETE</literal>) it is possible that you might cancel the
2112 * request after the server acts on it, but before it returns a
2113 * response, leaving the remote resource in an unknown state.
2115 * If the message is cancelled while its response body is being read,
2116 * then the response body in @msg will be left partially-filled-in.
2117 * The response headers, on the other hand, will always be either
2118 * empty or complete.
2120 * For messages queued with soup_session_queue_message() (and
2121 * cancelled from the same thread), the callback will be invoked
2122 * before soup_session_cancel_message() returns.
2125 soup_session_cancel_message (SoupSession *session, SoupMessage *msg,
2128 SoupSessionPrivate *priv;
2129 SoupMessageQueueItem *item;
2131 g_return_if_fail (SOUP_IS_SESSION (session));
2132 g_return_if_fail (SOUP_IS_MESSAGE (msg));
2134 priv = SOUP_SESSION_GET_PRIVATE (session);
2135 item = soup_message_queue_lookup (priv->queue, msg);
2136 /* If the message is already ending, don't do anything */
2139 if (item->state == SOUP_MESSAGE_FINISHED) {
2140 soup_message_queue_item_unref (item);
2144 SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg, status_code);
2145 soup_message_queue_item_unref (item);
2149 soup_session_real_flush_queue (SoupSession *session)
2151 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2152 SoupMessageQueueItem *item;
2153 GHashTable *current = NULL;
2154 gboolean done = FALSE;
2156 if (SOUP_IS_SESSION_SYNC (session)) {
2157 /* Record the current contents of the queue */
2158 current = g_hash_table_new (NULL, NULL);
2159 for (item = soup_message_queue_first (priv->queue);
2161 item = soup_message_queue_next (priv->queue, item))
2162 g_hash_table_insert (current, item, item);
2165 /* Cancel everything */
2166 for (item = soup_message_queue_first (priv->queue);
2168 item = soup_message_queue_next (priv->queue, item)) {
2169 soup_session_cancel_message (session, item->msg,
2170 SOUP_STATUS_CANCELLED);
2173 if (SOUP_IS_SESSION_SYNC (session)) {
2174 /* Wait until all of the items in @current have been
2175 * removed from the queue. (This is not the same as
2176 * "wait for the queue to be empty", because the app
2177 * may queue new requests in response to the
2178 * cancellation of the old ones. We don't try to
2179 * cancel those requests as well, since we'd likely
2180 * just end up looping forever.)
2182 g_mutex_lock (&priv->conn_lock);
2185 for (item = soup_message_queue_first (priv->queue);
2187 item = soup_message_queue_next (priv->queue, item)) {
2188 if (g_hash_table_lookup (current, item))
2193 g_cond_wait (&priv->conn_cond, &priv->conn_lock);
2195 g_mutex_unlock (&priv->conn_lock);
2197 g_hash_table_destroy (current);
2202 * soup_session_abort:
2203 * @session: the session
2205 * Cancels all pending requests in @session and closes all idle
2206 * persistent connections.
2209 soup_session_abort (SoupSession *session)
2211 SoupSessionPrivate *priv;
2213 GHashTableIter iter;
2214 gpointer conn, host;
2216 g_return_if_fail (SOUP_IS_SESSION (session));
2217 priv = SOUP_SESSION_GET_PRIVATE (session);
2219 SOUP_SESSION_GET_CLASS (session)->flush_queue (session);
2221 /* Close all connections */
2222 g_mutex_lock (&priv->conn_lock);
2224 g_hash_table_iter_init (&iter, priv->conns);
2225 while (g_hash_table_iter_next (&iter, &conn, &host)) {
2226 conns = g_slist_prepend (conns, g_object_ref (conn));
2227 g_hash_table_iter_remove (&iter);
2228 drop_connection (session, host, conn);
2230 g_mutex_unlock (&priv->conn_lock);
2232 for (c = conns; c; c = c->next) {
2233 soup_connection_disconnect (c->data);
2234 g_object_unref (c->data);
2237 g_slist_free (conns);
2241 prefetch_uri (SoupSession *session, SoupURI *uri,
2242 GCancellable *cancellable,
2243 SoupAddressCallback callback, gpointer user_data)
2245 SoupSessionPrivate *priv;
2246 SoupSessionHost *host;
2249 priv = SOUP_SESSION_GET_PRIVATE (session);
2251 g_mutex_lock (&priv->conn_lock);
2252 host = get_host_for_uri (session, uri);
2253 addr = g_object_ref (host->addr);
2254 g_mutex_unlock (&priv->conn_lock);
2256 soup_address_resolve_async (addr,
2257 soup_session_get_async_context (session),
2258 cancellable, callback, user_data);
2259 g_object_unref (addr);
2263 * soup_session_prepare_for_uri:
2264 * @session: a #SoupSession
2265 * @uri: a #SoupURI which may be required
2267 * Tells @session that @uri may be requested shortly, and so the
2268 * session can try to prepare (resolving the domain name, obtaining
2269 * proxy address, etc.) in order to work more quickly once the URI is
2270 * actually requested.
2272 * This method acts asynchronously, in @session's
2273 * #SoupSession:async_context. If you are using #SoupSessionSync and do
2274 * not have a main loop running, then you can't use this method.
2278 * Deprecated: 2.38: use soup_session_prefetch_dns() instead
2281 soup_session_prepare_for_uri (SoupSession *session, SoupURI *uri)
2283 g_return_if_fail (SOUP_IS_SESSION (session));
2284 g_return_if_fail (uri != NULL);
2289 prefetch_uri (session, uri, NULL, NULL, NULL);
2293 * soup_session_prefetch_dns:
2294 * @session: a #SoupSession
2295 * @hostname: a hostname to be resolved
2296 * @cancellable: (allow-none): a #GCancellable object, or %NULL
2297 * @callback: (scope async) (allow-none): callback to call with the
2299 * @user_data: data for @callback
2301 * Tells @session that an URI from the given @hostname may be requested
2302 * shortly, and so the session can try to prepare by resolving the
2303 * domain name in advance, in order to work more quickly once the URI
2304 * is actually requested.
2306 * If @cancellable is non-%NULL, it can be used to cancel the
2307 * resolution. @callback will still be invoked in this case, with a
2308 * status of %SOUP_STATUS_CANCELLED.
2310 * This method acts asynchronously, in @session's
2311 * #SoupSession:async_context. If you are using #SoupSessionSync and do
2312 * not have a main loop running, then you can't use this method.
2317 soup_session_prefetch_dns (SoupSession *session, const char *hostname,
2318 GCancellable *cancellable,
2319 SoupAddressCallback callback, gpointer user_data)
2323 g_return_if_fail (SOUP_IS_SESSION (session));
2324 g_return_if_fail (hostname != NULL);
2326 /* FIXME: Prefetching should work for both HTTP and HTTPS */
2327 uri = soup_uri_new (NULL);
2328 soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP);
2329 soup_uri_set_host (uri, hostname);
2330 soup_uri_set_path (uri, "");
2332 prefetch_uri (session, uri, cancellable, callback, user_data);
2333 soup_uri_free (uri);
2337 * soup_session_add_feature:
2338 * @session: a #SoupSession
2339 * @feature: an object that implements #SoupSessionFeature
2341 * Adds @feature's functionality to @session. You can also add a
2342 * feature to the session at construct time by using the
2343 * %SOUP_SESSION_ADD_FEATURE property.
2348 soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature)
2350 SoupSessionPrivate *priv;
2352 g_return_if_fail (SOUP_IS_SESSION (session));
2353 g_return_if_fail (SOUP_IS_SESSION_FEATURE (feature));
2355 priv = SOUP_SESSION_GET_PRIVATE (session);
2356 priv->features = g_slist_prepend (priv->features, g_object_ref (feature));
2357 g_hash_table_remove_all (priv->features_cache);
2358 soup_session_feature_attach (feature, session);
2362 * soup_session_add_feature_by_type:
2363 * @session: a #SoupSession
2364 * @feature_type: a #GType
2366 * If @feature_type is the type of a class that implements
2367 * #SoupSessionFeature, this creates a new feature of that type and
2368 * adds it to @session as with soup_session_add_feature(). You can use
2369 * this when you don't need to customize the new feature in any way.
2371 * If @feature_type is not a #SoupSessionFeature type, this gives each
2372 * existing feature on @session the chance to accept @feature_type as
2373 * a "subfeature". This can be used to add new #SoupAuth or
2374 * #SoupRequest types, for instance.
2376 * You can also add a feature to the session at construct time by
2377 * using the %SOUP_SESSION_ADD_FEATURE_BY_TYPE property.
2382 soup_session_add_feature_by_type (SoupSession *session, GType feature_type)
2384 SoupSessionPrivate *priv;
2386 g_return_if_fail (SOUP_IS_SESSION (session));
2388 priv = SOUP_SESSION_GET_PRIVATE (session);
2390 if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2391 SoupSessionFeature *feature;
2393 feature = g_object_new (feature_type, NULL);
2394 soup_session_add_feature (session, feature);
2395 g_object_unref (feature);
2396 } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
2397 SoupRequestClass *request_class;
2400 request_class = g_type_class_ref (feature_type);
2401 for (i = 0; request_class->schemes[i]; i++) {
2402 g_hash_table_insert (priv->request_types,
2403 (char *)request_class->schemes[i],
2404 GSIZE_TO_POINTER (feature_type));
2409 for (f = priv->features; f; f = f->next) {
2410 if (soup_session_feature_add_feature (f->data, feature_type))
2413 g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
2418 * soup_session_remove_feature:
2419 * @session: a #SoupSession
2420 * @feature: a feature that has previously been added to @session
2422 * Removes @feature's functionality from @session.
2427 soup_session_remove_feature (SoupSession *session, SoupSessionFeature *feature)
2429 SoupSessionPrivate *priv;
2431 g_return_if_fail (SOUP_IS_SESSION (session));
2433 priv = SOUP_SESSION_GET_PRIVATE (session);
2434 if (g_slist_find (priv->features, feature)) {
2435 priv->features = g_slist_remove (priv->features, feature);
2436 g_hash_table_remove_all (priv->features_cache);
2437 soup_session_feature_detach (feature, session);
2438 g_object_unref (feature);
2443 * soup_session_remove_feature_by_type:
2444 * @session: a #SoupSession
2445 * @feature_type: a #GType
2447 * Removes all features of type @feature_type (or any subclass of
2448 * @feature_type) from @session. You can also remove standard features
2449 * from the session at construct time by using the
2450 * %SOUP_SESSION_REMOVE_FEATURE_BY_TYPE property.
2455 soup_session_remove_feature_by_type (SoupSession *session, GType feature_type)
2457 SoupSessionPrivate *priv;
2460 g_return_if_fail (SOUP_IS_SESSION (session));
2462 priv = SOUP_SESSION_GET_PRIVATE (session);
2464 if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2466 for (f = priv->features; f; f = f->next) {
2467 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type)) {
2468 soup_session_remove_feature (session, f->data);
2472 } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
2473 SoupRequestClass *request_class;
2476 request_class = g_type_class_peek (feature_type);
2479 for (i = 0; request_class->schemes[i]; i++) {
2480 g_hash_table_remove (priv->request_types,
2481 request_class->schemes[i]);
2484 for (f = priv->features; f; f = f->next) {
2485 if (soup_session_feature_remove_feature (f->data, feature_type))
2488 g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
2493 * soup_session_has_feature:
2494 * @session: a #SoupSession
2495 * @feature_type: the #GType of the class of features to check for
2497 * Tests if @session has at a feature of type @feature_type (which can
2498 * be the type of either a #SoupSessionFeature, or else a subtype of
2499 * some class managed by another feature, such as #SoupAuth or
2502 * Return value: %TRUE or %FALSE
2507 soup_session_has_feature (SoupSession *session,
2510 SoupSessionPrivate *priv;
2513 g_return_val_if_fail (SOUP_IS_SESSION (session), FALSE);
2515 priv = SOUP_SESSION_GET_PRIVATE (session);
2517 if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2518 for (f = priv->features; f; f = f->next) {
2519 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
2522 } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
2523 return g_hash_table_lookup (priv->request_types,
2524 GSIZE_TO_POINTER (feature_type)) != NULL;
2526 for (f = priv->features; f; f = f->next) {
2527 if (soup_session_feature_has_feature (f->data, feature_type))
2536 * soup_session_get_features:
2537 * @session: a #SoupSession
2538 * @feature_type: the #GType of the class of features to get
2540 * Generates a list of @session's features of type @feature_type. (If
2541 * you want to see all features, you can pass %SOUP_TYPE_SESSION_FEATURE
2542 * for @feature_type.)
2544 * Return value: (transfer container) (element-type Soup.SessionFeature):
2545 * a list of features. You must free the list, but not its contents
2550 soup_session_get_features (SoupSession *session, GType feature_type)
2552 SoupSessionPrivate *priv;
2555 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
2557 priv = SOUP_SESSION_GET_PRIVATE (session);
2558 for (f = priv->features, ret = NULL; f; f = f->next) {
2559 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
2560 ret = g_slist_prepend (ret, f->data);
2562 return g_slist_reverse (ret);
2566 * soup_session_get_feature:
2567 * @session: a #SoupSession
2568 * @feature_type: the #GType of the feature to get
2570 * Gets the first feature in @session of type @feature_type. For
2571 * features where there may be more than one feature of a given type,
2572 * use soup_session_get_features().
2574 * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
2575 * feature is owned by @session.
2579 SoupSessionFeature *
2580 soup_session_get_feature (SoupSession *session, GType feature_type)
2582 SoupSessionPrivate *priv;
2583 SoupSessionFeature *feature;
2586 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
2588 priv = SOUP_SESSION_GET_PRIVATE (session);
2590 feature = g_hash_table_lookup (priv->features_cache,
2591 GSIZE_TO_POINTER (feature_type));
2595 for (f = priv->features; f; f = f->next) {
2597 if (G_TYPE_CHECK_INSTANCE_TYPE (feature, feature_type)) {
2598 g_hash_table_insert (priv->features_cache,
2599 GSIZE_TO_POINTER (feature_type),
2608 * soup_session_get_feature_for_message:
2609 * @session: a #SoupSession
2610 * @feature_type: the #GType of the feature to get
2611 * @msg: a #SoupMessage
2613 * Gets the first feature in @session of type @feature_type, provided
2614 * that it is not disabled for @msg. As with
2615 * soup_session_get_feature(), this should only be used for features
2616 * where @feature_type is only expected to match a single feature. In
2617 * particular, if there are two matching features, and the first is
2618 * disabled on @msg, and the second is not, then this will return
2619 * %NULL, not the second feature.
2621 * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
2622 * feature is owned by @session.
2626 SoupSessionFeature *
2627 soup_session_get_feature_for_message (SoupSession *session, GType feature_type,
2630 SoupSessionFeature *feature;
2632 feature = soup_session_get_feature (session, feature_type);
2633 if (feature && soup_message_disables_feature (msg, feature))
2639 soup_session_class_init (SoupSessionClass *session_class)
2641 GObjectClass *object_class = G_OBJECT_CLASS (session_class);
2643 g_type_class_add_private (session_class, sizeof (SoupSessionPrivate));
2645 /* virtual method definition */
2646 session_class->requeue_message = soup_session_real_requeue_message;
2647 session_class->cancel_message = soup_session_real_cancel_message;
2648 session_class->auth_required = soup_session_real_auth_required;
2649 session_class->flush_queue = soup_session_real_flush_queue;
2650 session_class->kick = soup_session_real_kick_queue;
2652 /* virtual method override */
2653 object_class->constructor = soup_session_constructor;
2654 object_class->dispose = soup_session_dispose;
2655 object_class->finalize = soup_session_finalize;
2656 object_class->set_property = soup_session_set_property;
2657 object_class->get_property = soup_session_get_property;
2662 * SoupSession::request-queued:
2663 * @session: the session
2664 * @msg: the request that was queued
2666 * Emitted when a request is queued on @session. (Note that
2667 * "queued" doesn't just mean soup_session_queue_message();
2668 * soup_session_send_message() implicitly queues the message
2671 * When sending a request, first #SoupSession::request_queued
2672 * is emitted, indicating that the session has become aware of
2675 * Once a connection is available to send the request on, the
2676 * session emits #SoupSession::request_started. Then, various
2677 * #SoupMessage signals are emitted as the message is
2678 * processed. If the message is requeued, it will emit
2679 * #SoupMessage::restarted, which will then be followed by
2680 * another #SoupSession::request_started and another set of
2681 * #SoupMessage signals when the message is re-sent.
2683 * Eventually, the message will emit #SoupMessage::finished.
2684 * Normally, this signals the completion of message
2685 * processing. However, it is possible that the application
2686 * will requeue the message from the "finished" handler (or
2687 * equivalently, from the soup_session_queue_message()
2688 * callback). In that case, the process will loop back to
2689 * #SoupSession::request_started.
2691 * Eventually, a message will reach "finished" and not be
2692 * requeued. At that point, the session will emit
2693 * #SoupSession::request_unqueued to indicate that it is done
2696 * To sum up: #SoupSession::request_queued and
2697 * #SoupSession::request_unqueued are guaranteed to be emitted
2698 * exactly once, but #SoupSession::request_started and
2699 * #SoupMessage::finished (and all of the other #SoupMessage
2700 * signals) may be invoked multiple times for a given message.
2704 signals[REQUEST_QUEUED] =
2705 g_signal_new ("request-queued",
2706 G_OBJECT_CLASS_TYPE (object_class),
2710 _soup_marshal_NONE__OBJECT,
2715 * SoupSession::request-started:
2716 * @session: the session
2717 * @msg: the request being sent
2718 * @socket: the socket the request is being sent on
2720 * Emitted just before a request is sent. See
2721 * #SoupSession::request_queued for a detailed description of
2722 * the message lifecycle within a session.
2724 signals[REQUEST_STARTED] =
2725 g_signal_new ("request-started",
2726 G_OBJECT_CLASS_TYPE (object_class),
2728 G_STRUCT_OFFSET (SoupSessionClass, request_started),
2730 _soup_marshal_NONE__OBJECT_OBJECT,
2736 * SoupSession::request-unqueued:
2737 * @session: the session
2738 * @msg: the request that was unqueued
2740 * Emitted when a request is removed from @session's queue,
2741 * indicating that @session is done with it. See
2742 * #SoupSession::request_queued for a detailed description of the
2743 * message lifecycle within a session.
2747 signals[REQUEST_UNQUEUED] =
2748 g_signal_new ("request-unqueued",
2749 G_OBJECT_CLASS_TYPE (object_class),
2753 _soup_marshal_NONE__OBJECT,
2758 * SoupSession::authenticate:
2759 * @session: the session
2760 * @msg: the #SoupMessage being sent
2761 * @auth: the #SoupAuth to authenticate
2762 * @retrying: %TRUE if this is the second (or later) attempt
2764 * Emitted when the session requires authentication. If
2765 * credentials are available call soup_auth_authenticate() on
2766 * @auth. If these credentials fail, the signal will be
2767 * emitted again, with @retrying set to %TRUE, which will
2768 * continue until you return without calling
2769 * soup_auth_authenticate() on @auth.
2771 * Note that this may be emitted before @msg's body has been
2774 * If you call soup_session_pause_message() on @msg before
2775 * returning, then you can authenticate @auth asynchronously
2776 * (as long as you g_object_ref() it to make sure it doesn't
2777 * get destroyed), and then unpause @msg when you are ready
2778 * for it to continue.
2780 signals[AUTHENTICATE] =
2781 g_signal_new ("authenticate",
2782 G_OBJECT_CLASS_TYPE (object_class),
2784 G_STRUCT_OFFSET (SoupSessionClass, authenticate),
2786 _soup_marshal_NONE__OBJECT_OBJECT_BOOLEAN,
2793 * SoupSession::connection-created:
2794 * @session: the #SoupSession
2795 * @connection: the connection
2797 * Emitted when a new connection is created. This is an
2798 * internal signal intended only to be used for debugging
2799 * purposes, and may go away in the future.
2803 signals[CONNECTION_CREATED] =
2804 g_signal_new ("connection-created",
2805 G_OBJECT_CLASS_TYPE (object_class),
2809 _soup_marshal_NONE__OBJECT,
2811 /* SoupConnection is private, so we can't use
2812 * SOUP_TYPE_CONNECTION here.
2817 * SoupSession::tunneling:
2818 * @session: the #SoupSession
2819 * @connection: the connection
2821 * Emitted when an SSL tunnel is being created on a proxy
2822 * connection. This is an internal signal intended only to be
2823 * used for debugging purposes, and may go away in the future.
2827 signals[TUNNELING] =
2828 g_signal_new ("tunneling",
2829 G_OBJECT_CLASS_TYPE (object_class),
2833 _soup_marshal_NONE__OBJECT,
2835 /* SoupConnection is private, so we can't use
2836 * SOUP_TYPE_CONNECTION here.
2843 * SOUP_SESSION_PROXY_URI:
2845 * Alias for the #SoupSession:proxy-uri property. (The HTTP
2846 * proxy to use for this session.)
2848 g_object_class_install_property (
2849 object_class, PROP_PROXY_URI,
2850 g_param_spec_boxed (SOUP_SESSION_PROXY_URI,
2852 "The HTTP Proxy to use for this session",
2854 G_PARAM_READWRITE));
2856 * SOUP_SESSION_MAX_CONNS:
2858 * Alias for the #SoupSession:max-conns property. (The maximum
2859 * number of connections that the session can open at once.)
2861 g_object_class_install_property (
2862 object_class, PROP_MAX_CONNS,
2863 g_param_spec_int (SOUP_SESSION_MAX_CONNS,
2864 "Max Connection Count",
2865 "The maximum number of connections that the session can open at once",
2868 SOUP_SESSION_MAX_CONNS_DEFAULT,
2869 G_PARAM_READWRITE));
2871 * SOUP_SESSION_MAX_CONNS_PER_HOST:
2873 * Alias for the #SoupSession:max-conns-per-host property.
2874 * (The maximum number of connections that the session can
2875 * open at once to a given host.)
2877 g_object_class_install_property (
2878 object_class, PROP_MAX_CONNS_PER_HOST,
2879 g_param_spec_int (SOUP_SESSION_MAX_CONNS_PER_HOST,
2880 "Max Per-Host Connection Count",
2881 "The maximum number of connections that the session can open at once to a given host",
2884 SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT,
2885 G_PARAM_READWRITE));
2887 * SoupSession:idle-timeout:
2889 * Connection lifetime (in seconds) when idle. Any connection
2890 * left idle longer than this will be closed.
2892 * Although you can change this property at any time, it will
2893 * only affect newly-created connections, not currently-open
2894 * ones. You can call soup_session_abort() after setting this
2895 * if you want to ensure that all future connections will have
2896 * this timeout value.
2901 * SOUP_SESSION_IDLE_TIMEOUT:
2903 * Alias for the #SoupSession:idle-timeout property. (The idle
2904 * connection lifetime.)
2908 g_object_class_install_property (
2909 object_class, PROP_IDLE_TIMEOUT,
2910 g_param_spec_uint (SOUP_SESSION_IDLE_TIMEOUT,
2912 "Connection lifetime when idle",
2914 G_PARAM_READWRITE));
2916 * SoupSession:use-ntlm:
2918 * Whether or not to use NTLM authentication.
2920 * Deprecated: use soup_session_add_feature_by_type() with
2921 * #SOUP_TYPE_AUTH_NTLM.
2924 * SOUP_SESSION_USE_NTLM:
2926 * Alias for the #SoupSession:use-ntlm property. (Whether or
2927 * not to use NTLM authentication.)
2929 g_object_class_install_property (
2930 object_class, PROP_USE_NTLM,
2931 g_param_spec_boolean (SOUP_SESSION_USE_NTLM,
2933 "Whether or not to use NTLM authentication",
2935 G_PARAM_READWRITE | G_PARAM_DEPRECATED));
2937 * SoupSession:ssl-ca-file:
2939 * File containing SSL CA certificates.
2941 * Deprecated: use #SoupSession:ssl-use-system-ca-file or
2942 * #SoupSession:tls-database instead
2945 * SOUP_SESSION_SSL_CA_FILE:
2947 * Alias for the #SoupSession:ssl-ca-file property. (File
2948 * containing SSL CA certificates.).
2950 * Deprecated: use #SoupSession:ssl-use-system-ca-file or
2951 * #SoupSession:tls-database instead
2953 g_object_class_install_property (
2954 object_class, PROP_SSL_CA_FILE,
2955 g_param_spec_string (SOUP_SESSION_SSL_CA_FILE,
2957 "File containing SSL CA certificates",
2959 G_PARAM_READWRITE | G_PARAM_DEPRECATED));
2961 * SOUP_SESSION_USE_SYSTEM_CA_FILE:
2963 * Alias for the #SoupSession:ssl-use-system-ca-file property,
2969 * SoupSession:ssl-use-system-ca-file:
2971 * Setting this to %TRUE is equivalent to setting
2972 * #SoupSession:tls-database to the default system CA database.
2973 * (and likewise, setting #SoupSession:tls-database to the
2974 * default database by hand will cause this property to
2977 * Setting this to %FALSE (when it was previously %TRUE) will
2978 * clear the #SoupSession:tls-database field.
2980 * See #SoupSession:ssl-strict for more information on how
2981 * https certificate validation is handled.
2985 g_object_class_install_property (
2986 object_class, PROP_SSL_USE_SYSTEM_CA_FILE,
2987 g_param_spec_boolean (SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE,
2988 "Use system CA file",
2989 "Use the system certificate database",
2991 G_PARAM_READWRITE));
2993 * SOUP_SESSION_TLS_DATABASE:
2995 * Alias for the #SoupSession:tls-database property, qv.
3000 * SoupSession:tls-database:
3002 * Sets the #GTlsDatabase to use for validating SSL/TLS
3005 * Note that setting the #SoupSession:ssl-ca-file or
3006 * #SoupSession:ssl-use-system-ca-file property will cause
3007 * this property to be set to a #GTlsDatabase corresponding to
3008 * the indicated file or system default.
3010 * See #SoupSession:ssl-strict for more information on how
3011 * https certificate validation is handled.
3015 g_object_class_install_property (
3016 object_class, PROP_TLS_DATABASE,
3017 g_param_spec_object (SOUP_SESSION_TLS_DATABASE,
3019 "TLS database to use",
3020 G_TYPE_TLS_DATABASE,
3021 G_PARAM_READWRITE));
3023 * SOUP_SESSION_SSL_STRICT:
3025 * Alias for the #SoupSession:ssl-strict property, qv.
3030 * SoupSession:ssl-strict:
3032 * Normally, if #SoupSession:tlsdb is set (including if it was
3033 * set via #SoupSession:ssl-use-system-ca-file or
3034 * #SoupSession:ssl-ca-file), then libsoup will reject any
3035 * certificate that is invalid (ie, expired) or that is not
3036 * signed by one of the given CA certificates, and the
3037 * #SoupMessage will fail with the status
3038 * %SOUP_STATUS_SSL_FAILED.
3040 * If you set #SoupSession:ssl-strict to %FALSE, then all
3041 * certificates will be accepted, and you will need to call
3042 * soup_message_get_https_status() to distinguish valid from
3043 * invalid certificates. (This can be used, eg, if you want to
3044 * accept invalid certificates after giving some sort of
3047 * If the session has no CA file or TLS database, then all
3048 * certificates are always accepted, and this property has no
3053 g_object_class_install_property (
3054 object_class, PROP_SSL_STRICT,
3055 g_param_spec_boolean (SOUP_SESSION_SSL_STRICT,
3056 "Strictly validate SSL certificates",
3057 "Whether certificate errors should be considered a connection error",
3059 G_PARAM_READWRITE));
3061 * SOUP_SESSION_ASYNC_CONTEXT:
3063 * Alias for the #SoupSession:async-context property. (The
3064 * session's #GMainContext.)
3066 g_object_class_install_property (
3067 object_class, PROP_ASYNC_CONTEXT,
3068 g_param_spec_pointer (SOUP_SESSION_ASYNC_CONTEXT,
3069 "Async GMainContext",
3070 "The GMainContext to dispatch async I/O in",
3071 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
3073 * SOUP_SESSION_USE_THREAD_CONTEXT:
3075 * Alias for the #SoupSession:use-thread-context property, qv.
3080 * SoupSession:use-thread-context:
3082 * If set, asynchronous operations in this session will run in
3083 * whatever the thread-default #GMainContext is at the time
3084 * they are started, rather than always occurring in a context
3085 * fixed at the session's construction time. "Bookkeeping"
3086 * tasks (like expiring idle connections) will happen in the
3087 * context that was thread-default at the time the session was
3092 g_object_class_install_property (
3093 object_class, PROP_USE_THREAD_CONTEXT,
3094 g_param_spec_boolean (SOUP_SESSION_USE_THREAD_CONTEXT,
3095 "Use thread-default GMainContext",
3096 "Whether to use thread-default main contexts",
3098 G_PARAM_READWRITE));
3100 * SoupSession:timeout:
3102 * The timeout (in seconds) for socket I/O operations
3103 * (including connecting to a server, and waiting for a reply
3104 * to an HTTP request).
3106 * Although you can change this property at any time, it will
3107 * only affect newly-created connections, not currently-open
3108 * ones. You can call soup_session_abort() after setting this
3109 * if you want to ensure that all future connections will have
3110 * this timeout value.
3113 * SOUP_SESSION_TIMEOUT:
3115 * Alias for the #SoupSession:timeout property. (The timeout
3116 * in seconds for socket I/O operations.)
3118 g_object_class_install_property (
3119 object_class, PROP_TIMEOUT,
3120 g_param_spec_uint (SOUP_SESSION_TIMEOUT,
3122 "Value in seconds to timeout a blocking I/O",
3124 G_PARAM_READWRITE));
3127 * SoupSession:user-agent:
3129 * If non-%NULL, the value to use for the "User-Agent" header
3130 * on #SoupMessage<!-- -->s sent from this session.
3132 * RFC 2616 says: "The User-Agent request-header field
3133 * contains information about the user agent originating the
3134 * request. This is for statistical purposes, the tracing of
3135 * protocol violations, and automated recognition of user
3136 * agents for the sake of tailoring responses to avoid
3137 * particular user agent limitations. User agents SHOULD
3138 * include this field with requests."
3140 * The User-Agent header contains a list of one or more
3141 * product tokens, separated by whitespace, with the most
3142 * significant product token coming first. The tokens must be
3143 * brief, ASCII, and mostly alphanumeric (although "-", "_",
3144 * and "." are also allowed), and may optionally include a "/"
3145 * followed by a version string. You may also put comments,
3146 * enclosed in parentheses, between or after the tokens.
3148 * If you set a #SoupSession:user_agent property that has trailing
3149 * whitespace, #SoupSession will append its own product token
3150 * (eg, "<literal>libsoup/2.3.2</literal>") to the end of the
3154 * SOUP_SESSION_USER_AGENT:
3156 * Alias for the #SoupSession:user-agent property, qv.
3158 g_object_class_install_property (
3159 object_class, PROP_USER_AGENT,
3160 g_param_spec_string (SOUP_SESSION_USER_AGENT,
3161 "User-Agent string",
3162 "User-Agent string",
3164 G_PARAM_READWRITE));
3167 * SoupSession:accept-language:
3169 * If non-%NULL, the value to use for the "Accept-Language" header
3170 * on #SoupMessage<!-- -->s sent from this session.
3172 * Setting this will disable
3173 * #SoupSession:accept-language-auto.
3178 * SOUP_SESSION_ACCEPT_LANGUAGE:
3180 * Alias for the #SoupSession:accept-language property, qv.
3184 g_object_class_install_property (
3185 object_class, PROP_ACCEPT_LANGUAGE,
3186 g_param_spec_string (SOUP_SESSION_ACCEPT_LANGUAGE,
3187 "Accept-Language string",
3188 "Accept-Language string",
3190 G_PARAM_READWRITE));
3193 * SoupSession:accept-language-auto:
3195 * If %TRUE, #SoupSession will automatically set the string
3196 * for the "Accept-Language" header on every #SoupMessage
3197 * sent, based on the return value of g_get_language_names().
3199 * Setting this will override any previous value of
3200 * #SoupSession:accept-language.
3205 * SOUP_SESSION_ACCEPT_LANGUAGE_AUTO:
3207 * Alias for the #SoupSession:accept-language-auto property, qv.
3211 g_object_class_install_property (
3212 object_class, PROP_ACCEPT_LANGUAGE_AUTO,
3213 g_param_spec_boolean (SOUP_SESSION_ACCEPT_LANGUAGE_AUTO,
3214 "Accept-Language automatic mode",
3215 "Accept-Language automatic mode",
3217 G_PARAM_READWRITE));
3220 * SoupSession:add-feature: (skip)
3222 * Add a feature object to the session. (Shortcut for calling
3223 * soup_session_add_feature().)
3228 * SOUP_SESSION_ADD_FEATURE: (skip)
3230 * Alias for the #SoupSession:add-feature property. (Shortcut
3231 * for calling soup_session_add_feature().
3235 g_object_class_install_property (
3236 object_class, PROP_ADD_FEATURE,
3237 g_param_spec_object (SOUP_SESSION_ADD_FEATURE,
3239 "Add a feature object to the session",
3240 SOUP_TYPE_SESSION_FEATURE,
3241 G_PARAM_READWRITE));
3243 * SoupSession:add-feature-by-type: (skip)
3245 * Add a feature object of the given type to the session.
3246 * (Shortcut for calling soup_session_add_feature_by_type().)
3251 * SOUP_SESSION_ADD_FEATURE_BY_TYPE: (skip)
3253 * Alias for the #SoupSession:add-feature-by-type property.
3254 * (Shortcut for calling soup_session_add_feature_by_type().
3258 g_object_class_install_property (
3259 object_class, PROP_ADD_FEATURE_BY_TYPE,
3260 g_param_spec_gtype (SOUP_SESSION_ADD_FEATURE_BY_TYPE,
3261 "Add Feature By Type",
3262 "Add a feature object of the given type to the session",
3263 SOUP_TYPE_SESSION_FEATURE,
3264 G_PARAM_READWRITE));
3266 * SoupSession:remove-feature-by-type: (skip)
3268 * Remove feature objects from the session. (Shortcut for
3269 * calling soup_session_remove_feature_by_type().)
3274 * SOUP_SESSION_REMOVE_FEATURE_BY_TYPE: (skip)
3276 * Alias for the #SoupSession:remove-feature-by-type
3277 * property. (Shortcut for calling
3278 * soup_session_remove_feature_by_type().
3282 g_object_class_install_property (
3283 object_class, PROP_REMOVE_FEATURE_BY_TYPE,
3284 g_param_spec_gtype (SOUP_SESSION_REMOVE_FEATURE_BY_TYPE,
3285 "Remove Feature By Type",
3286 "Remove features of the given type from the session",
3287 SOUP_TYPE_SESSION_FEATURE,
3288 G_PARAM_READWRITE));
3290 * SoupSession:http-aliases:
3292 * A %NULL-terminated array of URI schemes that should be
3293 * considered to be aliases for "http". Eg, if this included
3294 * <literal>"dav"</literal>, than a URI of
3295 * <literal>dav://example.com/path</literal> would be treated
3296 * identically to <literal>http://example.com/path</literal>.
3297 * If the value is %NULL, then only "http" is recognized as
3300 * For backward-compatibility reasons, the default value for
3301 * this property is an array containing the single element
3302 * <literal>"*"</literal>, a special value which means that
3303 * any scheme except "https" is considered to be an alias for
3306 * See also #SoupSession:https-aliases.
3311 * SOUP_SESSION_HTTP_ALIASES:
3313 * Alias for the #SoupSession:http-aliases property. (URI
3314 * schemes that will be considered aliases for "http".)
3318 g_object_class_install_property (
3319 object_class, PROP_HTTP_ALIASES,
3320 g_param_spec_boxed (SOUP_SESSION_HTTP_ALIASES,
3322 "URI schemes that are considered aliases for 'http'",
3324 G_PARAM_READWRITE));
3326 * SoupSession:https-aliases:
3328 * A comma-delimited list of URI schemes that should be
3329 * considered to be aliases for "https". See
3330 * #SoupSession:http-aliases for more information.
3332 * The default value is %NULL, meaning that no URI schemes
3333 * are considered aliases for "https".
3338 * SOUP_SESSION_HTTPS_ALIASES:
3340 * Alias for the #SoupSession:https-aliases property. (URI
3341 * schemes that will be considered aliases for "https".)
3345 g_object_class_install_property (
3346 object_class, PROP_HTTPS_ALIASES,
3347 g_param_spec_boxed (SOUP_SESSION_HTTPS_ALIASES,
3349 "URI schemes that are considered aliases for 'https'",
3351 G_PARAM_READWRITE));
3355 /* send_request_async */
3358 async_send_request_return_result (SoupMessageQueueItem *item,
3359 gpointer stream, GError *error)
3363 g_return_if_fail (item->task != NULL);
3365 g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
3366 0, 0, NULL, NULL, item);
3371 if (item->io_source) {
3372 g_source_destroy (item->io_source);
3373 g_clear_pointer (&item->io_source, g_source_unref);
3377 g_task_return_error (task, error);
3378 else if (SOUP_STATUS_IS_TRANSPORT_ERROR (item->msg->status_code)) {
3380 g_object_unref (stream);
3381 g_task_return_new_error (task, SOUP_HTTP_ERROR,
3382 item->msg->status_code,
3384 item->msg->reason_phrase);
3386 g_task_return_pointer (task, stream, g_object_unref);
3387 g_object_unref (task);
3391 async_send_request_restarted (SoupMessage *msg, gpointer user_data)
3393 SoupMessageQueueItem *item = user_data;
3395 /* We won't be needing this, then. */
3396 g_object_set_data (G_OBJECT (item->msg), "SoupSession:ostream", NULL);
3397 item->io_started = FALSE;
3401 async_send_request_finished (SoupMessage *msg, gpointer user_data)
3403 SoupMessageQueueItem *item = user_data;
3404 GMemoryOutputStream *mostream;
3405 GInputStream *istream = NULL;
3406 GError *error = NULL;
3409 /* Something else already took care of it. */
3413 mostream = g_object_get_data (G_OBJECT (item->task), "SoupSession:ostream");
3418 /* We thought it would be requeued, but it wasn't, so
3419 * return the original body.
3421 size = g_memory_output_stream_get_data_size (mostream);
3422 data = size ? g_memory_output_stream_steal_data (mostream) : g_strdup ("");
3423 istream = g_memory_input_stream_new_from_data (data, size, g_free);
3424 } else if (item->io_started) {
3425 /* The message finished before becoming readable. This
3426 * will happen, eg, if it's cancelled from got-headers.
3427 * Do nothing; the op will complete via read_ready_cb()
3432 /* The message finished before even being started;
3433 * probably a tunnel connect failure.
3435 istream = g_memory_input_stream_new ();
3438 async_send_request_return_result (item, istream, error);
3442 send_async_spliced (GObject *source, GAsyncResult *result, gpointer user_data)
3444 SoupMessageQueueItem *item = user_data;
3445 GInputStream *istream = g_object_get_data (source, "istream");
3446 GError *error = NULL;
3448 /* It should be safe to call the sync close() method here since
3449 * the message body has already been written.
3451 g_input_stream_close (istream, NULL, NULL);
3452 g_object_unref (istream);
3454 /* If the message was cancelled, it will be completed via other means */
3455 if (g_cancellable_is_cancelled (item->cancellable) ||
3457 soup_message_queue_item_unref (item);
3461 if (g_output_stream_splice_finish (G_OUTPUT_STREAM (source),
3462 result, &error) == -1) {
3463 async_send_request_return_result (item, NULL, error);
3464 soup_message_queue_item_unref (item);
3468 /* Otherwise either restarted or finished will eventually be called. */
3469 soup_session_kick_queue (item->session);
3470 soup_message_queue_item_unref (item);
3474 send_async_maybe_complete (SoupMessageQueueItem *item,
3475 GInputStream *stream)
3477 if (item->msg->status_code == SOUP_STATUS_UNAUTHORIZED ||
3478 item->msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED ||
3479 soup_session_would_redirect (item->session, item->msg)) {
3480 GOutputStream *ostream;
3482 /* Message may be requeued, so gather the current message body... */
3483 ostream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
3484 g_object_set_data_full (G_OBJECT (item->task), "SoupSession:ostream",
3485 ostream, g_object_unref);
3487 g_object_set_data (G_OBJECT (ostream), "istream", stream);
3489 /* Give the splice op its own ref on item */
3490 soup_message_queue_item_ref (item);
3491 g_output_stream_splice_async (ostream, stream,
3492 /* We can't use CLOSE_SOURCE because it
3493 * might get closed in the wrong thread.
3495 G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
3498 send_async_spliced, item);
3502 async_send_request_return_result (item, stream, NULL);
3505 static void try_run_until_read (SoupMessageQueueItem *item);
3508 read_ready_cb (SoupMessage *msg, gpointer user_data)
3510 SoupMessageQueueItem *item = user_data;
3512 g_clear_pointer (&item->io_source, g_source_unref);
3513 try_run_until_read (item);
3518 try_run_until_read (SoupMessageQueueItem *item)
3520 GError *error = NULL;
3521 GInputStream *stream = NULL;
3523 if (soup_message_io_run_until_read (item->msg, item->cancellable, &error))
3524 stream = soup_message_io_get_response_istream (item->msg, &error);
3526 send_async_maybe_complete (item, stream);
3530 if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) {
3531 item->state = SOUP_MESSAGE_RESTARTING;
3532 soup_message_io_finished (item->msg);
3533 g_error_free (error);
3537 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
3538 if (item->state != SOUP_MESSAGE_FINISHED) {
3539 if (soup_message_io_in_progress (item->msg))
3540 soup_message_io_finished (item->msg);
3541 item->state = SOUP_MESSAGE_FINISHING;
3542 soup_session_process_queue_item (item->session, item, NULL, FALSE);
3544 async_send_request_return_result (item, NULL, error);
3548 g_clear_error (&error);
3549 item->io_source = soup_message_io_get_source (item->msg, item->cancellable,
3550 read_ready_cb, item);
3551 g_source_attach (item->io_source, soup_session_get_async_context (item->session));
3555 async_send_request_running (SoupSession *session, SoupMessageQueueItem *item)
3557 item->io_started = TRUE;
3558 try_run_until_read (item);
3562 soup_session_send_request_async (SoupSession *session,
3564 GCancellable *cancellable,
3565 GAsyncReadyCallback callback,
3568 SoupMessageQueueItem *item;
3569 gboolean use_thread_context;
3571 g_return_if_fail (SOUP_IS_SESSION (session));
3572 g_return_if_fail (!SOUP_IS_SESSION_SYNC (session));
3574 g_object_get (G_OBJECT (session),
3575 SOUP_SESSION_USE_THREAD_CONTEXT, &use_thread_context,
3577 g_return_if_fail (use_thread_context);
3579 item = soup_session_append_queue_item (session, msg, TRUE, TRUE,
3581 g_signal_connect (msg, "restarted",
3582 G_CALLBACK (async_send_request_restarted), item);
3583 g_signal_connect (msg, "finished",
3584 G_CALLBACK (async_send_request_finished), item);
3586 item->new_api = TRUE;
3587 item->task = g_task_new (session, cancellable, callback, user_data);
3588 g_task_set_task_data (item->task, item, (GDestroyNotify) soup_message_queue_item_unref);
3591 g_object_unref (item->cancellable);
3592 item->cancellable = g_object_ref (cancellable);
3595 soup_session_kick_queue (session);
3599 soup_session_send_request_finish (SoupSession *session,
3600 GAsyncResult *result,
3605 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
3606 g_return_val_if_fail (!SOUP_IS_SESSION_SYNC (session), NULL);
3607 g_return_val_if_fail (g_task_is_valid (result, session), NULL);
3609 task = G_TASK (result);
3610 if (g_task_had_error (task)) {
3611 SoupMessageQueueItem *item = g_task_get_task_data (task);
3613 if (soup_message_io_in_progress (item->msg))
3614 soup_message_io_finished (item->msg);
3615 else if (item->state != SOUP_MESSAGE_FINISHED)
3616 item->state = SOUP_MESSAGE_FINISHING;
3618 if (item->state != SOUP_MESSAGE_FINISHED)
3619 soup_session_process_queue_item (session, item, NULL, FALSE);
3622 return g_task_propagate_pointer (task, error);
3626 soup_session_send_request (SoupSession *session,
3628 GCancellable *cancellable,
3631 SoupMessageQueueItem *item;
3632 GInputStream *stream = NULL;
3633 GOutputStream *ostream;
3634 GMemoryOutputStream *mostream;
3636 GError *my_error = NULL;
3638 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
3639 g_return_val_if_fail (!SOUP_IS_SESSION_ASYNC (session), NULL);
3641 item = soup_session_append_queue_item (session, msg, FALSE, TRUE,
3644 item->new_api = TRUE;
3646 g_object_unref (item->cancellable);
3647 item->cancellable = g_object_ref (cancellable);
3651 /* Get a connection, etc */
3652 soup_session_process_queue_item (session, item, NULL, TRUE);
3653 if (item->state != SOUP_MESSAGE_RUNNING)
3656 /* Send request, read headers */
3657 if (!soup_message_io_run_until_read (msg, item->cancellable, &my_error)) {
3658 if (g_error_matches (my_error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) {
3659 item->state = SOUP_MESSAGE_RESTARTING;
3660 soup_message_io_finished (item->msg);
3661 g_clear_error (&my_error);
3667 stream = soup_message_io_get_response_istream (msg, &my_error);
3671 /* Break if the message doesn't look likely-to-be-requeued */
3672 if (msg->status_code != SOUP_STATUS_UNAUTHORIZED &&
3673 msg->status_code != SOUP_STATUS_PROXY_UNAUTHORIZED &&
3674 !soup_session_would_redirect (session, msg))
3677 /* Gather the current message body... */
3678 ostream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
3679 if (g_output_stream_splice (ostream, stream,
3680 G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
3681 G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
3682 item->cancellable, &my_error) == -1) {
3683 g_object_unref (stream);
3684 g_object_unref (ostream);
3688 g_object_unref (stream);
3691 /* If the message was requeued, loop */
3692 if (item->state == SOUP_MESSAGE_RESTARTING) {
3693 g_object_unref (ostream);
3697 /* Not requeued, so return the original body */
3698 mostream = G_MEMORY_OUTPUT_STREAM (ostream);
3699 size = g_memory_output_stream_get_data_size (mostream);
3700 stream = g_memory_input_stream_new ();
3702 g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (stream),
3703 g_memory_output_stream_steal_data (mostream),
3706 g_object_unref (ostream);
3710 g_propagate_error (error, my_error);
3711 else if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
3713 g_object_unref (stream);
3716 g_set_error_literal (error, SOUP_HTTP_ERROR, msg->status_code,
3717 msg->reason_phrase);
3719 stream = g_memory_input_stream_new ();
3722 if (soup_message_io_in_progress (msg))
3723 soup_message_io_finished (msg);
3724 else if (item->state != SOUP_MESSAGE_FINISHED)
3725 item->state = SOUP_MESSAGE_FINISHING;
3727 if (item->state != SOUP_MESSAGE_FINISHED)
3728 soup_session_process_queue_item (session, item, NULL, TRUE);
3731 soup_message_queue_item_unref (item);
3736 * soup_session_request:
3737 * @session: a #SoupSession
3738 * @uri_string: a URI, in string form
3739 * @error: return location for a #GError, or %NULL
3741 * Creates a #SoupRequest for retrieving @uri_string.
3743 * Return value: (transfer full): a new #SoupRequest, or
3749 soup_session_request (SoupSession *session, const char *uri_string,
3755 uri = soup_uri_new (uri_string);
3757 g_set_error (error, SOUP_REQUEST_ERROR,
3758 SOUP_REQUEST_ERROR_BAD_URI,
3759 _("Could not parse URI '%s'"), uri_string);
3763 req = soup_session_request_uri (session, uri, error);
3764 soup_uri_free (uri);
3769 * soup_session_request_uri:
3770 * @session: a #SoupSession
3771 * @uri: a #SoupURI representing the URI to retrieve
3772 * @error: return location for a #GError, or %NULL
3774 * Creates a #SoupRequest for retrieving @uri.
3776 * Return value: (transfer full): a new #SoupRequest, or
3782 soup_session_request_uri (SoupSession *session, SoupURI *uri,
3785 SoupSessionPrivate *priv;
3788 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
3790 priv = SOUP_SESSION_GET_PRIVATE (session);
3792 request_type = (GType)GPOINTER_TO_SIZE (g_hash_table_lookup (priv->request_types, uri->scheme));
3793 if (!request_type) {
3794 g_set_error (error, SOUP_REQUEST_ERROR,
3795 SOUP_REQUEST_ERROR_UNSUPPORTED_URI_SCHEME,
3796 _("Unsupported URI scheme '%s'"), uri->scheme);
3800 return g_initable_new (request_type, NULL, error,
3807 * SOUP_REQUEST_ERROR:
3809 * A #GError domain for #SoupRequest-related errors. Used with
3810 * #SoupRequestError.
3816 * @SOUP_REQUEST_ERROR_BAD_URI: the URI could not be parsed
3817 * @SOUP_REQUEST_ERROR_UNSUPPORTED_URI_SCHEME: the URI scheme is not
3818 * supported by this #SoupSession
3820 * A #SoupRequest error.
3826 soup_request_error_quark (void)
3828 static GQuark error;
3830 error = g_quark_from_static_string ("soup_request_error_quark");