1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
5 * Copyright (C) 2000-2003, Ximian, Inc.
16 #include <glib/gi18n-lib.h>
18 #include "soup-auth.h"
19 #include "soup-auth-basic.h"
20 #include "soup-auth-digest.h"
21 #include "soup-auth-manager-ntlm.h"
22 #include "soup-connection.h"
23 #include "soup-marshal.h"
24 #include "soup-message-private.h"
25 #include "soup-message-queue.h"
26 #include "soup-misc.h"
27 #include "soup-proxy-resolver-static.h"
28 #include "soup-proxy-uri-resolver.h"
29 #include "soup-session.h"
30 #include "soup-session-feature.h"
31 #include "soup-session-private.h"
32 #include "soup-socket.h"
35 #define HOST_KEEP_ALIVE 5 * 60 * 1000 /* 5 min in msecs */
38 * SECTION:soup-session
39 * @short_description: Soup session state object
41 * #SoupSession is the object that controls client-side HTTP. A
42 * #SoupSession encapsulates all of the state that libsoup is keeping
43 * on behalf of your program; cached HTTP connections, authentication
46 * Most applications will only need a single #SoupSession; the primary
47 * reason you might need multiple sessions is if you need to have
48 * multiple independent authentication contexts. (Eg, you are
49 * connecting to a server and authenticating as two different users at
50 * different times; the easiest way to ensure that each #SoupMessage
51 * is sent with the authentication information you intended is to use
52 * one session for the first user, and a second session for the other
55 * #SoupSession itself is an abstract class, with two subclasses. If
56 * you are using the glib main loop, you will generally want to use
57 * #SoupSessionAsync, which uses non-blocking I/O and callbacks. On
58 * the other hand, if your application is threaded and you want to do
59 * synchronous I/O in a separate thread from the UI, use
66 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
67 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
68 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
76 GSList *connections; /* CONTAINS: SoupConnection */
81 gboolean ssl_fallback;
83 GSource *keep_alive_src;
86 static guint soup_host_uri_hash (gconstpointer key);
87 gboolean soup_host_uri_equal (gconstpointer v1, gconstpointer v2);
94 SoupMessageQueue *queue;
97 char *accept_language;
98 gboolean accept_language_auto;
101 GHashTable *features_cache;
103 GHashTable *http_hosts, *https_hosts; /* char* -> SoupSessionHost */
104 GHashTable *conns; /* SoupConnection -> SoupSessionHost */
106 guint max_conns, max_conns_per_host;
107 guint io_timeout, idle_timeout;
109 /* Must hold the host_lock before potentially creating a
110 * new SoupSessionHost, or adding/removing a connection.
111 * Must not emit signals or destroy objects while holding it.
115 GMainContext *async_context;
116 gboolean use_thread_context;
120 char **http_aliases, **https_aliases;
121 } SoupSessionPrivate;
122 #define SOUP_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION, SoupSessionPrivate))
124 static void free_host (SoupSessionHost *host);
126 static void queue_message (SoupSession *session, SoupMessage *msg,
127 SoupSessionCallback callback, gpointer user_data);
128 static void requeue_message (SoupSession *session, SoupMessage *msg);
129 static void cancel_message (SoupSession *session, SoupMessage *msg,
131 static void auth_required (SoupSession *session, SoupMessage *msg,
132 SoupAuth *auth, gboolean retrying);
133 static void flush_queue (SoupSession *session);
135 static void auth_manager_authenticate (SoupAuthManager *manager,
136 SoupMessage *msg, SoupAuth *auth,
137 gboolean retrying, gpointer user_data);
139 #define SOUP_SESSION_MAX_CONNS_DEFAULT 10
140 #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2
142 #define SOUP_SESSION_MAX_REDIRECTION_COUNT 20
144 #define SOUP_SESSION_USER_AGENT_BASE "libsoup/" PACKAGE_VERSION
146 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (SoupSession, soup_session, G_TYPE_OBJECT,
160 static guint signals[LAST_SIGNAL] = { 0 };
167 PROP_MAX_CONNS_PER_HOST,
170 PROP_SSL_USE_SYSTEM_CA_FILE,
174 PROP_USE_THREAD_CONTEXT,
177 PROP_ACCEPT_LANGUAGE,
178 PROP_ACCEPT_LANGUAGE_AUTO,
181 PROP_ADD_FEATURE_BY_TYPE,
182 PROP_REMOVE_FEATURE_BY_TYPE,
189 static void set_property (GObject *object, guint prop_id,
190 const GValue *value, GParamSpec *pspec);
191 static void get_property (GObject *object, guint prop_id,
192 GValue *value, GParamSpec *pspec);
195 soup_session_init (SoupSession *session)
197 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
198 SoupAuthManager *auth_manager;
200 priv->queue = soup_message_queue_new (session);
202 g_mutex_init (&priv->host_lock);
203 priv->http_hosts = g_hash_table_new_full (soup_host_uri_hash,
205 NULL, (GDestroyNotify)free_host);
206 priv->https_hosts = g_hash_table_new_full (soup_host_uri_hash,
208 NULL, (GDestroyNotify)free_host);
209 priv->conns = g_hash_table_new (NULL, NULL);
211 priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
212 priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
214 priv->features_cache = g_hash_table_new (NULL, NULL);
216 auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER_NTLM, NULL);
217 g_signal_connect (auth_manager, "authenticate",
218 G_CALLBACK (auth_manager_authenticate), session);
219 soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
220 SOUP_TYPE_AUTH_BASIC);
221 soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
222 SOUP_TYPE_AUTH_DIGEST);
223 soup_session_add_feature (session, SOUP_SESSION_FEATURE (auth_manager));
224 g_object_unref (auth_manager);
226 /* We'll be doing DNS continuously-ish while the session is active,
227 * so hold a ref on the default GResolver.
229 priv->resolver = g_resolver_get_default ();
231 priv->ssl_strict = TRUE;
233 priv->http_aliases = g_new (char *, 2);
234 priv->http_aliases[0] = (char *)g_intern_string ("*");
235 priv->http_aliases[1] = NULL;
239 dispose (GObject *object)
241 SoupSession *session = SOUP_SESSION (object);
242 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
244 soup_session_abort (session);
246 while (priv->features)
247 soup_session_remove_feature (session, priv->features->data);
249 G_OBJECT_CLASS (soup_session_parent_class)->dispose (object);
253 finalize (GObject *object)
255 SoupSession *session = SOUP_SESSION (object);
256 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
258 soup_message_queue_destroy (priv->queue);
260 g_mutex_clear (&priv->host_lock);
261 g_hash_table_destroy (priv->http_hosts);
262 g_hash_table_destroy (priv->https_hosts);
263 g_hash_table_destroy (priv->conns);
265 g_free (priv->user_agent);
266 g_free (priv->accept_language);
269 g_object_unref (priv->tlsdb);
270 g_free (priv->ssl_ca_file);
272 if (priv->async_context)
273 g_main_context_unref (priv->async_context);
275 g_hash_table_destroy (priv->features_cache);
277 g_object_unref (priv->resolver);
279 g_free (priv->http_aliases);
280 g_free (priv->https_aliases);
282 G_OBJECT_CLASS (soup_session_parent_class)->finalize (object);
286 soup_session_class_init (SoupSessionClass *session_class)
288 GObjectClass *object_class = G_OBJECT_CLASS (session_class);
290 g_type_class_add_private (session_class, sizeof (SoupSessionPrivate));
292 /* virtual method definition */
293 session_class->queue_message = queue_message;
294 session_class->requeue_message = requeue_message;
295 session_class->cancel_message = cancel_message;
296 session_class->auth_required = auth_required;
297 session_class->flush_queue = flush_queue;
299 /* virtual method override */
300 object_class->dispose = dispose;
301 object_class->finalize = finalize;
302 object_class->set_property = set_property;
303 object_class->get_property = get_property;
308 * SoupSession::request-queued:
309 * @session: the session
310 * @msg: the request that was queued
312 * Emitted when a request is queued on @session. (Note that
313 * "queued" doesn't just mean soup_session_queue_message();
314 * soup_session_send_message() implicitly queues the message
317 * When sending a request, first #SoupSession::request_queued
318 * is emitted, indicating that the session has become aware of
321 * Once a connection is available to send the request on, the
322 * session emits #SoupSession::request_started. Then, various
323 * #SoupMessage signals are emitted as the message is
324 * processed. If the message is requeued, it will emit
325 * #SoupMessage::restarted, which will then be followed by
326 * another #SoupSession::request_started and another set of
327 * #SoupMessage signals when the message is re-sent.
329 * Eventually, the message will emit #SoupMessage::finished.
330 * Normally, this signals the completion of message
331 * processing. However, it is possible that the application
332 * will requeue the message from the "finished" handler (or
333 * equivalently, from the soup_session_queue_message()
334 * callback). In that case, the process will loop back to
335 * #SoupSession::request_started.
337 * Eventually, a message will reach "finished" and not be
338 * requeued. At that point, the session will emit
339 * #SoupSession::request_unqueued to indicate that it is done
342 * To sum up: #SoupSession::request_queued and
343 * #SoupSession::request_unqueued are guaranteed to be emitted
344 * exactly once, but #SoupSession::request_started and
345 * #SoupMessage::finished (and all of the other #SoupMessage
346 * signals) may be invoked multiple times for a given message.
350 signals[REQUEST_QUEUED] =
351 g_signal_new ("request-queued",
352 G_OBJECT_CLASS_TYPE (object_class),
356 _soup_marshal_NONE__OBJECT,
361 * SoupSession::request-started:
362 * @session: the session
363 * @msg: the request being sent
364 * @socket: the socket the request is being sent on
366 * Emitted just before a request is sent. See
367 * #SoupSession::request_queued for a detailed description of
368 * the message lifecycle within a session.
370 signals[REQUEST_STARTED] =
371 g_signal_new ("request-started",
372 G_OBJECT_CLASS_TYPE (object_class),
374 G_STRUCT_OFFSET (SoupSessionClass, request_started),
376 _soup_marshal_NONE__OBJECT_OBJECT,
382 * SoupSession::request-unqueued:
383 * @session: the session
384 * @msg: the request that was unqueued
386 * Emitted when a request is removed from @session's queue,
387 * indicating that @session is done with it. See
388 * #SoupSession::request_queued for a detailed description of the
389 * message lifecycle within a session.
393 signals[REQUEST_UNQUEUED] =
394 g_signal_new ("request-unqueued",
395 G_OBJECT_CLASS_TYPE (object_class),
399 _soup_marshal_NONE__OBJECT,
404 * SoupSession::authenticate:
405 * @session: the session
406 * @msg: the #SoupMessage being sent
407 * @auth: the #SoupAuth to authenticate
408 * @retrying: %TRUE if this is the second (or later) attempt
410 * Emitted when the session requires authentication. If
411 * credentials are available call soup_auth_authenticate() on
412 * @auth. If these credentials fail, the signal will be
413 * emitted again, with @retrying set to %TRUE, which will
414 * continue until you return without calling
415 * soup_auth_authenticate() on @auth.
417 * Note that this may be emitted before @msg's body has been
420 * If you call soup_session_pause_message() on @msg before
421 * returning, then you can authenticate @auth asynchronously
422 * (as long as you g_object_ref() it to make sure it doesn't
423 * get destroyed), and then unpause @msg when you are ready
424 * for it to continue.
426 signals[AUTHENTICATE] =
427 g_signal_new ("authenticate",
428 G_OBJECT_CLASS_TYPE (object_class),
430 G_STRUCT_OFFSET (SoupSessionClass, authenticate),
432 _soup_marshal_NONE__OBJECT_OBJECT_BOOLEAN,
439 * SoupSession::connection-created:
440 * @session: the #SoupSession
441 * @connection: the connection
443 * Emitted when a new connection is created. This is an
444 * internal signal intended only to be used for debugging
445 * purposes, and may go away in the future.
449 signals[CONNECTION_CREATED] =
450 g_signal_new ("connection-created",
451 G_OBJECT_CLASS_TYPE (object_class),
455 _soup_marshal_NONE__OBJECT,
457 /* SoupConnection is private, so we can't use
458 * SOUP_TYPE_CONNECTION here.
463 * SoupSession::tunneling:
464 * @session: the #SoupSession
465 * @connection: the connection
467 * Emitted when an SSL tunnel is being created on a proxy
468 * connection. This is an internal signal intended only to be
469 * used for debugging purposes, and may go away in the future.
474 g_signal_new ("tunneling",
475 G_OBJECT_CLASS_TYPE (object_class),
479 _soup_marshal_NONE__OBJECT,
481 /* SoupConnection is private, so we can't use
482 * SOUP_TYPE_CONNECTION here.
489 * SOUP_SESSION_PROXY_URI:
491 * Alias for the #SoupSession:proxy-uri property. (The HTTP
492 * proxy to use for this session.)
494 g_object_class_install_property (
495 object_class, PROP_PROXY_URI,
496 g_param_spec_boxed (SOUP_SESSION_PROXY_URI,
498 "The HTTP Proxy to use for this session",
502 * SOUP_SESSION_MAX_CONNS:
504 * Alias for the #SoupSession:max-conns property. (The maximum
505 * number of connections that the session can open at once.)
507 g_object_class_install_property (
508 object_class, PROP_MAX_CONNS,
509 g_param_spec_int (SOUP_SESSION_MAX_CONNS,
510 "Max Connection Count",
511 "The maximum number of connections that the session can open at once",
514 SOUP_SESSION_MAX_CONNS_DEFAULT,
517 * SOUP_SESSION_MAX_CONNS_PER_HOST:
519 * Alias for the #SoupSession:max-conns-per-host property.
520 * (The maximum number of connections that the session can
521 * open at once to a given host.)
523 g_object_class_install_property (
524 object_class, PROP_MAX_CONNS_PER_HOST,
525 g_param_spec_int (SOUP_SESSION_MAX_CONNS_PER_HOST,
526 "Max Per-Host Connection Count",
527 "The maximum number of connections that the session can open at once to a given host",
530 SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT,
533 * SoupSession:idle-timeout:
535 * Connection lifetime when idle
540 * SOUP_SESSION_IDLE_TIMEOUT:
542 * Alias for the #SoupSession:idle-timeout property. (The idle
543 * connection lifetime.)
547 g_object_class_install_property (
548 object_class, PROP_IDLE_TIMEOUT,
549 g_param_spec_uint (SOUP_SESSION_IDLE_TIMEOUT,
551 "Connection lifetime when idle",
555 * SoupSession:use-ntlm:
557 * Whether or not to use NTLM authentication.
559 * Deprecated: use soup_session_add_feature_by_type() with
560 * #SOUP_TYPE_AUTH_NTLM.
563 * SOUP_SESSION_USE_NTLM:
565 * Alias for the #SoupSession:use-ntlm property. (Whether or
566 * not to use NTLM authentication.)
568 g_object_class_install_property (
569 object_class, PROP_USE_NTLM,
570 g_param_spec_boolean (SOUP_SESSION_USE_NTLM,
572 "Whether or not to use NTLM authentication",
576 * SoupSession:ssl-ca-file:
578 * File containing SSL CA certificates.
580 * Deprecated: use #SoupSession:ssl-use-system-ca-file or
581 * #SoupSession:tls-database instead
584 * SOUP_SESSION_SSL_CA_FILE:
586 * Alias for the #SoupSession:ssl-ca-file property. (File
587 * containing SSL CA certificates.).
589 * Deprecated: use #SoupSession:ssl-use-system-ca-file or
590 * #SoupSession:tls-database instead
592 g_object_class_install_property (
593 object_class, PROP_SSL_CA_FILE,
594 g_param_spec_string (SOUP_SESSION_SSL_CA_FILE,
596 "File containing SSL CA certificates",
600 * SOUP_SESSION_USE_SYSTEM_CA_FILE:
602 * Alias for the #SoupSession:ssl-use-system-ca-file property,
608 * SoupSession:ssl-use-system-ca-file:
610 * Setting this to %TRUE is equivalent to setting
611 * #SoupSession:tls-database to the default system CA database.
612 * (and likewise, setting #SoupSession:tls-database to the
613 * default database by hand will cause this property to
616 * Setting this to %FALSE (when it was previously %TRUE) will
617 * clear the #SoupSession:tls-database field.
619 * See #SoupSession:ssl-strict for more information on how
620 * https certificate validation is handled.
624 g_object_class_install_property (
625 object_class, PROP_SSL_USE_SYSTEM_CA_FILE,
626 g_param_spec_boolean (SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE,
627 "Use system CA file",
628 "Use the system certificate database",
632 * SOUP_SESSION_TLS_DATABASE:
634 * Alias for the #SoupSession:tls-database property, qv.
639 * SoupSession:tls-database:
641 * Sets the #GTlsDatabase to use for validating SSL/TLS
644 * Note that setting the #SoupSession:ssl-ca-file or
645 * #SoupSession:ssl-use-system-ca-file property will cause
646 * this property to be set to a #GTlsDatabase corresponding to
647 * the indicated file or system default.
649 * See #SoupSession:ssl-strict for more information on how
650 * https certificate validation is handled.
654 g_object_class_install_property (
655 object_class, PROP_TLS_DATABASE,
656 g_param_spec_object (SOUP_SESSION_TLS_DATABASE,
658 "TLS database to use",
662 * SOUP_SESSION_SSL_STRICT:
664 * Alias for the #SoupSession:ssl-strict property, qv.
669 * SoupSession:ssl-strict:
671 * Normally, if #SoupSession:tlsdb is set (including if it was
672 * set via #SoupSession:ssl-use-system-ca-file or
673 * #SoupSession:ssl-ca-file), then libsoup will reject any
674 * certificate that is invalid (ie, expired) or that is not
675 * signed by one of the given CA certificates, and the
676 * #SoupMessage will fail with the status
677 * %SOUP_STATUS_SSL_FAILED.
679 * If you set #SoupSession:ssl-strict to %FALSE, then all
680 * certificates will be accepted, and you will need to call
681 * soup_message_get_https_status() to distinguish valid from
682 * invalid certificates. (This can be used, eg, if you want to
683 * accept invalid certificates after giving some sort of
686 * If the session has no CA file or TLS database, then all
687 * certificates are always accepted, and this property has no
692 g_object_class_install_property (
693 object_class, PROP_SSL_STRICT,
694 g_param_spec_boolean (SOUP_SESSION_SSL_STRICT,
695 "Strictly validate SSL certificates",
696 "Whether certificate errors should be considered a connection error",
700 * SOUP_SESSION_ASYNC_CONTEXT:
702 * Alias for the #SoupSession:async-context property. (The
703 * session's #GMainContext.)
705 g_object_class_install_property (
706 object_class, PROP_ASYNC_CONTEXT,
707 g_param_spec_pointer (SOUP_SESSION_ASYNC_CONTEXT,
708 "Async GMainContext",
709 "The GMainContext to dispatch async I/O in",
710 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
712 * SOUP_SESSION_USE_THREAD_CONTEXT:
714 * Alias for the #SoupSession:use-thread-context property, qv.
719 * SoupSession:use-thread-context:
721 * If set, asynchronous operations in this session will run in
722 * whatever the thread-default #GMainContext is at the time
723 * they are started, rather than always occurring in a context
724 * fixed at the session's construction time. "Bookkeeping"
725 * tasks (like expiring idle connections) will happen in the
726 * context that was thread-default at the time the session was
731 g_object_class_install_property (
732 object_class, PROP_USE_THREAD_CONTEXT,
733 g_param_spec_boolean (SOUP_SESSION_USE_THREAD_CONTEXT,
734 "Use thread-default GMainContext",
735 "Whether to use thread-default main contexts",
739 * SOUP_SESSION_TIMEOUT:
741 * Alias for the #SoupSession:timeout property. (The timeout
742 * in seconds for blocking socket I/O operations.)
744 g_object_class_install_property (
745 object_class, PROP_TIMEOUT,
746 g_param_spec_uint (SOUP_SESSION_TIMEOUT,
748 "Value in seconds to timeout a blocking I/O",
753 * SoupSession:user-agent:
755 * If non-%NULL, the value to use for the "User-Agent" header
756 * on #SoupMessage<!-- -->s sent from this session.
758 * RFC 2616 says: "The User-Agent request-header field
759 * contains information about the user agent originating the
760 * request. This is for statistical purposes, the tracing of
761 * protocol violations, and automated recognition of user
762 * agents for the sake of tailoring responses to avoid
763 * particular user agent limitations. User agents SHOULD
764 * include this field with requests."
766 * The User-Agent header contains a list of one or more
767 * product tokens, separated by whitespace, with the most
768 * significant product token coming first. The tokens must be
769 * brief, ASCII, and mostly alphanumeric (although "-", "_",
770 * and "." are also allowed), and may optionally include a "/"
771 * followed by a version string. You may also put comments,
772 * enclosed in parentheses, between or after the tokens.
774 * If you set a #SoupSession:user_agent property that has trailing
775 * whitespace, #SoupSession will append its own product token
776 * (eg, "<literal>libsoup/2.3.2</literal>") to the end of the
780 * SOUP_SESSION_USER_AGENT:
782 * Alias for the #SoupSession:user-agent property, qv.
784 g_object_class_install_property (
785 object_class, PROP_USER_AGENT,
786 g_param_spec_string (SOUP_SESSION_USER_AGENT,
793 * SoupSession:accept-language:
795 * If non-%NULL, the value to use for the "Accept-Language" header
796 * on #SoupMessage<!-- -->s sent from this session.
798 * Setting this will disable
799 * #SoupSession:accept-language-auto.
804 * SOUP_SESSION_ACCEPT_LANGUAGE:
806 * Alias for the #SoupSession:accept-language property, qv.
810 g_object_class_install_property (
811 object_class, PROP_ACCEPT_LANGUAGE,
812 g_param_spec_string (SOUP_SESSION_ACCEPT_LANGUAGE,
813 "Accept-Language string",
814 "Accept-Language string",
819 * SoupSession:accept-language-auto:
821 * If %TRUE, #SoupSession will automatically set the string
822 * for the "Accept-Language" header on every #SoupMessage
823 * sent, based on the return value of g_get_language_names().
825 * Setting this will override any previous value of
826 * #SoupSession:accept-language.
831 * SOUP_SESSION_ACCEPT_LANGUAGE_AUTO:
833 * Alias for the #SoupSession:accept-language-auto property, qv.
837 g_object_class_install_property (
838 object_class, PROP_ACCEPT_LANGUAGE_AUTO,
839 g_param_spec_boolean (SOUP_SESSION_ACCEPT_LANGUAGE_AUTO,
840 "Accept-Language automatic mode",
841 "Accept-Language automatic mode",
846 * SoupSession:add-feature: (skip)
848 * Add a feature object to the session. (Shortcut for calling
849 * soup_session_add_feature().)
854 * SOUP_SESSION_ADD_FEATURE: (skip)
856 * Alias for the #SoupSession:add-feature property. (Shortcut
857 * for calling soup_session_add_feature().
861 g_object_class_install_property (
862 object_class, PROP_ADD_FEATURE,
863 g_param_spec_object (SOUP_SESSION_ADD_FEATURE,
865 "Add a feature object to the session",
866 SOUP_TYPE_SESSION_FEATURE,
869 * SoupSession:add-feature-by-type: (skip)
871 * Add a feature object of the given type to the session.
872 * (Shortcut for calling soup_session_add_feature_by_type().)
877 * SOUP_SESSION_ADD_FEATURE_BY_TYPE: (skip)
879 * Alias for the #SoupSession:add-feature-by-type property.
880 * (Shortcut for calling soup_session_add_feature_by_type().
884 g_object_class_install_property (
885 object_class, PROP_ADD_FEATURE_BY_TYPE,
886 g_param_spec_gtype (SOUP_SESSION_ADD_FEATURE_BY_TYPE,
887 "Add Feature By Type",
888 "Add a feature object of the given type to the session",
889 SOUP_TYPE_SESSION_FEATURE,
892 * SoupSession:remove-feature-by-type: (skip)
894 * Remove feature objects from the session. (Shortcut for
895 * calling soup_session_remove_feature_by_type().)
900 * SOUP_SESSION_REMOVE_FEATURE_BY_TYPE: (skip)
902 * Alias for the #SoupSession:remove-feature-by-type
903 * property. (Shortcut for calling
904 * soup_session_remove_feature_by_type().
908 g_object_class_install_property (
909 object_class, PROP_REMOVE_FEATURE_BY_TYPE,
910 g_param_spec_gtype (SOUP_SESSION_REMOVE_FEATURE_BY_TYPE,
911 "Remove Feature By Type",
912 "Remove features of the given type from the session",
913 SOUP_TYPE_SESSION_FEATURE,
916 * SoupSession:http-aliases:
918 * A %NULL-terminated array of URI schemes that should be
919 * considered to be aliases for "http". Eg, if this included
920 * <literal>"dav"</literal>, than a URI of
921 * <literal>dav://example.com/path</literal> would be treated
922 * identically to <literal>http://example.com/path</literal>.
923 * If the value is %NULL, then only "http" is recognized as
926 * For backward-compatibility reasons, the default value for
927 * this property is an array containing the single element
928 * <literal>"*"</literal>, a special value which means that
929 * any scheme except "https" is considered to be an alias for
932 * See also #SoupSession:https-aliases.
937 * SOUP_SESSION_HTTP_ALIASES:
939 * Alias for the #SoupSession:http-aliases property. (URI
940 * schemes that will be considered aliases for "http".)
944 g_object_class_install_property (
945 object_class, PROP_HTTP_ALIASES,
946 g_param_spec_boxed (SOUP_SESSION_HTTP_ALIASES,
948 "URI schemes that are considered aliases for 'http'",
952 * SoupSession:https-aliases:
954 * A comma-delimited list of URI schemes that should be
955 * considered to be aliases for "https". See
956 * #SoupSession:http-aliases for more information.
958 * The default value is %NULL, meaning that no URI schemes
959 * are considered aliases for "https".
964 * SOUP_SESSION_HTTPS_ALIASES:
966 * Alias for the #SoupSession:https-aliases property. (URI
967 * schemes that will be considered aliases for "https".)
971 g_object_class_install_property (
972 object_class, PROP_HTTPS_ALIASES,
973 g_param_spec_boxed (SOUP_SESSION_HTTPS_ALIASES,
975 "URI schemes that are considered aliases for 'https'",
980 /* Converts a language in POSIX format and to be RFC2616 compliant */
981 /* Based on code from epiphany-webkit (ephy_langs_append_languages()) */
983 posix_lang_to_rfc2616 (const gchar *language)
985 /* Don't include charset variants, etc */
986 if (strchr (language, '.') || strchr (language, '@'))
989 /* Ignore "C" locale, which g_get_language_names() always
990 * includes as a fallback.
992 if (!strcmp (language, "C"))
995 return g_strdelimit (g_ascii_strdown (language, -1), "_", '-');
998 /* Converts @quality from 0-100 to 0.0-1.0 and appends to @str */
1000 add_quality_value (const gchar *str, int quality)
1002 g_return_val_if_fail (str != NULL, NULL);
1004 if (quality >= 0 && quality < 100) {
1005 /* We don't use %.02g because of "." vs "," locale issues */
1007 return g_strdup_printf ("%s;q=0.%02d", str, quality);
1009 return g_strdup_printf ("%s;q=0.%d", str, quality / 10);
1011 return g_strdup (str);
1014 /* Returns a RFC2616 compliant languages list from system locales */
1016 accept_languages_from_system (void)
1018 const char * const * lang_names;
1019 GPtrArray *langs = NULL;
1020 char *lang, **langs_array, *langs_str;
1024 lang_names = g_get_language_names ();
1025 g_return_val_if_fail (lang_names != NULL, NULL);
1027 /* Build the array of languages */
1028 langs = g_ptr_array_new ();
1029 for (i = 0; lang_names[i] != NULL; i++) {
1030 lang = posix_lang_to_rfc2616 (lang_names[i]);
1032 g_ptr_array_add (langs, lang);
1035 /* Add quality values */
1036 if (langs->len < 10)
1038 else if (langs->len < 20)
1043 for (i = 0; i < langs->len; i++) {
1044 lang = langs->pdata[i];
1045 langs->pdata[i] = add_quality_value (lang, 100 - i * delta);
1049 /* Fallback: add "en" if list is empty */
1050 if (langs->len == 0)
1051 g_ptr_array_add (langs, g_strdup ("en"));
1053 g_ptr_array_add (langs, NULL);
1054 langs_array = (char **)langs->pdata;
1055 langs_str = g_strjoinv (", ", langs_array);
1057 g_strfreev (langs_array);
1058 g_ptr_array_free (langs, FALSE);
1064 set_tlsdb (SoupSession *session, GTlsDatabase *tlsdb)
1066 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1067 GTlsDatabase *system_default;
1069 if (tlsdb == priv->tlsdb)
1072 g_object_freeze_notify (G_OBJECT (session));
1074 system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
1075 if (priv->tlsdb == system_default || tlsdb == system_default) {
1076 g_object_notify (G_OBJECT (session), "ssl-use-system-ca-file");
1078 g_object_unref (system_default);
1080 if (priv->ssl_ca_file) {
1081 g_free (priv->ssl_ca_file);
1082 priv->ssl_ca_file = NULL;
1083 g_object_notify (G_OBJECT (session), "ssl-ca-file");
1087 g_object_unref (priv->tlsdb);
1088 priv->tlsdb = tlsdb;
1090 g_object_ref (priv->tlsdb);
1092 g_object_notify (G_OBJECT (session), "tls-database");
1093 g_object_thaw_notify (G_OBJECT (session));
1097 set_use_system_ca_file (SoupSession *session, gboolean use_system_ca_file)
1099 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1100 GTlsDatabase *system_default;
1102 system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
1104 if (use_system_ca_file)
1105 set_tlsdb (session, system_default);
1106 else if (priv->tlsdb == system_default)
1107 set_tlsdb (session, NULL);
1109 g_object_unref (system_default);
1113 set_ssl_ca_file (SoupSession *session, const char *ssl_ca_file)
1115 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1116 GTlsDatabase *tlsdb;
1117 GError *error = NULL;
1119 if (!g_strcmp0 (priv->ssl_ca_file, ssl_ca_file))
1122 g_object_freeze_notify (G_OBJECT (session));
1124 if (g_path_is_absolute (ssl_ca_file))
1125 tlsdb = g_tls_file_database_new (ssl_ca_file, &error);
1129 cwd = g_get_current_dir ();
1130 path = g_build_filename (cwd, ssl_ca_file, NULL);
1131 tlsdb = g_tls_file_database_new (path, &error);
1136 if (!g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_UNAVAILABLE)) {
1137 g_warning ("Could not set SSL credentials from '%s': %s",
1138 ssl_ca_file, error->message);
1140 tlsdb = g_tls_file_database_new ("/dev/null", NULL);
1142 g_error_free (error);
1145 set_tlsdb (session, tlsdb);
1146 g_object_unref (tlsdb);
1148 priv->ssl_ca_file = g_strdup (ssl_ca_file);
1149 g_object_notify (G_OBJECT (session), "ssl-ca-file");
1151 g_object_thaw_notify (G_OBJECT (session));
1154 /* priv->http_aliases and priv->https_aliases are stored as arrays of
1155 * *interned* strings, so we can't just use g_strdupv() to set them.
1158 set_aliases (char ***variable, char **value)
1170 len = g_strv_length (value);
1171 *variable = g_new (char *, len + 1);
1172 for (i = 0; i < len; i++)
1173 (*variable)[i] = (char *)g_intern_string (value[i]);
1174 (*variable)[i] = NULL;
1178 set_property (GObject *object, guint prop_id,
1179 const GValue *value, GParamSpec *pspec)
1181 SoupSession *session = SOUP_SESSION (object);
1182 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1184 const char *user_agent;
1185 SoupSessionFeature *feature;
1188 case PROP_PROXY_URI:
1189 uri = g_value_get_boxed (value);
1192 soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER);
1193 feature = SOUP_SESSION_FEATURE (soup_proxy_resolver_static_new (uri));
1194 soup_session_add_feature (session, feature);
1195 g_object_unref (feature);
1197 soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
1199 soup_session_abort (session);
1201 case PROP_MAX_CONNS:
1202 priv->max_conns = g_value_get_int (value);
1204 case PROP_MAX_CONNS_PER_HOST:
1205 priv->max_conns_per_host = g_value_get_int (value);
1208 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
1210 if (g_value_get_boolean (value))
1211 soup_session_feature_add_feature (feature, SOUP_TYPE_AUTH_NTLM);
1213 soup_session_feature_remove_feature (feature, SOUP_TYPE_AUTH_NTLM);
1215 g_warning ("Trying to set use-ntlm on session with no auth-manager");
1217 case PROP_SSL_CA_FILE:
1218 set_ssl_ca_file (session, g_value_get_string (value));
1220 case PROP_SSL_USE_SYSTEM_CA_FILE:
1221 set_use_system_ca_file (session, g_value_get_boolean (value));
1223 case PROP_TLS_DATABASE:
1224 set_tlsdb (session, g_value_get_object (value));
1226 case PROP_SSL_STRICT:
1227 priv->ssl_strict = g_value_get_boolean (value);
1229 case PROP_ASYNC_CONTEXT:
1230 priv->async_context = g_value_get_pointer (value);
1231 if (priv->async_context)
1232 g_main_context_ref (priv->async_context);
1234 case PROP_USE_THREAD_CONTEXT:
1235 priv->use_thread_context = g_value_get_boolean (value);
1236 if (priv->use_thread_context) {
1237 if (priv->async_context)
1238 g_main_context_unref (priv->async_context);
1239 priv->async_context = g_main_context_get_thread_default ();
1240 if (priv->async_context)
1241 g_main_context_ref (priv->async_context);
1245 priv->io_timeout = g_value_get_uint (value);
1247 case PROP_USER_AGENT:
1248 g_free (priv->user_agent);
1249 user_agent = g_value_get_string (value);
1251 priv->user_agent = NULL;
1252 else if (!*user_agent) {
1254 g_strdup (SOUP_SESSION_USER_AGENT_BASE);
1255 } else if (g_str_has_suffix (user_agent, " ")) {
1257 g_strdup_printf ("%s%s", user_agent,
1258 SOUP_SESSION_USER_AGENT_BASE);
1260 priv->user_agent = g_strdup (user_agent);
1262 case PROP_ACCEPT_LANGUAGE:
1263 g_free (priv->accept_language);
1264 priv->accept_language = g_strdup (g_value_get_string (value));
1265 priv->accept_language_auto = FALSE;
1267 case PROP_ACCEPT_LANGUAGE_AUTO:
1268 priv->accept_language_auto = g_value_get_boolean (value);
1269 if (priv->accept_language) {
1270 g_free (priv->accept_language);
1271 priv->accept_language = NULL;
1274 /* Get languages from system if needed */
1275 if (priv->accept_language_auto)
1276 priv->accept_language = accept_languages_from_system ();
1278 case PROP_IDLE_TIMEOUT:
1279 priv->idle_timeout = g_value_get_uint (value);
1281 case PROP_ADD_FEATURE:
1282 soup_session_add_feature (session, g_value_get_object (value));
1284 case PROP_ADD_FEATURE_BY_TYPE:
1285 soup_session_add_feature_by_type (session, g_value_get_gtype (value));
1287 case PROP_REMOVE_FEATURE_BY_TYPE:
1288 soup_session_remove_feature_by_type (session, g_value_get_gtype (value));
1290 case PROP_HTTP_ALIASES:
1291 set_aliases (&priv->http_aliases, g_value_get_boxed (value));
1293 case PROP_HTTPS_ALIASES:
1294 set_aliases (&priv->https_aliases, g_value_get_boxed (value));
1297 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1303 get_property (GObject *object, guint prop_id,
1304 GValue *value, GParamSpec *pspec)
1306 SoupSession *session = SOUP_SESSION (object);
1307 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1308 SoupSessionFeature *feature;
1309 GTlsDatabase *tlsdb;
1312 case PROP_PROXY_URI:
1313 feature = soup_session_get_feature (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
1315 g_object_get_property (G_OBJECT (feature),
1316 SOUP_PROXY_RESOLVER_STATIC_PROXY_URI,
1319 g_value_set_boxed (value, NULL);
1321 case PROP_MAX_CONNS:
1322 g_value_set_int (value, priv->max_conns);
1324 case PROP_MAX_CONNS_PER_HOST:
1325 g_value_set_int (value, priv->max_conns_per_host);
1328 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
1330 g_value_set_boolean (value, soup_session_feature_has_feature (feature, SOUP_TYPE_AUTH_NTLM));
1332 g_value_set_boolean (value, FALSE);
1334 case PROP_SSL_CA_FILE:
1335 g_value_set_string (value, priv->ssl_ca_file);
1337 case PROP_SSL_USE_SYSTEM_CA_FILE:
1338 tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
1339 g_value_set_boolean (value, priv->tlsdb == tlsdb);
1340 g_object_unref (tlsdb);
1342 case PROP_TLS_DATABASE:
1343 g_value_set_object (value, priv->tlsdb);
1345 case PROP_SSL_STRICT:
1346 g_value_set_boolean (value, priv->ssl_strict);
1348 case PROP_ASYNC_CONTEXT:
1349 g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
1351 case PROP_USE_THREAD_CONTEXT:
1352 g_value_set_boolean (value, priv->use_thread_context);
1355 g_value_set_uint (value, priv->io_timeout);
1357 case PROP_USER_AGENT:
1358 g_value_set_string (value, priv->user_agent);
1360 case PROP_ACCEPT_LANGUAGE:
1361 g_value_set_string (value, priv->accept_language);
1363 case PROP_ACCEPT_LANGUAGE_AUTO:
1364 g_value_set_boolean (value, priv->accept_language_auto);
1366 case PROP_IDLE_TIMEOUT:
1367 g_value_set_uint (value, priv->idle_timeout);
1369 case PROP_HTTP_ALIASES:
1370 g_value_set_boxed (value, priv->http_aliases);
1372 case PROP_HTTPS_ALIASES:
1373 g_value_set_boxed (value, priv->https_aliases);
1376 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1382 uri_is_http (SoupSessionPrivate *priv, SoupURI *uri)
1386 if (uri->scheme == SOUP_URI_SCHEME_HTTP)
1388 else if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
1390 else if (!priv->http_aliases)
1393 for (i = 0; priv->http_aliases[i]; i++) {
1394 if (uri->scheme == priv->http_aliases[i])
1398 if (!priv->http_aliases[1] && !strcmp (priv->http_aliases[0], "*"))
1405 uri_is_https (SoupSessionPrivate *priv, SoupURI *uri)
1409 if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
1411 else if (uri->scheme == SOUP_URI_SCHEME_HTTP)
1413 else if (!priv->https_aliases)
1416 for (i = 0; priv->https_aliases[i]; i++) {
1417 if (uri->scheme == priv->https_aliases[i])
1425 * soup_session_get_async_context:
1426 * @session: a #SoupSession
1428 * Gets @session's async_context. This does not add a ref to the
1429 * context, so you will need to ref it yourself if you want it to
1430 * outlive its session.
1432 * If #SoupSession:use-thread-context is true, this will return the
1433 * current thread-default main context.
1435 * Return value: (transfer none): @session's #GMainContext, which may
1439 soup_session_get_async_context (SoupSession *session)
1441 SoupSessionPrivate *priv;
1443 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
1444 priv = SOUP_SESSION_GET_PRIVATE (session);
1446 if (priv->use_thread_context)
1447 return g_main_context_get_thread_default ();
1449 return priv->async_context;
1455 soup_host_uri_hash (gconstpointer key)
1457 const SoupURI *uri = key;
1459 g_return_val_if_fail (uri != NULL && uri->host != NULL, 0);
1461 return uri->port + soup_str_case_hash (uri->host);
1465 soup_host_uri_equal (gconstpointer v1, gconstpointer v2)
1467 const SoupURI *one = v1;
1468 const SoupURI *two = v2;
1470 g_return_val_if_fail (one != NULL && two != NULL, one == two);
1471 g_return_val_if_fail (one->host != NULL && two->host != NULL, one->host == two->host);
1473 if (one->port != two->port)
1476 return g_ascii_strcasecmp (one->host, two->host) == 0;
1480 static SoupSessionHost *
1481 soup_session_host_new (SoupSession *session, SoupURI *uri)
1483 SoupSessionHost *host;
1485 host = g_slice_new0 (SoupSessionHost);
1486 host->uri = soup_uri_copy_host (uri);
1487 if (host->uri->scheme != SOUP_URI_SCHEME_HTTP &&
1488 host->uri->scheme != SOUP_URI_SCHEME_HTTPS) {
1489 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1491 if (uri_is_https (priv, host->uri))
1492 host->uri->scheme = SOUP_URI_SCHEME_HTTPS;
1494 host->uri->scheme = SOUP_URI_SCHEME_HTTP;
1497 host->addr = g_object_new (SOUP_TYPE_ADDRESS,
1498 SOUP_ADDRESS_NAME, host->uri->host,
1499 SOUP_ADDRESS_PORT, host->uri->port,
1500 SOUP_ADDRESS_PROTOCOL, host->uri->scheme,
1502 host->keep_alive_src = NULL;
1503 host->session = session;
1508 /* Requires host_lock to be locked */
1509 static SoupSessionHost *
1510 get_host_for_uri (SoupSession *session, SoupURI *uri)
1512 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1513 SoupSessionHost *host;
1515 if (uri_is_https (priv, uri))
1516 host = g_hash_table_lookup (priv->https_hosts, uri);
1518 host = g_hash_table_lookup (priv->http_hosts, uri);
1522 host = soup_session_host_new (session, uri);
1524 if (uri_is_https (priv, uri))
1525 g_hash_table_insert (priv->https_hosts, host->uri, host);
1527 g_hash_table_insert (priv->http_hosts, host->uri, host);
1532 /* Note: get_host_for_message doesn't lock the host_lock. The caller
1533 * must do it itself if there's a chance the host doesn't already
1536 static SoupSessionHost *
1537 get_host_for_message (SoupSession *session, SoupMessage *msg)
1539 return get_host_for_uri (session, soup_message_get_uri (msg));
1543 free_host (SoupSessionHost *host)
1545 while (host->connections) {
1546 SoupConnection *conn = host->connections->data;
1548 host->connections = g_slist_remove (host->connections, conn);
1549 soup_connection_disconnect (conn);
1552 if (host->keep_alive_src) {
1553 g_source_destroy (host->keep_alive_src);
1554 g_source_unref (host->keep_alive_src);
1557 soup_uri_free (host->uri);
1558 g_object_unref (host->addr);
1559 g_slice_free (SoupSessionHost, host);
1563 auth_required (SoupSession *session, SoupMessage *msg,
1564 SoupAuth *auth, gboolean retrying)
1566 g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
1570 auth_manager_authenticate (SoupAuthManager *manager, SoupMessage *msg,
1571 SoupAuth *auth, gboolean retrying,
1574 SOUP_SESSION_GET_CLASS (session)->auth_required (
1575 session, msg, auth, retrying);
1578 /* At some point it might be possible to mark additional methods
1579 * safe or idempotent...
1581 #define SOUP_METHOD_IS_SAFE(method) (method == SOUP_METHOD_GET || \
1582 method == SOUP_METHOD_HEAD || \
1583 method == SOUP_METHOD_OPTIONS || \
1584 method == SOUP_METHOD_PROPFIND)
1586 #define SOUP_METHOD_IS_IDEMPOTENT(method) (method == SOUP_METHOD_GET || \
1587 method == SOUP_METHOD_HEAD || \
1588 method == SOUP_METHOD_OPTIONS || \
1589 method == SOUP_METHOD_PROPFIND || \
1590 method == SOUP_METHOD_PUT || \
1591 method == SOUP_METHOD_DELETE)
1594 #define SOUP_SESSION_WOULD_REDIRECT_AS_GET(session, msg) \
1595 ((msg)->status_code == SOUP_STATUS_SEE_OTHER || \
1596 ((msg)->status_code == SOUP_STATUS_FOUND && \
1597 !SOUP_METHOD_IS_SAFE ((msg)->method)) || \
1598 ((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY && \
1599 (msg)->method == SOUP_METHOD_POST))
1601 #define SOUP_SESSION_WOULD_REDIRECT_AS_SAFE(session, msg) \
1602 (((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY || \
1603 (msg)->status_code == SOUP_STATUS_TEMPORARY_REDIRECT || \
1604 (msg)->status_code == SOUP_STATUS_FOUND) && \
1605 SOUP_METHOD_IS_SAFE ((msg)->method))
1607 static inline SoupURI *
1608 redirection_uri (SoupMessage *msg)
1610 const char *new_loc;
1613 new_loc = soup_message_headers_get_one (msg->response_headers,
1617 new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
1618 if (!new_uri || !new_uri->host) {
1620 soup_uri_free (new_uri);
1628 * soup_session_would_redirect:
1629 * @session: a #SoupSession
1630 * @msg: a #SoupMessage that has response headers
1632 * Checks if @msg contains a response that would cause @session to
1633 * redirect it to a new URL (ignoring @msg's %SOUP_MESSAGE_NO_REDIRECT
1634 * flag, and the number of times it has already been redirected).
1636 * Return value: whether @msg would be redirected
1641 soup_session_would_redirect (SoupSession *session, SoupMessage *msg)
1643 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1646 /* It must have an appropriate status code and method */
1647 if (!SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg) &&
1648 !SOUP_SESSION_WOULD_REDIRECT_AS_SAFE (session, msg))
1651 /* and a Location header that parses to an http URI */
1652 if (!soup_message_headers_get_one (msg->response_headers, "Location"))
1654 new_uri = redirection_uri (msg);
1657 if (!new_uri->host || !*new_uri->host ||
1658 (!uri_is_http (priv, new_uri) && !uri_is_https (priv, new_uri))) {
1659 soup_uri_free (new_uri);
1663 soup_uri_free (new_uri);
1668 * soup_session_redirect_message:
1669 * @session: the session
1670 * @msg: a #SoupMessage that has received a 3xx response
1672 * Updates @msg's URI according to its status code and "Location"
1673 * header, and requeues it on @session. Use this when you have set
1674 * %SOUP_MESSAGE_NO_REDIRECT on a message, but have decided to allow a
1675 * particular redirection to occur, or if you want to allow a
1676 * redirection that #SoupSession will not perform automatically (eg,
1677 * redirecting a non-safe method such as DELETE).
1679 * If @msg's status code indicates that it should be retried as a GET
1680 * request, then @msg will be modified accordingly.
1682 * If @msg has already been redirected too many times, this will
1683 * cause it to fail with %SOUP_STATUS_TOO_MANY_REDIRECTS.
1685 * Return value: %TRUE if a redirection was applied, %FALSE if not
1686 * (eg, because there was no Location header, or it could not be
1692 soup_session_redirect_message (SoupSession *session, SoupMessage *msg)
1694 SoupMessageQueueItem *item;
1697 new_uri = redirection_uri (msg);
1701 item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
1703 soup_uri_free (new_uri);
1706 if (item->redirection_count >= SOUP_SESSION_MAX_REDIRECTION_COUNT) {
1707 soup_uri_free (new_uri);
1708 soup_session_cancel_message (session, msg, SOUP_STATUS_TOO_MANY_REDIRECTS);
1709 soup_message_queue_item_unref (item);
1712 item->redirection_count++;
1713 soup_message_queue_item_unref (item);
1715 if (SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg)) {
1716 if (msg->method != SOUP_METHOD_HEAD) {
1718 SOUP_MESSAGE_METHOD, SOUP_METHOD_GET,
1721 soup_message_set_request (msg, NULL,
1722 SOUP_MEMORY_STATIC, NULL, 0);
1723 soup_message_headers_set_encoding (msg->request_headers,
1724 SOUP_ENCODING_NONE);
1727 soup_message_set_uri (msg, new_uri);
1728 soup_uri_free (new_uri);
1730 soup_session_requeue_message (session, msg);
1735 redirect_handler (SoupMessage *msg, gpointer user_data)
1737 SoupMessageQueueItem *item = user_data;
1738 SoupSession *session = item->session;
1740 if (!soup_session_would_redirect (session, msg)) {
1741 SoupURI *new_uri = redirection_uri (msg);
1742 gboolean invalid = !new_uri || !new_uri->host;
1745 soup_uri_free (new_uri);
1746 if (invalid && !item->new_api) {
1747 soup_message_set_status_full (msg,
1748 SOUP_STATUS_MALFORMED,
1749 "Invalid Redirect URL");
1754 soup_session_redirect_message (session, msg);
1758 soup_session_send_queue_item (SoupSession *session,
1759 SoupMessageQueueItem *item,
1760 SoupMessageCompletionFn completion_cb)
1762 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1763 const char *conn_header;
1765 if (priv->user_agent) {
1766 soup_message_headers_replace (item->msg->request_headers,
1767 "User-Agent", priv->user_agent);
1770 if (priv->accept_language &&
1771 !soup_message_headers_get_list (item->msg->request_headers,
1772 "Accept-Language")) {
1773 soup_message_headers_append (item->msg->request_headers,
1775 priv->accept_language);
1778 /* Force keep alive connections for HTTP 1.0. Performance will
1779 * improve when issuing multiple requests to the same host in
1780 * a short period of time, as we wouldn't need to establish
1781 * new connections. Keep alive is implicit for HTTP 1.1.
1783 conn_header = soup_message_headers_get_list (item->msg->request_headers, "Connection");
1785 (!soup_header_contains (conn_header, "Keep-Alive") &&
1786 !soup_header_contains (conn_header, "close")))
1787 soup_message_headers_append (item->msg->request_headers,
1788 "Connection", "Keep-Alive");
1790 g_signal_emit (session, signals[REQUEST_STARTED], 0,
1791 item->msg, soup_connection_get_socket (item->conn));
1792 if (item->state == SOUP_MESSAGE_RUNNING)
1793 soup_connection_send_request (item->conn, item, completion_cb, item);
1797 soup_session_cleanup_connections (SoupSession *session,
1798 gboolean prune_idle)
1800 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1801 GSList *conns = NULL, *c;
1802 GHashTableIter iter;
1803 gpointer conn, host;
1804 SoupConnectionState state;
1806 g_mutex_lock (&priv->host_lock);
1807 g_hash_table_iter_init (&iter, priv->conns);
1808 while (g_hash_table_iter_next (&iter, &conn, &host)) {
1809 state = soup_connection_get_state (conn);
1810 if (state == SOUP_CONNECTION_REMOTE_DISCONNECTED ||
1811 (prune_idle && state == SOUP_CONNECTION_IDLE))
1812 conns = g_slist_prepend (conns, g_object_ref (conn));
1814 g_mutex_unlock (&priv->host_lock);
1819 for (c = conns; c; c = c->next) {
1821 soup_connection_disconnect (conn);
1822 g_object_unref (conn);
1824 g_slist_free (conns);
1830 free_unused_host (gpointer user_data)
1832 SoupSessionHost *host = (SoupSessionHost *) user_data;
1833 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (host->session);
1835 g_mutex_lock (&priv->host_lock);
1836 /* This will free the host in addition to removing it from the
1839 if (host->uri->scheme == SOUP_URI_SCHEME_HTTPS)
1840 g_hash_table_remove (priv->https_hosts, host->uri);
1842 g_hash_table_remove (priv->http_hosts, host->uri);
1843 g_mutex_unlock (&priv->host_lock);
1849 connection_disconnected (SoupConnection *conn, gpointer user_data)
1851 SoupSession *session = user_data;
1852 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1853 SoupSessionHost *host;
1855 g_mutex_lock (&priv->host_lock);
1857 host = g_hash_table_lookup (priv->conns, conn);
1859 g_hash_table_remove (priv->conns, conn);
1860 host->connections = g_slist_remove (host->connections, conn);
1863 /* Free the SoupHost (and its SoupAddress) if there
1864 * has not been any new connection to the host during
1865 * the last HOST_KEEP_ALIVE msecs.
1867 if (host->num_conns == 0) {
1868 g_assert (host->keep_alive_src == NULL);
1869 host->keep_alive_src = soup_add_timeout (priv->async_context,
1873 host->keep_alive_src = g_source_ref (host->keep_alive_src);
1876 if (soup_connection_get_ssl_fallback (conn))
1877 host->ssl_fallback = TRUE;
1880 g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
1883 g_mutex_unlock (&priv->host_lock);
1884 g_object_unref (conn);
1887 SoupMessageQueueItem *
1888 soup_session_make_connect_message (SoupSession *session,
1889 SoupConnection *conn)
1891 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1894 SoupMessageQueueItem *item;
1896 uri = soup_connection_get_remote_uri (conn);
1897 msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT, uri);
1898 soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
1900 /* Call the base implementation of soup_session_queue_message
1901 * directly, to add msg to the SoupMessageQueue and cause all
1902 * the right signals to be emitted.
1904 queue_message (session, msg, NULL, NULL);
1905 item = soup_message_queue_lookup (priv->queue, msg);
1906 soup_message_queue_item_set_connection (item, conn);
1907 g_object_unref (msg);
1908 item->state = SOUP_MESSAGE_RUNNING;
1910 g_signal_emit (session, signals[TUNNELING], 0, conn);
1915 soup_session_get_connection (SoupSession *session,
1916 SoupMessageQueueItem *item,
1917 gboolean *try_pruning)
1919 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1920 SoupConnection *conn;
1921 SoupSessionHost *host;
1923 int num_pending = 0;
1924 gboolean need_new_connection;
1927 g_return_val_if_fail (soup_connection_get_state (item->conn) != SOUP_CONNECTION_DISCONNECTED, FALSE);
1931 need_new_connection =
1932 (soup_message_get_flags (item->msg) & SOUP_MESSAGE_NEW_CONNECTION) ||
1933 !SOUP_METHOD_IS_IDEMPOTENT (item->msg->method);
1935 g_mutex_lock (&priv->host_lock);
1937 host = get_host_for_message (session, item->msg);
1938 for (conns = host->connections; conns; conns = conns->next) {
1939 if (!need_new_connection && soup_connection_get_state (conns->data) == SOUP_CONNECTION_IDLE) {
1940 soup_connection_set_state (conns->data, SOUP_CONNECTION_IN_USE);
1941 g_mutex_unlock (&priv->host_lock);
1942 soup_message_queue_item_set_connection (item, conns->data);
1943 soup_message_set_https_status (item->msg, item->conn);
1945 } else if (soup_connection_get_state (conns->data) == SOUP_CONNECTION_CONNECTING)
1949 /* Limit the number of pending connections; num_messages / 2
1950 * is somewhat arbitrary...
1952 if (num_pending > host->num_messages / 2) {
1953 g_mutex_unlock (&priv->host_lock);
1957 if (host->num_conns >= priv->max_conns_per_host) {
1958 if (need_new_connection)
1959 *try_pruning = TRUE;
1960 g_mutex_unlock (&priv->host_lock);
1964 if (priv->num_conns >= priv->max_conns) {
1965 *try_pruning = TRUE;
1966 g_mutex_unlock (&priv->host_lock);
1970 conn = g_object_new (
1971 SOUP_TYPE_CONNECTION,
1972 SOUP_CONNECTION_REMOTE_URI, host->uri,
1973 SOUP_CONNECTION_PROXY_RESOLVER, soup_session_get_feature (session, SOUP_TYPE_PROXY_URI_RESOLVER),
1974 SOUP_CONNECTION_SSL, uri_is_https (priv, soup_message_get_uri (item->msg)),
1975 SOUP_CONNECTION_SSL_CREDENTIALS, priv->tlsdb,
1976 SOUP_CONNECTION_SSL_STRICT, (priv->tlsdb != NULL) && priv->ssl_strict,
1977 SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
1978 SOUP_CONNECTION_USE_THREAD_CONTEXT, priv->use_thread_context,
1979 SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
1980 SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
1981 SOUP_CONNECTION_SSL_FALLBACK, host->ssl_fallback,
1983 g_signal_connect (conn, "disconnected",
1984 G_CALLBACK (connection_disconnected),
1987 g_signal_emit (session, signals[CONNECTION_CREATED], 0, conn);
1989 g_hash_table_insert (priv->conns, conn, host);
1993 host->connections = g_slist_prepend (host->connections, conn);
1995 if (host->keep_alive_src) {
1996 g_source_destroy (host->keep_alive_src);
1997 g_source_unref (host->keep_alive_src);
1998 host->keep_alive_src = NULL;
2001 g_mutex_unlock (&priv->host_lock);
2002 soup_message_queue_item_set_connection (item, conn);
2007 soup_session_get_queue (SoupSession *session)
2009 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2015 soup_session_unqueue_item (SoupSession *session,
2016 SoupMessageQueueItem *item)
2018 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2019 SoupSessionHost *host;
2022 if (soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE)
2023 soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
2024 soup_message_queue_item_set_connection (item, NULL);
2027 if (item->state != SOUP_MESSAGE_FINISHED) {
2028 g_warning ("finished an item with state %d", item->state);
2032 soup_message_queue_remove (priv->queue, item);
2034 g_mutex_lock (&priv->host_lock);
2035 host = get_host_for_message (session, item->msg);
2036 host->num_messages--;
2037 g_mutex_unlock (&priv->host_lock);
2039 /* g_signal_handlers_disconnect_by_func doesn't work if you
2040 * have a metamarshal, meaning it doesn't work with
2041 * soup_message_add_header_handler()
2043 g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
2044 0, 0, NULL, NULL, item);
2045 g_signal_emit (session, signals[REQUEST_UNQUEUED], 0, item->msg);
2046 soup_message_queue_item_unref (item);
2050 soup_session_set_item_status (SoupSession *session,
2051 SoupMessageQueueItem *item,
2057 switch (status_code) {
2058 case SOUP_STATUS_CANT_RESOLVE:
2059 case SOUP_STATUS_CANT_CONNECT:
2060 uri = soup_message_get_uri (item->msg);
2061 msg = g_strdup_printf ("%s (%s)",
2062 soup_status_get_phrase (status_code),
2064 soup_message_set_status_full (item->msg, status_code, msg);
2068 case SOUP_STATUS_CANT_RESOLVE_PROXY:
2069 case SOUP_STATUS_CANT_CONNECT_PROXY:
2070 if (item->proxy_uri && item->proxy_uri->host) {
2071 msg = g_strdup_printf ("%s (%s)",
2072 soup_status_get_phrase (status_code),
2073 item->proxy_uri->host);
2074 soup_message_set_status_full (item->msg, status_code, msg);
2078 soup_message_set_status (item->msg, status_code);
2081 case SOUP_STATUS_SSL_FAILED:
2082 if (!g_tls_backend_supports_tls (g_tls_backend_get_default ())) {
2083 soup_message_set_status_full (item->msg, status_code,
2084 "TLS/SSL support not available; install glib-networking");
2086 soup_message_set_status (item->msg, status_code);
2090 soup_message_set_status (item->msg, status_code);
2096 queue_message (SoupSession *session, SoupMessage *msg,
2097 SoupSessionCallback callback, gpointer user_data)
2099 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2100 SoupMessageQueueItem *item;
2101 SoupSessionHost *host;
2103 item = soup_message_queue_append (priv->queue, msg, callback, user_data);
2105 g_mutex_lock (&priv->host_lock);
2106 host = get_host_for_message (session, item->msg);
2107 host->num_messages++;
2108 g_mutex_unlock (&priv->host_lock);
2110 if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
2111 soup_message_add_header_handler (
2112 msg, "got_body", "Location",
2113 G_CALLBACK (redirect_handler), item);
2116 g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
2120 * SoupSessionCallback:
2121 * @session: the session
2122 * @msg: the message that has finished
2123 * @user_data: the data passed to soup_session_queue_message
2125 * Prototype for the callback passed to soup_session_queue_message(),
2130 * soup_session_queue_message:
2131 * @session: a #SoupSession
2132 * @msg: (transfer full): the message to queue
2133 * @callback: (allow-none) (scope async): a #SoupSessionCallback which will
2134 * be called after the message completes or when an unrecoverable error occurs.
2135 * @user_data: (allow-none): a pointer passed to @callback.
2137 * Queues the message @msg for sending. All messages are processed
2138 * while the glib main loop runs. If @msg has been processed before,
2139 * any resources related to the time it was last sent are freed.
2141 * Upon message completion, the callback specified in @callback will
2142 * be invoked (in the thread associated with @session's async
2143 * context). If after returning from this callback the message has not
2144 * been requeued, @msg will be unreffed.
2147 soup_session_queue_message (SoupSession *session, SoupMessage *msg,
2148 SoupSessionCallback callback, gpointer user_data)
2150 g_return_if_fail (SOUP_IS_SESSION (session));
2151 g_return_if_fail (SOUP_IS_MESSAGE (msg));
2153 SOUP_SESSION_GET_CLASS (session)->queue_message (session, msg,
2154 callback, user_data);
2158 requeue_message (SoupSession *session, SoupMessage *msg)
2160 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2161 SoupMessageQueueItem *item;
2163 item = soup_message_queue_lookup (priv->queue, msg);
2164 g_return_if_fail (item != NULL);
2165 item->state = SOUP_MESSAGE_RESTARTING;
2166 soup_message_queue_item_unref (item);
2170 * soup_session_requeue_message:
2171 * @session: a #SoupSession
2172 * @msg: the message to requeue
2174 * This causes @msg to be placed back on the queue to be attempted
2178 soup_session_requeue_message (SoupSession *session, SoupMessage *msg)
2180 g_return_if_fail (SOUP_IS_SESSION (session));
2181 g_return_if_fail (SOUP_IS_MESSAGE (msg));
2183 SOUP_SESSION_GET_CLASS (session)->requeue_message (session, msg);
2188 * soup_session_send_message:
2189 * @session: a #SoupSession
2190 * @msg: the message to send
2192 * Synchronously send @msg. This call will not return until the
2193 * transfer is finished successfully or there is an unrecoverable
2196 * @msg is not freed upon return.
2198 * Return value: the HTTP status code of the response
2201 soup_session_send_message (SoupSession *session, SoupMessage *msg)
2203 g_return_val_if_fail (SOUP_IS_SESSION (session), SOUP_STATUS_MALFORMED);
2204 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_STATUS_MALFORMED);
2206 return SOUP_SESSION_GET_CLASS (session)->send_message (session, msg);
2211 * soup_session_pause_message:
2212 * @session: a #SoupSession
2213 * @msg: a #SoupMessage currently running on @session
2215 * Pauses HTTP I/O on @msg. Call soup_session_unpause_message() to
2219 soup_session_pause_message (SoupSession *session,
2222 SoupSessionPrivate *priv;
2223 SoupMessageQueueItem *item;
2225 g_return_if_fail (SOUP_IS_SESSION (session));
2226 g_return_if_fail (SOUP_IS_MESSAGE (msg));
2228 priv = SOUP_SESSION_GET_PRIVATE (session);
2229 item = soup_message_queue_lookup (priv->queue, msg);
2230 g_return_if_fail (item != NULL);
2232 item->paused = TRUE;
2233 if (item->state == SOUP_MESSAGE_RUNNING)
2234 soup_message_io_pause (msg);
2235 soup_message_queue_item_unref (item);
2239 * soup_session_unpause_message:
2240 * @session: a #SoupSession
2241 * @msg: a #SoupMessage currently running on @session
2243 * Resumes HTTP I/O on @msg. Use this to resume after calling
2244 * soup_session_pause_message().
2246 * If @msg is being sent via blocking I/O, this will resume reading or
2247 * writing immediately. If @msg is using non-blocking I/O, then
2248 * reading or writing won't resume until you return to the main loop.
2251 soup_session_unpause_message (SoupSession *session,
2254 SoupSessionPrivate *priv;
2255 SoupMessageQueueItem *item;
2257 g_return_if_fail (SOUP_IS_SESSION (session));
2258 g_return_if_fail (SOUP_IS_MESSAGE (msg));
2260 priv = SOUP_SESSION_GET_PRIVATE (session);
2261 item = soup_message_queue_lookup (priv->queue, msg);
2262 g_return_if_fail (item != NULL);
2264 item->paused = FALSE;
2265 if (item->state == SOUP_MESSAGE_RUNNING)
2266 soup_message_io_unpause (msg);
2267 soup_message_queue_item_unref (item);
2269 SOUP_SESSION_GET_CLASS (session)->kick (session);
2274 cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
2276 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2277 SoupMessageQueueItem *item;
2279 item = soup_message_queue_lookup (priv->queue, msg);
2280 g_return_if_fail (item != NULL);
2282 item->paused = FALSE;
2283 soup_message_set_status (msg, status_code);
2284 g_cancellable_cancel (item->cancellable);
2286 soup_message_queue_item_unref (item);
2290 * soup_session_cancel_message:
2291 * @session: a #SoupSession
2292 * @msg: the message to cancel
2293 * @status_code: status code to set on @msg (generally
2294 * %SOUP_STATUS_CANCELLED)
2296 * Causes @session to immediately finish processing @msg (regardless
2297 * of its current state) with a final status_code of @status_code. You
2298 * may call this at any time after handing @msg off to @session; if
2299 * @session has started sending the request but has not yet received
2300 * the complete response, then it will close the request's connection.
2301 * Note that with non-idempotent requests (eg,
2302 * <literal>POST</literal>, <literal>PUT</literal>,
2303 * <literal>DELETE</literal>) it is possible that you might cancel the
2304 * request after the server acts on it, but before it returns a
2305 * response, leaving the remote resource in an unknown state.
2307 * If the message is cancelled while its response body is being read,
2308 * then the response body in @msg will be left partially-filled-in.
2309 * The response headers, on the other hand, will always be either
2310 * empty or complete.
2312 * For messages queued with soup_session_queue_message() (and
2313 * cancelled from the same thread), the callback will be invoked
2314 * before soup_session_cancel_message() returns.
2317 soup_session_cancel_message (SoupSession *session, SoupMessage *msg,
2320 SoupSessionPrivate *priv;
2321 SoupMessageQueueItem *item;
2323 g_return_if_fail (SOUP_IS_SESSION (session));
2324 g_return_if_fail (SOUP_IS_MESSAGE (msg));
2326 priv = SOUP_SESSION_GET_PRIVATE (session);
2327 item = soup_message_queue_lookup (priv->queue, msg);
2328 /* If the message is already ending, don't do anything */
2331 if (item->state == SOUP_MESSAGE_FINISHED) {
2332 soup_message_queue_item_unref (item);
2336 SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg, status_code);
2337 soup_message_queue_item_unref (item);
2341 gather_conns (gpointer key, gpointer host, gpointer data)
2343 SoupConnection *conn = key;
2344 GSList **conns = data;
2346 *conns = g_slist_prepend (*conns, g_object_ref (conn));
2350 flush_queue (SoupSession *session)
2352 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2353 SoupMessageQueueItem *item;
2355 for (item = soup_message_queue_first (priv->queue);
2357 item = soup_message_queue_next (priv->queue, item)) {
2358 soup_session_cancel_message (session, item->msg,
2359 SOUP_STATUS_CANCELLED);
2364 * soup_session_abort:
2365 * @session: the session
2367 * Cancels all pending requests in @session.
2370 soup_session_abort (SoupSession *session)
2372 SoupSessionPrivate *priv;
2375 g_return_if_fail (SOUP_IS_SESSION (session));
2376 priv = SOUP_SESSION_GET_PRIVATE (session);
2378 SOUP_SESSION_GET_CLASS (session)->flush_queue (session);
2380 /* Close all connections */
2381 g_mutex_lock (&priv->host_lock);
2383 g_hash_table_foreach (priv->conns, gather_conns, &conns);
2385 g_mutex_unlock (&priv->host_lock);
2386 for (c = conns; c; c = c->next) {
2387 soup_connection_disconnect (c->data);
2388 g_object_unref (c->data);
2391 g_slist_free (conns);
2395 prefetch_uri(SoupSession *session, SoupURI *uri,
2396 GCancellable *cancellable,
2397 SoupAddressCallback callback, gpointer user_data)
2399 SoupSessionPrivate *priv;
2400 SoupSessionHost *host;
2403 priv = SOUP_SESSION_GET_PRIVATE (session);
2405 g_mutex_lock (&priv->host_lock);
2406 host = get_host_for_uri (session, uri);
2407 addr = g_object_ref (host->addr);
2408 g_mutex_unlock (&priv->host_lock);
2410 soup_address_resolve_async (addr,
2411 soup_session_get_async_context (session),
2412 cancellable, callback, user_data);
2413 g_object_unref (addr);
2417 * soup_session_prepare_for_uri:
2418 * @session: a #SoupSession
2419 * @uri: a #SoupURI which may be required
2421 * Tells @session that @uri may be requested shortly, and so the
2422 * session can try to prepare (resolving the domain name, obtaining
2423 * proxy address, etc.) in order to work more quickly once the URI is
2424 * actually requested.
2426 * This method acts asynchronously, in @session's
2427 * #SoupSession:async_context. If you are using #SoupSessionSync and do
2428 * not have a main loop running, then you can't use this method.
2432 * Deprecated: 2.38: use soup_session_prefetch_dns() instead
2435 soup_session_prepare_for_uri (SoupSession *session, SoupURI *uri)
2437 g_return_if_fail (SOUP_IS_SESSION (session));
2438 g_return_if_fail (uri != NULL);
2443 prefetch_uri (session, uri, NULL, NULL, NULL);
2447 * soup_session_prefetch_dns:
2448 * @session: a #SoupSession
2449 * @hostname: a hostname to be resolved
2450 * @cancellable: (allow-none): a #GCancellable object, or %NULL
2451 * @callback: (scope async) (allow-none): callback to call with the
2453 * @user_data: data for @callback
2455 * Tells @session that an URI from the given @hostname may be requested
2456 * shortly, and so the session can try to prepare by resolving the
2457 * domain name in advance, in order to work more quickly once the URI
2458 * is actually requested.
2460 * If @cancellable is non-%NULL, it can be used to cancel the
2461 * resolution. @callback will still be invoked in this case, with a
2462 * status of %SOUP_STATUS_CANCELLED.
2464 * This method acts asynchronously, in @session's
2465 * #SoupSession:async_context. If you are using #SoupSessionSync and do
2466 * not have a main loop running, then you can't use this method.
2471 soup_session_prefetch_dns (SoupSession *session, const char *hostname,
2472 GCancellable *cancellable,
2473 SoupAddressCallback callback, gpointer user_data)
2477 g_return_if_fail (SOUP_IS_SESSION (session));
2478 g_return_if_fail (hostname != NULL);
2480 /* FIXME: Prefetching should work for both HTTP and HTTPS */
2481 uri = soup_uri_new (NULL);
2482 soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP);
2483 soup_uri_set_host (uri, hostname);
2484 soup_uri_set_path (uri, "");
2486 prefetch_uri (session, uri, cancellable, callback, user_data);
2487 soup_uri_free (uri);
2491 * soup_session_add_feature:
2492 * @session: a #SoupSession
2493 * @feature: an object that implements #SoupSessionFeature
2495 * Adds @feature's functionality to @session. You can also add a
2496 * feature to the session at construct time by using the
2497 * %SOUP_SESSION_ADD_FEATURE property.
2502 soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature)
2504 SoupSessionPrivate *priv;
2506 g_return_if_fail (SOUP_IS_SESSION (session));
2507 g_return_if_fail (SOUP_IS_SESSION_FEATURE (feature));
2509 priv = SOUP_SESSION_GET_PRIVATE (session);
2510 priv->features = g_slist_prepend (priv->features, g_object_ref (feature));
2511 g_hash_table_remove_all (priv->features_cache);
2512 soup_session_feature_attach (feature, session);
2516 * soup_session_add_feature_by_type:
2517 * @session: a #SoupSession
2518 * @feature_type: a #GType
2520 * If @feature_type is the type of a class that implements
2521 * #SoupSessionFeature, this creates a new feature of that type and
2522 * adds it to @session as with soup_session_add_feature(). You can use
2523 * this when you don't need to customize the new feature in any way.
2525 * If @feature_type is not a #SoupSessionFeature type, this gives
2526 * each existing feature on @session the chance to accept @feature_type
2527 * as a "subfeature". This can be used to add new #SoupAuth types,
2530 * You can also add a feature to the session at construct time by
2531 * using the %SOUP_SESSION_ADD_FEATURE_BY_TYPE property.
2536 soup_session_add_feature_by_type (SoupSession *session, GType feature_type)
2538 g_return_if_fail (SOUP_IS_SESSION (session));
2540 if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2541 SoupSessionFeature *feature;
2543 feature = g_object_new (feature_type, NULL);
2544 soup_session_add_feature (session, feature);
2545 g_object_unref (feature);
2547 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2550 for (f = priv->features; f; f = f->next) {
2551 if (soup_session_feature_add_feature (f->data, feature_type))
2554 g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
2559 * soup_session_remove_feature:
2560 * @session: a #SoupSession
2561 * @feature: a feature that has previously been added to @session
2563 * Removes @feature's functionality from @session.
2568 soup_session_remove_feature (SoupSession *session, SoupSessionFeature *feature)
2570 SoupSessionPrivate *priv;
2572 g_return_if_fail (SOUP_IS_SESSION (session));
2574 priv = SOUP_SESSION_GET_PRIVATE (session);
2575 if (g_slist_find (priv->features, feature)) {
2576 priv->features = g_slist_remove (priv->features, feature);
2577 g_hash_table_remove_all (priv->features_cache);
2578 soup_session_feature_detach (feature, session);
2579 g_object_unref (feature);
2584 * soup_session_remove_feature_by_type:
2585 * @session: a #SoupSession
2586 * @feature_type: a #GType
2588 * Removes all features of type @feature_type (or any subclass of
2589 * @feature_type) from @session. You can also remove standard features
2590 * from the session at construct time by using the
2591 * %SOUP_SESSION_REMOVE_FEATURE_BY_TYPE property.
2596 soup_session_remove_feature_by_type (SoupSession *session, GType feature_type)
2598 SoupSessionPrivate *priv;
2601 g_return_if_fail (SOUP_IS_SESSION (session));
2603 priv = SOUP_SESSION_GET_PRIVATE (session);
2605 if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2607 for (f = priv->features; f; f = f->next) {
2608 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type)) {
2609 soup_session_remove_feature (session, f->data);
2614 for (f = priv->features; f; f = f->next) {
2615 if (soup_session_feature_remove_feature (f->data, feature_type))
2618 g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
2623 * soup_session_get_features:
2624 * @session: a #SoupSession
2625 * @feature_type: the #GType of the class of features to get
2627 * Generates a list of @session's features of type @feature_type. (If
2628 * you want to see all features, you can pass %SOUP_TYPE_SESSION_FEATURE
2629 * for @feature_type.)
2631 * Return value: (transfer container) (element-type Soup.SessionFeature):
2632 * a list of features. You must free the list, but not its contents
2637 soup_session_get_features (SoupSession *session, GType feature_type)
2639 SoupSessionPrivate *priv;
2642 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
2644 priv = SOUP_SESSION_GET_PRIVATE (session);
2645 for (f = priv->features, ret = NULL; f; f = f->next) {
2646 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
2647 ret = g_slist_prepend (ret, f->data);
2649 return g_slist_reverse (ret);
2653 * soup_session_get_feature:
2654 * @session: a #SoupSession
2655 * @feature_type: the #GType of the feature to get
2657 * Gets the first feature in @session of type @feature_type. For
2658 * features where there may be more than one feature of a given type,
2659 * use soup_session_get_features().
2661 * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
2662 * feature is owned by @session.
2666 SoupSessionFeature *
2667 soup_session_get_feature (SoupSession *session, GType feature_type)
2669 SoupSessionPrivate *priv;
2670 SoupSessionFeature *feature;
2673 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
2675 priv = SOUP_SESSION_GET_PRIVATE (session);
2677 feature = g_hash_table_lookup (priv->features_cache,
2678 GSIZE_TO_POINTER (feature_type));
2682 for (f = priv->features; f; f = f->next) {
2684 if (G_TYPE_CHECK_INSTANCE_TYPE (feature, feature_type)) {
2685 g_hash_table_insert (priv->features_cache,
2686 GSIZE_TO_POINTER (feature_type),
2695 * soup_session_get_feature_for_message:
2696 * @session: a #SoupSession
2697 * @feature_type: the #GType of the feature to get
2698 * @msg: a #SoupMessage
2700 * Gets the first feature in @session of type @feature_type, provided
2701 * that it is not disabled for @msg. As with
2702 * soup_session_get_feature(), this should only be used for features
2703 * where @feature_type is only expected to match a single feature. In
2704 * particular, if there are two matching features, and the first is
2705 * disabled on @msg, and the second is not, then this will return
2706 * %NULL, not the second feature.
2708 * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
2709 * feature is owned by @session.
2713 SoupSessionFeature *
2714 soup_session_get_feature_for_message (SoupSession *session, GType feature_type,
2717 SoupSessionFeature *feature;
2719 feature = soup_session_get_feature (session, feature_type);
2720 if (feature && soup_message_disables_feature (msg, feature))