1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
5 * Copyright (C) 2000-2003, Ximian, Inc.
12 #include <glib/gi18n-lib.h>
14 #include "soup-session.h"
16 #include "soup-auth-manager.h"
17 #include "soup-cache-private.h"
18 #include "soup-connection.h"
19 #include "soup-marshal.h"
20 #include "soup-message-private.h"
21 #include "soup-misc-private.h"
22 #include "soup-message-queue.h"
23 #include "soup-proxy-resolver-static.h"
24 #include "soup-session-private.h"
26 #define HOST_KEEP_ALIVE 5 * 60 * 1000 /* 5 min in msecs */
29 * SECTION:soup-session
30 * @short_description: Soup session state object
32 * #SoupSession is the object that controls client-side HTTP. A
33 * #SoupSession encapsulates all of the state that libsoup is keeping
34 * on behalf of your program; cached HTTP connections, authentication
35 * information, etc. It also keeps track of various global options
36 * and features that you are using.
38 * Most applications will only need a single #SoupSession; the primary
39 * reason you might need multiple sessions is if you need to have
40 * multiple independent authentication contexts. (Eg, you are
41 * connecting to a server and authenticating as two different users at
42 * different times; the easiest way to ensure that each #SoupMessage
43 * is sent with the authentication information you intended is to use
44 * one session for the first user, and a second session for the other
47 * In the past, #SoupSession was an abstract class, and users needed
48 * to choose between #SoupSessionAsync (which always uses
49 * #GMainLoop<!-- -->-based I/O), or #SoupSessionSync (which always uses
50 * blocking I/O and can be used from multiple threads simultaneously).
51 * This is no longer necessary; you can (and should) use a plain
52 * #SoupSession, which supports both synchronous and asynchronous use.
53 * (When using a plain #SoupSession, soup_session_queue_message()
54 * behaves like it traditionally did on a #SoupSessionAsync, and
55 * soup_session_send_message() behaves like it traditionally did on a
62 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
63 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
64 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
72 GSList *connections; /* CONTAINS: SoupConnection */
77 gboolean ssl_fallback;
79 GSource *keep_alive_src;
82 static guint soup_host_uri_hash (gconstpointer key);
83 static gboolean soup_host_uri_equal (gconstpointer v1, gconstpointer v2);
93 SoupMessageQueue *queue;
96 char *accept_language;
97 gboolean accept_language_auto;
100 GHashTable *features_cache;
102 GHashTable *http_hosts, *https_hosts; /* char* -> SoupSessionHost */
103 GHashTable *conns; /* SoupConnection -> SoupSessionHost */
105 guint max_conns, max_conns_per_host;
106 guint io_timeout, idle_timeout;
108 /* Must hold the conn_lock before potentially creating a new
109 * SoupSessionHost, adding/removing a connection,
110 * disconnecting a connection, or moving a connection from
111 * IDLE to IN_USE. Must not emit signals or destroy objects
112 * while holding it. conn_cond is signaled when it may be
113 * possible for a previously-blocked message to continue.
118 GMainContext *async_context;
119 gboolean use_thread_context;
120 GSList *run_queue_sources;
124 char **http_aliases, **https_aliases;
126 GHashTable *request_types;
127 } SoupSessionPrivate;
128 #define SOUP_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION, SoupSessionPrivate))
130 #define SOUP_IS_PLAIN_SESSION(o) (G_TYPE_FROM_INSTANCE (o) == SOUP_TYPE_SESSION)
132 static void free_host (SoupSessionHost *host);
133 static void connection_state_changed (GObject *object, GParamSpec *param,
135 static void connection_disconnected (SoupConnection *conn, gpointer user_data);
136 static void drop_connection (SoupSession *session, SoupSessionHost *host,
137 SoupConnection *conn);
139 static void auth_manager_authenticate (SoupAuthManager *manager,
140 SoupMessage *msg, SoupAuth *auth,
141 gboolean retrying, gpointer user_data);
143 static void async_run_queue (SoupSession *session);
145 static void async_send_request_running (SoupSession *session, SoupMessageQueueItem *item);
147 #define SOUP_SESSION_MAX_CONNS_DEFAULT 10
148 #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2
150 #define SOUP_SESSION_MAX_REDIRECTION_COUNT 20
152 #define SOUP_SESSION_USER_AGENT_BASE "libsoup/" PACKAGE_VERSION
154 G_DEFINE_TYPE_WITH_CODE (SoupSession, soup_session, G_TYPE_OBJECT,
168 static guint signals[LAST_SIGNAL] = { 0 };
175 PROP_MAX_CONNS_PER_HOST,
178 PROP_SSL_USE_SYSTEM_CA_FILE,
182 PROP_USE_THREAD_CONTEXT,
185 PROP_ACCEPT_LANGUAGE,
186 PROP_ACCEPT_LANGUAGE_AUTO,
189 PROP_ADD_FEATURE_BY_TYPE,
190 PROP_REMOVE_FEATURE_BY_TYPE,
198 soup_session_init (SoupSession *session)
200 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
201 SoupAuthManager *auth_manager;
203 priv->session = session;
205 priv->queue = soup_message_queue_new (session);
207 g_mutex_init (&priv->conn_lock);
208 g_cond_init (&priv->conn_cond);
209 priv->http_hosts = g_hash_table_new_full (soup_host_uri_hash,
211 NULL, (GDestroyNotify)free_host);
212 priv->https_hosts = g_hash_table_new_full (soup_host_uri_hash,
214 NULL, (GDestroyNotify)free_host);
215 priv->conns = g_hash_table_new (NULL, NULL);
217 priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
218 priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
220 priv->features_cache = g_hash_table_new (NULL, NULL);
222 auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER, NULL);
223 g_signal_connect (auth_manager, "authenticate",
224 G_CALLBACK (auth_manager_authenticate), session);
225 soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
226 SOUP_TYPE_AUTH_BASIC);
227 soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
228 SOUP_TYPE_AUTH_DIGEST);
229 soup_session_add_feature (session, SOUP_SESSION_FEATURE (auth_manager));
230 g_object_unref (auth_manager);
232 /* We'll be doing DNS continuously-ish while the session is active,
233 * so hold a ref on the default GResolver.
235 priv->resolver = g_resolver_get_default ();
237 priv->ssl_strict = TRUE;
239 priv->http_aliases = g_new (char *, 2);
240 priv->http_aliases[0] = (char *)g_intern_string ("*");
241 priv->http_aliases[1] = NULL;
243 priv->request_types = g_hash_table_new (soup_str_case_hash,
244 soup_str_case_equal);
245 soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_HTTP);
246 soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_FILE);
247 soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_DATA);
251 soup_session_constructor (GType type,
252 guint n_construct_properties,
253 GObjectConstructParam *construct_params)
257 object = G_OBJECT_CLASS (soup_session_parent_class)->constructor (type, n_construct_properties, construct_params);
259 /* If this is a "plain" SoupSession, fix up the default
260 * properties values, etc.
262 if (type == SOUP_TYPE_SESSION) {
263 SoupSession *session = SOUP_SESSION (object);
264 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
266 g_clear_object (&priv->tlsdb);
267 priv->tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
269 g_clear_pointer (&priv->async_context, g_main_context_unref);
270 priv->async_context = g_main_context_ref_thread_default ();
271 priv->use_thread_context = TRUE;
273 priv->io_timeout = priv->idle_timeout = 60;
275 priv->http_aliases[0] = NULL;
277 soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_DECODER);
278 soup_session_add_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_DEFAULT);
285 soup_session_dispose (GObject *object)
287 SoupSession *session = SOUP_SESSION (object);
288 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
291 priv->disposed = TRUE;
293 for (iter = priv->run_queue_sources; iter; iter = iter->next) {
294 g_source_destroy (iter->data);
295 g_source_unref (iter->data);
297 g_clear_pointer (&priv->run_queue_sources, g_slist_free);
299 priv->disposed = TRUE;
300 soup_session_abort (session);
301 g_warn_if_fail (g_hash_table_size (priv->conns) == 0);
303 while (priv->features)
304 soup_session_remove_feature (session, priv->features->data);
306 G_OBJECT_CLASS (soup_session_parent_class)->dispose (object);
310 soup_session_finalize (GObject *object)
312 SoupSession *session = SOUP_SESSION (object);
313 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
315 soup_message_queue_destroy (priv->queue);
317 g_mutex_clear (&priv->conn_lock);
318 g_cond_clear (&priv->conn_cond);
319 g_hash_table_destroy (priv->http_hosts);
320 g_hash_table_destroy (priv->https_hosts);
321 g_hash_table_destroy (priv->conns);
323 g_free (priv->user_agent);
324 g_free (priv->accept_language);
326 g_clear_object (&priv->tlsdb);
327 g_free (priv->ssl_ca_file);
329 g_clear_pointer (&priv->async_context, g_main_context_unref);
331 g_hash_table_destroy (priv->features_cache);
333 g_object_unref (priv->resolver);
335 g_free (priv->http_aliases);
336 g_free (priv->https_aliases);
338 g_hash_table_destroy (priv->request_types);
340 G_OBJECT_CLASS (soup_session_parent_class)->finalize (object);
343 /* Converts a language in POSIX format and to be RFC2616 compliant */
344 /* Based on code from epiphany-webkit (ephy_langs_append_languages()) */
346 posix_lang_to_rfc2616 (const gchar *language)
348 /* Don't include charset variants, etc */
349 if (strchr (language, '.') || strchr (language, '@'))
352 /* Ignore "C" locale, which g_get_language_names() always
353 * includes as a fallback.
355 if (!strcmp (language, "C"))
358 return g_strdelimit (g_ascii_strdown (language, -1), "_", '-');
361 /* Converts @quality from 0-100 to 0.0-1.0 and appends to @str */
363 add_quality_value (const gchar *str, int quality)
365 g_return_val_if_fail (str != NULL, NULL);
367 if (quality >= 0 && quality < 100) {
368 /* We don't use %.02g because of "." vs "," locale issues */
370 return g_strdup_printf ("%s;q=0.%02d", str, quality);
372 return g_strdup_printf ("%s;q=0.%d", str, quality / 10);
374 return g_strdup (str);
377 /* Returns a RFC2616 compliant languages list from system locales */
379 accept_languages_from_system (void)
381 const char * const * lang_names;
382 GPtrArray *langs = NULL;
383 char *lang, *langs_str;
387 lang_names = g_get_language_names ();
388 g_return_val_if_fail (lang_names != NULL, NULL);
390 /* Build the array of languages */
391 langs = g_ptr_array_new_with_free_func (g_free);
392 for (i = 0; lang_names[i] != NULL; i++) {
393 lang = posix_lang_to_rfc2616 (lang_names[i]);
395 g_ptr_array_add (langs, lang);
398 /* Add quality values */
401 else if (langs->len < 20)
406 for (i = 0; i < langs->len; i++) {
407 lang = langs->pdata[i];
408 langs->pdata[i] = add_quality_value (lang, 100 - i * delta);
412 /* Fallback: add "en" if list is empty */
414 g_ptr_array_add (langs, g_strdup ("en"));
416 g_ptr_array_add (langs, NULL);
417 langs_str = g_strjoinv (", ", (char **)langs->pdata);
418 g_ptr_array_free (langs, TRUE);
424 set_tlsdb (SoupSession *session, GTlsDatabase *tlsdb)
426 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
427 GTlsDatabase *system_default;
429 if (tlsdb == priv->tlsdb)
432 g_object_freeze_notify (G_OBJECT (session));
434 system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
435 if (priv->tlsdb == system_default || tlsdb == system_default) {
436 g_object_notify (G_OBJECT (session), "ssl-use-system-ca-file");
438 g_object_unref (system_default);
440 if (priv->ssl_ca_file) {
441 g_free (priv->ssl_ca_file);
442 priv->ssl_ca_file = NULL;
443 g_object_notify (G_OBJECT (session), "ssl-ca-file");
447 g_object_unref (priv->tlsdb);
450 g_object_ref (priv->tlsdb);
452 g_object_notify (G_OBJECT (session), "tls-database");
453 g_object_thaw_notify (G_OBJECT (session));
457 set_use_system_ca_file (SoupSession *session, gboolean use_system_ca_file)
459 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
460 GTlsDatabase *system_default;
462 system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
464 if (use_system_ca_file)
465 set_tlsdb (session, system_default);
466 else if (priv->tlsdb == system_default)
467 set_tlsdb (session, NULL);
469 g_object_unref (system_default);
473 set_ssl_ca_file (SoupSession *session, const char *ssl_ca_file)
475 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
477 GError *error = NULL;
479 if (!g_strcmp0 (priv->ssl_ca_file, ssl_ca_file))
482 g_object_freeze_notify (G_OBJECT (session));
484 if (g_path_is_absolute (ssl_ca_file))
485 tlsdb = g_tls_file_database_new (ssl_ca_file, &error);
489 cwd = g_get_current_dir ();
490 path = g_build_filename (cwd, ssl_ca_file, NULL);
491 tlsdb = g_tls_file_database_new (path, &error);
496 if (!g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_UNAVAILABLE)) {
497 g_warning ("Could not set SSL credentials from '%s': %s",
498 ssl_ca_file, error->message);
500 tlsdb = g_tls_file_database_new ("/dev/null", NULL);
502 g_error_free (error);
505 set_tlsdb (session, tlsdb);
506 g_object_unref (tlsdb);
508 priv->ssl_ca_file = g_strdup (ssl_ca_file);
509 g_object_notify (G_OBJECT (session), "ssl-ca-file");
511 g_object_thaw_notify (G_OBJECT (session));
514 /* priv->http_aliases and priv->https_aliases are stored as arrays of
515 * *interned* strings, so we can't just use g_strdupv() to set them.
518 set_aliases (char ***variable, char **value)
530 len = g_strv_length (value);
531 *variable = g_new (char *, len + 1);
532 for (i = 0; i < len; i++)
533 (*variable)[i] = (char *)g_intern_string (value[i]);
534 (*variable)[i] = NULL;
538 soup_session_set_property (GObject *object, guint prop_id,
539 const GValue *value, GParamSpec *pspec)
541 SoupSession *session = SOUP_SESSION (object);
542 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
544 const char *user_agent;
545 SoupSessionFeature *feature;
546 GMainContext *async_context;
550 uri = g_value_get_boxed (value);
553 #ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
554 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
556 soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER);
557 #ifdef G_GNUC_END_IGNORE_DEPRECATIONS
558 G_GNUC_END_IGNORE_DEPRECATIONS
560 feature = SOUP_SESSION_FEATURE (soup_proxy_resolver_static_new (uri));
561 soup_session_add_feature (session, feature);
562 g_object_unref (feature);
564 soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
566 soup_session_abort (session);
569 priv->max_conns = g_value_get_int (value);
571 case PROP_MAX_CONNS_PER_HOST:
572 priv->max_conns_per_host = g_value_get_int (value);
575 g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
576 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
578 if (g_value_get_boolean (value))
579 soup_session_feature_add_feature (feature, SOUP_TYPE_AUTH_NTLM);
581 soup_session_feature_remove_feature (feature, SOUP_TYPE_AUTH_NTLM);
583 g_warning ("Trying to set use-ntlm on session with no auth-manager");
585 case PROP_SSL_CA_FILE:
586 set_ssl_ca_file (session, g_value_get_string (value));
588 case PROP_SSL_USE_SYSTEM_CA_FILE:
589 set_use_system_ca_file (session, g_value_get_boolean (value));
591 case PROP_TLS_DATABASE:
592 set_tlsdb (session, g_value_get_object (value));
594 case PROP_SSL_STRICT:
595 priv->ssl_strict = g_value_get_boolean (value);
597 case PROP_ASYNC_CONTEXT:
598 async_context = g_value_get_pointer (value);
599 if (async_context && async_context != g_main_context_get_thread_default ())
600 g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
601 priv->async_context = async_context;
602 if (priv->async_context)
603 g_main_context_ref (priv->async_context);
605 case PROP_USE_THREAD_CONTEXT:
606 if (!g_value_get_boolean (value))
607 g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
608 priv->use_thread_context = g_value_get_boolean (value);
609 if (priv->use_thread_context) {
610 if (priv->async_context)
611 g_main_context_unref (priv->async_context);
612 priv->async_context = g_main_context_get_thread_default ();
613 if (priv->async_context)
614 g_main_context_ref (priv->async_context);
618 priv->io_timeout = g_value_get_uint (value);
620 case PROP_USER_AGENT:
621 g_free (priv->user_agent);
622 user_agent = g_value_get_string (value);
624 priv->user_agent = NULL;
625 else if (!*user_agent) {
627 g_strdup (SOUP_SESSION_USER_AGENT_BASE);
628 } else if (g_str_has_suffix (user_agent, " ")) {
630 g_strdup_printf ("%s%s", user_agent,
631 SOUP_SESSION_USER_AGENT_BASE);
633 priv->user_agent = g_strdup (user_agent);
635 case PROP_ACCEPT_LANGUAGE:
636 g_free (priv->accept_language);
637 priv->accept_language = g_strdup (g_value_get_string (value));
638 priv->accept_language_auto = FALSE;
640 case PROP_ACCEPT_LANGUAGE_AUTO:
641 priv->accept_language_auto = g_value_get_boolean (value);
642 if (priv->accept_language) {
643 g_free (priv->accept_language);
644 priv->accept_language = NULL;
647 /* Get languages from system if needed */
648 if (priv->accept_language_auto)
649 priv->accept_language = accept_languages_from_system ();
651 case PROP_IDLE_TIMEOUT:
652 priv->idle_timeout = g_value_get_uint (value);
654 case PROP_ADD_FEATURE:
655 soup_session_add_feature (session, g_value_get_object (value));
657 case PROP_ADD_FEATURE_BY_TYPE:
658 soup_session_add_feature_by_type (session, g_value_get_gtype (value));
660 case PROP_REMOVE_FEATURE_BY_TYPE:
661 soup_session_remove_feature_by_type (session, g_value_get_gtype (value));
663 case PROP_HTTP_ALIASES:
664 set_aliases (&priv->http_aliases, g_value_get_boxed (value));
666 case PROP_HTTPS_ALIASES:
667 set_aliases (&priv->https_aliases, g_value_get_boxed (value));
670 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
676 soup_session_get_property (GObject *object, guint prop_id,
677 GValue *value, GParamSpec *pspec)
679 SoupSession *session = SOUP_SESSION (object);
680 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
681 SoupSessionFeature *feature;
686 feature = soup_session_get_feature (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
688 g_object_get_property (G_OBJECT (feature),
689 SOUP_PROXY_RESOLVER_STATIC_PROXY_URI,
692 g_value_set_boxed (value, NULL);
695 g_value_set_int (value, priv->max_conns);
697 case PROP_MAX_CONNS_PER_HOST:
698 g_value_set_int (value, priv->max_conns_per_host);
701 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
703 g_value_set_boolean (value, soup_session_feature_has_feature (feature, SOUP_TYPE_AUTH_NTLM));
705 g_value_set_boolean (value, FALSE);
707 case PROP_SSL_CA_FILE:
708 g_value_set_string (value, priv->ssl_ca_file);
710 case PROP_SSL_USE_SYSTEM_CA_FILE:
711 tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
712 g_value_set_boolean (value, priv->tlsdb == tlsdb);
713 g_object_unref (tlsdb);
715 case PROP_TLS_DATABASE:
716 g_value_set_object (value, priv->tlsdb);
718 case PROP_SSL_STRICT:
719 g_value_set_boolean (value, priv->ssl_strict);
721 case PROP_ASYNC_CONTEXT:
722 g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
724 case PROP_USE_THREAD_CONTEXT:
725 g_value_set_boolean (value, priv->use_thread_context);
728 g_value_set_uint (value, priv->io_timeout);
730 case PROP_USER_AGENT:
731 g_value_set_string (value, priv->user_agent);
733 case PROP_ACCEPT_LANGUAGE:
734 g_value_set_string (value, priv->accept_language);
736 case PROP_ACCEPT_LANGUAGE_AUTO:
737 g_value_set_boolean (value, priv->accept_language_auto);
739 case PROP_IDLE_TIMEOUT:
740 g_value_set_uint (value, priv->idle_timeout);
742 case PROP_HTTP_ALIASES:
743 g_value_set_boxed (value, priv->http_aliases);
745 case PROP_HTTPS_ALIASES:
746 g_value_set_boxed (value, priv->https_aliases);
749 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
757 * Creates a #SoupSession with the default options.
759 * Return value: the new session.
764 soup_session_new (void)
766 return g_object_new (SOUP_TYPE_SESSION, NULL);
770 * soup_session_new_with_options:
771 * @optname1: name of first property to set
772 * @...: value of @optname1, followed by additional property/value pairs
774 * Creates a #SoupSession with the specified options.
776 * Return value: the new session.
781 soup_session_new_with_options (const char *optname1,
784 SoupSession *session;
787 va_start (ap, optname1);
788 session = (SoupSession *)g_object_new_valist (SOUP_TYPE_SESSION,
796 uri_is_http (SoupSessionPrivate *priv, SoupURI *uri)
800 if (uri->scheme == SOUP_URI_SCHEME_HTTP)
802 else if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
804 else if (!priv->http_aliases)
807 for (i = 0; priv->http_aliases[i]; i++) {
808 if (uri->scheme == priv->http_aliases[i])
812 if (!priv->http_aliases[1] && !strcmp (priv->http_aliases[0], "*"))
819 uri_is_https (SoupSessionPrivate *priv, SoupURI *uri)
823 if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
825 else if (uri->scheme == SOUP_URI_SCHEME_HTTP)
827 else if (!priv->https_aliases)
830 for (i = 0; priv->https_aliases[i]; i++) {
831 if (uri->scheme == priv->https_aliases[i])
839 * soup_session_get_async_context:
840 * @session: a #SoupSession
842 * Gets @session's #SoupSession:async-context. This does not add a ref
843 * to the context, so you will need to ref it yourself if you want it
844 * to outlive its session.
846 * For a modern #SoupSession, this will always just return the
847 * thread-default #GMainContext, and so is not especially useful.
849 * Return value: (transfer none): @session's #GMainContext, which may
853 soup_session_get_async_context (SoupSession *session)
855 SoupSessionPrivate *priv;
857 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
858 priv = SOUP_SESSION_GET_PRIVATE (session);
860 if (priv->use_thread_context)
861 return g_main_context_get_thread_default ();
863 return priv->async_context;
868 /* Note that we can't use soup_uri_host_hash() and soup_uri_host_equal()
869 * because we want to ignore the protocol; http://example.com and
870 * webcal://example.com are the same host.
873 soup_host_uri_hash (gconstpointer key)
875 const SoupURI *uri = key;
877 g_return_val_if_fail (uri != NULL && uri->host != NULL, 0);
879 return uri->port + soup_str_case_hash (uri->host);
883 soup_host_uri_equal (gconstpointer v1, gconstpointer v2)
885 const SoupURI *one = v1;
886 const SoupURI *two = v2;
888 g_return_val_if_fail (one != NULL && two != NULL, one == two);
889 g_return_val_if_fail (one->host != NULL && two->host != NULL, one->host == two->host);
891 if (one->port != two->port)
894 return g_ascii_strcasecmp (one->host, two->host) == 0;
898 static SoupSessionHost *
899 soup_session_host_new (SoupSession *session, SoupURI *uri)
901 SoupSessionHost *host;
903 host = g_slice_new0 (SoupSessionHost);
904 host->uri = soup_uri_copy_host (uri);
905 if (host->uri->scheme != SOUP_URI_SCHEME_HTTP &&
906 host->uri->scheme != SOUP_URI_SCHEME_HTTPS) {
907 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
909 if (uri_is_https (priv, host->uri))
910 host->uri->scheme = SOUP_URI_SCHEME_HTTPS;
912 host->uri->scheme = SOUP_URI_SCHEME_HTTP;
915 host->addr = g_object_new (SOUP_TYPE_ADDRESS,
916 SOUP_ADDRESS_NAME, host->uri->host,
917 SOUP_ADDRESS_PORT, host->uri->port,
918 SOUP_ADDRESS_PROTOCOL, host->uri->scheme,
920 host->keep_alive_src = NULL;
921 host->session = session;
926 /* Requires conn_lock to be locked */
927 static SoupSessionHost *
928 get_host_for_uri (SoupSession *session, SoupURI *uri)
930 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
931 SoupSessionHost *host;
933 if (uri_is_https (priv, uri))
934 host = g_hash_table_lookup (priv->https_hosts, uri);
936 host = g_hash_table_lookup (priv->http_hosts, uri);
940 host = soup_session_host_new (session, uri);
942 if (uri_is_https (priv, uri))
943 g_hash_table_insert (priv->https_hosts, host->uri, host);
945 g_hash_table_insert (priv->http_hosts, host->uri, host);
950 /* Requires conn_lock to be locked */
951 static SoupSessionHost *
952 get_host_for_message (SoupSession *session, SoupMessage *msg)
954 return get_host_for_uri (session, soup_message_get_uri (msg));
958 free_host (SoupSessionHost *host)
960 g_warn_if_fail (host->connections == NULL);
962 if (host->keep_alive_src) {
963 g_source_destroy (host->keep_alive_src);
964 g_source_unref (host->keep_alive_src);
967 soup_uri_free (host->uri);
968 g_object_unref (host->addr);
969 g_slice_free (SoupSessionHost, host);
973 auth_manager_authenticate (SoupAuthManager *manager, SoupMessage *msg,
974 SoupAuth *auth, gboolean retrying,
977 g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
980 #define SOUP_SESSION_WOULD_REDIRECT_AS_GET(session, msg) \
981 ((msg)->status_code == SOUP_STATUS_SEE_OTHER || \
982 ((msg)->status_code == SOUP_STATUS_FOUND && \
983 !SOUP_METHOD_IS_SAFE ((msg)->method)) || \
984 ((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY && \
985 (msg)->method == SOUP_METHOD_POST))
987 #define SOUP_SESSION_WOULD_REDIRECT_AS_SAFE(session, msg) \
988 (((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY || \
989 (msg)->status_code == SOUP_STATUS_TEMPORARY_REDIRECT || \
990 (msg)->status_code == SOUP_STATUS_FOUND) && \
991 SOUP_METHOD_IS_SAFE ((msg)->method))
993 static inline SoupURI *
994 redirection_uri (SoupMessage *msg)
999 new_loc = soup_message_headers_get_one (msg->response_headers,
1003 new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
1004 if (!new_uri || !new_uri->host) {
1006 soup_uri_free (new_uri);
1014 * soup_session_would_redirect:
1015 * @session: a #SoupSession
1016 * @msg: a #SoupMessage that has response headers
1018 * Checks if @msg contains a response that would cause @session to
1019 * redirect it to a new URL (ignoring @msg's %SOUP_MESSAGE_NO_REDIRECT
1020 * flag, and the number of times it has already been redirected).
1022 * Return value: whether @msg would be redirected
1027 soup_session_would_redirect (SoupSession *session, SoupMessage *msg)
1029 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1032 /* It must have an appropriate status code and method */
1033 if (!SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg) &&
1034 !SOUP_SESSION_WOULD_REDIRECT_AS_SAFE (session, msg))
1037 /* and a Location header that parses to an http URI */
1038 if (!soup_message_headers_get_one (msg->response_headers, "Location"))
1040 new_uri = redirection_uri (msg);
1043 if (!new_uri->host || !*new_uri->host ||
1044 (!uri_is_http (priv, new_uri) && !uri_is_https (priv, new_uri))) {
1045 soup_uri_free (new_uri);
1049 soup_uri_free (new_uri);
1054 * soup_session_redirect_message:
1055 * @session: the session
1056 * @msg: a #SoupMessage that has received a 3xx response
1058 * Updates @msg's URI according to its status code and "Location"
1059 * header, and requeues it on @session. Use this when you have set
1060 * %SOUP_MESSAGE_NO_REDIRECT on a message, but have decided to allow a
1061 * particular redirection to occur, or if you want to allow a
1062 * redirection that #SoupSession will not perform automatically (eg,
1063 * redirecting a non-safe method such as DELETE).
1065 * If @msg's status code indicates that it should be retried as a GET
1066 * request, then @msg will be modified accordingly.
1068 * If @msg has already been redirected too many times, this will
1069 * cause it to fail with %SOUP_STATUS_TOO_MANY_REDIRECTS.
1071 * Return value: %TRUE if a redirection was applied, %FALSE if not
1072 * (eg, because there was no Location header, or it could not be
1078 soup_session_redirect_message (SoupSession *session, SoupMessage *msg)
1080 SoupMessageQueueItem *item;
1083 new_uri = redirection_uri (msg);
1087 item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
1089 soup_uri_free (new_uri);
1092 if (item->redirection_count >= SOUP_SESSION_MAX_REDIRECTION_COUNT) {
1093 soup_uri_free (new_uri);
1094 soup_session_cancel_message (session, msg, SOUP_STATUS_TOO_MANY_REDIRECTS);
1095 soup_message_queue_item_unref (item);
1098 item->redirection_count++;
1099 soup_message_queue_item_unref (item);
1101 if (SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg)) {
1102 if (msg->method != SOUP_METHOD_HEAD) {
1104 SOUP_MESSAGE_METHOD, SOUP_METHOD_GET,
1107 soup_message_set_request (msg, NULL,
1108 SOUP_MEMORY_STATIC, NULL, 0);
1109 soup_message_headers_set_encoding (msg->request_headers,
1110 SOUP_ENCODING_NONE);
1113 soup_message_set_uri (msg, new_uri);
1114 soup_uri_free (new_uri);
1116 soup_session_requeue_message (session, msg);
1121 redirect_handler (SoupMessage *msg, gpointer user_data)
1123 SoupMessageQueueItem *item = user_data;
1124 SoupSession *session = item->session;
1126 if (!soup_session_would_redirect (session, msg)) {
1127 SoupURI *new_uri = redirection_uri (msg);
1128 gboolean invalid = !new_uri || !new_uri->host;
1131 soup_uri_free (new_uri);
1132 if (invalid && !item->new_api) {
1133 soup_message_set_status_full (msg,
1134 SOUP_STATUS_MALFORMED,
1135 "Invalid Redirect URL");
1140 soup_session_redirect_message (session, msg);
1144 proxy_connection_event (SoupConnection *conn,
1145 GSocketClientEvent event,
1146 GIOStream *connection,
1149 SoupMessageQueueItem *item = user_data;
1151 soup_message_network_event (item->msg, event, connection);
1155 soup_session_set_item_connection (SoupSession *session,
1156 SoupMessageQueueItem *item,
1157 SoupConnection *conn)
1160 g_signal_handlers_disconnect_by_func (item->conn, proxy_connection_event, item);
1161 g_object_unref (item->conn);
1165 soup_message_set_connection (item->msg, conn);
1168 g_object_ref (item->conn);
1169 g_signal_connect (item->conn, "event",
1170 G_CALLBACK (proxy_connection_event), item);
1175 message_restarted (SoupMessage *msg, gpointer user_data)
1177 SoupMessageQueueItem *item = user_data;
1180 (!soup_message_is_keepalive (msg) ||
1181 SOUP_STATUS_IS_REDIRECTION (msg->status_code))) {
1182 if (soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE)
1183 soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
1184 soup_session_set_item_connection (item->session, item, NULL);
1187 soup_message_cleanup_response (msg);
1190 SoupMessageQueueItem *
1191 soup_session_append_queue_item (SoupSession *session, SoupMessage *msg,
1192 gboolean async, gboolean new_api,
1193 SoupSessionCallback callback, gpointer user_data)
1195 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1196 SoupMessageQueueItem *item;
1197 SoupSessionHost *host;
1199 soup_message_cleanup_response (msg);
1201 item = soup_message_queue_append (priv->queue, msg, callback, user_data);
1202 item->async = async;
1203 item->new_api = new_api;
1205 g_mutex_lock (&priv->conn_lock);
1206 host = get_host_for_message (session, item->msg);
1207 host->num_messages++;
1208 g_mutex_unlock (&priv->conn_lock);
1210 if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
1211 soup_message_add_header_handler (
1212 msg, "got_body", "Location",
1213 G_CALLBACK (redirect_handler), item);
1215 g_signal_connect (msg, "restarted",
1216 G_CALLBACK (message_restarted), item);
1218 g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
1220 soup_message_queue_item_ref (item);
1225 soup_session_send_queue_item (SoupSession *session,
1226 SoupMessageQueueItem *item,
1227 SoupMessageCompletionFn completion_cb)
1229 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1230 const char *conn_header;
1232 if (priv->user_agent) {
1233 soup_message_headers_replace (item->msg->request_headers,
1234 "User-Agent", priv->user_agent);
1237 if (priv->accept_language &&
1238 !soup_message_headers_get_list (item->msg->request_headers,
1239 "Accept-Language")) {
1240 soup_message_headers_append (item->msg->request_headers,
1242 priv->accept_language);
1245 /* Force keep alive connections for HTTP 1.0. Performance will
1246 * improve when issuing multiple requests to the same host in
1247 * a short period of time, as we wouldn't need to establish
1248 * new connections. Keep alive is implicit for HTTP 1.1.
1250 conn_header = soup_message_headers_get_list (item->msg->request_headers, "Connection");
1252 (!soup_header_contains (conn_header, "Keep-Alive") &&
1253 !soup_header_contains (conn_header, "close")))
1254 soup_message_headers_append (item->msg->request_headers,
1255 "Connection", "Keep-Alive");
1257 g_signal_emit (session, signals[REQUEST_STARTED], 0,
1258 item->msg, soup_connection_get_socket (item->conn));
1259 if (item->state == SOUP_MESSAGE_RUNNING)
1260 soup_connection_send_request (item->conn, item, completion_cb, item);
1264 soup_session_cleanup_connections (SoupSession *session,
1265 gboolean cleanup_idle)
1267 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1268 GSList *conns = NULL, *c;
1269 GHashTableIter iter;
1270 gpointer conn, host;
1271 SoupConnectionState state;
1273 g_mutex_lock (&priv->conn_lock);
1274 g_hash_table_iter_init (&iter, priv->conns);
1275 while (g_hash_table_iter_next (&iter, &conn, &host)) {
1276 state = soup_connection_get_state (conn);
1277 if (state == SOUP_CONNECTION_REMOTE_DISCONNECTED ||
1278 (cleanup_idle && state == SOUP_CONNECTION_IDLE)) {
1279 conns = g_slist_prepend (conns, g_object_ref (conn));
1280 g_hash_table_iter_remove (&iter);
1281 drop_connection (session, host, conn);
1284 g_mutex_unlock (&priv->conn_lock);
1289 for (c = conns; c; c = c->next) {
1291 soup_connection_disconnect (conn);
1292 g_object_unref (conn);
1294 g_slist_free (conns);
1300 free_unused_host (gpointer user_data)
1302 SoupSessionHost *host = (SoupSessionHost *) user_data;
1303 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (host->session);
1305 g_mutex_lock (&priv->conn_lock);
1307 /* In a multithreaded session, a connection might have been
1308 * added while we were waiting for conn_lock.
1310 if (host->connections) {
1311 g_mutex_unlock (&priv->conn_lock);
1315 /* This will free the host in addition to removing it from the
1318 if (host->uri->scheme == SOUP_URI_SCHEME_HTTPS)
1319 g_hash_table_remove (priv->https_hosts, host->uri);
1321 g_hash_table_remove (priv->http_hosts, host->uri);
1322 g_mutex_unlock (&priv->conn_lock);
1328 drop_connection (SoupSession *session, SoupSessionHost *host, SoupConnection *conn)
1330 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1332 /* Note: caller must hold conn_lock, and must remove @conn
1333 * from priv->conns itself.
1337 host->connections = g_slist_remove (host->connections, conn);
1340 /* Free the SoupHost (and its SoupAddress) if there
1341 * has not been any new connection to the host during
1342 * the last HOST_KEEP_ALIVE msecs.
1344 if (host->num_conns == 0) {
1345 g_assert (host->keep_alive_src == NULL);
1346 host->keep_alive_src = soup_add_timeout (priv->async_context,
1350 host->keep_alive_src = g_source_ref (host->keep_alive_src);
1353 if (soup_connection_get_ssl_fallback (conn))
1354 host->ssl_fallback = TRUE;
1357 g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
1358 g_signal_handlers_disconnect_by_func (conn, connection_state_changed, session);
1361 g_object_unref (conn);
1365 connection_disconnected (SoupConnection *conn, gpointer user_data)
1367 SoupSession *session = user_data;
1368 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1369 SoupSessionHost *host;
1371 g_mutex_lock (&priv->conn_lock);
1373 host = g_hash_table_lookup (priv->conns, conn);
1375 g_hash_table_remove (priv->conns, conn);
1376 drop_connection (session, host, conn);
1378 g_mutex_unlock (&priv->conn_lock);
1380 soup_session_kick_queue (session);
1384 connection_state_changed (GObject *object, GParamSpec *param, gpointer user_data)
1386 SoupSession *session = user_data;
1387 SoupConnection *conn = SOUP_CONNECTION (object);
1389 if (soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE)
1390 soup_session_kick_queue (session);
1394 soup_session_get_queue (SoupSession *session)
1396 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1402 soup_session_unqueue_item (SoupSession *session,
1403 SoupMessageQueueItem *item)
1405 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1406 SoupSessionHost *host;
1409 if (item->msg->method != SOUP_METHOD_CONNECT ||
1410 !SOUP_STATUS_IS_SUCCESSFUL (item->msg->status_code))
1411 soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
1412 soup_session_set_item_connection (session, item, NULL);
1415 if (item->state != SOUP_MESSAGE_FINISHED) {
1416 g_warning ("finished an item with state %d", item->state);
1420 soup_message_queue_remove (priv->queue, item);
1422 g_mutex_lock (&priv->conn_lock);
1423 host = get_host_for_message (session, item->msg);
1424 host->num_messages--;
1425 g_mutex_unlock (&priv->conn_lock);
1427 /* g_signal_handlers_disconnect_by_func doesn't work if you
1428 * have a metamarshal, meaning it doesn't work with
1429 * soup_message_add_header_handler()
1431 g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
1432 0, 0, NULL, NULL, item);
1433 g_signal_emit (session, signals[REQUEST_UNQUEUED], 0, item->msg);
1434 soup_message_queue_item_unref (item);
1438 soup_session_set_item_status (SoupSession *session,
1439 SoupMessageQueueItem *item,
1445 switch (status_code) {
1446 case SOUP_STATUS_CANT_RESOLVE:
1447 case SOUP_STATUS_CANT_CONNECT:
1448 uri = soup_message_get_uri (item->msg);
1449 msg = g_strdup_printf ("%s (%s)",
1450 soup_status_get_phrase (status_code),
1452 soup_message_set_status_full (item->msg, status_code, msg);
1456 case SOUP_STATUS_CANT_RESOLVE_PROXY:
1457 case SOUP_STATUS_CANT_CONNECT_PROXY:
1458 if (item->proxy_uri && item->proxy_uri->host) {
1459 msg = g_strdup_printf ("%s (%s)",
1460 soup_status_get_phrase (status_code),
1461 item->proxy_uri->host);
1462 soup_message_set_status_full (item->msg, status_code, msg);
1466 soup_message_set_status (item->msg, status_code);
1469 case SOUP_STATUS_SSL_FAILED:
1470 if (!g_tls_backend_supports_tls (g_tls_backend_get_default ())) {
1471 soup_message_set_status_full (item->msg, status_code,
1472 "TLS/SSL support not available; install glib-networking");
1474 soup_message_set_status (item->msg, status_code);
1478 soup_message_set_status (item->msg, status_code);
1485 message_completed (SoupMessage *msg, gpointer user_data)
1487 SoupMessageQueueItem *item = user_data;
1490 soup_session_kick_queue (item->session);
1492 if (item->state != SOUP_MESSAGE_RESTARTING) {
1493 item->state = SOUP_MESSAGE_FINISHING;
1495 if (item->new_api && !item->async)
1496 soup_session_process_queue_item (item->session, item, NULL, TRUE);
1501 tunnel_complete (SoupConnection *conn, guint status, gpointer user_data)
1503 SoupMessageQueueItem *tunnel_item = user_data;
1504 SoupMessageQueueItem *item = tunnel_item->related;
1505 SoupSession *session = tunnel_item->session;
1507 soup_message_finished (tunnel_item->msg);
1508 soup_message_queue_item_unref (tunnel_item);
1510 if (item->msg->status_code)
1511 item->state = SOUP_MESSAGE_FINISHING;
1513 soup_message_set_https_status (item->msg, item->conn);
1515 if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
1516 soup_session_set_item_connection (session, item, NULL);
1517 soup_session_set_item_status (session, item, status);
1520 item->state = SOUP_MESSAGE_READY;
1522 soup_session_kick_queue (session);
1523 soup_message_queue_item_unref (item);
1527 tunnel_message_completed (SoupMessage *msg, gpointer user_data)
1529 SoupMessageQueueItem *tunnel_item = user_data;
1530 SoupMessageQueueItem *item = tunnel_item->related;
1531 SoupSession *session = tunnel_item->session;
1534 if (tunnel_item->state == SOUP_MESSAGE_RESTARTING) {
1535 soup_message_restarted (msg);
1536 if (tunnel_item->conn) {
1537 tunnel_item->state = SOUP_MESSAGE_RUNNING;
1538 soup_session_send_queue_item (session, tunnel_item,
1539 tunnel_message_completed);
1543 soup_message_set_status (msg, SOUP_STATUS_TRY_AGAIN);
1546 tunnel_item->state = SOUP_MESSAGE_FINISHED;
1547 soup_session_unqueue_item (session, tunnel_item);
1549 status = tunnel_item->msg->status_code;
1550 if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
1551 tunnel_complete (item->conn, status, tunnel_item);
1555 if (tunnel_item->async) {
1556 soup_connection_start_ssl_async (item->conn, item->cancellable,
1557 tunnel_complete, tunnel_item);
1559 status = soup_connection_start_ssl_sync (item->conn, item->cancellable);
1560 tunnel_complete (item->conn, status, tunnel_item);
1565 tunnel_connect (SoupMessageQueueItem *item)
1567 SoupSession *session = item->session;
1568 SoupMessageQueueItem *tunnel_item;
1572 item->state = SOUP_MESSAGE_TUNNELING;
1574 uri = soup_connection_get_remote_uri (item->conn);
1575 msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT, uri);
1576 soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
1578 tunnel_item = soup_session_append_queue_item (session, msg,
1581 g_object_unref (msg);
1582 tunnel_item->related = item;
1583 soup_message_queue_item_ref (item);
1584 soup_session_set_item_connection (session, tunnel_item, item->conn);
1585 tunnel_item->state = SOUP_MESSAGE_RUNNING;
1587 g_signal_emit (session, signals[TUNNELING], 0, tunnel_item->conn);
1589 soup_session_send_queue_item (session, tunnel_item,
1590 tunnel_message_completed);
1594 got_connection (SoupConnection *conn, guint status, gpointer user_data)
1596 SoupMessageQueueItem *item = user_data;
1597 SoupSession *session = item->session;
1599 if (status != SOUP_STATUS_OK) {
1600 if (item->state == SOUP_MESSAGE_CONNECTING) {
1601 soup_session_set_item_status (session, item, status);
1602 soup_session_set_item_connection (session, item, NULL);
1603 item->state = SOUP_MESSAGE_READY;
1606 item->state = SOUP_MESSAGE_CONNECTED;
1609 if (item->state == SOUP_MESSAGE_CONNECTED ||
1610 item->state == SOUP_MESSAGE_READY)
1611 async_run_queue (item->session);
1613 soup_session_kick_queue (item->session);
1615 soup_message_queue_item_unref (item);
1619 /* requires conn_lock */
1620 static SoupConnection *
1621 get_connection_for_host (SoupSession *session,
1622 SoupMessageQueueItem *item,
1623 SoupSessionHost *host,
1624 gboolean need_new_connection,
1625 gboolean *try_cleanup)
1627 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1628 SoupConnection *conn;
1630 int num_pending = 0;
1636 g_return_val_if_fail (soup_connection_get_state (item->conn) != SOUP_CONNECTION_DISCONNECTED, FALSE);
1640 for (conns = host->connections; conns; conns = conns->next) {
1643 if (!need_new_connection && soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE) {
1644 soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
1646 } else if (soup_connection_get_state (conn) == SOUP_CONNECTION_CONNECTING)
1650 /* Limit the number of pending connections; num_messages / 2
1651 * is somewhat arbitrary...
1653 if (num_pending > host->num_messages / 2)
1656 if (host->num_conns >= priv->max_conns_per_host) {
1657 if (need_new_connection)
1658 *try_cleanup = TRUE;
1662 if (priv->num_conns >= priv->max_conns) {
1663 *try_cleanup = TRUE;
1667 conn = g_object_new (
1668 SOUP_TYPE_CONNECTION,
1669 SOUP_CONNECTION_REMOTE_URI, host->uri,
1670 SOUP_CONNECTION_PROXY_RESOLVER, soup_session_get_feature (session, SOUP_TYPE_PROXY_URI_RESOLVER),
1671 SOUP_CONNECTION_SSL, uri_is_https (priv, soup_message_get_uri (item->msg)),
1672 SOUP_CONNECTION_SSL_CREDENTIALS, priv->tlsdb,
1673 SOUP_CONNECTION_SSL_STRICT, priv->ssl_strict && (priv->tlsdb != NULL || SOUP_IS_PLAIN_SESSION (session)),
1674 SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
1675 SOUP_CONNECTION_USE_THREAD_CONTEXT, priv->use_thread_context,
1676 SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
1677 SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
1678 SOUP_CONNECTION_SSL_FALLBACK, host->ssl_fallback,
1680 g_signal_connect (conn, "disconnected",
1681 G_CALLBACK (connection_disconnected),
1683 g_signal_connect (conn, "notify::state",
1684 G_CALLBACK (connection_state_changed),
1687 /* This is a debugging-related signal, and so can ignore the
1688 * usual rule about not emitting signals while holding
1691 g_signal_emit (session, signals[CONNECTION_CREATED], 0, conn);
1693 g_hash_table_insert (priv->conns, conn, host);
1697 host->connections = g_slist_prepend (host->connections, conn);
1699 if (host->keep_alive_src) {
1700 g_source_destroy (host->keep_alive_src);
1701 g_source_unref (host->keep_alive_src);
1702 host->keep_alive_src = NULL;
1709 get_connection (SoupMessageQueueItem *item, gboolean *should_cleanup)
1711 SoupSession *session = item->session;
1712 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1713 SoupSessionHost *host;
1714 SoupConnection *conn = NULL;
1715 gboolean my_should_cleanup = FALSE;
1716 gboolean need_new_connection;
1718 need_new_connection =
1719 (soup_message_get_flags (item->msg) & SOUP_MESSAGE_NEW_CONNECTION) ||
1720 (!(soup_message_get_flags (item->msg) & SOUP_MESSAGE_IDEMPOTENT) &&
1721 !SOUP_METHOD_IS_IDEMPOTENT (item->msg->method));
1723 g_mutex_lock (&priv->conn_lock);
1724 host = get_host_for_message (session, item->msg);
1726 conn = get_connection_for_host (session, item, host,
1727 need_new_connection,
1728 &my_should_cleanup);
1729 if (conn || item->async)
1732 if (my_should_cleanup) {
1733 g_mutex_unlock (&priv->conn_lock);
1734 soup_session_cleanup_connections (session, TRUE);
1735 g_mutex_lock (&priv->conn_lock);
1737 my_should_cleanup = FALSE;
1741 g_cond_wait (&priv->conn_cond, &priv->conn_lock);
1743 g_mutex_unlock (&priv->conn_lock);
1747 *should_cleanup = my_should_cleanup;
1751 soup_session_set_item_connection (session, item, conn);
1752 soup_message_set_https_status (item->msg, item->conn);
1754 if (soup_connection_get_state (item->conn) != SOUP_CONNECTION_NEW) {
1755 item->state = SOUP_MESSAGE_READY;
1759 item->state = SOUP_MESSAGE_CONNECTING;
1762 soup_message_queue_item_ref (item);
1763 soup_connection_connect_async (item->conn, item->cancellable,
1764 got_connection, item);
1769 status = soup_connection_connect_sync (item->conn, item->cancellable);
1770 got_connection (item->conn, status, item);
1777 soup_session_process_queue_item (SoupSession *session,
1778 SoupMessageQueueItem *item,
1779 gboolean *should_cleanup,
1782 g_assert (item->session == session);
1788 switch (item->state) {
1789 case SOUP_MESSAGE_STARTING:
1790 if (!get_connection (item, should_cleanup))
1794 case SOUP_MESSAGE_CONNECTED:
1795 if (soup_connection_is_tunnelled (item->conn))
1796 tunnel_connect (item);
1798 item->state = SOUP_MESSAGE_READY;
1801 case SOUP_MESSAGE_READY:
1802 soup_message_set_https_status (item->msg, item->conn);
1803 if (item->msg->status_code) {
1804 if (item->msg->status_code == SOUP_STATUS_TRY_AGAIN) {
1805 soup_message_cleanup_response (item->msg);
1806 item->state = SOUP_MESSAGE_STARTING;
1808 item->state = SOUP_MESSAGE_FINISHING;
1812 item->state = SOUP_MESSAGE_RUNNING;
1814 soup_session_send_queue_item (session, item, message_completed);
1816 if (item->new_api) {
1818 async_send_request_running (session, item);
1823 case SOUP_MESSAGE_RUNNING:
1827 g_warn_if_fail (item->new_api);
1828 item->state = SOUP_MESSAGE_FINISHING;
1831 case SOUP_MESSAGE_CACHED:
1832 /* Will be handled elsewhere */
1835 case SOUP_MESSAGE_RESTARTING:
1836 item->state = SOUP_MESSAGE_STARTING;
1837 soup_message_restarted (item->msg);
1840 case SOUP_MESSAGE_FINISHING:
1841 item->state = SOUP_MESSAGE_FINISHED;
1842 soup_message_finished (item->msg);
1843 if (item->state != SOUP_MESSAGE_FINISHED) {
1844 g_return_if_fail (!item->new_api);
1848 soup_message_queue_item_ref (item);
1849 soup_session_unqueue_item (session, item);
1850 if (item->async && item->callback)
1851 item->callback (session, item->msg, item->callback_data);
1852 soup_message_queue_item_unref (item);
1856 /* Nothing to do with this message in any
1859 g_warn_if_fail (item->async);
1862 } while (loop && item->state != SOUP_MESSAGE_FINISHED);
1866 async_run_queue (SoupSession *session)
1868 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1869 SoupMessageQueueItem *item;
1871 gboolean try_cleanup = TRUE, should_cleanup = FALSE;
1873 g_object_ref (session);
1874 soup_session_cleanup_connections (session, FALSE);
1877 for (item = soup_message_queue_first (priv->queue);
1879 item = soup_message_queue_next (priv->queue, item)) {
1882 /* CONNECT messages are handled specially */
1883 if (msg->method == SOUP_METHOD_CONNECT)
1886 if (item->async_context != soup_session_get_async_context (session))
1889 soup_session_process_queue_item (session, item, &should_cleanup, TRUE);
1892 if (try_cleanup && should_cleanup) {
1893 /* There is at least one message in the queue that
1894 * could be sent if we cleanupd an idle connection from
1895 * some other server.
1897 if (soup_session_cleanup_connections (session, TRUE)) {
1898 try_cleanup = should_cleanup = FALSE;
1903 g_object_unref (session);
1907 idle_run_queue (gpointer user_data)
1909 SoupSessionPrivate *priv = user_data;
1915 source = g_main_current_source ();
1916 priv->run_queue_sources = g_slist_remove (priv->run_queue_sources, source);
1918 /* Ensure that the source is destroyed before running the queue */
1919 g_source_destroy (source);
1920 g_source_unref (source);
1922 g_assert (priv->session);
1923 async_run_queue (priv->session);
1928 * SoupSessionCallback:
1929 * @session: the session
1930 * @msg: the message that has finished
1931 * @user_data: the data passed to soup_session_queue_message
1933 * Prototype for the callback passed to soup_session_queue_message(),
1938 soup_session_real_queue_message (SoupSession *session, SoupMessage *msg,
1939 SoupSessionCallback callback, gpointer user_data)
1941 SoupMessageQueueItem *item;
1943 item = soup_session_append_queue_item (session, msg, TRUE, FALSE,
1944 callback, user_data);
1945 soup_session_kick_queue (session);
1946 soup_message_queue_item_unref (item);
1950 * soup_session_queue_message:
1951 * @session: a #SoupSession
1952 * @msg: (transfer full): the message to queue
1953 * @callback: (allow-none) (scope async): a #SoupSessionCallback which will
1954 * be called after the message completes or when an unrecoverable error occurs.
1955 * @user_data: (allow-none): a pointer passed to @callback.
1957 * Queues the message @msg for asynchronously sending the request and
1958 * receiving a response in the current thread-default #GMainContext.
1959 * If @msg has been processed before, any resources related to the
1960 * time it was last sent are freed.
1962 * Upon message completion, the callback specified in @callback will
1963 * be invoked. If after returning from this callback the message has not
1964 * been requeued, @msg will be unreffed.
1966 * (The behavior above applies to a plain #SoupSession; if you are
1967 * using #SoupSessionAsync or #SoupSessionSync, then the #GMainContext
1968 * that is used depends on the settings of #SoupSession:async-context
1969 * and #SoupSession:use-thread-context, and for #SoupSessionSync, the
1970 * message will actually be sent and processed in another thread, with
1971 * only the final callback occurring in the indicated #GMainContext.)
1974 soup_session_queue_message (SoupSession *session, SoupMessage *msg,
1975 SoupSessionCallback callback, gpointer user_data)
1977 g_return_if_fail (SOUP_IS_SESSION (session));
1978 g_return_if_fail (SOUP_IS_MESSAGE (msg));
1980 SOUP_SESSION_GET_CLASS (session)->queue_message (session, msg,
1981 callback, user_data);
1982 /* The SoupMessageQueueItem will hold a ref on @msg until it is
1983 * finished, so we can drop the ref adopted from the caller now.
1985 g_object_unref (msg);
1989 soup_session_real_requeue_message (SoupSession *session, SoupMessage *msg)
1991 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1992 SoupMessageQueueItem *item;
1994 item = soup_message_queue_lookup (priv->queue, msg);
1995 g_return_if_fail (item != NULL);
1996 item->state = SOUP_MESSAGE_RESTARTING;
1997 soup_message_queue_item_unref (item);
2001 * soup_session_requeue_message:
2002 * @session: a #SoupSession
2003 * @msg: the message to requeue
2005 * This causes @msg to be placed back on the queue to be attempted
2009 soup_session_requeue_message (SoupSession *session, SoupMessage *msg)
2011 g_return_if_fail (SOUP_IS_SESSION (session));
2012 g_return_if_fail (SOUP_IS_MESSAGE (msg));
2014 SOUP_SESSION_GET_CLASS (session)->requeue_message (session, msg);
2018 soup_session_real_send_message (SoupSession *session, SoupMessage *msg)
2020 SoupMessageQueueItem *item;
2023 item = soup_session_append_queue_item (session, msg, FALSE, FALSE,
2025 soup_session_process_queue_item (session, item, NULL, TRUE);
2026 status = msg->status_code;
2027 soup_message_queue_item_unref (item);
2032 * soup_session_send_message:
2033 * @session: a #SoupSession
2034 * @msg: the message to send
2036 * Synchronously send @msg. This call will not return until the
2037 * transfer is finished successfully or there is an unrecoverable
2040 * Unlike with soup_session_queue_message(), @msg is not freed upon
2043 * (Note that if you call this method on a #SoupSessionAsync, it will
2044 * still use asynchronous I/O internally, running the glib main loop
2045 * to process the message, which may also cause other events to be
2048 * Return value: the HTTP status code of the response
2051 soup_session_send_message (SoupSession *session, SoupMessage *msg)
2053 g_return_val_if_fail (SOUP_IS_SESSION (session), SOUP_STATUS_MALFORMED);
2054 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_STATUS_MALFORMED);
2056 return SOUP_SESSION_GET_CLASS (session)->send_message (session, msg);
2061 * soup_session_pause_message:
2062 * @session: a #SoupSession
2063 * @msg: a #SoupMessage currently running on @session
2065 * Pauses HTTP I/O on @msg. Call soup_session_unpause_message() to
2069 soup_session_pause_message (SoupSession *session,
2072 SoupSessionPrivate *priv;
2073 SoupMessageQueueItem *item;
2075 g_return_if_fail (SOUP_IS_SESSION (session));
2076 g_return_if_fail (SOUP_IS_MESSAGE (msg));
2078 priv = SOUP_SESSION_GET_PRIVATE (session);
2079 item = soup_message_queue_lookup (priv->queue, msg);
2080 g_return_if_fail (item != NULL);
2082 item->paused = TRUE;
2083 if (item->state == SOUP_MESSAGE_RUNNING)
2084 soup_message_io_pause (msg);
2085 soup_message_queue_item_unref (item);
2089 soup_session_real_kick_queue (SoupSession *session)
2091 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2092 SoupMessageQueueItem *item;
2093 gboolean have_sync_items = FALSE;
2098 for (item = soup_message_queue_first (priv->queue);
2100 item = soup_message_queue_next (priv->queue, item)) {
2104 /* We use priv rather than session as the
2105 * source data, because other parts of libsoup
2106 * (or the calling app) may have sources using
2107 * the session as the source data.
2109 source = g_main_context_find_source_by_user_data (item->async_context, priv);
2111 source = soup_add_completion_reffed (item->async_context,
2112 idle_run_queue, priv);
2113 priv->run_queue_sources = g_slist_prepend (priv->run_queue_sources,
2117 have_sync_items = TRUE;
2120 if (have_sync_items)
2121 g_cond_broadcast (&priv->conn_cond);
2125 soup_session_kick_queue (SoupSession *session)
2127 SOUP_SESSION_GET_CLASS (session)->kick (session);
2131 * soup_session_unpause_message:
2132 * @session: a #SoupSession
2133 * @msg: a #SoupMessage currently running on @session
2135 * Resumes HTTP I/O on @msg. Use this to resume after calling
2136 * soup_session_pause_message().
2138 * If @msg is being sent via blocking I/O, this will resume reading or
2139 * writing immediately. If @msg is using non-blocking I/O, then
2140 * reading or writing won't resume until you return to the main loop.
2143 soup_session_unpause_message (SoupSession *session,
2146 SoupSessionPrivate *priv;
2147 SoupMessageQueueItem *item;
2149 g_return_if_fail (SOUP_IS_SESSION (session));
2150 g_return_if_fail (SOUP_IS_MESSAGE (msg));
2152 priv = SOUP_SESSION_GET_PRIVATE (session);
2153 item = soup_message_queue_lookup (priv->queue, msg);
2154 g_return_if_fail (item != NULL);
2156 item->paused = FALSE;
2157 if (item->state == SOUP_MESSAGE_RUNNING)
2158 soup_message_io_unpause (msg);
2159 soup_message_queue_item_unref (item);
2161 soup_session_kick_queue (session);
2166 soup_session_real_cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
2168 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2169 SoupMessageQueueItem *item;
2171 item = soup_message_queue_lookup (priv->queue, msg);
2172 g_return_if_fail (item != NULL);
2174 item->paused = FALSE;
2175 soup_message_set_status (msg, status_code);
2176 g_cancellable_cancel (item->cancellable);
2178 soup_session_kick_queue (item->session);
2179 soup_message_queue_item_unref (item);
2183 * soup_session_cancel_message:
2184 * @session: a #SoupSession
2185 * @msg: the message to cancel
2186 * @status_code: status code to set on @msg (generally
2187 * %SOUP_STATUS_CANCELLED)
2189 * Causes @session to immediately finish processing @msg (regardless
2190 * of its current state) with a final status_code of @status_code. You
2191 * may call this at any time after handing @msg off to @session; if
2192 * @session has started sending the request but has not yet received
2193 * the complete response, then it will close the request's connection.
2194 * Note that with requests that have side effects (eg,
2195 * <literal>POST</literal>, <literal>PUT</literal>,
2196 * <literal>DELETE</literal>) it is possible that you might cancel the
2197 * request after the server acts on it, but before it returns a
2198 * response, leaving the remote resource in an unknown state.
2200 * If the message is cancelled while its response body is being read,
2201 * then the response body in @msg will be left partially-filled-in.
2202 * The response headers, on the other hand, will always be either
2203 * empty or complete.
2205 * Beware that with the deprecated #SoupSessionAsync, messages queued
2206 * with soup_session_queue_message() will have their callbacks invoked
2207 * before soup_session_cancel_message() returns. The plain
2208 * #SoupSession does not have this behavior; cancelling an
2209 * asynchronous message will merely queue its callback to be run after
2210 * returning to the main loop.
2213 soup_session_cancel_message (SoupSession *session, SoupMessage *msg,
2216 SoupSessionPrivate *priv;
2217 SoupMessageQueueItem *item;
2219 g_return_if_fail (SOUP_IS_SESSION (session));
2220 g_return_if_fail (SOUP_IS_MESSAGE (msg));
2222 priv = SOUP_SESSION_GET_PRIVATE (session);
2223 item = soup_message_queue_lookup (priv->queue, msg);
2224 /* If the message is already ending, don't do anything */
2227 if (item->state == SOUP_MESSAGE_FINISHED) {
2228 soup_message_queue_item_unref (item);
2232 SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg, status_code);
2233 soup_message_queue_item_unref (item);
2237 soup_session_real_flush_queue (SoupSession *session)
2239 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2240 SoupMessageQueueItem *item;
2241 GHashTable *current = NULL;
2242 gboolean done = FALSE;
2244 if (SOUP_IS_SESSION_SYNC (session)) {
2245 /* Record the current contents of the queue */
2246 current = g_hash_table_new (NULL, NULL);
2247 for (item = soup_message_queue_first (priv->queue);
2249 item = soup_message_queue_next (priv->queue, item))
2250 g_hash_table_insert (current, item, item);
2253 /* Cancel everything */
2254 for (item = soup_message_queue_first (priv->queue);
2256 item = soup_message_queue_next (priv->queue, item)) {
2257 soup_session_cancel_message (session, item->msg,
2258 SOUP_STATUS_CANCELLED);
2261 if (SOUP_IS_SESSION_SYNC (session)) {
2262 /* Wait until all of the items in @current have been
2263 * removed from the queue. (This is not the same as
2264 * "wait for the queue to be empty", because the app
2265 * may queue new requests in response to the
2266 * cancellation of the old ones. We don't try to
2267 * cancel those requests as well, since we'd likely
2268 * just end up looping forever.)
2270 g_mutex_lock (&priv->conn_lock);
2273 for (item = soup_message_queue_first (priv->queue);
2275 item = soup_message_queue_next (priv->queue, item)) {
2276 if (g_hash_table_lookup (current, item))
2281 g_cond_wait (&priv->conn_cond, &priv->conn_lock);
2283 g_mutex_unlock (&priv->conn_lock);
2285 g_hash_table_destroy (current);
2290 * soup_session_abort:
2291 * @session: the session
2293 * Cancels all pending requests in @session and closes all idle
2294 * persistent connections.
2296 * The message cancellation has the same semantics as with
2297 * soup_session_cancel_message(); asynchronous requests on a
2298 * #SoupSessionAsync will have their callback called before
2299 * soup_session_abort() returns. Requests on a plain #SoupSession will
2303 soup_session_abort (SoupSession *session)
2305 SoupSessionPrivate *priv;
2307 GHashTableIter iter;
2308 gpointer conn, host;
2310 g_return_if_fail (SOUP_IS_SESSION (session));
2311 priv = SOUP_SESSION_GET_PRIVATE (session);
2313 SOUP_SESSION_GET_CLASS (session)->flush_queue (session);
2315 /* Close all connections */
2316 g_mutex_lock (&priv->conn_lock);
2318 g_hash_table_iter_init (&iter, priv->conns);
2319 while (g_hash_table_iter_next (&iter, &conn, &host)) {
2320 conns = g_slist_prepend (conns, g_object_ref (conn));
2321 g_hash_table_iter_remove (&iter);
2322 drop_connection (session, host, conn);
2324 g_mutex_unlock (&priv->conn_lock);
2326 for (c = conns; c; c = c->next) {
2327 soup_connection_disconnect (c->data);
2328 g_object_unref (c->data);
2331 g_slist_free (conns);
2335 prefetch_uri (SoupSession *session, SoupURI *uri,
2336 GCancellable *cancellable,
2337 SoupAddressCallback callback, gpointer user_data)
2339 SoupSessionPrivate *priv;
2340 SoupSessionHost *host;
2343 priv = SOUP_SESSION_GET_PRIVATE (session);
2345 g_mutex_lock (&priv->conn_lock);
2346 host = get_host_for_uri (session, uri);
2347 addr = g_object_ref (host->addr);
2348 g_mutex_unlock (&priv->conn_lock);
2350 soup_address_resolve_async (addr,
2351 soup_session_get_async_context (session),
2352 cancellable, callback, user_data);
2353 g_object_unref (addr);
2357 * soup_session_prepare_for_uri:
2358 * @session: a #SoupSession
2359 * @uri: a #SoupURI which may be required
2361 * Tells @session that @uri may be requested shortly, and so the
2362 * session can try to prepare (resolving the domain name, obtaining
2363 * proxy address, etc.) in order to work more quickly once the URI is
2364 * actually requested.
2368 * Deprecated: 2.38: use soup_session_prefetch_dns() instead
2371 soup_session_prepare_for_uri (SoupSession *session, SoupURI *uri)
2373 g_return_if_fail (SOUP_IS_SESSION (session));
2374 g_return_if_fail (uri != NULL);
2379 prefetch_uri (session, uri, NULL, NULL, NULL);
2383 * soup_session_prefetch_dns:
2384 * @session: a #SoupSession
2385 * @hostname: a hostname to be resolved
2386 * @cancellable: (allow-none): a #GCancellable object, or %NULL
2387 * @callback: (scope async) (allow-none): callback to call with the
2389 * @user_data: data for @callback
2391 * Tells @session that an URI from the given @hostname may be requested
2392 * shortly, and so the session can try to prepare by resolving the
2393 * domain name in advance, in order to work more quickly once the URI
2394 * is actually requested.
2396 * If @cancellable is non-%NULL, it can be used to cancel the
2397 * resolution. @callback will still be invoked in this case, with a
2398 * status of %SOUP_STATUS_CANCELLED.
2403 soup_session_prefetch_dns (SoupSession *session, const char *hostname,
2404 GCancellable *cancellable,
2405 SoupAddressCallback callback, gpointer user_data)
2409 g_return_if_fail (SOUP_IS_SESSION (session));
2410 g_return_if_fail (hostname != NULL);
2412 /* FIXME: Prefetching should work for both HTTP and HTTPS */
2413 uri = soup_uri_new (NULL);
2414 soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP);
2415 soup_uri_set_host (uri, hostname);
2416 soup_uri_set_path (uri, "");
2418 prefetch_uri (session, uri, cancellable, callback, user_data);
2419 soup_uri_free (uri);
2423 * soup_session_add_feature:
2424 * @session: a #SoupSession
2425 * @feature: an object that implements #SoupSessionFeature
2427 * Adds @feature's functionality to @session. You can also add a
2428 * feature to the session at construct time by using the
2429 * %SOUP_SESSION_ADD_FEATURE property.
2431 * Note that a #SoupProxyResolverDefault and a #SoupContentDecoder are
2432 * added to the session by default (unless you are using one of the
2433 * deprecated session subclasses).
2438 soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature)
2440 SoupSessionPrivate *priv;
2442 g_return_if_fail (SOUP_IS_SESSION (session));
2443 g_return_if_fail (SOUP_IS_SESSION_FEATURE (feature));
2445 priv = SOUP_SESSION_GET_PRIVATE (session);
2446 priv->features = g_slist_prepend (priv->features, g_object_ref (feature));
2447 g_hash_table_remove_all (priv->features_cache);
2448 soup_session_feature_attach (feature, session);
2452 * soup_session_add_feature_by_type:
2453 * @session: a #SoupSession
2454 * @feature_type: a #GType
2456 * If @feature_type is the type of a class that implements
2457 * #SoupSessionFeature, this creates a new feature of that type and
2458 * adds it to @session as with soup_session_add_feature(). You can use
2459 * this when you don't need to customize the new feature in any way.
2461 * If @feature_type is not a #SoupSessionFeature type, this gives each
2462 * existing feature on @session the chance to accept @feature_type as
2463 * a "subfeature". This can be used to add new #SoupAuth or
2464 * #SoupRequest types, for instance.
2466 * You can also add a feature to the session at construct time by
2467 * using the %SOUP_SESSION_ADD_FEATURE_BY_TYPE property.
2469 * Note that a #SoupProxyResolverDefault and a #SoupContentDecoder are
2470 * added to the session by default (unless you are using one of the
2471 * deprecated session subclasses).
2476 soup_session_add_feature_by_type (SoupSession *session, GType feature_type)
2478 SoupSessionPrivate *priv;
2480 g_return_if_fail (SOUP_IS_SESSION (session));
2482 priv = SOUP_SESSION_GET_PRIVATE (session);
2484 if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2485 SoupSessionFeature *feature;
2487 feature = g_object_new (feature_type, NULL);
2488 soup_session_add_feature (session, feature);
2489 g_object_unref (feature);
2490 } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
2491 SoupRequestClass *request_class;
2494 request_class = g_type_class_ref (feature_type);
2495 for (i = 0; request_class->schemes[i]; i++) {
2496 g_hash_table_insert (priv->request_types,
2497 (char *)request_class->schemes[i],
2498 GSIZE_TO_POINTER (feature_type));
2503 for (f = priv->features; f; f = f->next) {
2504 if (soup_session_feature_add_feature (f->data, feature_type))
2507 g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
2512 * soup_session_remove_feature:
2513 * @session: a #SoupSession
2514 * @feature: a feature that has previously been added to @session
2516 * Removes @feature's functionality from @session.
2521 soup_session_remove_feature (SoupSession *session, SoupSessionFeature *feature)
2523 SoupSessionPrivate *priv;
2525 g_return_if_fail (SOUP_IS_SESSION (session));
2527 priv = SOUP_SESSION_GET_PRIVATE (session);
2528 if (g_slist_find (priv->features, feature)) {
2529 priv->features = g_slist_remove (priv->features, feature);
2530 g_hash_table_remove_all (priv->features_cache);
2531 soup_session_feature_detach (feature, session);
2532 g_object_unref (feature);
2537 * soup_session_remove_feature_by_type:
2538 * @session: a #SoupSession
2539 * @feature_type: a #GType
2541 * Removes all features of type @feature_type (or any subclass of
2542 * @feature_type) from @session. You can also remove standard features
2543 * from the session at construct time by using the
2544 * %SOUP_SESSION_REMOVE_FEATURE_BY_TYPE property.
2549 soup_session_remove_feature_by_type (SoupSession *session, GType feature_type)
2551 SoupSessionPrivate *priv;
2554 g_return_if_fail (SOUP_IS_SESSION (session));
2556 priv = SOUP_SESSION_GET_PRIVATE (session);
2558 if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2560 for (f = priv->features; f; f = f->next) {
2561 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type)) {
2562 soup_session_remove_feature (session, f->data);
2566 } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
2567 SoupRequestClass *request_class;
2570 request_class = g_type_class_peek (feature_type);
2573 for (i = 0; request_class->schemes[i]; i++) {
2574 g_hash_table_remove (priv->request_types,
2575 request_class->schemes[i]);
2578 for (f = priv->features; f; f = f->next) {
2579 if (soup_session_feature_remove_feature (f->data, feature_type))
2582 g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
2587 * soup_session_has_feature:
2588 * @session: a #SoupSession
2589 * @feature_type: the #GType of the class of features to check for
2591 * Tests if @session has at a feature of type @feature_type (which can
2592 * be the type of either a #SoupSessionFeature, or else a subtype of
2593 * some class managed by another feature, such as #SoupAuth or
2596 * Return value: %TRUE or %FALSE
2601 soup_session_has_feature (SoupSession *session,
2604 SoupSessionPrivate *priv;
2607 g_return_val_if_fail (SOUP_IS_SESSION (session), FALSE);
2609 priv = SOUP_SESSION_GET_PRIVATE (session);
2611 if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2612 for (f = priv->features; f; f = f->next) {
2613 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
2616 } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
2617 return g_hash_table_lookup (priv->request_types,
2618 GSIZE_TO_POINTER (feature_type)) != NULL;
2620 for (f = priv->features; f; f = f->next) {
2621 if (soup_session_feature_has_feature (f->data, feature_type))
2630 * soup_session_get_features:
2631 * @session: a #SoupSession
2632 * @feature_type: the #GType of the class of features to get
2634 * Generates a list of @session's features of type @feature_type. (If
2635 * you want to see all features, you can pass %SOUP_TYPE_SESSION_FEATURE
2636 * for @feature_type.)
2638 * Return value: (transfer container) (element-type Soup.SessionFeature):
2639 * a list of features. You must free the list, but not its contents
2644 soup_session_get_features (SoupSession *session, GType feature_type)
2646 SoupSessionPrivate *priv;
2649 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
2651 priv = SOUP_SESSION_GET_PRIVATE (session);
2652 for (f = priv->features, ret = NULL; f; f = f->next) {
2653 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
2654 ret = g_slist_prepend (ret, f->data);
2656 return g_slist_reverse (ret);
2660 * soup_session_get_feature:
2661 * @session: a #SoupSession
2662 * @feature_type: the #GType of the feature to get
2664 * Gets the first feature in @session of type @feature_type. For
2665 * features where there may be more than one feature of a given type,
2666 * use soup_session_get_features().
2668 * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
2669 * feature is owned by @session.
2673 SoupSessionFeature *
2674 soup_session_get_feature (SoupSession *session, GType feature_type)
2676 SoupSessionPrivate *priv;
2677 SoupSessionFeature *feature;
2680 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
2682 priv = SOUP_SESSION_GET_PRIVATE (session);
2684 feature = g_hash_table_lookup (priv->features_cache,
2685 GSIZE_TO_POINTER (feature_type));
2689 for (f = priv->features; f; f = f->next) {
2691 if (G_TYPE_CHECK_INSTANCE_TYPE (feature, feature_type)) {
2692 g_hash_table_insert (priv->features_cache,
2693 GSIZE_TO_POINTER (feature_type),
2702 * soup_session_get_feature_for_message:
2703 * @session: a #SoupSession
2704 * @feature_type: the #GType of the feature to get
2705 * @msg: a #SoupMessage
2707 * Gets the first feature in @session of type @feature_type, provided
2708 * that it is not disabled for @msg. As with
2709 * soup_session_get_feature(), this should only be used for features
2710 * where @feature_type is only expected to match a single feature. In
2711 * particular, if there are two matching features, and the first is
2712 * disabled on @msg, and the second is not, then this will return
2713 * %NULL, not the second feature.
2715 * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
2716 * feature is owned by @session.
2720 SoupSessionFeature *
2721 soup_session_get_feature_for_message (SoupSession *session, GType feature_type,
2724 SoupSessionFeature *feature;
2726 feature = soup_session_get_feature (session, feature_type);
2727 if (feature && soup_message_disables_feature (msg, feature))
2733 soup_session_class_init (SoupSessionClass *session_class)
2735 GObjectClass *object_class = G_OBJECT_CLASS (session_class);
2737 g_type_class_add_private (session_class, sizeof (SoupSessionPrivate));
2739 /* virtual method definition */
2740 session_class->queue_message = soup_session_real_queue_message;
2741 session_class->send_message = soup_session_real_send_message;
2742 session_class->requeue_message = soup_session_real_requeue_message;
2743 session_class->cancel_message = soup_session_real_cancel_message;
2744 session_class->flush_queue = soup_session_real_flush_queue;
2745 session_class->kick = soup_session_real_kick_queue;
2747 /* virtual method override */
2748 object_class->constructor = soup_session_constructor;
2749 object_class->dispose = soup_session_dispose;
2750 object_class->finalize = soup_session_finalize;
2751 object_class->set_property = soup_session_set_property;
2752 object_class->get_property = soup_session_get_property;
2757 * SoupSession::request-queued:
2758 * @session: the session
2759 * @msg: the request that was queued
2761 * Emitted when a request is queued on @session. (Note that
2762 * "queued" doesn't just mean soup_session_queue_message();
2763 * soup_session_send_message() implicitly queues the message
2766 * When sending a request, first #SoupSession::request_queued
2767 * is emitted, indicating that the session has become aware of
2770 * Once a connection is available to send the request on, the
2771 * session emits #SoupSession::request_started. Then, various
2772 * #SoupMessage signals are emitted as the message is
2773 * processed. If the message is requeued, it will emit
2774 * #SoupMessage::restarted, which will then be followed by
2775 * another #SoupSession::request_started and another set of
2776 * #SoupMessage signals when the message is re-sent.
2778 * Eventually, the message will emit #SoupMessage::finished.
2779 * Normally, this signals the completion of message
2780 * processing. However, it is possible that the application
2781 * will requeue the message from the "finished" handler (or
2782 * equivalently, from the soup_session_queue_message()
2783 * callback). In that case, the process will loop back to
2784 * #SoupSession::request_started.
2786 * Eventually, a message will reach "finished" and not be
2787 * requeued. At that point, the session will emit
2788 * #SoupSession::request_unqueued to indicate that it is done
2791 * To sum up: #SoupSession::request_queued and
2792 * #SoupSession::request_unqueued are guaranteed to be emitted
2793 * exactly once, but #SoupSession::request_started and
2794 * #SoupMessage::finished (and all of the other #SoupMessage
2795 * signals) may be invoked multiple times for a given message.
2799 signals[REQUEST_QUEUED] =
2800 g_signal_new ("request-queued",
2801 G_OBJECT_CLASS_TYPE (object_class),
2805 _soup_marshal_NONE__OBJECT,
2810 * SoupSession::request-started:
2811 * @session: the session
2812 * @msg: the request being sent
2813 * @socket: the socket the request is being sent on
2815 * Emitted just before a request is sent. See
2816 * #SoupSession::request_queued for a detailed description of
2817 * the message lifecycle within a session.
2819 signals[REQUEST_STARTED] =
2820 g_signal_new ("request-started",
2821 G_OBJECT_CLASS_TYPE (object_class),
2823 G_STRUCT_OFFSET (SoupSessionClass, request_started),
2825 _soup_marshal_NONE__OBJECT_OBJECT,
2831 * SoupSession::request-unqueued:
2832 * @session: the session
2833 * @msg: the request that was unqueued
2835 * Emitted when a request is removed from @session's queue,
2836 * indicating that @session is done with it. See
2837 * #SoupSession::request_queued for a detailed description of the
2838 * message lifecycle within a session.
2842 signals[REQUEST_UNQUEUED] =
2843 g_signal_new ("request-unqueued",
2844 G_OBJECT_CLASS_TYPE (object_class),
2848 _soup_marshal_NONE__OBJECT,
2853 * SoupSession::authenticate:
2854 * @session: the session
2855 * @msg: the #SoupMessage being sent
2856 * @auth: the #SoupAuth to authenticate
2857 * @retrying: %TRUE if this is the second (or later) attempt
2859 * Emitted when the session requires authentication. If
2860 * credentials are available call soup_auth_authenticate() on
2861 * @auth. If these credentials fail, the signal will be
2862 * emitted again, with @retrying set to %TRUE, which will
2863 * continue until you return without calling
2864 * soup_auth_authenticate() on @auth.
2866 * Note that this may be emitted before @msg's body has been
2869 * If you call soup_session_pause_message() on @msg before
2870 * returning, then you can authenticate @auth asynchronously
2871 * (as long as you g_object_ref() it to make sure it doesn't
2872 * get destroyed), and then unpause @msg when you are ready
2873 * for it to continue.
2875 signals[AUTHENTICATE] =
2876 g_signal_new ("authenticate",
2877 G_OBJECT_CLASS_TYPE (object_class),
2879 G_STRUCT_OFFSET (SoupSessionClass, authenticate),
2881 _soup_marshal_NONE__OBJECT_OBJECT_BOOLEAN,
2888 * SoupSession::connection-created:
2889 * @session: the #SoupSession
2890 * @connection: the connection
2892 * Emitted when a new connection is created. This is an
2893 * internal signal intended only to be used for debugging
2894 * purposes, and may go away in the future.
2898 signals[CONNECTION_CREATED] =
2899 g_signal_new ("connection-created",
2900 G_OBJECT_CLASS_TYPE (object_class),
2904 _soup_marshal_NONE__OBJECT,
2906 /* SoupConnection is private, so we can't use
2907 * SOUP_TYPE_CONNECTION here.
2912 * SoupSession::tunneling:
2913 * @session: the #SoupSession
2914 * @connection: the connection
2916 * Emitted when an SSL tunnel is being created on a proxy
2917 * connection. This is an internal signal intended only to be
2918 * used for debugging purposes, and may go away in the future.
2922 signals[TUNNELING] =
2923 g_signal_new ("tunneling",
2924 G_OBJECT_CLASS_TYPE (object_class),
2928 _soup_marshal_NONE__OBJECT,
2930 /* SoupConnection is private, so we can't use
2931 * SOUP_TYPE_CONNECTION here.
2938 * SoupSession:proxy-uri:
2940 * An http proxy to use for all http and https requests in
2941 * this session. Setting this will remove any
2942 * #SoupProxyURIResolver features that have been added to the
2945 * Note that #SoupProxyResolverDefault will handle looking up
2946 * the user's proxy settings for you; you should only use
2947 * #SoupSession:proxy-uri if you need to override the user's
2948 * normal proxy settings.
2951 * SOUP_SESSION_PROXY_URI:
2953 * Alias for the #SoupSession:proxy-uri property, qv.
2955 g_object_class_install_property (
2956 object_class, PROP_PROXY_URI,
2957 g_param_spec_boxed (SOUP_SESSION_PROXY_URI,
2959 "The HTTP Proxy to use for this session",
2961 G_PARAM_READWRITE));
2963 * SOUP_SESSION_MAX_CONNS:
2965 * Alias for the #SoupSession:max-conns property, qv.
2967 g_object_class_install_property (
2968 object_class, PROP_MAX_CONNS,
2969 g_param_spec_int (SOUP_SESSION_MAX_CONNS,
2970 "Max Connection Count",
2971 "The maximum number of connections that the session can open at once",
2974 SOUP_SESSION_MAX_CONNS_DEFAULT,
2975 G_PARAM_READWRITE));
2977 * SOUP_SESSION_MAX_CONNS_PER_HOST:
2979 * Alias for the #SoupSession:max-conns-per-host property, qv.
2981 g_object_class_install_property (
2982 object_class, PROP_MAX_CONNS_PER_HOST,
2983 g_param_spec_int (SOUP_SESSION_MAX_CONNS_PER_HOST,
2984 "Max Per-Host Connection Count",
2985 "The maximum number of connections that the session can open at once to a given host",
2988 SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT,
2989 G_PARAM_READWRITE));
2991 * SoupSession:idle-timeout:
2993 * Connection lifetime (in seconds) when idle. Any connection
2994 * left idle longer than this will be closed.
2996 * Although you can change this property at any time, it will
2997 * only affect newly-created connections, not currently-open
2998 * ones. You can call soup_session_abort() after setting this
2999 * if you want to ensure that all future connections will have
3000 * this timeout value.
3002 * Note that the default value of 60 seconds only applies to
3003 * plain #SoupSessions. If you are using #SoupSessionAsync or
3004 * #SoupSessionSync, the default value is 0 (meaning idle
3005 * connections will never time out).
3010 * SOUP_SESSION_IDLE_TIMEOUT:
3012 * Alias for the #SoupSession:idle-timeout property, qv.
3016 g_object_class_install_property (
3017 object_class, PROP_IDLE_TIMEOUT,
3018 g_param_spec_uint (SOUP_SESSION_IDLE_TIMEOUT,
3020 "Connection lifetime when idle",
3022 G_PARAM_READWRITE));
3024 * SoupSession:use-ntlm:
3026 * Whether or not to use NTLM authentication.
3028 * Deprecated: use soup_session_add_feature_by_type() with
3029 * #SOUP_TYPE_AUTH_NTLM.
3032 * SOUP_SESSION_USE_NTLM:
3034 * Alias for the #SoupSession:use-ntlm property, qv.
3036 g_object_class_install_property (
3037 object_class, PROP_USE_NTLM,
3038 g_param_spec_boolean (SOUP_SESSION_USE_NTLM,
3040 "Whether or not to use NTLM authentication",
3042 G_PARAM_READWRITE | G_PARAM_DEPRECATED));
3044 * SoupSession:ssl-ca-file:
3046 * File containing SSL CA certificates.
3048 * If the specified file does not exist or cannot be read,
3049 * then libsoup will print a warning, and then behave as
3050 * though it had read in a empty CA file, meaning that all SSL
3051 * certificates will be considered invalid.
3053 * Deprecated: use #SoupSession:ssl-use-system-ca-file, or
3054 * else #SoupSession:tls-database with a #GTlsFileDatabase
3055 * (which allows you to do explicit error handling).
3058 * SOUP_SESSION_SSL_CA_FILE:
3060 * Alias for the #SoupSession:ssl-ca-file property, qv.
3062 g_object_class_install_property (
3063 object_class, PROP_SSL_CA_FILE,
3064 g_param_spec_string (SOUP_SESSION_SSL_CA_FILE,
3066 "File containing SSL CA certificates",
3068 G_PARAM_READWRITE | G_PARAM_DEPRECATED));
3070 * SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE:
3072 * Alias for the #SoupSession:ssl-use-system-ca-file property,
3078 * SoupSession:ssl-use-system-ca-file:
3080 * Setting this to %TRUE is equivalent to setting
3081 * #SoupSession:tls-database to the default system CA database.
3082 * (and likewise, setting #SoupSession:tls-database to the
3083 * default database by hand will cause this property to
3086 * Setting this to %FALSE (when it was previously %TRUE) will
3087 * clear the #SoupSession:tls-database field.
3089 * See #SoupSession:ssl-strict for more information on how
3090 * https certificate validation is handled.
3092 * Note that the default value of %TRUE only applies to plain
3093 * #SoupSessions. If you are using #SoupSessionAsync or
3094 * #SoupSessionSync, the default value is %FALSE, for backward
3099 g_object_class_install_property (
3100 object_class, PROP_SSL_USE_SYSTEM_CA_FILE,
3101 g_param_spec_boolean (SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE,
3102 "Use system CA file",
3103 "Use the system certificate database",
3105 G_PARAM_READWRITE));
3107 * SOUP_SESSION_TLS_DATABASE:
3109 * Alias for the #SoupSession:tls-database property, qv.
3114 * SoupSession:tls-database:
3116 * Sets the #GTlsDatabase to use for validating SSL/TLS
3119 * Note that setting the #SoupSession:ssl-ca-file or
3120 * #SoupSession:ssl-use-system-ca-file property will cause
3121 * this property to be set to a #GTlsDatabase corresponding to
3122 * the indicated file or system default.
3124 * See #SoupSession:ssl-strict for more information on how
3125 * https certificate validation is handled.
3127 * If you are using a plain #SoupSession then
3128 * #SoupSession:ssl-use-system-ca-file will be %TRUE by
3129 * default, and so this property will be a copy of the system
3130 * CA database. If you are using #SoupSessionAsync or
3131 * #SoupSessionSync, this property will be %NULL by default.
3135 g_object_class_install_property (
3136 object_class, PROP_TLS_DATABASE,
3137 g_param_spec_object (SOUP_SESSION_TLS_DATABASE,
3139 "TLS database to use",
3140 G_TYPE_TLS_DATABASE,
3141 G_PARAM_READWRITE));
3143 * SOUP_SESSION_SSL_STRICT:
3145 * Alias for the #SoupSession:ssl-strict property, qv.
3150 * SoupSession:ssl-strict:
3152 * Normally, if #SoupSession:tls-database is set (including if
3153 * it was set via #SoupSession:ssl-use-system-ca-file or
3154 * #SoupSession:ssl-ca-file), then libsoup will reject any
3155 * certificate that is invalid (ie, expired) or that is not
3156 * signed by one of the given CA certificates, and the
3157 * #SoupMessage will fail with the status
3158 * %SOUP_STATUS_SSL_FAILED.
3160 * If you set #SoupSession:ssl-strict to %FALSE, then all
3161 * certificates will be accepted, and you will need to call
3162 * soup_message_get_https_status() to distinguish valid from
3163 * invalid certificates. (This can be used, eg, if you want to
3164 * accept invalid certificates after giving some sort of
3167 * For a plain #SoupSession, if the session has no CA file or
3168 * TLS database, and this property is %TRUE, then all
3169 * certificates will be rejected. However, beware that the
3170 * deprecated #SoupSession subclasses (#SoupSessionAsync and
3171 * #SoupSessionSync) have the opposite behavior: if there is
3172 * no CA file or TLS database, then all certificates are always
3173 * accepted, and this property has no effect.
3177 g_object_class_install_property (
3178 object_class, PROP_SSL_STRICT,
3179 g_param_spec_boolean (SOUP_SESSION_SSL_STRICT,
3180 "Strictly validate SSL certificates",
3181 "Whether certificate errors should be considered a connection error",
3183 G_PARAM_READWRITE));
3185 * SoupSession:async-context:
3187 * The #GMainContext that miscellaneous session-related
3188 * asynchronous callbacks are invoked on. (Eg, setting
3189 * #SoupSession:idle-timeout will add a timeout source on this
3192 * For a plain #SoupSession, this property is always set to
3193 * the #GMainContext that is the thread-default at the time
3194 * the session was created, and cannot be overridden. For the
3195 * deprecated #SoupSession subclasses, the default value is
3196 * %NULL, meaning to use the global default #GMainContext.
3198 * If #SoupSession:use-thread-context is %FALSE, this context
3199 * will also be used for asynchronous HTTP I/O.
3202 * SOUP_SESSION_ASYNC_CONTEXT:
3204 * Alias for the #SoupSession:async-context property, qv.
3206 g_object_class_install_property (
3207 object_class, PROP_ASYNC_CONTEXT,
3208 g_param_spec_pointer (SOUP_SESSION_ASYNC_CONTEXT,
3209 "Async GMainContext",
3210 "The GMainContext to dispatch async I/O in",
3211 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
3213 * SOUP_SESSION_USE_THREAD_CONTEXT:
3215 * Alias for the #SoupSession:use-thread-context property, qv.
3220 * SoupSession:use-thread-context:
3222 * If %TRUE (which it always is on a plain #SoupSession),
3223 * asynchronous HTTP requests in this session will run in
3224 * whatever the thread-default #GMainContext is at the time
3225 * they are started, rather than always occurring in
3226 * #SoupSession:async-context.
3230 g_object_class_install_property (
3231 object_class, PROP_USE_THREAD_CONTEXT,
3232 g_param_spec_boolean (SOUP_SESSION_USE_THREAD_CONTEXT,
3233 "Use thread-default GMainContext",
3234 "Whether to use thread-default main contexts",
3236 G_PARAM_READWRITE));
3238 * SoupSession:timeout:
3240 * The timeout (in seconds) for socket I/O operations
3241 * (including connecting to a server, and waiting for a reply
3242 * to an HTTP request).
3244 * Although you can change this property at any time, it will
3245 * only affect newly-created connections, not currently-open
3246 * ones. You can call soup_session_abort() after setting this
3247 * if you want to ensure that all future connections will have
3248 * this timeout value.
3250 * Note that the default value of 60 seconds only applies to
3251 * plain #SoupSessions. If you are using #SoupSessionAsync or
3252 * #SoupSessionSync, the default value is 0 (meaning socket I/O
3253 * will not time out).
3255 * Not to be confused with #SoupSession:idle-timeout (which is
3256 * the length of time that idle persistent connections will be
3260 * SOUP_SESSION_TIMEOUT:
3262 * Alias for the #SoupSession:timeout property, qv.
3264 g_object_class_install_property (
3265 object_class, PROP_TIMEOUT,
3266 g_param_spec_uint (SOUP_SESSION_TIMEOUT,
3268 "Value in seconds to timeout a blocking I/O",
3270 G_PARAM_READWRITE));
3273 * SoupSession:user-agent:
3275 * If non-%NULL, the value to use for the "User-Agent" header
3276 * on #SoupMessage<!-- -->s sent from this session.
3278 * RFC 2616 says: "The User-Agent request-header field
3279 * contains information about the user agent originating the
3280 * request. This is for statistical purposes, the tracing of
3281 * protocol violations, and automated recognition of user
3282 * agents for the sake of tailoring responses to avoid
3283 * particular user agent limitations. User agents SHOULD
3284 * include this field with requests."
3286 * The User-Agent header contains a list of one or more
3287 * product tokens, separated by whitespace, with the most
3288 * significant product token coming first. The tokens must be
3289 * brief, ASCII, and mostly alphanumeric (although "-", "_",
3290 * and "." are also allowed), and may optionally include a "/"
3291 * followed by a version string. You may also put comments,
3292 * enclosed in parentheses, between or after the tokens.
3294 * If you set a #SoupSession:user_agent property that has trailing
3295 * whitespace, #SoupSession will append its own product token
3296 * (eg, "<literal>libsoup/2.3.2</literal>") to the end of the
3300 * SOUP_SESSION_USER_AGENT:
3302 * Alias for the #SoupSession:user-agent property, qv.
3304 g_object_class_install_property (
3305 object_class, PROP_USER_AGENT,
3306 g_param_spec_string (SOUP_SESSION_USER_AGENT,
3307 "User-Agent string",
3308 "User-Agent string",
3310 G_PARAM_READWRITE));
3313 * SoupSession:accept-language:
3315 * If non-%NULL, the value to use for the "Accept-Language" header
3316 * on #SoupMessage<!-- -->s sent from this session.
3318 * Setting this will disable
3319 * #SoupSession:accept-language-auto.
3324 * SOUP_SESSION_ACCEPT_LANGUAGE:
3326 * Alias for the #SoupSession:accept-language property, qv.
3330 g_object_class_install_property (
3331 object_class, PROP_ACCEPT_LANGUAGE,
3332 g_param_spec_string (SOUP_SESSION_ACCEPT_LANGUAGE,
3333 "Accept-Language string",
3334 "Accept-Language string",
3336 G_PARAM_READWRITE));
3339 * SoupSession:accept-language-auto:
3341 * If %TRUE, #SoupSession will automatically set the string
3342 * for the "Accept-Language" header on every #SoupMessage
3343 * sent, based on the return value of g_get_language_names().
3345 * Setting this will override any previous value of
3346 * #SoupSession:accept-language.
3351 * SOUP_SESSION_ACCEPT_LANGUAGE_AUTO:
3353 * Alias for the #SoupSession:accept-language-auto property, qv.
3357 g_object_class_install_property (
3358 object_class, PROP_ACCEPT_LANGUAGE_AUTO,
3359 g_param_spec_boolean (SOUP_SESSION_ACCEPT_LANGUAGE_AUTO,
3360 "Accept-Language automatic mode",
3361 "Accept-Language automatic mode",
3363 G_PARAM_READWRITE));
3366 * SoupSession:add-feature: (skip)
3368 * Add a feature object to the session. (Shortcut for calling
3369 * soup_session_add_feature().)
3374 * SOUP_SESSION_ADD_FEATURE: (skip)
3376 * Alias for the #SoupSession:add-feature property, qv.
3380 g_object_class_install_property (
3381 object_class, PROP_ADD_FEATURE,
3382 g_param_spec_object (SOUP_SESSION_ADD_FEATURE,
3384 "Add a feature object to the session",
3385 SOUP_TYPE_SESSION_FEATURE,
3386 G_PARAM_READWRITE));
3388 * SoupSession:add-feature-by-type: (skip)
3390 * Add a feature object of the given type to the session.
3391 * (Shortcut for calling soup_session_add_feature_by_type().)
3396 * SOUP_SESSION_ADD_FEATURE_BY_TYPE: (skip)
3398 * Alias for the #SoupSession:add-feature-by-type property, qv.
3402 g_object_class_install_property (
3403 object_class, PROP_ADD_FEATURE_BY_TYPE,
3404 g_param_spec_gtype (SOUP_SESSION_ADD_FEATURE_BY_TYPE,
3405 "Add Feature By Type",
3406 "Add a feature object of the given type to the session",
3407 SOUP_TYPE_SESSION_FEATURE,
3408 G_PARAM_READWRITE));
3410 * SoupSession:remove-feature-by-type: (skip)
3412 * Remove feature objects from the session. (Shortcut for
3413 * calling soup_session_remove_feature_by_type().)
3418 * SOUP_SESSION_REMOVE_FEATURE_BY_TYPE: (skip)
3420 * Alias for the #SoupSession:remove-feature-by-type property,
3425 g_object_class_install_property (
3426 object_class, PROP_REMOVE_FEATURE_BY_TYPE,
3427 g_param_spec_gtype (SOUP_SESSION_REMOVE_FEATURE_BY_TYPE,
3428 "Remove Feature By Type",
3429 "Remove features of the given type from the session",
3430 SOUP_TYPE_SESSION_FEATURE,
3431 G_PARAM_READWRITE));
3433 * SoupSession:http-aliases:
3435 * A %NULL-terminated array of URI schemes that should be
3436 * considered to be aliases for "http". Eg, if this included
3437 * <literal>"dav"</literal>, than a URI of
3438 * <literal>dav://example.com/path</literal> would be treated
3439 * identically to <literal>http://example.com/path</literal>.
3441 * In a plain #SoupSession, the default value is %NULL,
3442 * meaning that only "http" is recognized as meaning "http".
3443 * In #SoupSessionAsync and #SoupSessionSync, for backward
3444 * compatibility, the default value is an array containing the
3445 * single element <literal>"*"</literal>, a special value
3446 * which means that any scheme except "https" is considered to
3447 * be an alias for "http".
3449 * See also #SoupSession:https-aliases.
3454 * SOUP_SESSION_HTTP_ALIASES:
3456 * Alias for the #SoupSession:http-aliases property, qv.
3460 g_object_class_install_property (
3461 object_class, PROP_HTTP_ALIASES,
3462 g_param_spec_boxed (SOUP_SESSION_HTTP_ALIASES,
3464 "URI schemes that are considered aliases for 'http'",
3466 G_PARAM_READWRITE));
3468 * SoupSession:https-aliases:
3470 * A comma-delimited list of URI schemes that should be
3471 * considered to be aliases for "https". See
3472 * #SoupSession:http-aliases for more information.
3474 * The default value is %NULL, meaning that no URI schemes
3475 * are considered aliases for "https".
3480 * SOUP_SESSION_HTTPS_ALIASES:
3482 * Alias for the #SoupSession:https-aliases property, qv.
3486 g_object_class_install_property (
3487 object_class, PROP_HTTPS_ALIASES,
3488 g_param_spec_boxed (SOUP_SESSION_HTTPS_ALIASES,
3490 "URI schemes that are considered aliases for 'https'",
3492 G_PARAM_READWRITE));
3496 /* send_request_async */
3499 async_send_request_return_result (SoupMessageQueueItem *item,
3500 gpointer stream, GError *error)
3504 g_return_if_fail (item->task != NULL);
3506 g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
3507 0, 0, NULL, NULL, item);
3512 if (item->io_source) {
3513 g_source_destroy (item->io_source);
3514 g_clear_pointer (&item->io_source, g_source_unref);
3518 g_task_return_error (task, error);
3519 else if (SOUP_STATUS_IS_TRANSPORT_ERROR (item->msg->status_code)) {
3521 g_object_unref (stream);
3522 g_task_return_new_error (task, SOUP_HTTP_ERROR,
3523 item->msg->status_code,
3525 item->msg->reason_phrase);
3527 g_task_return_pointer (task, stream, g_object_unref);
3528 g_object_unref (task);
3532 async_send_request_restarted (SoupMessage *msg, gpointer user_data)
3534 SoupMessageQueueItem *item = user_data;
3536 /* We won't be needing this, then. */
3537 g_object_set_data (G_OBJECT (item->msg), "SoupSession:ostream", NULL);
3538 item->io_started = FALSE;
3542 async_send_request_finished (SoupMessage *msg, gpointer user_data)
3544 SoupMessageQueueItem *item = user_data;
3545 GMemoryOutputStream *mostream;
3546 GInputStream *istream = NULL;
3547 GError *error = NULL;
3550 /* Something else already took care of it. */
3554 mostream = g_object_get_data (G_OBJECT (item->task), "SoupSession:ostream");
3559 /* We thought it would be requeued, but it wasn't, so
3560 * return the original body.
3562 size = g_memory_output_stream_get_data_size (mostream);
3563 data = size ? g_memory_output_stream_steal_data (mostream) : g_strdup ("");
3564 istream = g_memory_input_stream_new_from_data (data, size, g_free);
3565 } else if (item->io_started) {
3566 /* The message finished before becoming readable. This
3567 * will happen, eg, if it's cancelled from got-headers.
3568 * Do nothing; the op will complete via read_ready_cb()
3573 /* The message finished before even being started;
3574 * probably a tunnel connect failure.
3576 istream = g_memory_input_stream_new ();
3579 async_send_request_return_result (item, istream, error);
3583 send_async_spliced (GObject *source, GAsyncResult *result, gpointer user_data)
3585 SoupMessageQueueItem *item = user_data;
3586 GInputStream *istream = g_object_get_data (source, "istream");
3587 GError *error = NULL;
3589 /* It should be safe to call the sync close() method here since
3590 * the message body has already been written.
3592 g_input_stream_close (istream, NULL, NULL);
3593 g_object_unref (istream);
3595 /* If the message was cancelled, it will be completed via other means */
3596 if (g_cancellable_is_cancelled (item->cancellable) ||
3598 soup_message_queue_item_unref (item);
3602 if (g_output_stream_splice_finish (G_OUTPUT_STREAM (source),
3603 result, &error) == -1) {
3604 async_send_request_return_result (item, NULL, error);
3605 soup_message_queue_item_unref (item);
3609 /* Otherwise either restarted or finished will eventually be called. */
3610 soup_session_kick_queue (item->session);
3611 soup_message_queue_item_unref (item);
3615 send_async_maybe_complete (SoupMessageQueueItem *item,
3616 GInputStream *stream)
3618 if (item->msg->status_code == SOUP_STATUS_UNAUTHORIZED ||
3619 item->msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED ||
3620 soup_session_would_redirect (item->session, item->msg)) {
3621 GOutputStream *ostream;
3623 /* Message may be requeued, so gather the current message body... */
3624 ostream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
3625 g_object_set_data_full (G_OBJECT (item->task), "SoupSession:ostream",
3626 ostream, g_object_unref);
3628 g_object_set_data (G_OBJECT (ostream), "istream", stream);
3630 /* Give the splice op its own ref on item */
3631 soup_message_queue_item_ref (item);
3632 g_output_stream_splice_async (ostream, stream,
3633 /* We can't use CLOSE_SOURCE because it
3634 * might get closed in the wrong thread.
3636 G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
3639 send_async_spliced, item);
3643 async_send_request_return_result (item, stream, NULL);
3646 static void try_run_until_read (SoupMessageQueueItem *item);
3649 read_ready_cb (SoupMessage *msg, gpointer user_data)
3651 SoupMessageQueueItem *item = user_data;
3653 g_clear_pointer (&item->io_source, g_source_unref);
3654 try_run_until_read (item);
3659 try_run_until_read (SoupMessageQueueItem *item)
3661 GError *error = NULL;
3662 GInputStream *stream = NULL;
3664 if (soup_message_io_run_until_read (item->msg, item->cancellable, &error))
3665 stream = soup_message_io_get_response_istream (item->msg, &error);
3667 send_async_maybe_complete (item, stream);
3671 if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) {
3672 item->state = SOUP_MESSAGE_RESTARTING;
3673 soup_message_io_finished (item->msg);
3674 g_error_free (error);
3678 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
3679 if (item->state != SOUP_MESSAGE_FINISHED) {
3680 if (soup_message_io_in_progress (item->msg))
3681 soup_message_io_finished (item->msg);
3682 item->state = SOUP_MESSAGE_FINISHING;
3683 soup_session_process_queue_item (item->session, item, NULL, FALSE);
3685 async_send_request_return_result (item, NULL, error);
3689 g_clear_error (&error);
3690 item->io_source = soup_message_io_get_source (item->msg, item->cancellable,
3691 read_ready_cb, item);
3692 g_source_attach (item->io_source, soup_session_get_async_context (item->session));
3696 async_send_request_running (SoupSession *session, SoupMessageQueueItem *item)
3698 item->io_started = TRUE;
3699 try_run_until_read (item);
3703 async_return_from_cache (SoupMessageQueueItem *item,
3704 GInputStream *stream)
3706 const char *content_type;
3709 soup_message_got_headers (item->msg);
3711 content_type = soup_message_headers_get_content_type (item->msg->response_headers, ¶ms);
3712 soup_message_content_sniffed (item->msg, content_type, params);
3713 g_hash_table_unref (params);
3715 item->state = SOUP_MESSAGE_FINISHING;
3716 async_send_request_return_result (item, g_object_ref (stream), NULL);
3720 conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
3722 SoupMessageQueueItem *item = user_data;
3723 GInputStream *stream;
3725 if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) {
3726 SoupCache *cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
3728 stream = soup_cache_send_response (cache, item->msg);
3730 async_return_from_cache (item, stream);
3731 g_object_unref (stream);
3736 /* The resource was modified or the server returned a 200
3737 * OK. Either way we reload it. FIXME.
3739 item->state = SOUP_MESSAGE_STARTING;
3740 soup_session_kick_queue (session);
3744 SoupMessageQueueItem *item;
3745 GInputStream *stream;
3746 } SendAsyncCacheData;
3749 free_send_async_cache_data (SendAsyncCacheData *sacd)
3751 soup_message_queue_item_unref (sacd->item);
3752 g_object_unref (sacd->stream);
3753 g_slice_free (SendAsyncCacheData, sacd);
3757 idle_return_from_cache_cb (gpointer data)
3760 SendAsyncCacheData *sacd = g_task_get_task_data (task);
3762 async_return_from_cache (sacd->item, sacd->stream);
3767 async_respond_from_cache (SoupSession *session,
3768 SoupMessageQueueItem *item)
3771 SoupCacheResponse response;
3773 cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
3777 response = soup_cache_has_response (cache, item->msg);
3778 if (response == SOUP_CACHE_RESPONSE_FRESH) {
3779 GInputStream *stream;
3780 SendAsyncCacheData *sacd;
3783 stream = soup_cache_send_response (cache, item->msg);
3785 /* Cached file was deleted? */
3789 sacd = g_slice_new (SendAsyncCacheData);
3791 soup_message_queue_item_ref (item);
3792 sacd->stream = stream;
3794 g_task_set_task_data (item->task, sacd,
3795 (GDestroyNotify) free_send_async_cache_data);
3797 source = g_timeout_source_new (0);
3798 g_task_attach_source (item->task, source,
3799 (GSourceFunc) idle_return_from_cache_cb);
3800 g_source_unref (source);
3802 } else if (response == SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) {
3803 SoupMessage *conditional_msg;
3805 conditional_msg = soup_cache_generate_conditional_request (cache, item->msg);
3806 if (!conditional_msg)
3809 soup_session_queue_message (session, conditional_msg,
3810 conditional_get_ready_cb,
3818 soup_session_send_request_async (SoupSession *session,
3820 GCancellable *cancellable,
3821 GAsyncReadyCallback callback,
3824 SoupMessageQueueItem *item;
3825 gboolean use_thread_context;
3827 g_return_if_fail (SOUP_IS_SESSION (session));
3828 g_return_if_fail (!SOUP_IS_SESSION_SYNC (session));
3830 g_object_get (G_OBJECT (session),
3831 SOUP_SESSION_USE_THREAD_CONTEXT, &use_thread_context,
3833 g_return_if_fail (use_thread_context);
3835 item = soup_session_append_queue_item (session, msg, TRUE, TRUE,
3837 g_signal_connect (msg, "restarted",
3838 G_CALLBACK (async_send_request_restarted), item);
3839 g_signal_connect (msg, "finished",
3840 G_CALLBACK (async_send_request_finished), item);
3842 item->new_api = TRUE;
3843 item->task = g_task_new (session, cancellable, callback, user_data);
3844 g_task_set_task_data (item->task, item, (GDestroyNotify) soup_message_queue_item_unref);
3847 g_object_unref (item->cancellable);
3848 item->cancellable = g_object_ref (cancellable);
3851 if (async_respond_from_cache (session, item))
3852 item->state = SOUP_MESSAGE_CACHED;
3854 soup_session_kick_queue (session);
3858 soup_session_send_request_finish (SoupSession *session,
3859 GAsyncResult *result,
3864 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
3865 g_return_val_if_fail (!SOUP_IS_SESSION_SYNC (session), NULL);
3866 g_return_val_if_fail (g_task_is_valid (result, session), NULL);
3868 task = G_TASK (result);
3869 if (g_task_had_error (task)) {
3870 SoupMessageQueueItem *item = g_task_get_task_data (task);
3872 if (soup_message_io_in_progress (item->msg))
3873 soup_message_io_finished (item->msg);
3874 else if (item->state != SOUP_MESSAGE_FINISHED)
3875 item->state = SOUP_MESSAGE_FINISHING;
3877 if (item->state != SOUP_MESSAGE_FINISHED)
3878 soup_session_process_queue_item (session, item, NULL, FALSE);
3881 return g_task_propagate_pointer (task, error);
3885 soup_session_send_request (SoupSession *session,
3887 GCancellable *cancellable,
3890 SoupMessageQueueItem *item;
3891 GInputStream *stream = NULL;
3892 GOutputStream *ostream;
3893 GMemoryOutputStream *mostream;
3895 GError *my_error = NULL;
3897 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
3898 g_return_val_if_fail (!SOUP_IS_SESSION_ASYNC (session), NULL);
3900 item = soup_session_append_queue_item (session, msg, FALSE, TRUE,
3903 item->new_api = TRUE;
3905 g_object_unref (item->cancellable);
3906 item->cancellable = g_object_ref (cancellable);
3910 /* Get a connection, etc */
3911 soup_session_process_queue_item (session, item, NULL, TRUE);
3912 if (item->state != SOUP_MESSAGE_RUNNING)
3915 /* Send request, read headers */
3916 if (!soup_message_io_run_until_read (msg, item->cancellable, &my_error)) {
3917 if (g_error_matches (my_error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) {
3918 item->state = SOUP_MESSAGE_RESTARTING;
3919 soup_message_io_finished (item->msg);
3920 g_clear_error (&my_error);
3926 stream = soup_message_io_get_response_istream (msg, &my_error);
3930 /* Break if the message doesn't look likely-to-be-requeued */
3931 if (msg->status_code != SOUP_STATUS_UNAUTHORIZED &&
3932 msg->status_code != SOUP_STATUS_PROXY_UNAUTHORIZED &&
3933 !soup_session_would_redirect (session, msg))
3936 /* Gather the current message body... */
3937 ostream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
3938 if (g_output_stream_splice (ostream, stream,
3939 G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
3940 G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
3941 item->cancellable, &my_error) == -1) {
3942 g_object_unref (stream);
3943 g_object_unref (ostream);
3947 g_object_unref (stream);
3950 /* If the message was requeued, loop */
3951 if (item->state == SOUP_MESSAGE_RESTARTING) {
3952 g_object_unref (ostream);
3956 /* Not requeued, so return the original body */
3957 mostream = G_MEMORY_OUTPUT_STREAM (ostream);
3958 size = g_memory_output_stream_get_data_size (mostream);
3959 stream = g_memory_input_stream_new ();
3961 g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (stream),
3962 g_memory_output_stream_steal_data (mostream),
3965 g_object_unref (ostream);
3969 g_propagate_error (error, my_error);
3970 else if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
3972 g_object_unref (stream);
3975 g_set_error_literal (error, SOUP_HTTP_ERROR, msg->status_code,
3976 msg->reason_phrase);
3978 stream = g_memory_input_stream_new ();
3981 if (soup_message_io_in_progress (msg))
3982 soup_message_io_finished (msg);
3983 else if (item->state != SOUP_MESSAGE_FINISHED)
3984 item->state = SOUP_MESSAGE_FINISHING;
3986 if (item->state != SOUP_MESSAGE_FINISHED)
3987 soup_session_process_queue_item (session, item, NULL, TRUE);
3990 soup_message_queue_item_unref (item);
3995 * soup_session_request:
3996 * @session: a #SoupSession
3997 * @uri_string: a URI, in string form
3998 * @error: return location for a #GError, or %NULL
4000 * Creates a #SoupRequest for retrieving @uri_string.
4002 * Return value: (transfer full): a new #SoupRequest, or
4008 soup_session_request (SoupSession *session, const char *uri_string,
4014 uri = soup_uri_new (uri_string);
4016 g_set_error (error, SOUP_REQUEST_ERROR,
4017 SOUP_REQUEST_ERROR_BAD_URI,
4018 _("Could not parse URI '%s'"), uri_string);
4022 req = soup_session_request_uri (session, uri, error);
4023 soup_uri_free (uri);
4028 * soup_session_request_uri:
4029 * @session: a #SoupSession
4030 * @uri: a #SoupURI representing the URI to retrieve
4031 * @error: return location for a #GError, or %NULL
4033 * Creates a #SoupRequest for retrieving @uri.
4035 * Return value: (transfer full): a new #SoupRequest, or
4041 soup_session_request_uri (SoupSession *session, SoupURI *uri,
4044 SoupSessionPrivate *priv;
4047 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
4049 priv = SOUP_SESSION_GET_PRIVATE (session);
4051 request_type = (GType)GPOINTER_TO_SIZE (g_hash_table_lookup (priv->request_types, uri->scheme));
4052 if (!request_type) {
4053 g_set_error (error, SOUP_REQUEST_ERROR,
4054 SOUP_REQUEST_ERROR_UNSUPPORTED_URI_SCHEME,
4055 _("Unsupported URI scheme '%s'"), uri->scheme);
4059 return g_initable_new (request_type, NULL, error,
4065 static SoupRequestHTTP *
4066 initialize_http_request (SoupRequest *req,
4070 SoupRequestHTTP *http;
4073 if (!SOUP_IS_REQUEST_HTTP (req)) {
4074 g_object_unref (req);
4075 g_set_error (error, SOUP_REQUEST_ERROR,
4076 SOUP_REQUEST_ERROR_BAD_URI,
4077 _("Not an HTTP URI"));
4081 http = SOUP_REQUEST_HTTP (req);
4082 msg = soup_request_http_get_message (http);
4083 g_object_set (G_OBJECT (msg),
4084 SOUP_MESSAGE_METHOD, method,
4086 g_object_unref (msg);
4092 * soup_session_request_http:
4093 * @session: a #SoupSession
4094 * @method: an HTTP method
4095 * @uri_string: a URI, in string form
4096 * @error: return location for a #GError, or %NULL
4098 * Creates a #SoupRequest for retrieving @uri_string, which must be an
4099 * "http" or "https" URI (or another protocol listed in @session's
4100 * #SoupSession:http-aliases or #SoupSession:https-aliases).
4102 * Return value: (transfer full): a new #SoupRequestHTTP, or
4108 soup_session_request_http (SoupSession *session,
4110 const char *uri_string,
4115 req = soup_session_request (session, uri_string, error);
4119 return initialize_http_request (req, method, error);
4123 * soup_session_request_http_uri:
4124 * @session: a #SoupSession
4125 * @method: an HTTP method
4126 * @uri: a #SoupURI representing the URI to retrieve
4127 * @error: return location for a #GError, or %NULL
4129 * Creates a #SoupRequest for retrieving @uri, which must be an
4130 * "http" or "https" URI (or another protocol listed in @session's
4131 * #SoupSession:http-aliases or #SoupSession:https-aliases).
4133 * Return value: (transfer full): a new #SoupRequestHTTP, or
4139 soup_session_request_http_uri (SoupSession *session,
4146 req = soup_session_request_uri (session, uri, error);
4150 return initialize_http_request (req, method, error);
4154 * SOUP_REQUEST_ERROR:
4156 * A #GError domain for #SoupRequest<!-- -->-related errors. Used with
4157 * #SoupRequestError.
4163 * @SOUP_REQUEST_ERROR_BAD_URI: the URI could not be parsed
4164 * @SOUP_REQUEST_ERROR_UNSUPPORTED_URI_SCHEME: the URI scheme is not
4165 * supported by this #SoupSession
4166 * @SOUP_REQUEST_ERROR_PARSING: the server's response could not
4168 * @SOUP_REQUEST_ERROR_ENCODING: the server's response was in an
4169 * unsupported format
4171 * A #SoupRequest error.
4177 soup_request_error_quark (void)
4179 static GQuark error;
4181 error = g_quark_from_static_string ("soup_request_error_quark");