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"
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
67 GSList *connections; /* CONTAINS: SoupConnection */
72 gboolean ssl_fallback;
77 SoupSSLCredentials *ssl_creds;
80 SoupMessageQueue *queue;
83 char *accept_language;
84 gboolean accept_language_auto;
87 GHashTable *features_cache;
89 GHashTable *hosts; /* char* -> SoupSessionHost */
90 GHashTable *conns; /* SoupConnection -> SoupSessionHost */
92 guint max_conns, max_conns_per_host;
93 guint io_timeout, idle_timeout;
95 /* Must hold the host_lock before potentially creating a
96 * new SoupSessionHost, or adding/removing a connection.
97 * Must not emit signals or destroy objects while holding it.
101 GMainContext *async_context;
104 } SoupSessionPrivate;
105 #define SOUP_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION, SoupSessionPrivate))
107 static void free_host (SoupSessionHost *host);
109 static void queue_message (SoupSession *session, SoupMessage *msg,
110 SoupSessionCallback callback, gpointer user_data);
111 static void requeue_message (SoupSession *session, SoupMessage *msg);
112 static void cancel_message (SoupSession *session, SoupMessage *msg,
114 static void auth_required (SoupSession *session, SoupMessage *msg,
115 SoupAuth *auth, gboolean retrying);
116 static void flush_queue (SoupSession *session);
118 static void auth_manager_authenticate (SoupAuthManager *manager,
119 SoupMessage *msg, SoupAuth *auth,
120 gboolean retrying, gpointer user_data);
122 #define SOUP_SESSION_MAX_CONNS_DEFAULT 10
123 #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2
125 #define SOUP_SESSION_MAX_REDIRECTION_COUNT 20
127 #define SOUP_SESSION_USER_AGENT_BASE "libsoup/" PACKAGE_VERSION
129 G_DEFINE_ABSTRACT_TYPE (SoupSession, soup_session, G_TYPE_OBJECT)
141 static guint signals[LAST_SIGNAL] = { 0 };
148 PROP_MAX_CONNS_PER_HOST,
155 PROP_ACCEPT_LANGUAGE,
156 PROP_ACCEPT_LANGUAGE_AUTO,
159 PROP_ADD_FEATURE_BY_TYPE,
160 PROP_REMOVE_FEATURE_BY_TYPE,
165 static void set_property (GObject *object, guint prop_id,
166 const GValue *value, GParamSpec *pspec);
167 static void get_property (GObject *object, guint prop_id,
168 GValue *value, GParamSpec *pspec);
171 soup_session_init (SoupSession *session)
173 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
174 SoupAuthManager *auth_manager;
176 priv->queue = soup_message_queue_new (session);
178 priv->host_lock = g_mutex_new ();
179 priv->hosts = g_hash_table_new_full (soup_uri_host_hash,
181 NULL, (GDestroyNotify)free_host);
182 priv->conns = g_hash_table_new (NULL, NULL);
184 priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
185 priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
187 priv->features_cache = g_hash_table_new (NULL, NULL);
189 auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER_NTLM, NULL);
190 g_signal_connect (auth_manager, "authenticate",
191 G_CALLBACK (auth_manager_authenticate), session);
192 soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
193 SOUP_TYPE_AUTH_BASIC);
194 soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
195 SOUP_TYPE_AUTH_DIGEST);
196 soup_session_add_feature (session, SOUP_SESSION_FEATURE (auth_manager));
197 g_object_unref (auth_manager);
199 /* We'll be doing DNS continuously-ish while the session is active,
200 * so hold a ref on the default GResolver.
202 priv->resolver = g_resolver_get_default ();
204 priv->ssl_strict = TRUE;
208 dispose (GObject *object)
210 SoupSession *session = SOUP_SESSION (object);
211 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
213 soup_session_abort (session);
215 while (priv->features)
216 soup_session_remove_feature (session, priv->features->data);
218 G_OBJECT_CLASS (soup_session_parent_class)->dispose (object);
222 finalize (GObject *object)
224 SoupSession *session = SOUP_SESSION (object);
225 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
227 soup_message_queue_destroy (priv->queue);
229 g_mutex_free (priv->host_lock);
230 g_hash_table_destroy (priv->hosts);
231 g_hash_table_destroy (priv->conns);
233 g_free (priv->user_agent);
234 g_free (priv->accept_language);
236 if (priv->ssl_ca_file)
237 g_free (priv->ssl_ca_file);
239 soup_ssl_free_client_credentials (priv->ssl_creds);
241 if (priv->async_context)
242 g_main_context_unref (priv->async_context);
244 g_hash_table_destroy (priv->features_cache);
246 g_object_unref (priv->resolver);
248 G_OBJECT_CLASS (soup_session_parent_class)->finalize (object);
252 soup_session_class_init (SoupSessionClass *session_class)
254 GObjectClass *object_class = G_OBJECT_CLASS (session_class);
256 g_type_class_add_private (session_class, sizeof (SoupSessionPrivate));
258 /* virtual method definition */
259 session_class->queue_message = queue_message;
260 session_class->requeue_message = requeue_message;
261 session_class->cancel_message = cancel_message;
262 session_class->auth_required = auth_required;
263 session_class->flush_queue = flush_queue;
265 /* virtual method override */
266 object_class->dispose = dispose;
267 object_class->finalize = finalize;
268 object_class->set_property = set_property;
269 object_class->get_property = get_property;
274 * SoupSession::request-queued:
275 * @session: the session
276 * @msg: the request that was queued
278 * Emitted when a request is queued on @session. (Note that
279 * "queued" doesn't just mean soup_session_queue_message();
280 * soup_session_send_message() implicitly queues the message
283 * When sending a request, first #SoupSession::request_queued
284 * is emitted, indicating that the session has become aware of
287 * Once a connection is available to send the request on, the
288 * session emits #SoupSession::request_started. Then, various
289 * #SoupMessage signals are emitted as the message is
290 * processed. If the message is requeued, it will emit
291 * #SoupMessage::restarted, which will then be followed by
292 * another #SoupSession::request_started and another set of
293 * #SoupMessage signals when the message is re-sent.
295 * Eventually, the message will emit #SoupMessage::finished.
296 * Normally, this signals the completion of message
297 * processing. However, it is possible that the application
298 * will requeue the message from the "finished" handler (or
299 * equivalently, from the soup_session_queue_message()
300 * callback). In that case, the process will loop back to
301 * #SoupSession::request_started.
303 * Eventually, a message will reach "finished" and not be
304 * requeued. At that point, the session will emit
305 * #SoupSession::request_unqueued to indicate that it is done
308 * To sum up: #SoupSession::request_queued and
309 * #SoupSession::request_unqueued are guaranteed to be emitted
310 * exactly once, but #SoupSession::request_started and
311 * #SoupMessage::finished (and all of the other #SoupMessage
312 * signals) may be invoked multiple times for a given message.
316 signals[REQUEST_QUEUED] =
317 g_signal_new ("request-queued",
318 G_OBJECT_CLASS_TYPE (object_class),
322 soup_marshal_NONE__OBJECT,
327 * SoupSession::request-started:
328 * @session: the session
329 * @msg: the request being sent
330 * @socket: the socket the request is being sent on
332 * Emitted just before a request is sent. See
333 * #SoupSession::request_queued for a detailed description of
334 * the message lifecycle within a session.
336 signals[REQUEST_STARTED] =
337 g_signal_new ("request-started",
338 G_OBJECT_CLASS_TYPE (object_class),
340 G_STRUCT_OFFSET (SoupSessionClass, request_started),
342 soup_marshal_NONE__OBJECT_OBJECT,
348 * SoupSession::request-unqueued:
349 * @session: the session
350 * @msg: the request that was unqueued
352 * Emitted when a request is removed from @session's queue,
353 * indicating that @session is done with it. See
354 * #SoupSession::request_queued for a detailed description of the
355 * message lifecycle within a session.
359 signals[REQUEST_UNQUEUED] =
360 g_signal_new ("request-unqueued",
361 G_OBJECT_CLASS_TYPE (object_class),
365 soup_marshal_NONE__OBJECT,
370 * SoupSession::authenticate:
371 * @session: the session
372 * @msg: the #SoupMessage being sent
373 * @auth: the #SoupAuth to authenticate
374 * @retrying: %TRUE if this is the second (or later) attempt
376 * Emitted when the session requires authentication. If
377 * credentials are available call soup_auth_authenticate() on
378 * @auth. If these credentials fail, the signal will be
379 * emitted again, with @retrying set to %TRUE, which will
380 * continue until you return without calling
381 * soup_auth_authenticate() on @auth.
383 * Note that this may be emitted before @msg's body has been
386 * If you call soup_session_pause_message() on @msg before
387 * returning, then you can authenticate @auth asynchronously
388 * (as long as you g_object_ref() it to make sure it doesn't
389 * get destroyed), and then unpause @msg when you are ready
390 * for it to continue.
392 signals[AUTHENTICATE] =
393 g_signal_new ("authenticate",
394 G_OBJECT_CLASS_TYPE (object_class),
396 G_STRUCT_OFFSET (SoupSessionClass, authenticate),
398 soup_marshal_NONE__OBJECT_OBJECT_BOOLEAN,
404 signals[CONNECTION_CREATED] =
405 g_signal_new ("connection-created",
406 G_OBJECT_CLASS_TYPE (object_class),
410 soup_marshal_NONE__OBJECT,
412 /* SoupConnection is private, so we can't use
413 * SOUP_TYPE_CONNECTION here.
418 g_signal_new ("tunneling",
419 G_OBJECT_CLASS_TYPE (object_class),
423 soup_marshal_NONE__OBJECT,
425 /* SoupConnection is private, so we can't use
426 * SOUP_TYPE_CONNECTION here.
433 * SOUP_SESSION_PROXY_URI:
435 * Alias for the #SoupSession:proxy-uri property. (The HTTP
436 * proxy to use for this session.)
438 g_object_class_install_property (
439 object_class, PROP_PROXY_URI,
440 g_param_spec_boxed (SOUP_SESSION_PROXY_URI,
442 "The HTTP Proxy to use for this session",
446 * SOUP_SESSION_MAX_CONNS:
448 * Alias for the #SoupSession:max-conns property. (The maximum
449 * number of connections that the session can open at once.)
451 g_object_class_install_property (
452 object_class, PROP_MAX_CONNS,
453 g_param_spec_int (SOUP_SESSION_MAX_CONNS,
454 "Max Connection Count",
455 "The maximum number of connections that the session can open at once",
458 SOUP_SESSION_MAX_CONNS_DEFAULT,
461 * SOUP_SESSION_MAX_CONNS_PER_HOST:
463 * Alias for the #SoupSession:max-conns-per-host property.
464 * (The maximum number of connections that the session can
465 * open at once to a given host.)
467 g_object_class_install_property (
468 object_class, PROP_MAX_CONNS_PER_HOST,
469 g_param_spec_int (SOUP_SESSION_MAX_CONNS_PER_HOST,
470 "Max Per-Host Connection Count",
471 "The maximum number of connections that the session can open at once to a given host",
474 SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT,
477 * SoupSession:idle-timeout:
479 * Connection lifetime when idle
484 * SOUP_SESSION_IDLE_TIMEOUT:
486 * Alias for the #SoupSession:idle-timeout property. (The idle
487 * connection lifetime.)
491 g_object_class_install_property (
492 object_class, PROP_IDLE_TIMEOUT,
493 g_param_spec_uint (SOUP_SESSION_IDLE_TIMEOUT,
495 "Connection lifetime when idle",
499 * SoupSession:use-ntlm:
501 * Whether or not to use NTLM authentication.
503 * Deprecated: use soup_session_add_feature_by_type() with
504 * #SOUP_TYPE_AUTH_NTLM.
507 * SOUP_SESSION_USE_NTLM:
509 * Alias for the #SoupSession:use-ntlm property. (Whether or
510 * not to use NTLM authentication.)
512 g_object_class_install_property (
513 object_class, PROP_USE_NTLM,
514 g_param_spec_boolean (SOUP_SESSION_USE_NTLM,
516 "Whether or not to use NTLM authentication",
520 * SOUP_SESSION_SSL_CA_FILE:
522 * Alias for the #SoupSession:ssl-ca-file property. (File
523 * containing SSL CA certificates.)
525 g_object_class_install_property (
526 object_class, PROP_SSL_CA_FILE,
527 g_param_spec_string (SOUP_SESSION_SSL_CA_FILE,
529 "File containing SSL CA certificates",
533 * SOUP_SESSION_SSL_STRICT:
535 * Alias for the #SoupSession:ignore-ssl-cert-errors
536 * property. By default, when validating certificates against
537 * a CA file, Soup will consider invalid certificates as a
538 * connection error. Setting this property to %TRUE makes soup
539 * ignore the errors, and make the connection.
543 g_object_class_install_property (
544 object_class, PROP_SSL_STRICT,
545 g_param_spec_boolean (SOUP_SESSION_SSL_STRICT,
546 "Strictly validate SSL certificates",
547 "Whether certificate errors should be considered a connection error",
551 * SOUP_SESSION_ASYNC_CONTEXT:
553 * Alias for the #SoupSession:async-context property. (The
554 * session's #GMainContext.)
556 g_object_class_install_property (
557 object_class, PROP_ASYNC_CONTEXT,
558 g_param_spec_pointer (SOUP_SESSION_ASYNC_CONTEXT,
559 "Async GMainContext",
560 "The GMainContext to dispatch async I/O in",
561 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
563 * SOUP_SESSION_TIMEOUT:
565 * Alias for the #SoupSession:timeout property. (The timeout
566 * in seconds for blocking socket I/O operations.)
568 g_object_class_install_property (
569 object_class, PROP_TIMEOUT,
570 g_param_spec_uint (SOUP_SESSION_TIMEOUT,
572 "Value in seconds to timeout a blocking I/O",
577 * SoupSession:user-agent:
579 * If non-%NULL, the value to use for the "User-Agent" header
580 * on #SoupMessage<!-- -->s sent from this session.
582 * RFC 2616 says: "The User-Agent request-header field
583 * contains information about the user agent originating the
584 * request. This is for statistical purposes, the tracing of
585 * protocol violations, and automated recognition of user
586 * agents for the sake of tailoring responses to avoid
587 * particular user agent limitations. User agents SHOULD
588 * include this field with requests."
590 * The User-Agent header contains a list of one or more
591 * product tokens, separated by whitespace, with the most
592 * significant product token coming first. The tokens must be
593 * brief, ASCII, and mostly alphanumeric (although "-", "_",
594 * and "." are also allowed), and may optionally include a "/"
595 * followed by a version string. You may also put comments,
596 * enclosed in parentheses, between or after the tokens.
598 * If you set a %user_agent property that has trailing
599 * whitespace, #SoupSession will append its own product token
600 * (eg, "<literal>libsoup/2.3.2</literal>") to the end of the
604 * SOUP_SESSION_USER_AGENT:
606 * Alias for the #SoupSession:user-agent property, qv.
608 g_object_class_install_property (
609 object_class, PROP_USER_AGENT,
610 g_param_spec_string (SOUP_SESSION_USER_AGENT,
617 * SoupSession:accept-language:
619 * If non-%NULL, the value to use for the "Accept-Language" header
620 * on #SoupMessage<!-- -->s sent from this session.
622 * Setting this will disable
623 * #SoupSession:accept-language-auto.
628 * SOUP_SESSION_ACCEPT_LANGUAGE:
630 * Alias for the #SoupSession:accept-language property, qv.
634 g_object_class_install_property (
635 object_class, PROP_ACCEPT_LANGUAGE,
636 g_param_spec_string (SOUP_SESSION_ACCEPT_LANGUAGE,
637 "Accept-Language string",
638 "Accept-Language string",
643 * SoupSession:accept-language-auto:
645 * If %TRUE, #SoupSession will automatically set the string
646 * for the "Accept-Language" header on every #SoupMessage
647 * sent, based on the return value of g_get_language_names().
649 * Setting this will override any previous value of
650 * #SoupSession:accept-language.
655 * SOUP_SESSION_ACCEPT_LANGUAGE_AUTO:
657 * Alias for the #SoupSession:accept-language-auto property, qv.
661 g_object_class_install_property (
662 object_class, PROP_ACCEPT_LANGUAGE_AUTO,
663 g_param_spec_boolean (SOUP_SESSION_ACCEPT_LANGUAGE_AUTO,
664 "Accept-Language automatic mode",
665 "Accept-Language automatic mode",
670 * SoupSession:add-feature: (skip)
672 * Add a feature object to the session. (Shortcut for calling
673 * soup_session_add_feature().)
678 * SOUP_SESSION_ADD_FEATURE: (skip)
680 * Alias for the #SoupSession:add-feature property. (Shortcut
681 * for calling soup_session_add_feature().
685 g_object_class_install_property (
686 object_class, PROP_ADD_FEATURE,
687 g_param_spec_object (SOUP_SESSION_ADD_FEATURE,
689 "Add a feature object to the session",
690 SOUP_TYPE_SESSION_FEATURE,
693 * SoupSession:add-feature-by-type: (skip)
695 * Add a feature object of the given type to the session.
696 * (Shortcut for calling soup_session_add_feature_by_type().)
701 * SOUP_SESSION_ADD_FEATURE_BY_TYPE: (skip)
703 * Alias for the #SoupSession:add-feature-by-type property.
704 * (Shortcut for calling soup_session_add_feature_by_type().
708 g_object_class_install_property (
709 object_class, PROP_ADD_FEATURE_BY_TYPE,
710 g_param_spec_gtype (SOUP_SESSION_ADD_FEATURE_BY_TYPE,
711 "Add Feature By Type",
712 "Add a feature object of the given type to the session",
713 SOUP_TYPE_SESSION_FEATURE,
716 * SoupSession:remove-feature-by-type: (skip)
718 * Remove feature objects from the session. (Shortcut for
719 * calling soup_session_remove_feature_by_type().)
724 * SOUP_SESSION_REMOVE_FEATURE_BY_TYPE: (skip)
726 * Alias for the #SoupSession:remove-feature-by-type
727 * property. (Shortcut for calling
728 * soup_session_remove_feature_by_type().
732 g_object_class_install_property (
733 object_class, PROP_REMOVE_FEATURE_BY_TYPE,
734 g_param_spec_gtype (SOUP_SESSION_REMOVE_FEATURE_BY_TYPE,
735 "Remove Feature By Type",
736 "Remove features of the given type from the session",
737 SOUP_TYPE_SESSION_FEATURE,
742 safe_str_equal (const char *a, const char *b)
747 if ((a && !b) || (b && !a))
750 return strcmp (a, b) == 0;
753 /* Converts a language in POSIX format and to be RFC2616 compliant */
754 /* Based on code from epiphany-webkit (ephy_langs_append_languages()) */
756 posix_lang_to_rfc2616 (const gchar *language)
758 /* Don't include charset variants, etc */
759 if (strchr (language, '.') || strchr (language, '@'))
762 /* Ignore "C" locale, which g_get_language_names() always
763 * includes as a fallback.
765 if (!strcmp (language, "C"))
768 return g_strdelimit (g_ascii_strdown (language, -1), "_", '-');
771 /* Converts @quality from 0-100 to 0.0-1.0 and appends to @str */
773 add_quality_value (const gchar *str, int quality)
775 g_return_val_if_fail (str != NULL, NULL);
777 if (quality >= 0 && quality < 100) {
778 /* We don't use %.02g because of "." vs "," locale issues */
780 return g_strdup_printf ("%s;q=0.%02d", str, quality);
782 return g_strdup_printf ("%s;q=0.%d", str, quality / 10);
784 return g_strdup (str);
787 /* Returns a RFC2616 compliant languages list from system locales */
789 accept_languages_from_system (void)
791 const char * const * lang_names;
792 GPtrArray *langs = NULL;
793 char *lang, **langs_array, *langs_str;
797 lang_names = g_get_language_names ();
798 g_return_val_if_fail (lang_names != NULL, NULL);
800 /* Build the array of languages */
801 langs = g_ptr_array_new ();
802 for (i = 0; lang_names[i] != NULL; i++) {
803 lang = posix_lang_to_rfc2616 (lang_names[i]);
805 g_ptr_array_add (langs, lang);
808 /* Add quality values */
811 else if (langs->len < 20)
816 for (i = 0; i < langs->len; i++) {
817 lang = langs->pdata[i];
818 langs->pdata[i] = add_quality_value (lang, 100 - i * delta);
822 /* Fallback: add "en" if list is empty */
824 g_ptr_array_add (langs, g_strdup ("en"));
826 g_ptr_array_add (langs, NULL);
827 langs_array = (char **)langs->pdata;
828 langs_str = g_strjoinv (", ", langs_array);
830 g_strfreev (langs_array);
831 g_ptr_array_free (langs, FALSE);
837 set_property (GObject *object, guint prop_id,
838 const GValue *value, GParamSpec *pspec)
840 SoupSession *session = SOUP_SESSION (object);
841 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
843 gboolean ca_file_changed = FALSE;
844 const char *new_ca_file, *user_agent;
845 SoupSessionFeature *feature;
849 uri = g_value_get_boxed (value);
852 soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER);
853 feature = SOUP_SESSION_FEATURE (soup_proxy_resolver_static_new (uri));
854 soup_session_add_feature (session, feature);
855 g_object_unref (feature);
857 soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
859 soup_session_abort (session);
862 priv->max_conns = g_value_get_int (value);
864 case PROP_MAX_CONNS_PER_HOST:
865 priv->max_conns_per_host = g_value_get_int (value);
868 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
870 if (g_value_get_boolean (value))
871 soup_session_feature_add_feature (feature, SOUP_TYPE_AUTH_NTLM);
873 soup_session_feature_remove_feature (feature, SOUP_TYPE_AUTH_NTLM);
875 g_warning ("Trying to set use-ntlm on session with no auth-manager");
877 case PROP_SSL_CA_FILE:
878 new_ca_file = g_value_get_string (value);
880 if (!safe_str_equal (priv->ssl_ca_file, new_ca_file))
881 ca_file_changed = TRUE;
883 g_free (priv->ssl_ca_file);
884 priv->ssl_ca_file = g_strdup (new_ca_file);
886 if (ca_file_changed && priv->ssl_creds) {
887 soup_ssl_free_client_credentials (priv->ssl_creds);
888 priv->ssl_creds = NULL;
892 case PROP_SSL_STRICT:
893 priv->ssl_strict = g_value_get_boolean (value);
895 case PROP_ASYNC_CONTEXT:
896 priv->async_context = g_value_get_pointer (value);
897 if (priv->async_context)
898 g_main_context_ref (priv->async_context);
901 priv->io_timeout = g_value_get_uint (value);
903 case PROP_USER_AGENT:
904 g_free (priv->user_agent);
905 user_agent = g_value_get_string (value);
907 priv->user_agent = NULL;
908 else if (!*user_agent) {
910 g_strdup (SOUP_SESSION_USER_AGENT_BASE);
911 } else if (g_str_has_suffix (user_agent, " ")) {
913 g_strdup_printf ("%s%s", user_agent,
914 SOUP_SESSION_USER_AGENT_BASE);
916 priv->user_agent = g_strdup (user_agent);
918 case PROP_ACCEPT_LANGUAGE:
919 g_free (priv->accept_language);
920 priv->accept_language = g_strdup (g_value_get_string (value));
921 priv->accept_language_auto = FALSE;
923 case PROP_ACCEPT_LANGUAGE_AUTO:
924 priv->accept_language_auto = g_value_get_boolean (value);
925 if (priv->accept_language) {
926 g_free (priv->accept_language);
927 priv->accept_language = NULL;
930 /* Get languages from system if needed */
931 if (priv->accept_language_auto)
932 priv->accept_language = accept_languages_from_system ();
934 case PROP_IDLE_TIMEOUT:
935 priv->idle_timeout = g_value_get_uint (value);
937 case PROP_ADD_FEATURE:
938 soup_session_add_feature (session, g_value_get_object (value));
940 case PROP_ADD_FEATURE_BY_TYPE:
941 soup_session_add_feature_by_type (session, g_value_get_gtype (value));
943 case PROP_REMOVE_FEATURE_BY_TYPE:
944 soup_session_remove_feature_by_type (session, g_value_get_gtype (value));
947 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
953 get_property (GObject *object, guint prop_id,
954 GValue *value, GParamSpec *pspec)
956 SoupSession *session = SOUP_SESSION (object);
957 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
958 SoupSessionFeature *feature;
962 feature = soup_session_get_feature (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
964 g_object_get_property (G_OBJECT (feature),
965 SOUP_PROXY_RESOLVER_STATIC_PROXY_URI,
968 g_value_set_boxed (value, NULL);
971 g_value_set_int (value, priv->max_conns);
973 case PROP_MAX_CONNS_PER_HOST:
974 g_value_set_int (value, priv->max_conns_per_host);
977 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
979 g_value_set_boolean (value, soup_session_feature_has_feature (feature, SOUP_TYPE_AUTH_NTLM));
981 g_value_set_boolean (value, FALSE);
983 case PROP_SSL_CA_FILE:
984 g_value_set_string (value, priv->ssl_ca_file);
986 case PROP_SSL_STRICT:
987 g_value_set_boolean (value, priv->ssl_strict);
989 case PROP_ASYNC_CONTEXT:
990 g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
993 g_value_set_uint (value, priv->io_timeout);
995 case PROP_USER_AGENT:
996 g_value_set_string (value, priv->user_agent);
998 case PROP_ACCEPT_LANGUAGE:
999 g_value_set_string (value, priv->accept_language);
1001 case PROP_ACCEPT_LANGUAGE_AUTO:
1002 g_value_set_boolean (value, priv->accept_language_auto);
1004 case PROP_IDLE_TIMEOUT:
1005 g_value_set_uint (value, priv->idle_timeout);
1008 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1015 * soup_session_get_async_context:
1016 * @session: a #SoupSession
1018 * Gets @session's async_context. This does not add a ref to the
1019 * context, so you will need to ref it yourself if you want it to
1020 * outlive its session.
1022 * Return value: (transfer none): @session's #GMainContext, which may
1026 soup_session_get_async_context (SoupSession *session)
1028 SoupSessionPrivate *priv;
1030 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
1031 priv = SOUP_SESSION_GET_PRIVATE (session);
1033 return priv->async_context;
1038 static SoupSessionHost *
1039 soup_session_host_new (SoupSession *session, SoupURI *uri)
1041 SoupSessionHost *host;
1043 host = g_slice_new0 (SoupSessionHost);
1044 host->uri = soup_uri_copy_host (uri);
1045 host->addr = soup_address_new (host->uri->host, host->uri->port);
1050 /* Requires host_lock to be locked */
1051 static SoupSessionHost *
1052 get_host_for_uri (SoupSession *session, SoupURI *uri)
1054 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1055 SoupSessionHost *host;
1057 host = g_hash_table_lookup (priv->hosts, uri);
1061 host = soup_session_host_new (session, uri);
1062 g_hash_table_insert (priv->hosts, host->uri, host);
1067 /* Note: get_host_for_message doesn't lock the host_lock. The caller
1068 * must do it itself if there's a chance the host doesn't already
1071 static SoupSessionHost *
1072 get_host_for_message (SoupSession *session, SoupMessage *msg)
1074 return get_host_for_uri (session, soup_message_get_uri (msg));
1078 free_host (SoupSessionHost *host)
1080 while (host->connections) {
1081 SoupConnection *conn = host->connections->data;
1083 host->connections = g_slist_remove (host->connections, conn);
1084 soup_connection_disconnect (conn);
1087 soup_uri_free (host->uri);
1088 g_object_unref (host->addr);
1089 g_slice_free (SoupSessionHost, host);
1093 auth_required (SoupSession *session, SoupMessage *msg,
1094 SoupAuth *auth, gboolean retrying)
1096 g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
1100 auth_manager_authenticate (SoupAuthManager *manager, SoupMessage *msg,
1101 SoupAuth *auth, gboolean retrying,
1104 SOUP_SESSION_GET_CLASS (session)->auth_required (
1105 session, msg, auth, retrying);
1108 #define SOUP_METHOD_IS_SAFE(method) (method == SOUP_METHOD_GET || \
1109 method == SOUP_METHOD_HEAD || \
1110 method == SOUP_METHOD_OPTIONS || \
1111 method == SOUP_METHOD_PROPFIND)
1114 redirect_handler (SoupMessage *msg, gpointer user_data)
1116 SoupMessageQueueItem *item = user_data;
1117 SoupSession *session = item->session;
1118 const char *new_loc;
1121 new_loc = soup_message_headers_get_one (msg->response_headers,
1123 g_return_if_fail (new_loc != NULL);
1125 if (item->redirection_count >= SOUP_SESSION_MAX_REDIRECTION_COUNT) {
1126 soup_session_cancel_message (session, msg, SOUP_STATUS_TOO_MANY_REDIRECTS);
1129 item->redirection_count++;
1131 if (msg->status_code == SOUP_STATUS_SEE_OTHER ||
1132 (msg->status_code == SOUP_STATUS_FOUND &&
1133 !SOUP_METHOD_IS_SAFE (msg->method)) ||
1134 (msg->status_code == SOUP_STATUS_MOVED_PERMANENTLY &&
1135 msg->method == SOUP_METHOD_POST)) {
1136 if (msg->method != SOUP_METHOD_HEAD) {
1137 /* Redirect using a GET */
1139 SOUP_MESSAGE_METHOD, SOUP_METHOD_GET,
1142 soup_message_set_request (msg, NULL,
1143 SOUP_MEMORY_STATIC, NULL, 0);
1144 soup_message_headers_set_encoding (msg->request_headers,
1145 SOUP_ENCODING_NONE);
1146 } else if (msg->status_code == SOUP_STATUS_MOVED_PERMANENTLY ||
1147 msg->status_code == SOUP_STATUS_TEMPORARY_REDIRECT ||
1148 msg->status_code == SOUP_STATUS_FOUND) {
1149 /* Don't redirect non-safe methods */
1150 if (!SOUP_METHOD_IS_SAFE (msg->method))
1153 /* Three possibilities:
1155 * 1) This was a non-3xx response that happened to
1156 * have a "Location" header
1157 * 2) It's a non-redirecty 3xx response (300, 304,
1159 * 3) It's some newly-defined 3xx response (308+)
1161 * We ignore all of these cases. In the first two,
1162 * redirecting would be explicitly wrong, and in the
1163 * last case, we have no clue if the 3xx response is
1164 * supposed to be redirecty or non-redirecty. Plus,
1165 * 2616 says unrecognized status codes should be
1166 * treated as the equivalent to the x00 code, and we
1167 * don't redirect on 300, so therefore we shouldn't
1168 * redirect on 308+ either.
1173 /* Location is supposed to be an absolute URI, but some sites
1174 * are lame, so we use soup_uri_new_with_base().
1176 new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
1177 if (!new_uri || !new_uri->host) {
1179 soup_uri_free (new_uri);
1180 soup_message_set_status_full (msg,
1181 SOUP_STATUS_MALFORMED,
1182 "Invalid Redirect URL");
1186 soup_message_set_uri (msg, new_uri);
1187 soup_uri_free (new_uri);
1189 soup_session_requeue_message (session, msg);
1193 soup_session_send_queue_item (SoupSession *session,
1194 SoupMessageQueueItem *item,
1195 SoupMessageCompletionFn completion_cb)
1197 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1198 const char *conn_header;
1200 if (priv->user_agent) {
1201 soup_message_headers_replace (item->msg->request_headers,
1202 "User-Agent", priv->user_agent);
1205 if (priv->accept_language &&
1206 !soup_message_headers_get_list (item->msg->request_headers,
1207 "Accept-Language")) {
1208 soup_message_headers_append (item->msg->request_headers,
1210 priv->accept_language);
1213 /* Force keep alive connections for HTTP 1.0. Performance will
1214 * improve when issuing multiple requests to the same host in
1215 * a short period of time, as we wouldn't need to establish
1216 * new connections. Keep alive is implicit for HTTP 1.1.
1218 conn_header = soup_message_headers_get_list (item->msg->request_headers, "Connection");
1220 (!soup_header_contains (conn_header, "Keep-Alive") &&
1221 !soup_header_contains (conn_header, "close")))
1222 soup_message_headers_append (item->msg->request_headers,
1223 "Connection", "Keep-Alive");
1225 g_signal_emit (session, signals[REQUEST_STARTED], 0,
1226 item->msg, soup_connection_get_socket (item->conn));
1227 soup_connection_send_request (item->conn, item, completion_cb, item);
1231 soup_session_cleanup_connections (SoupSession *session,
1232 gboolean prune_idle)
1234 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1235 GSList *conns = NULL, *c;
1236 GHashTableIter iter;
1237 gpointer conn, host;
1238 SoupConnectionState state;
1240 g_mutex_lock (priv->host_lock);
1241 g_hash_table_iter_init (&iter, priv->conns);
1242 while (g_hash_table_iter_next (&iter, &conn, &host)) {
1243 state = soup_connection_get_state (conn);
1244 if (state == SOUP_CONNECTION_REMOTE_DISCONNECTED ||
1245 (prune_idle && state == SOUP_CONNECTION_IDLE))
1246 conns = g_slist_prepend (conns, g_object_ref (conn));
1248 g_mutex_unlock (priv->host_lock);
1253 for (c = conns; c; c = c->next) {
1255 soup_connection_disconnect (conn);
1256 g_object_unref (conn);
1258 g_slist_free (conns);
1264 connection_disconnected (SoupConnection *conn, gpointer user_data)
1266 SoupSession *session = user_data;
1267 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1268 SoupSessionHost *host;
1270 g_mutex_lock (priv->host_lock);
1272 host = g_hash_table_lookup (priv->conns, conn);
1274 g_hash_table_remove (priv->conns, conn);
1275 host->connections = g_slist_remove (host->connections, conn);
1278 if (soup_connection_get_ssl_fallback (conn))
1279 host->ssl_fallback = TRUE;
1282 g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
1285 g_mutex_unlock (priv->host_lock);
1286 g_object_unref (conn);
1289 SoupMessageQueueItem *
1290 soup_session_make_connect_message (SoupSession *session,
1291 SoupConnection *conn)
1293 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1294 SoupAddress *server_addr = soup_connection_get_tunnel_addr (conn);
1297 SoupMessageQueueItem *item;
1299 uri = soup_uri_new (NULL);
1300 soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTPS);
1301 soup_uri_set_host (uri, soup_address_get_name (server_addr));
1302 soup_uri_set_port (uri, soup_address_get_port (server_addr));
1303 soup_uri_set_path (uri, "");
1304 msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT, uri);
1305 soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
1306 soup_uri_free (uri);
1308 /* Call the base implementation of soup_session_queue_message
1309 * directly, to add msg to the SoupMessageQueue and cause all
1310 * the right signals to be emitted.
1312 queue_message (session, msg, NULL, NULL);
1313 item = soup_message_queue_lookup (priv->queue, msg);
1314 item->conn = g_object_ref (conn);
1315 g_object_unref (msg);
1317 g_signal_emit (session, signals[TUNNELING], 0, conn);
1322 soup_session_get_connection (SoupSession *session,
1323 SoupMessageQueueItem *item,
1324 gboolean *try_pruning)
1326 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1327 SoupConnection *conn;
1328 SoupSessionHost *host;
1329 SoupAddress *remote_addr, *tunnel_addr;
1330 SoupSSLCredentials *ssl_creds;
1332 int num_pending = 0;
1336 g_return_val_if_fail (soup_connection_get_state (item->conn) != SOUP_CONNECTION_DISCONNECTED, FALSE);
1340 g_mutex_lock (priv->host_lock);
1342 host = get_host_for_message (session, item->msg);
1343 for (conns = host->connections; conns; conns = conns->next) {
1344 if (soup_connection_get_state (conns->data) == SOUP_CONNECTION_IDLE) {
1345 soup_connection_set_state (conns->data, SOUP_CONNECTION_IN_USE);
1346 g_mutex_unlock (priv->host_lock);
1347 item->conn = g_object_ref (conns->data);
1349 } else if (soup_connection_get_state (conns->data) == SOUP_CONNECTION_CONNECTING)
1353 /* Limit the number of pending connections; num_messages / 2
1354 * is somewhat arbitrary...
1356 if (num_pending > host->num_messages / 2) {
1357 g_mutex_unlock (priv->host_lock);
1361 if (host->num_conns >= priv->max_conns_per_host) {
1362 g_mutex_unlock (priv->host_lock);
1366 if (priv->num_conns >= priv->max_conns) {
1367 *try_pruning = TRUE;
1368 g_mutex_unlock (priv->host_lock);
1372 if (item->proxy_addr) {
1373 remote_addr = item->proxy_addr;
1376 remote_addr = host->addr;
1380 uri = soup_message_get_uri (item->msg);
1381 if (uri->scheme == SOUP_URI_SCHEME_HTTPS) {
1382 if (!priv->ssl_creds)
1383 priv->ssl_creds = soup_ssl_get_client_credentials (priv->ssl_ca_file);
1384 ssl_creds = priv->ssl_creds;
1386 if (item->proxy_addr)
1387 tunnel_addr = host->addr;
1391 conn = soup_connection_new (
1392 SOUP_CONNECTION_REMOTE_ADDRESS, remote_addr,
1393 SOUP_CONNECTION_TUNNEL_ADDRESS, tunnel_addr,
1394 SOUP_CONNECTION_PROXY_URI, item->proxy_uri,
1395 SOUP_CONNECTION_SSL_CREDENTIALS, ssl_creds,
1396 SOUP_CONNECTION_SSL_STRICT, priv->ssl_strict,
1397 SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
1398 SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
1399 SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
1400 SOUP_CONNECTION_SSL_FALLBACK, host->ssl_fallback,
1402 g_signal_connect (conn, "disconnected",
1403 G_CALLBACK (connection_disconnected),
1406 g_signal_emit (session, signals[CONNECTION_CREATED], 0, conn);
1408 g_hash_table_insert (priv->conns, conn, host);
1412 host->connections = g_slist_prepend (host->connections, conn);
1414 g_mutex_unlock (priv->host_lock);
1415 item->conn = g_object_ref (conn);
1420 soup_session_get_queue (SoupSession *session)
1422 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1428 soup_session_unqueue_item (SoupSession *session,
1429 SoupMessageQueueItem *item)
1431 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1432 SoupSessionHost *host;
1435 g_object_unref (item->conn);
1439 if (item->state != SOUP_MESSAGE_FINISHED) {
1440 g_warning ("finished an item with state %d", item->state);
1444 soup_message_queue_remove (priv->queue, item);
1446 g_mutex_lock (priv->host_lock);
1447 host = get_host_for_message (session, item->msg);
1448 host->num_messages--;
1449 g_mutex_unlock (priv->host_lock);
1451 /* g_signal_handlers_disconnect_by_func doesn't work if you
1452 * have a metamarshal, meaning it doesn't work with
1453 * soup_message_add_header_handler()
1455 g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
1456 0, 0, NULL, NULL, item);
1457 g_signal_emit (session, signals[REQUEST_UNQUEUED], 0, item->msg);
1458 soup_message_queue_item_unref (item);
1462 soup_session_set_item_status (SoupSession *session,
1463 SoupMessageQueueItem *item,
1469 switch (status_code) {
1470 case SOUP_STATUS_CANT_RESOLVE:
1471 case SOUP_STATUS_CANT_CONNECT:
1472 uri = soup_message_get_uri (item->msg);
1473 msg = g_strdup_printf ("%s (%s)",
1474 soup_status_get_phrase (status_code),
1476 soup_message_set_status_full (item->msg, status_code, msg);
1480 case SOUP_STATUS_CANT_RESOLVE_PROXY:
1481 case SOUP_STATUS_CANT_CONNECT_PROXY:
1482 if (item->proxy_uri && item->proxy_uri->host) {
1483 msg = g_strdup_printf ("%s (%s)",
1484 soup_status_get_phrase (status_code),
1485 item->proxy_uri->host);
1486 soup_message_set_status_full (item->msg, status_code, msg);
1490 soup_message_set_status (item->msg, status_code);
1493 case SOUP_STATUS_SSL_FAILED:
1494 if (!g_tls_backend_supports_tls (g_tls_backend_get_default ())) {
1495 soup_message_set_status_full (item->msg, status_code,
1496 "TLS/SSL support not available; install glib-networking");
1498 soup_message_set_status (item->msg, status_code);
1502 soup_message_set_status (item->msg, status_code);
1508 queue_message (SoupSession *session, SoupMessage *msg,
1509 SoupSessionCallback callback, gpointer user_data)
1511 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1512 SoupMessageQueueItem *item;
1513 SoupSessionHost *host;
1515 item = soup_message_queue_append (priv->queue, msg, callback, user_data);
1517 g_mutex_lock (priv->host_lock);
1518 host = get_host_for_message (session, item->msg);
1519 host->num_messages++;
1520 g_mutex_unlock (priv->host_lock);
1522 if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
1523 soup_message_add_header_handler (
1524 msg, "got_body", "Location",
1525 G_CALLBACK (redirect_handler), item);
1528 g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
1532 * SoupSessionCallback:
1533 * @session: the session
1534 * @msg: the message that has finished
1535 * @user_data: the data passed to soup_session_queue_message
1537 * Prototype for the callback passed to soup_session_queue_message(),
1542 * soup_session_queue_message:
1543 * @session: a #SoupSession
1544 * @msg: (transfer full): the message to queue
1545 * @callback: (allow-none) (scope async): a #SoupSessionCallback which will
1546 * be called after the message completes or when an unrecoverable error occurs.
1547 * @user_data: (allow-none): a pointer passed to @callback.
1549 * Queues the message @msg for sending. All messages are processed
1550 * while the glib main loop runs. If @msg has been processed before,
1551 * any resources related to the time it was last sent are freed.
1553 * Upon message completion, the callback specified in @callback will
1554 * be invoked (in the thread associated with @session's async
1555 * context). If after returning from this callback the message has not
1556 * been requeued, @msg will be unreffed.
1559 soup_session_queue_message (SoupSession *session, SoupMessage *msg,
1560 SoupSessionCallback callback, gpointer user_data)
1562 g_return_if_fail (SOUP_IS_SESSION (session));
1563 g_return_if_fail (SOUP_IS_MESSAGE (msg));
1565 SOUP_SESSION_GET_CLASS (session)->queue_message (session, msg,
1566 callback, user_data);
1570 requeue_message (SoupSession *session, SoupMessage *msg)
1572 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1573 SoupMessageQueueItem *item;
1575 item = soup_message_queue_lookup (priv->queue, msg);
1576 g_return_if_fail (item != NULL);
1577 item->state = SOUP_MESSAGE_RESTARTING;
1578 soup_message_queue_item_unref (item);
1582 * soup_session_requeue_message:
1583 * @session: a #SoupSession
1584 * @msg: the message to requeue
1586 * This causes @msg to be placed back on the queue to be attempted
1590 soup_session_requeue_message (SoupSession *session, SoupMessage *msg)
1592 g_return_if_fail (SOUP_IS_SESSION (session));
1593 g_return_if_fail (SOUP_IS_MESSAGE (msg));
1595 SOUP_SESSION_GET_CLASS (session)->requeue_message (session, msg);
1600 * soup_session_send_message:
1601 * @session: a #SoupSession
1602 * @msg: the message to send
1604 * Synchronously send @msg. This call will not return until the
1605 * transfer is finished successfully or there is an unrecoverable
1608 * @msg is not freed upon return.
1610 * Return value: the HTTP status code of the response
1613 soup_session_send_message (SoupSession *session, SoupMessage *msg)
1615 g_return_val_if_fail (SOUP_IS_SESSION (session), SOUP_STATUS_MALFORMED);
1616 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_STATUS_MALFORMED);
1618 return SOUP_SESSION_GET_CLASS (session)->send_message (session, msg);
1623 * soup_session_pause_message:
1624 * @session: a #SoupSession
1625 * @msg: a #SoupMessage currently running on @session
1627 * Pauses HTTP I/O on @msg. Call soup_session_unpause_message() to
1631 soup_session_pause_message (SoupSession *session,
1634 g_return_if_fail (SOUP_IS_SESSION (session));
1635 g_return_if_fail (SOUP_IS_MESSAGE (msg));
1637 #if ENABLE(TIZEN_FIX_PAUSE_MESSAGE)
1638 if(soup_message_io_in_progress (msg))
1639 soup_message_io_pause (msg);
1641 soup_message_io_pause (msg);
1646 * soup_session_unpause_message:
1647 * @session: a #SoupSession
1648 * @msg: a #SoupMessage currently running on @session
1650 * Resumes HTTP I/O on @msg. Use this to resume after calling
1651 * soup_session_pause_message().
1653 * If @msg is being sent via blocking I/O, this will resume reading or
1654 * writing immediately. If @msg is using non-blocking I/O, then
1655 * reading or writing won't resume until you return to the main loop.
1658 soup_session_unpause_message (SoupSession *session,
1661 g_return_if_fail (SOUP_IS_SESSION (session));
1662 g_return_if_fail (SOUP_IS_MESSAGE (msg));
1664 #if ENABLE(TIZEN_FIX_PAUSE_MESSAGE)
1665 if(soup_message_io_in_progress (msg))
1666 soup_message_io_unpause (msg);
1668 soup_message_io_unpause (msg);
1674 cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
1676 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1677 SoupMessageQueueItem *item;
1679 item = soup_message_queue_lookup (priv->queue, msg);
1680 g_return_if_fail (item != NULL);
1682 soup_message_set_status (msg, status_code);
1683 g_cancellable_cancel (item->cancellable);
1685 soup_message_queue_item_unref (item);
1689 * soup_session_cancel_message:
1690 * @session: a #SoupSession
1691 * @msg: the message to cancel
1692 * @status_code: status code to set on @msg (generally
1693 * %SOUP_STATUS_CANCELLED)
1695 * Causes @session to immediately finish processing @msg (regardless
1696 * of its current state) with a final status_code of @status_code. You
1697 * may call this at any time after handing @msg off to @session; if
1698 * @session has started sending the request but has not yet received
1699 * the complete response, then it will close the request's connection.
1700 * Note that with non-idempotent requests (eg, %POST, %PUT, %DELETE)
1701 * it is possible that you might cancel the request after the server
1702 * acts on it, but before it returns a response, leaving the remote
1703 * resource in an unknown state.
1705 * If the message is cancelled while its response body is being read,
1706 * then the response body in @msg will be left partially-filled-in.
1707 * The response headers, on the other hand, will always be either
1708 * empty or complete.
1710 * For messages queued with soup_session_queue_message() (and
1711 * cancelled from the same thread), the callback will be invoked
1712 * before soup_session_cancel_message() returns.
1715 soup_session_cancel_message (SoupSession *session, SoupMessage *msg,
1718 SoupSessionPrivate *priv;
1719 SoupMessageQueueItem *item;
1721 g_return_if_fail (SOUP_IS_SESSION (session));
1722 g_return_if_fail (SOUP_IS_MESSAGE (msg));
1724 priv = SOUP_SESSION_GET_PRIVATE (session);
1725 item = soup_message_queue_lookup (priv->queue, msg);
1726 /* If the message is already ending, don't do anything */
1729 if (item->state == SOUP_MESSAGE_FINISHED) {
1730 soup_message_queue_item_unref (item);
1734 SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg, status_code);
1735 soup_message_queue_item_unref (item);
1739 gather_conns (gpointer key, gpointer host, gpointer data)
1741 SoupConnection *conn = key;
1742 GSList **conns = data;
1744 *conns = g_slist_prepend (*conns, g_object_ref (conn));
1748 flush_queue (SoupSession *session)
1750 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1751 SoupMessageQueueItem *item;
1753 for (item = soup_message_queue_first (priv->queue);
1755 item = soup_message_queue_next (priv->queue, item)) {
1756 soup_session_cancel_message (session, item->msg,
1757 SOUP_STATUS_CANCELLED);
1762 * soup_session_abort:
1763 * @session: the session
1765 * Cancels all pending requests in @session.
1768 soup_session_abort (SoupSession *session)
1770 SoupSessionPrivate *priv;
1773 g_return_if_fail (SOUP_IS_SESSION (session));
1774 priv = SOUP_SESSION_GET_PRIVATE (session);
1776 SOUP_SESSION_GET_CLASS (session)->flush_queue (session);
1778 /* Close all connections */
1779 g_mutex_lock (priv->host_lock);
1781 g_hash_table_foreach (priv->conns, gather_conns, &conns);
1783 g_mutex_unlock (priv->host_lock);
1784 for (c = conns; c; c = c->next) {
1785 soup_connection_disconnect (c->data);
1786 g_object_unref (c->data);
1789 g_slist_free (conns);
1793 * soup_session_prepare_for_uri:
1794 * @session: a #SoupSession
1795 * @uri: a #SoupURI which may be required
1797 * Tells @session that @uri may be requested shortly, and so the
1798 * session can try to prepare (resolving the domain name, obtaining
1799 * proxy address, etc.) in order to work more quickly once the URI is
1800 * actually requested.
1802 * This method acts asynchronously, in @session's %async_context.
1803 * If you are using #SoupSessionSync and do not have a main loop running,
1804 * then you can't use this method.
1809 soup_session_prepare_for_uri (SoupSession *session, SoupURI *uri)
1811 SoupSessionPrivate *priv;
1812 SoupSessionHost *host;
1815 g_return_if_fail (SOUP_IS_SESSION (session));
1816 g_return_if_fail (uri != NULL);
1821 priv = SOUP_SESSION_GET_PRIVATE (session);
1823 g_mutex_lock (priv->host_lock);
1824 host = get_host_for_uri (session, uri);
1825 addr = g_object_ref (host->addr);
1826 g_mutex_unlock (priv->host_lock);
1828 soup_address_resolve_async (addr, priv->async_context,
1830 g_object_unref (addr);
1834 * soup_session_add_feature:
1835 * @session: a #SoupSession
1836 * @feature: an object that implements #SoupSessionFeature
1838 * Adds @feature's functionality to @session. You can also add a
1839 * feature to the session at construct time by using the
1840 * %SOUP_SESSION_ADD_FEATURE property.
1845 soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature)
1847 SoupSessionPrivate *priv;
1849 g_return_if_fail (SOUP_IS_SESSION (session));
1850 g_return_if_fail (SOUP_IS_SESSION_FEATURE (feature));
1852 priv = SOUP_SESSION_GET_PRIVATE (session);
1853 priv->features = g_slist_prepend (priv->features, g_object_ref (feature));
1854 g_hash_table_remove_all (priv->features_cache);
1855 soup_session_feature_attach (feature, session);
1859 * soup_session_add_feature_by_type:
1860 * @session: a #SoupSession
1861 * @feature_type: a #GType
1863 * If @feature_type is the type of a class that implements
1864 * #SoupSessionFeature, this creates a new feature of that type and
1865 * adds it to @session as with soup_session_add_feature(). You can use
1866 * this when you don't need to customize the new feature in any way.
1868 * If @feature_type is not a #SoupSessionFeature type, this gives
1869 * each existing feature on @session the chance to accept @feature_type
1870 * as a "subfeature". This can be used to add new #SoupAuth types,
1873 * You can also add a feature to the session at construct time by
1874 * using the %SOUP_SESSION_ADD_FEATURE_BY_TYPE property.
1879 soup_session_add_feature_by_type (SoupSession *session, GType feature_type)
1881 g_return_if_fail (SOUP_IS_SESSION (session));
1883 if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
1884 SoupSessionFeature *feature;
1886 feature = g_object_new (feature_type, NULL);
1887 soup_session_add_feature (session, feature);
1888 g_object_unref (feature);
1890 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1893 for (f = priv->features; f; f = f->next) {
1894 if (soup_session_feature_add_feature (f->data, feature_type))
1897 g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
1902 * soup_session_remove_feature:
1903 * @session: a #SoupSession
1904 * @feature: a feature that has previously been added to @session
1906 * Removes @feature's functionality from @session.
1911 soup_session_remove_feature (SoupSession *session, SoupSessionFeature *feature)
1913 SoupSessionPrivate *priv;
1915 g_return_if_fail (SOUP_IS_SESSION (session));
1917 priv = SOUP_SESSION_GET_PRIVATE (session);
1918 if (g_slist_find (priv->features, feature)) {
1919 priv->features = g_slist_remove (priv->features, feature);
1920 g_hash_table_remove_all (priv->features_cache);
1921 soup_session_feature_detach (feature, session);
1922 g_object_unref (feature);
1927 * soup_session_remove_feature_by_type:
1928 * @session: a #SoupSession
1929 * @feature_type: a #GType
1931 * Removes all features of type @feature_type (or any subclass of
1932 * @feature_type) from @session. You can also remove standard features
1933 * from the session at construct time by using the
1934 * %SOUP_SESSION_REMOVE_FEATURE_BY_TYPE property.
1939 soup_session_remove_feature_by_type (SoupSession *session, GType feature_type)
1941 SoupSessionPrivate *priv;
1944 g_return_if_fail (SOUP_IS_SESSION (session));
1946 priv = SOUP_SESSION_GET_PRIVATE (session);
1948 if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
1950 for (f = priv->features; f; f = f->next) {
1951 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type)) {
1952 soup_session_remove_feature (session, f->data);
1957 for (f = priv->features; f; f = f->next) {
1958 if (soup_session_feature_remove_feature (f->data, feature_type))
1961 g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
1966 * soup_session_get_features:
1967 * @session: a #SoupSession
1968 * @feature_type: the #GType of the class of features to get
1970 * Generates a list of @session's features of type @feature_type. (If
1971 * you want to see all features, you can pass %G_TYPE_SESSION_FEATURE
1972 * for @feature_type.)
1974 * Return value: (transfer container) (element-type Soup.SessionFeature):
1975 * a list of features. You must free the list, but not its contents
1980 soup_session_get_features (SoupSession *session, GType feature_type)
1982 SoupSessionPrivate *priv;
1985 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
1987 priv = SOUP_SESSION_GET_PRIVATE (session);
1988 for (f = priv->features, ret = NULL; f; f = f->next) {
1989 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
1990 ret = g_slist_prepend (ret, f->data);
1992 return g_slist_reverse (ret);
1996 * soup_session_get_feature:
1997 * @session: a #SoupSession
1998 * @feature_type: the #GType of the feature to get
2000 * Gets the first feature in @session of type @feature_type. For
2001 * features where there may be more than one feature of a given type,
2002 * use soup_session_get_features().
2004 * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
2005 * feature is owned by @session.
2009 SoupSessionFeature *
2010 soup_session_get_feature (SoupSession *session, GType feature_type)
2012 SoupSessionPrivate *priv;
2013 SoupSessionFeature *feature;
2016 g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
2018 priv = SOUP_SESSION_GET_PRIVATE (session);
2020 feature = g_hash_table_lookup (priv->features_cache,
2021 GSIZE_TO_POINTER (feature_type));
2025 for (f = priv->features; f; f = f->next) {
2027 if (G_TYPE_CHECK_INSTANCE_TYPE (feature, feature_type)) {
2028 g_hash_table_insert (priv->features_cache,
2029 GSIZE_TO_POINTER (feature_type),
2038 * soup_session_get_feature_for_message:
2039 * @session: a #SoupSession
2040 * @feature_type: the #GType of the feature to get
2041 * @msg: a #SoupMessage
2043 * Gets the first feature in @session of type @feature_type, provided
2044 * that it is not disabled for @msg. As with
2045 * soup_session_get_feature(), this should only be used for features
2046 * where @feature_type is only expected to match a single feature. In
2047 * particular, if there are two matching features, and the first is
2048 * disabled on @msg, and the second is not, then this will return
2049 * %NULL, not the second feature.
2051 * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
2052 * feature is owned by @session.
2056 SoupSessionFeature *
2057 soup_session_get_feature_for_message (SoupSession *session, GType feature_type,
2060 SoupSessionFeature *feature;
2062 feature = soup_session_get_feature (session, feature_type);
2063 if (feature && soup_message_disables_feature (msg, feature))