1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
5 * Copyright (C) 2000-2003, Ximian, Inc.
16 #include "soup-address.h"
17 #include "soup-auth.h"
18 #include "soup-auth-basic.h"
19 #include "soup-auth-digest.h"
20 #include "soup-auth-manager-ntlm.h"
21 #include "soup-connection.h"
22 #include "soup-marshal.h"
23 #include "soup-message-private.h"
24 #include "soup-message-queue.h"
25 #include "soup-misc.h"
26 #include "soup-proxy-resolver-static.h"
27 #include "soup-proxy-uri-resolver.h"
28 #include "soup-session.h"
29 #include "soup-session-feature.h"
30 #include "soup-session-private.h"
31 #include "soup-socket.h"
36 * SECTION:soup-session
37 * @short_description: Soup session state object
39 * #SoupSession is the object that controls client-side HTTP. A
40 * #SoupSession encapsulates all of the state that libsoup is keeping
41 * on behalf of your program; cached HTTP connections, authentication
44 * Most applications will only need a single #SoupSession; the primary
45 * reason you might need multiple sessions is if you need to have
46 * multiple independent authentication contexts. (Eg, you are
47 * connecting to a server and authenticating as two different users at
48 * different times; the easiest way to ensure that each #SoupMessage
49 * is sent with the authentication information you intended is to use
50 * one session for the first user, and a second session for the other
53 * #SoupSession itself is an abstract class, with two subclasses. If
54 * you are using the glib main loop, you will generally want to use
55 * #SoupSessionAsync, which uses non-blocking I/O and callbacks. On
56 * the other hand, if your application is threaded and you want to do
57 * synchronous I/O in a separate thread from the UI, use
65 GSList *connections; /* CONTAINS: SoupConnection */
73 SoupSSLCredentials *ssl_creds;
76 SoupMessageQueue *queue;
79 char *accept_language;
80 gboolean accept_language_auto;
83 GHashTable *features_cache;
85 GHashTable *hosts; /* char* -> SoupSessionHost */
86 GHashTable *conns; /* SoupConnection -> SoupSessionHost */
88 guint max_conns, max_conns_per_host;
89 guint io_timeout, idle_timeout;
91 /* Must hold the host_lock before potentially creating a
92 * new SoupSessionHost, or adding/removing a connection.
93 * Must not emit signals or destroy objects while holding it.
97 GMainContext *async_context;
100 } SoupSessionPrivate;
101 #define SOUP_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION, SoupSessionPrivate))
103 static void free_host (SoupSessionHost *host);
105 static void queue_message (SoupSession *session, SoupMessage *msg,
106 SoupSessionCallback callback, gpointer user_data);
107 static void requeue_message (SoupSession *session, SoupMessage *msg);
108 static void cancel_message (SoupSession *session, SoupMessage *msg,
110 static void auth_required (SoupSession *session, SoupMessage *msg,
111 SoupAuth *auth, gboolean retrying);
113 static void auth_manager_authenticate (SoupAuthManager *manager,
114 SoupMessage *msg, SoupAuth *auth,
115 gboolean retrying, gpointer user_data);
117 #define SOUP_SESSION_MAX_CONNS_DEFAULT 10
118 #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2
120 #define SOUP_SESSION_MAX_REDIRECTION_COUNT 20
122 #define SOUP_SESSION_USER_AGENT_BASE "libsoup/" PACKAGE_VERSION
124 G_DEFINE_ABSTRACT_TYPE (SoupSession, soup_session, G_TYPE_OBJECT)
136 static guint signals[LAST_SIGNAL] = { 0 };
143 PROP_MAX_CONNS_PER_HOST,
150 PROP_ACCEPT_LANGUAGE,
151 PROP_ACCEPT_LANGUAGE_AUTO,
154 PROP_ADD_FEATURE_BY_TYPE,
155 PROP_REMOVE_FEATURE_BY_TYPE,
160 static void set_property (GObject *object, guint prop_id,
161 const GValue *value, GParamSpec *pspec);
162 static void get_property (GObject *object, guint prop_id,
163 GValue *value, GParamSpec *pspec);
166 soup_session_init (SoupSession *session)
168 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
169 SoupAuthManager *auth_manager;
171 priv->queue = soup_message_queue_new (session);
173 priv->host_lock = g_mutex_new ();
174 priv->hosts = g_hash_table_new_full (soup_uri_host_hash,
176 NULL, (GDestroyNotify)free_host);
177 priv->conns = g_hash_table_new (NULL, NULL);
179 priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
180 priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
182 priv->features_cache = g_hash_table_new (NULL, NULL);
184 auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER_NTLM,
185 SOUP_AUTH_MANAGER_NTLM_USE_NTLM, FALSE,
187 g_signal_connect (auth_manager, "authenticate",
188 G_CALLBACK (auth_manager_authenticate), session);
189 soup_auth_manager_add_type (auth_manager, SOUP_TYPE_AUTH_BASIC);
190 soup_auth_manager_add_type (auth_manager, SOUP_TYPE_AUTH_DIGEST);
191 soup_session_add_feature (session, SOUP_SESSION_FEATURE (auth_manager));
192 g_object_unref (auth_manager);
194 /* We'll be doing DNS continuously-ish while the session is active,
195 * so hold a ref on the default GResolver.
197 priv->resolver = g_resolver_get_default ();
199 priv->ssl_strict = TRUE;
203 dispose (GObject *object)
205 SoupSession *session = SOUP_SESSION (object);
206 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
208 soup_session_abort (session);
210 while (priv->features)
211 soup_session_remove_feature (session, priv->features->data);
213 G_OBJECT_CLASS (soup_session_parent_class)->dispose (object);
217 finalize (GObject *object)
219 SoupSession *session = SOUP_SESSION (object);
220 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
222 soup_message_queue_destroy (priv->queue);
224 g_mutex_free (priv->host_lock);
225 g_hash_table_destroy (priv->hosts);
226 g_hash_table_destroy (priv->conns);
228 g_free (priv->user_agent);
229 g_free (priv->accept_language);
231 if (priv->ssl_ca_file)
232 g_free (priv->ssl_ca_file);
234 soup_ssl_free_client_credentials (priv->ssl_creds);
236 if (priv->async_context)
237 g_main_context_unref (priv->async_context);
239 g_hash_table_destroy (priv->features_cache);
241 g_object_unref (priv->resolver);
243 G_OBJECT_CLASS (soup_session_parent_class)->finalize (object);
247 soup_session_class_init (SoupSessionClass *session_class)
249 GObjectClass *object_class = G_OBJECT_CLASS (session_class);
251 g_type_class_add_private (session_class, sizeof (SoupSessionPrivate));
253 /* virtual method definition */
254 session_class->queue_message = queue_message;
255 session_class->requeue_message = requeue_message;
256 session_class->cancel_message = cancel_message;
257 session_class->auth_required = auth_required;
259 /* virtual method override */
260 object_class->dispose = dispose;
261 object_class->finalize = finalize;
262 object_class->set_property = set_property;
263 object_class->get_property = get_property;
268 * SoupSession::request-queued:
269 * @session: the session
270 * @msg: the request that was queued
272 * Emitted when a request is queued on @session. (Note that
273 * "queued" doesn't just mean soup_session_queue_message();
274 * soup_session_send_message() implicitly queues the message
277 * When sending a request, first #SoupSession::request_queued
278 * is emitted, indicating that the session has become aware of
281 * Once a connection is available to send the request on, the
282 * session emits #SoupSession::request_started. Then, various
283 * #SoupMessage signals are emitted as the message is
284 * processed. If the message is requeued, it will emit
285 * #SoupMessage::restarted, which will then be followed by
286 * another #SoupSession::request_started and another set of
287 * #SoupMessage signals when the message is re-sent.
289 * Eventually, the message will emit #SoupMessage::finished.
290 * Normally, this signals the completion of message
291 * processing. However, it is possible that the application
292 * will requeue the message from the "finished" handler (or
293 * equivalently, from the soup_session_queue_message()
294 * callback). In that case, the process will loop back to
295 * #SoupSession::request_started.
297 * Eventually, a message will reach "finished" and not be
298 * requeued. At that point, the session will emit
299 * #SoupSession::request_unqueued to indicate that it is done
302 * To sum up: #SoupSession::request_queued and
303 * #SoupSession::request_unqueued are guaranteed to be emitted
304 * exactly once, but #SoupSession::request_started and
305 * #SoupMessage::finished (and all of the other #SoupMessage
306 * signals) may be invoked multiple times for a given message.
310 signals[REQUEST_QUEUED] =
311 g_signal_new ("request-queued",
312 G_OBJECT_CLASS_TYPE (object_class),
316 soup_marshal_NONE__OBJECT,
321 * SoupSession::request-started:
322 * @session: the session
323 * @msg: the request being sent
324 * @socket: the socket the request is being sent on
326 * Emitted just before a request is sent. See
327 * #SoupSession::request_queued for a detailed description of
328 * the message lifecycle within a session.
330 signals[REQUEST_STARTED] =
331 g_signal_new ("request-started",
332 G_OBJECT_CLASS_TYPE (object_class),
334 G_STRUCT_OFFSET (SoupSessionClass, request_started),
336 soup_marshal_NONE__OBJECT_OBJECT,
342 * SoupSession::request-unqueued:
343 * @session: the session
344 * @msg: the request that was unqueued
346 * Emitted when a request is removed from @session's queue,
347 * indicating that @session is done with it. See
348 * #SoupSession::request_queued for a detailed description of the
349 * message lifecycle within a session.
353 signals[REQUEST_UNQUEUED] =
354 g_signal_new ("request-unqueued",
355 G_OBJECT_CLASS_TYPE (object_class),
359 soup_marshal_NONE__OBJECT,
364 * SoupSession::authenticate:
365 * @session: the session
366 * @msg: the #SoupMessage being sent
367 * @auth: the #SoupAuth to authenticate
368 * @retrying: %TRUE if this is the second (or later) attempt
370 * Emitted when the session requires authentication. If
371 * credentials are available call soup_auth_authenticate() on
372 * @auth. If these credentials fail, the signal will be
373 * emitted again, with @retrying set to %TRUE, which will
374 * continue until you return without calling
375 * soup_auth_authenticate() on @auth.
377 * Note that this may be emitted before @msg's body has been
380 * If you call soup_session_pause_message() on @msg before
381 * returning, then you can authenticate @auth asynchronously
382 * (as long as you g_object_ref() it to make sure it doesn't
383 * get destroyed), and then unpause @msg when you are ready
384 * for it to continue.
386 signals[AUTHENTICATE] =
387 g_signal_new ("authenticate",
388 G_OBJECT_CLASS_TYPE (object_class),
390 G_STRUCT_OFFSET (SoupSessionClass, authenticate),
392 soup_marshal_NONE__OBJECT_OBJECT_BOOLEAN,
398 signals[CONNECTION_CREATED] =
399 g_signal_new ("connection-created",
400 G_OBJECT_CLASS_TYPE (object_class),
404 soup_marshal_NONE__OBJECT,
406 /* SoupConnection is private, so we can't use
407 * SOUP_TYPE_CONNECTION here.
412 g_signal_new ("tunneling",
413 G_OBJECT_CLASS_TYPE (object_class),
417 soup_marshal_NONE__OBJECT,
419 /* SoupConnection is private, so we can't use
420 * SOUP_TYPE_CONNECTION here.
427 * SOUP_SESSION_PROXY_URI:
429 * Alias for the #SoupSession:proxy-uri property. (The HTTP
430 * proxy to use for this session.)
432 g_object_class_install_property (
433 object_class, PROP_PROXY_URI,
434 g_param_spec_boxed (SOUP_SESSION_PROXY_URI,
436 "The HTTP Proxy to use for this session",
440 * SOUP_SESSION_MAX_CONNS:
442 * Alias for the #SoupSession:max-conns property. (The maximum
443 * number of connections that the session can open at once.)
445 g_object_class_install_property (
446 object_class, PROP_MAX_CONNS,
447 g_param_spec_int (SOUP_SESSION_MAX_CONNS,
448 "Max Connection Count",
449 "The maximum number of connections that the session can open at once",
452 SOUP_SESSION_MAX_CONNS_DEFAULT,
455 * SOUP_SESSION_MAX_CONNS_PER_HOST:
457 * Alias for the #SoupSession:max-conns-per-host property.
458 * (The maximum number of connections that the session can
459 * open at once to a given host.)
461 g_object_class_install_property (
462 object_class, PROP_MAX_CONNS_PER_HOST,
463 g_param_spec_int (SOUP_SESSION_MAX_CONNS_PER_HOST,
464 "Max Per-Host Connection Count",
465 "The maximum number of connections that the session can open at once to a given host",
468 SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT,
471 * SoupSession:idle-timeout:
473 * Connection lifetime when idle
478 * SOUP_SESSION_IDLE_TIMEOUT:
480 * Alias for the #SoupSession:idle-timeout property. (The idle
481 * connection lifetime.)
485 g_object_class_install_property (
486 object_class, PROP_IDLE_TIMEOUT,
487 g_param_spec_uint (SOUP_SESSION_IDLE_TIMEOUT,
489 "Connection lifetime when idle",
493 * SOUP_SESSION_USE_NTLM:
495 * Alias for the #SoupSession:use-ntlm property. (Whether or
496 * not to use NTLM authentication.)
498 g_object_class_install_property (
499 object_class, PROP_USE_NTLM,
500 g_param_spec_boolean (SOUP_SESSION_USE_NTLM,
502 "Whether or not to use NTLM authentication",
506 * SOUP_SESSION_SSL_CA_FILE:
508 * Alias for the #SoupSession:ssl-ca-file property. (File
509 * containing SSL CA certificates.)
511 g_object_class_install_property (
512 object_class, PROP_SSL_CA_FILE,
513 g_param_spec_string (SOUP_SESSION_SSL_CA_FILE,
515 "File containing SSL CA certificates",
519 * SOUP_SESSION_SSL_STRICT:
521 * Alias for the #SoupSession:ignore-ssl-cert-errors
522 * property. By default, when validating certificates against
523 * a CA file, Soup will consider invalid certificates as a
524 * connection error. Setting this property to %TRUE makes soup
525 * ignore the errors, and make the connection.
529 g_object_class_install_property (
530 object_class, PROP_SSL_STRICT,
531 g_param_spec_boolean (SOUP_SESSION_SSL_STRICT,
532 "Strictly validate SSL certificates",
533 "Whether certificate errors should be considered a connection error",
537 * SOUP_SESSION_ASYNC_CONTEXT:
539 * Alias for the #SoupSession:async-context property. (The
540 * session's #GMainContext.)
542 g_object_class_install_property (
543 object_class, PROP_ASYNC_CONTEXT,
544 g_param_spec_pointer (SOUP_SESSION_ASYNC_CONTEXT,
545 "Async GMainContext",
546 "The GMainContext to dispatch async I/O in",
547 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
549 * SOUP_SESSION_TIMEOUT:
551 * Alias for the #SoupSession:timeout property. (The timeout
552 * in seconds for blocking socket I/O operations.)
554 g_object_class_install_property (
555 object_class, PROP_TIMEOUT,
556 g_param_spec_uint (SOUP_SESSION_TIMEOUT,
558 "Value in seconds to timeout a blocking I/O",
563 * SoupSession:user-agent:
565 * If non-%NULL, the value to use for the "User-Agent" header
566 * on #SoupMessage<!-- -->s sent from this session.
568 * RFC 2616 says: "The User-Agent request-header field
569 * contains information about the user agent originating the
570 * request. This is for statistical purposes, the tracing of
571 * protocol violations, and automated recognition of user
572 * agents for the sake of tailoring responses to avoid
573 * particular user agent limitations. User agents SHOULD
574 * include this field with requests."
576 * The User-Agent header contains a list of one or more
577 * product tokens, separated by whitespace, with the most
578 * significant product token coming first. The tokens must be
579 * brief, ASCII, and mostly alphanumeric (although "-", "_",
580 * and "." are also allowed), and may optionally include a "/"
581 * followed by a version string. You may also put comments,
582 * enclosed in parentheses, between or after the tokens.
584 * If you set a %user_agent property that has trailing
585 * whitespace, #SoupSession will append its own product token
586 * (eg, "<literal>libsoup/2.3.2</literal>") to the end of the
590 * SOUP_SESSION_USER_AGENT:
592 * Alias for the #SoupSession:user-agent property, qv.
594 g_object_class_install_property (
595 object_class, PROP_USER_AGENT,
596 g_param_spec_string (SOUP_SESSION_USER_AGENT,
603 * SoupSession:accept-language:
605 * If non-%NULL, the value to use for the "Accept-Language" header
606 * on #SoupMessage<!-- -->s sent from this session.
608 * Setting this will disable
609 * #SoupSession:accept-language-auto.
614 * SOUP_SESSION_ACCEPT_LANGUAGE:
616 * Alias for the #SoupSession:accept-language property, qv.
618 g_object_class_install_property (
619 object_class, PROP_ACCEPT_LANGUAGE,
620 g_param_spec_string (SOUP_SESSION_ACCEPT_LANGUAGE,
621 "Accept-Language string",
622 "Accept-Language string",
627 * SoupSession:accept-language-auto:
629 * If %TRUE, #SoupSession will automatically set the string
630 * for the "Accept-Language" header on every #SoupMessage
631 * sent, based on the return value of g_get_language_names().
633 * Setting this will override any previous value of
634 * #SoupSession:accept-language.
639 * SOUP_SESSION_ACCEPT_LANGUAGE_AUTO:
641 * Alias for the #SoupSession:accept-language-auto property, qv.
643 g_object_class_install_property (
644 object_class, PROP_ACCEPT_LANGUAGE_AUTO,
645 g_param_spec_boolean (SOUP_SESSION_ACCEPT_LANGUAGE_AUTO,
646 "Accept-Language automatic mode",
647 "Accept-Language automatic mode",
652 * SoupSession:add-feature:
654 * Add a feature object to the session. (Shortcut for calling
655 * soup_session_add_feature().)
660 * SOUP_SESSION_ADD_FEATURE:
662 * Alias for the #SoupSession:add-feature property. (Shortcut
663 * for calling soup_session_add_feature().
667 g_object_class_install_property (
668 object_class, PROP_ADD_FEATURE,
669 g_param_spec_object (SOUP_SESSION_ADD_FEATURE,
671 "Add a feature object to the session",
672 SOUP_TYPE_SESSION_FEATURE,
675 * SoupSession:add-feature-by-type:
677 * Add a feature object of the given type to the session.
678 * (Shortcut for calling soup_session_add_feature_by_type().)
683 * SOUP_SESSION_ADD_FEATURE_BY_TYPE:
685 * Alias for the #SoupSession:add-feature-by-type property.
686 * (Shortcut for calling soup_session_add_feature_by_type().
690 g_object_class_install_property (
691 object_class, PROP_ADD_FEATURE_BY_TYPE,
692 g_param_spec_gtype (SOUP_SESSION_ADD_FEATURE_BY_TYPE,
693 "Add Feature By Type",
694 "Add a feature object of the given type to the session",
695 SOUP_TYPE_SESSION_FEATURE,
698 * SoupSession:remove-feature-by-type:
700 * Remove feature objects from the session. (Shortcut for
701 * calling soup_session_remove_feature_by_type().)
706 * SOUP_SESSION_REMOVE_FEATURE_BY_TYPE:
708 * Alias for the #SoupSession:remove-feature-by-type
709 * property. (Shortcut for calling
710 * soup_session_remove_feature_by_type().
714 g_object_class_install_property (
715 object_class, PROP_REMOVE_FEATURE_BY_TYPE,
716 g_param_spec_gtype (SOUP_SESSION_REMOVE_FEATURE_BY_TYPE,
717 "Remove Feature By Type",
718 "Remove features of the given type from the session",
719 SOUP_TYPE_SESSION_FEATURE,
724 safe_str_equal (const char *a, const char *b)
729 if ((a && !b) || (b && !a))
732 return strcmp (a, b) == 0;
735 /* Converts a language in POSIX format and to be RFC2616 compliant */
736 /* Based on code from epiphany-webkit (ephy_langs_append_languages()) */
738 posix_lang_to_rfc2616 (const gchar *language)
740 /* Don't include charset variants, etc */
741 if (strchr (language, '.') || strchr (language, '@'))
744 /* Ignore "C" locale, which g_get_language_names() always
745 * includes as a fallback.
747 if (!strcmp (language, "C"))
750 return g_strdelimit (g_ascii_strdown (language, -1), "_", '-');
753 /* Converts @quality from 0-100 to 0.0-1.0 and appends to @str */
755 add_quality_value (const gchar *str, int quality)
757 g_return_val_if_fail (str != NULL, NULL);
759 if (quality >= 0 && quality < 100) {
760 /* We don't use %.02g because of "." vs "," locale issues */
762 return g_strdup_printf ("%s;q=0.%02d", str, quality);
764 return g_strdup_printf ("%s;q=0.%d", str, quality / 10);
766 return g_strdup (str);
769 /* Returns a RFC2616 compliant languages list from system locales */
771 accept_languages_from_system (void)
773 const char * const * lang_names;
774 GPtrArray *langs = NULL;
775 char *lang, **langs_array, *langs_str;
779 lang_names = g_get_language_names ();
780 g_return_val_if_fail (lang_names != NULL, NULL);
782 /* Build the array of languages */
783 langs = g_ptr_array_new ();
784 for (i = 0; lang_names[i] != NULL; i++) {
785 lang = posix_lang_to_rfc2616 (lang_names[i]);
787 g_ptr_array_add (langs, lang);
790 /* Add quality values */
793 else if (langs->len < 20)
798 for (i = 0; i < langs->len; i++) {
799 lang = langs->pdata[i];
800 langs->pdata[i] = add_quality_value (lang, 100 - i * delta);
804 /* Fallback: add "en" if list is empty */
806 g_ptr_array_add (langs, g_strdup ("en"));
808 g_ptr_array_add (langs, NULL);
809 langs_array = (char **)langs->pdata;
810 langs_str = g_strjoinv (", ", langs_array);
812 g_strfreev (langs_array);
813 g_ptr_array_free (langs, FALSE);
819 set_property (GObject *object, guint prop_id,
820 const GValue *value, GParamSpec *pspec)
822 SoupSession *session = SOUP_SESSION (object);
823 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
825 gboolean ca_file_changed = FALSE;
826 const char *new_ca_file, *user_agent;
827 SoupSessionFeature *feature;
831 uri = g_value_get_boxed (value);
834 soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER);
835 feature = SOUP_SESSION_FEATURE (soup_proxy_resolver_static_new (uri));
836 soup_session_add_feature (session, feature);
837 g_object_unref (feature);
839 soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
841 soup_session_abort (session);
844 priv->max_conns = g_value_get_int (value);
846 case PROP_MAX_CONNS_PER_HOST:
847 priv->max_conns_per_host = g_value_get_int (value);
850 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
852 g_object_set_property (G_OBJECT (feature),
853 SOUP_AUTH_MANAGER_NTLM_USE_NTLM,
856 g_warning ("Trying to set use-ntlm on session with no auth-manager");
858 case PROP_SSL_CA_FILE:
859 new_ca_file = g_value_get_string (value);
861 if (!safe_str_equal (priv->ssl_ca_file, new_ca_file))
862 ca_file_changed = TRUE;
864 g_free (priv->ssl_ca_file);
865 priv->ssl_ca_file = g_strdup (new_ca_file);
867 if (ca_file_changed && priv->ssl_creds) {
868 soup_ssl_free_client_credentials (priv->ssl_creds);
869 priv->ssl_creds = NULL;
873 case PROP_SSL_STRICT:
874 priv->ssl_strict = g_value_get_boolean (value);
876 case PROP_ASYNC_CONTEXT:
877 priv->async_context = g_value_get_pointer (value);
878 if (priv->async_context)
879 g_main_context_ref (priv->async_context);
882 priv->io_timeout = g_value_get_uint (value);
884 case PROP_USER_AGENT:
885 g_free (priv->user_agent);
886 user_agent = g_value_get_string (value);
888 priv->user_agent = NULL;
889 else if (!*user_agent) {
891 g_strdup (SOUP_SESSION_USER_AGENT_BASE);
892 } else if (g_str_has_suffix (user_agent, " ")) {
894 g_strdup_printf ("%s%s", user_agent,
895 SOUP_SESSION_USER_AGENT_BASE);
897 priv->user_agent = g_strdup (user_agent);
899 case PROP_ACCEPT_LANGUAGE:
900 g_free (priv->accept_language);
901 priv->accept_language = g_strdup (g_value_get_string (value));
902 priv->accept_language_auto = FALSE;
904 case PROP_ACCEPT_LANGUAGE_AUTO:
905 priv->accept_language_auto = g_value_get_boolean (value);
906 if (priv->accept_language) {
907 g_free (priv->accept_language);
908 priv->accept_language = NULL;
911 /* Get languages from system if needed */
912 if (priv->accept_language_auto)
913 priv->accept_language = accept_languages_from_system ();
915 case PROP_IDLE_TIMEOUT:
916 priv->idle_timeout = g_value_get_uint (value);
918 case PROP_ADD_FEATURE:
919 soup_session_add_feature (session, g_value_get_object (value));
921 case PROP_ADD_FEATURE_BY_TYPE:
922 soup_session_add_feature_by_type (session, g_value_get_gtype (value));
924 case PROP_REMOVE_FEATURE_BY_TYPE:
925 soup_session_remove_feature_by_type (session, g_value_get_gtype (value));
928 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
934 get_property (GObject *object, guint prop_id,
935 GValue *value, GParamSpec *pspec)
937 SoupSession *session = SOUP_SESSION (object);
938 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
939 SoupSessionFeature *feature;
943 feature = soup_session_get_feature (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
945 g_object_get_property (G_OBJECT (feature),
946 SOUP_PROXY_RESOLVER_STATIC_PROXY_URI,
949 g_value_set_boxed (value, NULL);
952 g_value_set_int (value, priv->max_conns);
954 case PROP_MAX_CONNS_PER_HOST:
955 g_value_set_int (value, priv->max_conns_per_host);
958 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
960 g_object_get_property (G_OBJECT (feature),
961 SOUP_AUTH_MANAGER_NTLM_USE_NTLM,
964 g_value_set_boolean (value, FALSE);
966 case PROP_SSL_CA_FILE:
967 g_value_set_string (value, priv->ssl_ca_file);
969 case PROP_SSL_STRICT:
970 g_value_set_boolean (value, priv->ssl_strict);
972 case PROP_ASYNC_CONTEXT:
973 g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
976 g_value_set_uint (value, priv->io_timeout);
978 case PROP_USER_AGENT:
979 g_value_set_string (value, priv->user_agent);
981 case PROP_ACCEPT_LANGUAGE:
982 g_value_set_string (value, priv->accept_language);
984 case PROP_ACCEPT_LANGUAGE_AUTO:
985 g_value_set_boolean (value, priv->accept_language_auto);
987 case PROP_IDLE_TIMEOUT:
988 g_value_set_uint (value, priv->idle_timeout);
991 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
998 * soup_session_get_async_context:
999 * @session: a #SoupSession
1001 * Gets @session's async_context. This does not add a ref to the
1002 * context, so you will need to ref it yourself if you want it to
1003 * outlive its session.
1005 * Return value: (transfer none): @session's #GMainContext, which may
1009 soup_session_get_async_context (SoupSession *session)
1011 SoupSessionPrivate *priv;
1013 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
1014 priv = SOUP_SESSION_GET_PRIVATE (session);
1016 return priv->async_context;
1021 static SoupSessionHost *
1022 soup_session_host_new (SoupSession *session, SoupURI *uri)
1024 SoupSessionHost *host;
1026 host = g_slice_new0 (SoupSessionHost);
1027 host->uri = soup_uri_copy_host (uri);
1028 host->addr = soup_address_new (host->uri->host, host->uri->port);
1033 /* Requires host_lock to be locked */
1034 static SoupSessionHost *
1035 get_host_for_uri (SoupSession *session, SoupURI *uri)
1037 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1038 SoupSessionHost *host;
1040 host = g_hash_table_lookup (priv->hosts, uri);
1044 host = soup_session_host_new (session, uri);
1045 g_hash_table_insert (priv->hosts, host->uri, host);
1050 /* Note: get_host_for_message doesn't lock the host_lock. The caller
1051 * must do it itself if there's a chance the host doesn't already
1054 static SoupSessionHost *
1055 get_host_for_message (SoupSession *session, SoupMessage *msg)
1057 return get_host_for_uri (session, soup_message_get_uri (msg));
1061 free_host (SoupSessionHost *host)
1063 while (host->connections) {
1064 SoupConnection *conn = host->connections->data;
1066 host->connections = g_slist_remove (host->connections, conn);
1067 soup_connection_disconnect (conn);
1070 soup_uri_free (host->uri);
1071 g_object_unref (host->addr);
1072 g_slice_free (SoupSessionHost, host);
1076 auth_required (SoupSession *session, SoupMessage *msg,
1077 SoupAuth *auth, gboolean retrying)
1079 g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
1083 auth_manager_authenticate (SoupAuthManager *manager, SoupMessage *msg,
1084 SoupAuth *auth, gboolean retrying,
1087 SOUP_SESSION_GET_CLASS (session)->auth_required (
1088 session, msg, auth, retrying);
1091 #define SOUP_METHOD_IS_SAFE(method) (method == SOUP_METHOD_GET || \
1092 method == SOUP_METHOD_HEAD || \
1093 method == SOUP_METHOD_OPTIONS || \
1094 method == SOUP_METHOD_PROPFIND)
1097 redirect_handler (SoupMessage *msg, gpointer user_data)
1099 SoupMessageQueueItem *item = user_data;
1100 SoupSession *session = item->session;
1101 const char *new_loc;
1104 new_loc = soup_message_headers_get_one (msg->response_headers,
1106 g_return_if_fail (new_loc != NULL);
1108 if (item->redirection_count >= SOUP_SESSION_MAX_REDIRECTION_COUNT) {
1109 soup_session_cancel_message (session, msg, SOUP_STATUS_TOO_MANY_REDIRECTS);
1112 item->redirection_count++;
1114 if (msg->status_code == SOUP_STATUS_SEE_OTHER ||
1115 (msg->status_code == SOUP_STATUS_FOUND &&
1116 !SOUP_METHOD_IS_SAFE (msg->method)) ||
1117 (msg->status_code == SOUP_STATUS_MOVED_PERMANENTLY &&
1118 msg->method == SOUP_METHOD_POST)) {
1119 if (msg->method != SOUP_METHOD_HEAD) {
1120 /* Redirect using a GET */
1122 SOUP_MESSAGE_METHOD, SOUP_METHOD_GET,
1125 soup_message_set_request (msg, NULL,
1126 SOUP_MEMORY_STATIC, NULL, 0);
1127 soup_message_headers_set_encoding (msg->request_headers,
1128 SOUP_ENCODING_NONE);
1129 } else if (msg->status_code == SOUP_STATUS_MOVED_PERMANENTLY ||
1130 msg->status_code == SOUP_STATUS_TEMPORARY_REDIRECT ||
1131 msg->status_code == SOUP_STATUS_FOUND) {
1132 /* Don't redirect non-safe methods */
1133 if (!SOUP_METHOD_IS_SAFE (msg->method))
1136 /* Three possibilities:
1138 * 1) This was a non-3xx response that happened to
1139 * have a "Location" header
1140 * 2) It's a non-redirecty 3xx response (300, 304,
1142 * 3) It's some newly-defined 3xx response (308+)
1144 * We ignore all of these cases. In the first two,
1145 * redirecting would be explicitly wrong, and in the
1146 * last case, we have no clue if the 3xx response is
1147 * supposed to be redirecty or non-redirecty. Plus,
1148 * 2616 says unrecognized status codes should be
1149 * treated as the equivalent to the x00 code, and we
1150 * don't redirect on 300, so therefore we shouldn't
1151 * redirect on 308+ either.
1156 /* Location is supposed to be an absolute URI, but some sites
1157 * are lame, so we use soup_uri_new_with_base().
1159 new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
1160 if (!new_uri || !new_uri->host) {
1162 soup_uri_free (new_uri);
1163 soup_message_set_status_full (msg,
1164 SOUP_STATUS_MALFORMED,
1165 "Invalid Redirect URL");
1169 soup_message_set_uri (msg, new_uri);
1170 soup_uri_free (new_uri);
1172 soup_session_requeue_message (session, msg);
1176 soup_session_send_queue_item (SoupSession *session,
1177 SoupMessageQueueItem *item,
1178 SoupMessageCompletionFn completion_cb)
1180 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1182 if (priv->user_agent) {
1183 soup_message_headers_replace (item->msg->request_headers,
1184 "User-Agent", priv->user_agent);
1187 if (priv->accept_language &&
1188 !soup_message_headers_get_list (item->msg->request_headers,
1189 "Accept-Language")) {
1190 soup_message_headers_append (item->msg->request_headers,
1192 priv->accept_language);
1195 g_signal_emit (session, signals[REQUEST_STARTED], 0,
1196 item->msg, soup_connection_get_socket (item->conn));
1197 soup_connection_send_request (item->conn, item, completion_cb, item);
1201 soup_session_cleanup_connections (SoupSession *session,
1202 gboolean prune_idle)
1204 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1205 GSList *conns = NULL, *c;
1206 GHashTableIter iter;
1207 gpointer conn, host;
1208 SoupConnectionState state;
1210 g_mutex_lock (priv->host_lock);
1211 g_hash_table_iter_init (&iter, priv->conns);
1212 while (g_hash_table_iter_next (&iter, &conn, &host)) {
1213 state = soup_connection_get_state (conn);
1214 if (state == SOUP_CONNECTION_REMOTE_DISCONNECTED ||
1215 (prune_idle && state == SOUP_CONNECTION_IDLE))
1216 conns = g_slist_prepend (conns, g_object_ref (conn));
1218 g_mutex_unlock (priv->host_lock);
1223 for (c = conns; c; c = c->next) {
1225 soup_connection_disconnect (conn);
1226 g_object_unref (conn);
1228 g_slist_free (conns);
1234 connection_disconnected (SoupConnection *conn, gpointer user_data)
1236 SoupSession *session = user_data;
1237 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1238 SoupSessionHost *host;
1240 g_mutex_lock (priv->host_lock);
1242 host = g_hash_table_lookup (priv->conns, conn);
1244 g_hash_table_remove (priv->conns, conn);
1245 host->connections = g_slist_remove (host->connections, conn);
1249 g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
1252 g_mutex_unlock (priv->host_lock);
1253 g_object_unref (conn);
1256 SoupMessageQueueItem *
1257 soup_session_make_connect_message (SoupSession *session,
1258 SoupConnection *conn)
1260 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1261 SoupAddress *server_addr = soup_connection_get_tunnel_addr (conn);
1264 SoupMessageQueueItem *item;
1266 uri = soup_uri_new (NULL);
1267 soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTPS);
1268 soup_uri_set_host (uri, soup_address_get_name (server_addr));
1269 soup_uri_set_port (uri, soup_address_get_port (server_addr));
1270 soup_uri_set_path (uri, "");
1271 msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT, uri);
1272 soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
1273 soup_uri_free (uri);
1275 /* Call the base implementation of soup_session_queue_message
1276 * directly, to add msg to the SoupMessageQueue and cause all
1277 * the right signals to be emitted.
1279 queue_message (session, msg, NULL, NULL);
1280 item = soup_message_queue_lookup (priv->queue, msg);
1281 item->conn = g_object_ref (conn);
1282 g_object_unref (msg);
1284 item->conn = g_object_ref (conn);
1285 g_signal_emit (session, signals[TUNNELING], 0, conn);
1290 soup_session_get_connection (SoupSession *session,
1291 SoupMessageQueueItem *item,
1292 gboolean *try_pruning)
1294 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1295 SoupConnection *conn;
1296 SoupSessionHost *host;
1297 SoupAddress *remote_addr, *tunnel_addr;
1298 SoupSSLCredentials *ssl_creds;
1300 int num_pending = 0;
1304 g_return_val_if_fail (soup_connection_get_state (item->conn) != SOUP_CONNECTION_DISCONNECTED, FALSE);
1308 g_mutex_lock (priv->host_lock);
1310 host = get_host_for_message (session, item->msg);
1311 for (conns = host->connections; conns; conns = conns->next) {
1312 if (soup_connection_get_state (conns->data) == SOUP_CONNECTION_IDLE) {
1313 soup_connection_set_state (conns->data, SOUP_CONNECTION_IN_USE);
1314 g_mutex_unlock (priv->host_lock);
1315 item->conn = g_object_ref (conns->data);
1317 } else if (soup_connection_get_state (conns->data) == SOUP_CONNECTION_CONNECTING)
1321 /* Limit the number of pending connections; num_messages / 2
1322 * is somewhat arbitrary...
1324 if (num_pending > host->num_messages / 2) {
1325 g_mutex_unlock (priv->host_lock);
1329 if (host->num_conns >= priv->max_conns_per_host) {
1330 g_mutex_unlock (priv->host_lock);
1334 if (priv->num_conns >= priv->max_conns) {
1335 *try_pruning = TRUE;
1336 g_mutex_unlock (priv->host_lock);
1340 if (item->proxy_addr) {
1341 remote_addr = item->proxy_addr;
1344 remote_addr = host->addr;
1348 uri = soup_message_get_uri (item->msg);
1349 if (uri->scheme == SOUP_URI_SCHEME_HTTPS) {
1350 if (!priv->ssl_creds)
1351 priv->ssl_creds = soup_ssl_get_client_credentials (priv->ssl_ca_file);
1352 ssl_creds = priv->ssl_creds;
1354 if (item->proxy_addr)
1355 tunnel_addr = host->addr;
1359 conn = soup_connection_new (
1360 SOUP_CONNECTION_REMOTE_ADDRESS, remote_addr,
1361 SOUP_CONNECTION_TUNNEL_ADDRESS, tunnel_addr,
1362 SOUP_CONNECTION_PROXY_URI, item->proxy_uri,
1363 SOUP_CONNECTION_SSL_CREDENTIALS, ssl_creds,
1364 SOUP_CONNECTION_SSL_STRICT, priv->ssl_strict,
1365 SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
1366 SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
1367 SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
1369 g_signal_connect (conn, "disconnected",
1370 G_CALLBACK (connection_disconnected),
1373 g_signal_emit (session, signals[CONNECTION_CREATED], 0, conn);
1375 g_hash_table_insert (priv->conns, conn, host);
1379 host->connections = g_slist_prepend (host->connections, conn);
1381 g_mutex_unlock (priv->host_lock);
1382 item->conn = g_object_ref (conn);
1387 soup_session_get_queue (SoupSession *session)
1389 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1395 soup_session_unqueue_item (SoupSession *session,
1396 SoupMessageQueueItem *item)
1398 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1399 SoupSessionHost *host;
1402 g_object_unref (item->conn);
1406 if (item->state != SOUP_MESSAGE_FINISHED) {
1407 g_warning ("finished an item with state %d", item->state);
1411 soup_message_queue_remove (priv->queue, item);
1413 g_mutex_lock (priv->host_lock);
1414 host = get_host_for_message (session, item->msg);
1415 host->num_messages--;
1416 g_mutex_unlock (priv->host_lock);
1418 /* g_signal_handlers_disconnect_by_func doesn't work if you
1419 * have a metamarshal, meaning it doesn't work with
1420 * soup_message_add_header_handler()
1422 g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
1423 0, 0, NULL, NULL, item);
1424 g_signal_emit (session, signals[REQUEST_UNQUEUED], 0, item->msg);
1425 soup_message_queue_item_unref (item);
1429 soup_session_set_item_status (SoupSession *session,
1430 SoupMessageQueueItem *item,
1436 switch (status_code) {
1437 case SOUP_STATUS_CANT_RESOLVE:
1438 case SOUP_STATUS_CANT_CONNECT:
1439 uri = soup_message_get_uri (item->msg);
1440 msg = g_strdup_printf ("%s (%s)",
1441 soup_status_get_phrase (status_code),
1443 soup_message_set_status_full (item->msg, status_code, msg);
1447 case SOUP_STATUS_CANT_RESOLVE_PROXY:
1448 case SOUP_STATUS_CANT_CONNECT_PROXY:
1449 if (item->proxy_uri && item->proxy_uri->host) {
1450 msg = g_strdup_printf ("%s (%s)",
1451 soup_status_get_phrase (status_code),
1452 item->proxy_uri->host);
1453 soup_message_set_status_full (item->msg, status_code, msg);
1457 /* else fall through */
1460 soup_message_set_status (item->msg, status_code);
1465 queue_message (SoupSession *session, SoupMessage *msg,
1466 SoupSessionCallback callback, gpointer user_data)
1468 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1469 SoupMessageQueueItem *item;
1470 SoupSessionHost *host;
1472 item = soup_message_queue_append (priv->queue, msg, callback, user_data);
1474 g_mutex_lock (priv->host_lock);
1475 host = get_host_for_message (session, item->msg);
1476 host->num_messages++;
1477 g_mutex_unlock (priv->host_lock);
1479 if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
1480 soup_message_add_header_handler (
1481 msg, "got_body", "Location",
1482 G_CALLBACK (redirect_handler), item);
1485 g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
1489 * SoupSessionCallback:
1490 * @session: the session
1491 * @msg: the message that has finished
1492 * @user_data: the data passed to soup_session_queue_message
1494 * Prototype for the callback passed to soup_session_queue_message(),
1499 * soup_session_queue_message:
1500 * @session: a #SoupSession
1501 * @msg: (transfer full): the message to queue
1502 * @callback: (allow-none) (scope async): a #SoupSessionCallback which will
1503 * be called after the message completes or when an unrecoverable error occurs.
1504 * @user_data: (allow-none): a pointer passed to @callback.
1506 * Queues the message @msg for sending. All messages are processed
1507 * while the glib main loop runs. If @msg has been processed before,
1508 * any resources related to the time it was last sent are freed.
1510 * Upon message completion, the callback specified in @callback will
1511 * be invoked (in the thread associated with @session's async
1512 * context). If after returning from this callback the message has not
1513 * been requeued, @msg will be unreffed.
1516 soup_session_queue_message (SoupSession *session, SoupMessage *msg,
1517 SoupSessionCallback callback, gpointer user_data)
1519 g_return_if_fail (SOUP_IS_SESSION (session));
1520 g_return_if_fail (SOUP_IS_MESSAGE (msg));
1522 SOUP_SESSION_GET_CLASS (session)->queue_message (session, msg,
1523 callback, user_data);
1527 requeue_message (SoupSession *session, SoupMessage *msg)
1529 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1530 SoupMessageQueueItem *item;
1532 item = soup_message_queue_lookup (priv->queue, msg);
1533 g_return_if_fail (item != NULL);
1534 item->state = SOUP_MESSAGE_RESTARTING;
1535 soup_message_queue_item_unref (item);
1539 * soup_session_requeue_message:
1540 * @session: a #SoupSession
1541 * @msg: the message to requeue
1543 * This causes @msg to be placed back on the queue to be attempted
1547 soup_session_requeue_message (SoupSession *session, SoupMessage *msg)
1549 g_return_if_fail (SOUP_IS_SESSION (session));
1550 g_return_if_fail (SOUP_IS_MESSAGE (msg));
1552 SOUP_SESSION_GET_CLASS (session)->requeue_message (session, msg);
1557 * soup_session_send_message:
1558 * @session: a #SoupSession
1559 * @msg: the message to send
1561 * Synchronously send @msg. This call will not return until the
1562 * transfer is finished successfully or there is an unrecoverable
1565 * @msg is not freed upon return.
1567 * Return value: the HTTP status code of the response
1570 soup_session_send_message (SoupSession *session, SoupMessage *msg)
1572 g_return_val_if_fail (SOUP_IS_SESSION (session), SOUP_STATUS_MALFORMED);
1573 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_STATUS_MALFORMED);
1575 return SOUP_SESSION_GET_CLASS (session)->send_message (session, msg);
1580 * soup_session_pause_message:
1581 * @session: a #SoupSession
1582 * @msg: a #SoupMessage currently running on @session
1584 * Pauses HTTP I/O on @msg. Call soup_session_unpause_message() to
1588 soup_session_pause_message (SoupSession *session,
1591 g_return_if_fail (SOUP_IS_SESSION (session));
1592 g_return_if_fail (SOUP_IS_MESSAGE (msg));
1594 soup_message_io_pause (msg);
1598 * soup_session_unpause_message:
1599 * @session: a #SoupSession
1600 * @msg: a #SoupMessage currently running on @session
1602 * Resumes HTTP I/O on @msg. Use this to resume after calling
1603 * soup_session_pause_message().
1605 * If @msg is being sent via blocking I/O, this will resume reading or
1606 * writing immediately. If @msg is using non-blocking I/O, then
1607 * reading or writing won't resume until you return to the main loop.
1610 soup_session_unpause_message (SoupSession *session,
1613 g_return_if_fail (SOUP_IS_SESSION (session));
1614 g_return_if_fail (SOUP_IS_MESSAGE (msg));
1616 soup_message_io_unpause (msg);
1621 cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
1623 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1624 SoupMessageQueueItem *item;
1626 item = soup_message_queue_lookup (priv->queue, msg);
1627 g_return_if_fail (item != NULL);
1629 if (item->cancellable)
1630 g_cancellable_cancel (item->cancellable);
1632 soup_message_set_status (msg, status_code);
1633 if (soup_message_io_in_progress (msg))
1634 soup_message_io_finished (msg);
1636 item->state = SOUP_MESSAGE_FINISHING;
1638 soup_message_queue_item_unref (item);
1642 * soup_session_cancel_message:
1643 * @session: a #SoupSession
1644 * @msg: the message to cancel
1645 * @status_code: status code to set on @msg (generally
1646 * %SOUP_STATUS_CANCELLED)
1648 * Causes @session to immediately finish processing @msg (regardless
1649 * of its current state) with a final status_code of @status_code. You
1650 * may call this at any time after handing @msg off to @session; if
1651 * @session has started sending the request but has not yet received
1652 * the complete response, then it will close the request's connection.
1653 * Note that with non-idempotent requests (eg, %POST, %PUT, %DELETE)
1654 * it is possible that you might cancel the request after the server
1655 * acts on it, but before it returns a response, leaving the remote
1656 * resource in an unknown state.
1658 * If the message is cancelled while its response body is being read,
1659 * then the response body in @msg will be left partially-filled-in.
1660 * The response headers, on the other hand, will always be either
1661 * empty or complete.
1663 * For messages queued with soup_session_queue_message() (and
1664 * cancelled from the same thread), the callback will be invoked
1665 * before soup_session_cancel_message() returns.
1668 soup_session_cancel_message (SoupSession *session, SoupMessage *msg,
1671 SoupSessionPrivate *priv;
1672 SoupMessageQueueItem *item;
1674 g_return_if_fail (SOUP_IS_SESSION (session));
1675 g_return_if_fail (SOUP_IS_MESSAGE (msg));
1677 priv = SOUP_SESSION_GET_PRIVATE (session);
1678 item = soup_message_queue_lookup (priv->queue, msg);
1679 /* If the message is already ending, don't do anything */
1682 if (item->state == SOUP_MESSAGE_FINISHED) {
1683 soup_message_queue_item_unref (item);
1687 SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg, status_code);
1688 soup_message_queue_item_unref (item);
1692 gather_conns (gpointer key, gpointer host, gpointer data)
1694 SoupConnection *conn = key;
1695 GSList **conns = data;
1697 *conns = g_slist_prepend (*conns, g_object_ref (conn));
1701 * soup_session_abort:
1702 * @session: the session
1704 * Cancels all pending requests in @session.
1707 soup_session_abort (SoupSession *session)
1709 SoupSessionPrivate *priv;
1710 SoupMessageQueueItem *item;
1713 g_return_if_fail (SOUP_IS_SESSION (session));
1714 priv = SOUP_SESSION_GET_PRIVATE (session);
1716 for (item = soup_message_queue_first (priv->queue);
1718 item = soup_message_queue_next (priv->queue, item)) {
1719 soup_session_cancel_message (session, item->msg,
1720 SOUP_STATUS_CANCELLED);
1723 /* Close all connections */
1724 g_mutex_lock (priv->host_lock);
1726 g_hash_table_foreach (priv->conns, gather_conns, &conns);
1728 g_mutex_unlock (priv->host_lock);
1729 for (c = conns; c; c = c->next) {
1730 soup_connection_disconnect (c->data);
1731 g_object_unref (c->data);
1734 g_slist_free (conns);
1738 * soup_session_prepare_for_uri:
1739 * @session: a #SoupSession
1740 * @uri: a #SoupURI which may be required
1742 * Tells @session that @uri may be requested shortly, and so the
1743 * session can try to prepare (resolving the domain name, obtaining
1744 * proxy address, etc.) in order to work more quickly once the URI is
1745 * actually requested.
1747 * This method acts asynchronously, in @session's %async_context.
1748 * If you are using #SoupSessionSync and do not have a main loop running,
1749 * then you can't use this method.
1754 soup_session_prepare_for_uri (SoupSession *session, SoupURI *uri)
1756 SoupSessionPrivate *priv;
1757 SoupSessionHost *host;
1760 g_return_if_fail (SOUP_IS_SESSION (session));
1761 g_return_if_fail (uri != NULL);
1766 priv = SOUP_SESSION_GET_PRIVATE (session);
1768 g_mutex_lock (priv->host_lock);
1769 host = get_host_for_uri (session, uri);
1770 addr = g_object_ref (host->addr);
1771 g_mutex_unlock (priv->host_lock);
1773 soup_address_resolve_async (addr, priv->async_context,
1778 * soup_session_add_feature:
1779 * @session: a #SoupSession
1780 * @feature: an object that implements #SoupSessionFeature
1782 * Adds @feature's functionality to @session. You can also add a
1783 * feature to the session at construct time by using the
1784 * %SOUP_SESSION_ADD_FEATURE property.
1789 soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature)
1791 SoupSessionPrivate *priv;
1793 g_return_if_fail (SOUP_IS_SESSION (session));
1794 g_return_if_fail (SOUP_IS_SESSION_FEATURE (feature));
1796 priv = SOUP_SESSION_GET_PRIVATE (session);
1797 priv->features = g_slist_prepend (priv->features, g_object_ref (feature));
1798 g_hash_table_remove_all (priv->features_cache);
1799 soup_session_feature_attach (feature, session);
1803 * soup_session_add_feature_by_type:
1804 * @session: a #SoupSession
1805 * @feature_type: the #GType of a class that implements #SoupSessionFeature
1807 * Creates a new feature of type @feature_type and adds it to
1808 * @session. You can use this instead of soup_session_add_feature() in
1809 * the case wher you don't need to customize the new feature in any
1810 * way. You can also add a feature to the session at construct time by
1811 * using the %SOUP_SESSION_ADD_FEATURE_BY_TYPE property.
1816 soup_session_add_feature_by_type (SoupSession *session, GType feature_type)
1818 SoupSessionFeature *feature;
1820 g_return_if_fail (SOUP_IS_SESSION (session));
1821 g_return_if_fail (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE));
1823 feature = g_object_new (feature_type, NULL);
1824 soup_session_add_feature (session, feature);
1825 g_object_unref (feature);
1829 * soup_session_remove_feature:
1830 * @session: a #SoupSession
1831 * @feature: a feature that has previously been added to @session
1833 * Removes @feature's functionality from @session.
1838 soup_session_remove_feature (SoupSession *session, SoupSessionFeature *feature)
1840 SoupSessionPrivate *priv;
1842 g_return_if_fail (SOUP_IS_SESSION (session));
1844 priv = SOUP_SESSION_GET_PRIVATE (session);
1845 if (g_slist_find (priv->features, feature)) {
1846 priv->features = g_slist_remove (priv->features, feature);
1847 g_hash_table_remove_all (priv->features_cache);
1848 soup_session_feature_detach (feature, session);
1849 g_object_unref (feature);
1854 * soup_session_remove_feature_by_type:
1855 * @session: a #SoupSession
1856 * @feature_type: the #GType of a class that implements #SoupSessionFeature
1858 * Removes all features of type @feature_type (or any subclass of
1859 * @feature_type) from @session. You can also remove standard features
1860 * from the session at construct time by using the
1861 * %SOUP_SESSION_REMOVE_FEATURE_BY_TYPE property.
1866 soup_session_remove_feature_by_type (SoupSession *session, GType feature_type)
1868 SoupSessionPrivate *priv;
1871 g_return_if_fail (SOUP_IS_SESSION (session));
1873 priv = SOUP_SESSION_GET_PRIVATE (session);
1875 for (f = priv->features; f; f = f->next) {
1876 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type)) {
1877 soup_session_remove_feature (session, f->data);
1884 * soup_session_get_features:
1885 * @session: a #SoupSession
1886 * @feature_type: the #GType of the class of features to get
1888 * Generates a list of @session's features of type @feature_type. (If
1889 * you want to see all features, you can pass %G_TYPE_SESSION_FEATURE
1890 * for @feature_type.)
1892 * Return value: (transfer container): a list of features. You must
1893 * free the list, but not its contents
1898 soup_session_get_features (SoupSession *session, GType feature_type)
1900 SoupSessionPrivate *priv;
1903 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
1905 priv = SOUP_SESSION_GET_PRIVATE (session);
1906 for (f = priv->features, ret = NULL; f; f = f->next) {
1907 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
1908 ret = g_slist_prepend (ret, f->data);
1910 return g_slist_reverse (ret);
1914 * soup_session_get_feature:
1915 * @session: a #SoupSession
1916 * @feature_type: the #GType of the feature to get
1918 * Gets the first feature in @session of type @feature_type. For
1919 * features where there may be more than one feature of a given type,
1920 * use soup_session_get_features().
1922 * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
1923 * feature is owned by @session.
1927 SoupSessionFeature *
1928 soup_session_get_feature (SoupSession *session, GType feature_type)
1930 SoupSessionPrivate *priv;
1931 SoupSessionFeature *feature;
1934 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
1936 priv = SOUP_SESSION_GET_PRIVATE (session);
1938 feature = g_hash_table_lookup (priv->features_cache,
1939 GSIZE_TO_POINTER (feature_type));
1943 for (f = priv->features; f; f = f->next) {
1945 if (G_TYPE_CHECK_INSTANCE_TYPE (feature, feature_type)) {
1946 g_hash_table_insert (priv->features_cache,
1947 GSIZE_TO_POINTER (feature_type),
1956 * soup_session_get_feature_for_message:
1957 * @session: a #SoupSession
1958 * @feature_type: the #GType of the feature to get
1959 * @msg: a #SoupMessage
1961 * Gets the first feature in @session of type @feature_type, provided
1962 * that it is not disabled for @msg. As with
1963 * soup_session_get_feature(), this should only be used for features
1964 * where @feature_type is only expected to match a single feature. In
1965 * particular, if there are two matching features, and the first is
1966 * disabled on @msg, and the second is not, then this will return
1967 * %NULL, not the second feature.
1969 * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
1970 * feature is owned by @session.
1974 SoupSessionFeature *
1975 soup_session_get_feature_for_message (SoupSession *session, GType feature_type,
1978 SoupSessionFeature *feature;
1980 feature = soup_session_get_feature (session, feature_type);
1981 if (feature && soup_message_disables_feature (msg, feature))