Move SoupRequester API into SoupSession, declare SoupRequest stable
[platform/upstream/libsoup.git] / libsoup / soup-session.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-session.c
4  *
5  * Copyright (C) 2000-2003, Ximian, Inc.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include <glib/gi18n-lib.h>
13
14 #include "soup-session.h"
15 #include "soup.h"
16 #include "soup-auth-manager-ntlm.h"
17 #include "soup-connection.h"
18 #include "soup-marshal.h"
19 #include "soup-message-private.h"
20 #include "soup-misc-private.h"
21 #include "soup-message-queue.h"
22 #include "soup-proxy-resolver-static.h"
23 #include "soup-session-private.h"
24
25 #define HOST_KEEP_ALIVE 5 * 60 * 1000 /* 5 min in msecs */
26
27 /**
28  * SECTION:soup-session
29  * @short_description: Soup session state object
30  *
31  * #SoupSession is the object that controls client-side HTTP. A
32  * #SoupSession encapsulates all of the state that libsoup is keeping
33  * on behalf of your program; cached HTTP connections, authentication
34  * information, etc.
35  *
36  * Most applications will only need a single #SoupSession; the primary
37  * reason you might need multiple sessions is if you need to have
38  * multiple independent authentication contexts. (Eg, you are
39  * connecting to a server and authenticating as two different users at
40  * different times; the easiest way to ensure that each #SoupMessage
41  * is sent with the authentication information you intended is to use
42  * one session for the first user, and a second session for the other
43  * user.)
44  *
45  * #SoupSession itself is an abstract class, with two subclasses. If
46  * you are using the glib main loop, you will generally want to use
47  * #SoupSessionAsync, which uses non-blocking I/O and callbacks. On
48  * the other hand, if your application is threaded and you want to do
49  * synchronous I/O in a separate thread from the UI, use
50  * #SoupSessionSync.
51  **/
52
53 static void
54 soup_init (void)
55 {
56         bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
57 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
58         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
59 #endif
60 }
61
62 typedef struct {
63         SoupURI     *uri;
64         SoupAddress *addr;
65
66         GSList      *connections;      /* CONTAINS: SoupConnection */
67         guint        num_conns;
68
69         guint        num_messages;
70
71         gboolean     ssl_fallback;
72
73         GSource     *keep_alive_src;
74         SoupSession *session;
75 } SoupSessionHost;
76 static guint soup_host_uri_hash (gconstpointer key);
77 static gboolean soup_host_uri_equal (gconstpointer v1, gconstpointer v2);
78
79 typedef struct {
80         SoupSession *session;
81         gboolean disposed;
82
83         GTlsDatabase *tlsdb;
84         char *ssl_ca_file;
85         gboolean ssl_strict;
86
87         SoupMessageQueue *queue;
88
89         char *user_agent;
90         char *accept_language;
91         gboolean accept_language_auto;
92
93         GSList *features;
94         GHashTable *features_cache;
95
96         GHashTable *http_hosts, *https_hosts; /* char* -> SoupSessionHost */
97         GHashTable *conns; /* SoupConnection -> SoupSessionHost */
98         guint num_conns;
99         guint max_conns, max_conns_per_host;
100         guint io_timeout, idle_timeout;
101
102         /* Must hold the conn_lock before potentially creating a new
103          * SoupSessionHost, adding/removing a connection,
104          * disconnecting a connection, or moving a connection from
105          * IDLE to IN_USE. Must not emit signals or destroy objects
106          * while holding it. conn_cond is signaled when it may be
107          * possible for a previously-blocked message to continue.
108          */
109         GMutex conn_lock;
110         GCond conn_cond;
111
112         GMainContext *async_context;
113         gboolean use_thread_context;
114         GSList *run_queue_sources;
115
116         GResolver *resolver;
117
118         char **http_aliases, **https_aliases;
119
120         GHashTable *request_types;
121 } SoupSessionPrivate;
122 #define SOUP_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION, SoupSessionPrivate))
123
124 #define SOUP_IS_PLAIN_SESSION(o) (G_TYPE_FROM_INSTANCE (o) == SOUP_TYPE_SESSION)
125
126 static void free_host (SoupSessionHost *host);
127 static void connection_state_changed (GObject *object, GParamSpec *param,
128                                       gpointer user_data);
129 static void connection_disconnected (SoupConnection *conn, gpointer user_data);
130 static void drop_connection (SoupSession *session, SoupSessionHost *host,
131                              SoupConnection *conn);
132
133 static void auth_manager_authenticate (SoupAuthManager *manager,
134                                        SoupMessage *msg, SoupAuth *auth,
135                                        gboolean retrying, gpointer user_data);
136
137 static void async_run_queue (SoupSession *session);
138
139 static void async_send_request_running (SoupSession *session, SoupMessageQueueItem *item);
140
141 #define SOUP_SESSION_MAX_CONNS_DEFAULT 10
142 #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2
143
144 #define SOUP_SESSION_MAX_REDIRECTION_COUNT 20
145
146 #define SOUP_SESSION_USER_AGENT_BASE "libsoup/" PACKAGE_VERSION
147
148 G_DEFINE_TYPE_WITH_CODE (SoupSession, soup_session, G_TYPE_OBJECT,
149                          soup_init ();
150                          )
151
152 enum {
153         REQUEST_QUEUED,
154         REQUEST_STARTED,
155         REQUEST_UNQUEUED,
156         AUTHENTICATE,
157         CONNECTION_CREATED,
158         TUNNELING,
159         LAST_SIGNAL
160 };
161
162 static guint signals[LAST_SIGNAL] = { 0 };
163
164 enum {
165         PROP_0,
166
167         PROP_PROXY_URI,
168         PROP_MAX_CONNS,
169         PROP_MAX_CONNS_PER_HOST,
170         PROP_USE_NTLM,
171         PROP_SSL_CA_FILE,
172         PROP_SSL_USE_SYSTEM_CA_FILE,
173         PROP_TLS_DATABASE,
174         PROP_SSL_STRICT,
175         PROP_ASYNC_CONTEXT,
176         PROP_USE_THREAD_CONTEXT,
177         PROP_TIMEOUT,
178         PROP_USER_AGENT,
179         PROP_ACCEPT_LANGUAGE,
180         PROP_ACCEPT_LANGUAGE_AUTO,
181         PROP_IDLE_TIMEOUT,
182         PROP_ADD_FEATURE,
183         PROP_ADD_FEATURE_BY_TYPE,
184         PROP_REMOVE_FEATURE_BY_TYPE,
185         PROP_HTTP_ALIASES,
186         PROP_HTTPS_ALIASES,
187
188         LAST_PROP
189 };
190
191 static void
192 soup_session_init (SoupSession *session)
193 {
194         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
195         SoupAuthManager *auth_manager;
196
197         priv->session = session;
198
199         priv->queue = soup_message_queue_new (session);
200
201         g_mutex_init (&priv->conn_lock);
202         g_cond_init (&priv->conn_cond);
203         priv->http_hosts = g_hash_table_new_full (soup_host_uri_hash,
204                                                   soup_host_uri_equal,
205                                                   NULL, (GDestroyNotify)free_host);
206         priv->https_hosts = g_hash_table_new_full (soup_host_uri_hash,
207                                                    soup_host_uri_equal,
208                                                    NULL, (GDestroyNotify)free_host);
209         priv->conns = g_hash_table_new (NULL, NULL);
210
211         priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
212         priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
213
214         priv->features_cache = g_hash_table_new (NULL, NULL);
215
216         auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER_NTLM, NULL);
217         g_signal_connect (auth_manager, "authenticate",
218                           G_CALLBACK (auth_manager_authenticate), session);
219         soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
220                                           SOUP_TYPE_AUTH_BASIC);
221         soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
222                                           SOUP_TYPE_AUTH_DIGEST);
223         soup_session_add_feature (session, SOUP_SESSION_FEATURE (auth_manager));
224         g_object_unref (auth_manager);
225
226         /* We'll be doing DNS continuously-ish while the session is active,
227          * so hold a ref on the default GResolver.
228          */
229         priv->resolver = g_resolver_get_default ();
230
231         priv->ssl_strict = TRUE;
232
233         priv->http_aliases = g_new (char *, 2);
234         priv->http_aliases[0] = (char *)g_intern_string ("*");
235         priv->http_aliases[1] = NULL;
236
237         priv->request_types = g_hash_table_new (soup_str_case_hash,
238                                                 soup_str_case_equal);
239         soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_HTTP);
240         soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_FILE);
241         soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_DATA);
242 }
243
244 static GObject *
245 soup_session_constructor (GType                  type,
246                           guint                  n_construct_properties,
247                           GObjectConstructParam *construct_params)
248 {
249         GObject *object;
250
251         object = G_OBJECT_CLASS (soup_session_parent_class)->constructor (type, n_construct_properties, construct_params);
252
253         /* If this is a "plain" SoupSession, fix up the default
254          * properties values, etc.
255          */
256         if (type == SOUP_TYPE_SESSION) {
257                 SoupSession *session = SOUP_SESSION (object);
258                 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
259
260                 g_clear_object (&priv->tlsdb);
261                 priv->tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
262
263                 g_clear_pointer (&priv->async_context, g_main_context_unref);
264                 priv->async_context = g_main_context_ref_thread_default ();
265                 priv->use_thread_context = TRUE;
266
267                 priv->io_timeout = priv->idle_timeout = 60;
268
269                 priv->http_aliases[0] = NULL;
270
271                 soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_DECODER);
272                 soup_session_add_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_DEFAULT);
273         }
274
275         return object;
276 }
277
278 static void
279 soup_session_dispose (GObject *object)
280 {
281         SoupSession *session = SOUP_SESSION (object);
282         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
283         GSList *iter;
284
285         priv->disposed = TRUE;
286
287         for (iter = priv->run_queue_sources; iter; iter = iter->next) {
288                 g_source_destroy (iter->data);
289                 g_source_unref (iter->data);
290         }
291         g_clear_pointer (&priv->run_queue_sources, g_slist_free);
292
293         priv->disposed = TRUE;
294         soup_session_abort (session);
295         g_warn_if_fail (g_hash_table_size (priv->conns) == 0);
296
297         while (priv->features)
298                 soup_session_remove_feature (session, priv->features->data);
299
300         G_OBJECT_CLASS (soup_session_parent_class)->dispose (object);
301 }
302
303 static void
304 soup_session_finalize (GObject *object)
305 {
306         SoupSession *session = SOUP_SESSION (object);
307         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
308
309         soup_message_queue_destroy (priv->queue);
310
311         g_mutex_clear (&priv->conn_lock);
312         g_cond_clear (&priv->conn_cond);
313         g_hash_table_destroy (priv->http_hosts);
314         g_hash_table_destroy (priv->https_hosts);
315         g_hash_table_destroy (priv->conns);
316
317         g_free (priv->user_agent);
318         g_free (priv->accept_language);
319
320         g_clear_object (&priv->tlsdb);
321         g_free (priv->ssl_ca_file);
322
323         g_clear_pointer (&priv->async_context, g_main_context_unref);
324
325         g_hash_table_destroy (priv->features_cache);
326
327         g_object_unref (priv->resolver);
328
329         g_free (priv->http_aliases);
330         g_free (priv->https_aliases);
331
332         g_hash_table_destroy (priv->request_types);
333
334         G_OBJECT_CLASS (soup_session_parent_class)->finalize (object);
335 }
336
337 /* Converts a language in POSIX format and to be RFC2616 compliant    */
338 /* Based on code from epiphany-webkit (ephy_langs_append_languages()) */
339 static gchar *
340 posix_lang_to_rfc2616 (const gchar *language)
341 {
342         /* Don't include charset variants, etc */
343         if (strchr (language, '.') || strchr (language, '@'))
344                 return NULL;
345
346         /* Ignore "C" locale, which g_get_language_names() always
347          * includes as a fallback.
348          */
349         if (!strcmp (language, "C"))
350                 return NULL;
351
352         return g_strdelimit (g_ascii_strdown (language, -1), "_", '-');
353 }
354
355 /* Converts @quality from 0-100 to 0.0-1.0 and appends to @str */
356 static gchar *
357 add_quality_value (const gchar *str, int quality)
358 {
359         g_return_val_if_fail (str != NULL, NULL);
360
361         if (quality >= 0 && quality < 100) {
362                 /* We don't use %.02g because of "." vs "," locale issues */
363                 if (quality % 10)
364                         return g_strdup_printf ("%s;q=0.%02d", str, quality);
365                 else
366                         return g_strdup_printf ("%s;q=0.%d", str, quality / 10);
367         } else
368                 return g_strdup (str);
369 }
370
371 /* Returns a RFC2616 compliant languages list from system locales */
372 static gchar *
373 accept_languages_from_system (void)
374 {
375         const char * const * lang_names;
376         GPtrArray *langs = NULL;
377         char *lang, *langs_str;
378         int delta;
379         int i;
380
381         lang_names = g_get_language_names ();
382         g_return_val_if_fail (lang_names != NULL, NULL);
383
384         /* Build the array of languages */
385         langs = g_ptr_array_new_with_free_func (g_free);
386         for (i = 0; lang_names[i] != NULL; i++) {
387                 lang = posix_lang_to_rfc2616 (lang_names[i]);
388                 if (lang)
389                         g_ptr_array_add (langs, lang);
390         }
391
392         /* Add quality values */
393         if (langs->len < 10)
394                 delta = 10;
395         else if (langs->len < 20)
396                 delta = 5;
397         else
398                 delta = 1;
399
400         for (i = 0; i < langs->len; i++) {
401                 lang = langs->pdata[i];
402                 langs->pdata[i] = add_quality_value (lang, 100 - i * delta);
403                 g_free (lang);
404         }
405
406         /* Fallback: add "en" if list is empty */
407         if (langs->len == 0)
408                 g_ptr_array_add (langs, g_strdup ("en"));
409
410         g_ptr_array_add (langs, NULL);
411         langs_str = g_strjoinv (", ", (char **)langs->pdata);
412         g_ptr_array_free (langs, TRUE);
413
414         return langs_str;
415 }
416
417 static void
418 set_tlsdb (SoupSession *session, GTlsDatabase *tlsdb)
419 {
420         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
421         GTlsDatabase *system_default;
422
423         if (tlsdb == priv->tlsdb)
424                 return;
425
426         g_object_freeze_notify (G_OBJECT (session));
427
428         system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
429         if (priv->tlsdb == system_default || tlsdb == system_default) {
430                 g_object_notify (G_OBJECT (session), "ssl-use-system-ca-file");
431         }
432         g_object_unref (system_default);
433
434         if (priv->ssl_ca_file) {
435                 g_free (priv->ssl_ca_file);
436                 priv->ssl_ca_file = NULL;
437                 g_object_notify (G_OBJECT (session), "ssl-ca-file");
438         }
439
440         if (priv->tlsdb)
441                 g_object_unref (priv->tlsdb);
442         priv->tlsdb = tlsdb;
443         if (priv->tlsdb)
444                 g_object_ref (priv->tlsdb);
445
446         g_object_notify (G_OBJECT (session), "tls-database");
447         g_object_thaw_notify (G_OBJECT (session));
448 }
449
450 static void
451 set_use_system_ca_file (SoupSession *session, gboolean use_system_ca_file)
452 {
453         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
454         GTlsDatabase *system_default;
455
456         system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
457
458         if (use_system_ca_file)
459                 set_tlsdb (session, system_default);
460         else if (priv->tlsdb == system_default)
461                 set_tlsdb (session, NULL);
462
463         g_object_unref (system_default);
464 }
465
466 static void
467 set_ssl_ca_file (SoupSession *session, const char *ssl_ca_file)
468 {
469         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
470         GTlsDatabase *tlsdb;
471         GError *error = NULL;
472
473         if (!g_strcmp0 (priv->ssl_ca_file, ssl_ca_file))
474                 return;
475
476         g_object_freeze_notify (G_OBJECT (session));
477
478         if (g_path_is_absolute (ssl_ca_file))
479                 tlsdb = g_tls_file_database_new (ssl_ca_file, &error);
480         else {
481                 char *path, *cwd;
482
483                 cwd = g_get_current_dir ();
484                 path = g_build_filename (cwd, ssl_ca_file, NULL);
485                 tlsdb = g_tls_file_database_new (path, &error);
486                 g_free (path);
487         }
488
489         if (error) {
490                 if (!g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_UNAVAILABLE)) {
491                         g_warning ("Could not set SSL credentials from '%s': %s",
492                                    ssl_ca_file, error->message);
493
494                         tlsdb = g_tls_file_database_new ("/dev/null", NULL);
495                 }
496                 g_error_free (error);
497         }
498
499         set_tlsdb (session, tlsdb);
500         g_object_unref (tlsdb);
501
502         priv->ssl_ca_file = g_strdup (ssl_ca_file);
503         g_object_notify (G_OBJECT (session), "ssl-ca-file");
504
505         g_object_thaw_notify (G_OBJECT (session));
506 }
507
508 /* priv->http_aliases and priv->https_aliases are stored as arrays of
509  * *interned* strings, so we can't just use g_strdupv() to set them.
510  */
511 static void
512 set_aliases (char ***variable, char **value)
513 {
514         int len, i;
515
516         if (*variable)
517                 g_free (*variable);
518
519         if (!value) {
520                 *variable = NULL;
521                 return;
522         }
523
524         len = g_strv_length (value);
525         *variable = g_new (char *, len + 1);
526         for (i = 0; i < len; i++)
527                 (*variable)[i] = (char *)g_intern_string (value[i]);
528         (*variable)[i] = NULL;
529 }
530
531 static void
532 soup_session_set_property (GObject *object, guint prop_id,
533                            const GValue *value, GParamSpec *pspec)
534 {
535         SoupSession *session = SOUP_SESSION (object);
536         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
537         SoupURI *uri;
538         const char *user_agent;
539         SoupSessionFeature *feature;
540         GMainContext *async_context;
541
542         switch (prop_id) {
543         case PROP_PROXY_URI:
544                 uri = g_value_get_boxed (value);
545
546                 if (uri) {
547 #ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
548 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
549 #endif
550                         soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER);
551 #ifdef G_GNUC_END_IGNORE_DEPRECATIONS
552 G_GNUC_END_IGNORE_DEPRECATIONS
553 #endif
554                         feature = SOUP_SESSION_FEATURE (soup_proxy_resolver_static_new (uri));
555                         soup_session_add_feature (session, feature);
556                         g_object_unref (feature);
557                 } else
558                         soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
559
560                 soup_session_abort (session);
561                 break;
562         case PROP_MAX_CONNS:
563                 priv->max_conns = g_value_get_int (value);
564                 break;
565         case PROP_MAX_CONNS_PER_HOST:
566                 priv->max_conns_per_host = g_value_get_int (value);
567                 break;
568         case PROP_USE_NTLM:
569                 g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
570                 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
571                 if (feature) {
572                         if (g_value_get_boolean (value))
573                                 soup_session_feature_add_feature (feature, SOUP_TYPE_AUTH_NTLM);
574                         else
575                                 soup_session_feature_remove_feature (feature, SOUP_TYPE_AUTH_NTLM);
576                 } else
577                         g_warning ("Trying to set use-ntlm on session with no auth-manager");
578                 break;
579         case PROP_SSL_CA_FILE:
580                 set_ssl_ca_file (session, g_value_get_string (value));
581                 break;
582         case PROP_SSL_USE_SYSTEM_CA_FILE:
583                 set_use_system_ca_file (session, g_value_get_boolean (value));
584                 break;
585         case PROP_TLS_DATABASE:
586                 set_tlsdb (session, g_value_get_object (value));
587                 break;
588         case PROP_SSL_STRICT:
589                 priv->ssl_strict = g_value_get_boolean (value);
590                 break;
591         case PROP_ASYNC_CONTEXT:
592                 async_context = g_value_get_pointer (value);
593                 if (async_context && async_context != g_main_context_get_thread_default ())
594                         g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
595                 priv->async_context = async_context;
596                 if (priv->async_context)
597                         g_main_context_ref (priv->async_context);
598                 break;
599         case PROP_USE_THREAD_CONTEXT:
600                 if (!g_value_get_boolean (value))
601                         g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
602                 priv->use_thread_context = g_value_get_boolean (value);
603                 if (priv->use_thread_context) {
604                         if (priv->async_context)
605                                 g_main_context_unref (priv->async_context);
606                         priv->async_context = g_main_context_get_thread_default ();
607                         if (priv->async_context)
608                                 g_main_context_ref (priv->async_context);
609                 }
610                 break;
611         case PROP_TIMEOUT:
612                 priv->io_timeout = g_value_get_uint (value);
613                 break;
614         case PROP_USER_AGENT:
615                 g_free (priv->user_agent);
616                 user_agent = g_value_get_string (value);
617                 if (!user_agent)
618                         priv->user_agent = NULL;
619                 else if (!*user_agent) {
620                         priv->user_agent =
621                                 g_strdup (SOUP_SESSION_USER_AGENT_BASE);
622                 } else if (g_str_has_suffix (user_agent, " ")) {
623                         priv->user_agent =
624                                 g_strdup_printf ("%s%s", user_agent,
625                                                  SOUP_SESSION_USER_AGENT_BASE);
626                 } else
627                         priv->user_agent = g_strdup (user_agent);
628                 break;
629         case PROP_ACCEPT_LANGUAGE:
630                 g_free (priv->accept_language);
631                 priv->accept_language = g_strdup (g_value_get_string (value));
632                 priv->accept_language_auto = FALSE;
633                 break;
634         case PROP_ACCEPT_LANGUAGE_AUTO:
635                 priv->accept_language_auto = g_value_get_boolean (value);
636                 if (priv->accept_language) {
637                         g_free (priv->accept_language);
638                         priv->accept_language = NULL;
639                 }
640
641                 /* Get languages from system if needed */
642                 if (priv->accept_language_auto)
643                         priv->accept_language = accept_languages_from_system ();
644                 break;
645         case PROP_IDLE_TIMEOUT:
646                 priv->idle_timeout = g_value_get_uint (value);
647                 break;
648         case PROP_ADD_FEATURE:
649                 soup_session_add_feature (session, g_value_get_object (value));
650                 break;
651         case PROP_ADD_FEATURE_BY_TYPE:
652                 soup_session_add_feature_by_type (session, g_value_get_gtype (value));
653                 break;
654         case PROP_REMOVE_FEATURE_BY_TYPE:
655                 soup_session_remove_feature_by_type (session, g_value_get_gtype (value));
656                 break;
657         case PROP_HTTP_ALIASES:
658                 set_aliases (&priv->http_aliases, g_value_get_boxed (value));
659                 break;
660         case PROP_HTTPS_ALIASES:
661                 set_aliases (&priv->https_aliases, g_value_get_boxed (value));
662                 break;
663         default:
664                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
665                 break;
666         }
667 }
668
669 static void
670 soup_session_get_property (GObject *object, guint prop_id,
671                            GValue *value, GParamSpec *pspec)
672 {
673         SoupSession *session = SOUP_SESSION (object);
674         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
675         SoupSessionFeature *feature;
676         GTlsDatabase *tlsdb;
677
678         switch (prop_id) {
679         case PROP_PROXY_URI:
680                 feature = soup_session_get_feature (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
681                 if (feature) {
682                         g_object_get_property (G_OBJECT (feature),
683                                                SOUP_PROXY_RESOLVER_STATIC_PROXY_URI,
684                                                value);
685                 } else
686                         g_value_set_boxed (value, NULL);
687                 break;
688         case PROP_MAX_CONNS:
689                 g_value_set_int (value, priv->max_conns);
690                 break;
691         case PROP_MAX_CONNS_PER_HOST:
692                 g_value_set_int (value, priv->max_conns_per_host);
693                 break;
694         case PROP_USE_NTLM:
695                 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
696                 if (feature)
697                         g_value_set_boolean (value, soup_session_feature_has_feature (feature, SOUP_TYPE_AUTH_NTLM));
698                 else
699                         g_value_set_boolean (value, FALSE);
700                 break;
701         case PROP_SSL_CA_FILE:
702                 g_value_set_string (value, priv->ssl_ca_file);
703                 break;
704         case PROP_SSL_USE_SYSTEM_CA_FILE:
705                 tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
706                 g_value_set_boolean (value, priv->tlsdb == tlsdb);
707                 g_object_unref (tlsdb);
708                 break;
709         case PROP_TLS_DATABASE:
710                 g_value_set_object (value, priv->tlsdb);
711                 break;
712         case PROP_SSL_STRICT:
713                 g_value_set_boolean (value, priv->ssl_strict);
714                 break;
715         case PROP_ASYNC_CONTEXT:
716                 g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
717                 break;
718         case PROP_USE_THREAD_CONTEXT:
719                 g_value_set_boolean (value, priv->use_thread_context);
720                 break;
721         case PROP_TIMEOUT:
722                 g_value_set_uint (value, priv->io_timeout);
723                 break;
724         case PROP_USER_AGENT:
725                 g_value_set_string (value, priv->user_agent);
726                 break;
727         case PROP_ACCEPT_LANGUAGE:
728                 g_value_set_string (value, priv->accept_language);
729                 break;
730         case PROP_ACCEPT_LANGUAGE_AUTO:
731                 g_value_set_boolean (value, priv->accept_language_auto);
732                 break;
733         case PROP_IDLE_TIMEOUT:
734                 g_value_set_uint (value, priv->idle_timeout);
735                 break;
736         case PROP_HTTP_ALIASES:
737                 g_value_set_boxed (value, priv->http_aliases);
738                 break;
739         case PROP_HTTPS_ALIASES:
740                 g_value_set_boxed (value, priv->https_aliases);
741                 break;
742         default:
743                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
744                 break;
745         }
746 }
747
748 static gboolean
749 uri_is_http (SoupSessionPrivate *priv, SoupURI *uri)
750 {
751         int i;
752
753         if (uri->scheme == SOUP_URI_SCHEME_HTTP)
754                 return TRUE;
755         else if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
756                 return FALSE;
757         else if (!priv->http_aliases)
758                 return FALSE;
759
760         for (i = 0; priv->http_aliases[i]; i++) {
761                 if (uri->scheme == priv->http_aliases[i])
762                         return TRUE;
763         }
764
765         if (!priv->http_aliases[1] && !strcmp (priv->http_aliases[0], "*"))
766                 return TRUE;
767         else
768                 return FALSE;
769 }
770
771 static gboolean
772 uri_is_https (SoupSessionPrivate *priv, SoupURI *uri)
773 {
774         int i;
775
776         if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
777                 return TRUE;
778         else if (uri->scheme == SOUP_URI_SCHEME_HTTP)
779                 return FALSE;
780         else if (!priv->https_aliases)
781                 return FALSE;
782
783         for (i = 0; priv->https_aliases[i]; i++) {
784                 if (uri->scheme == priv->https_aliases[i])
785                         return TRUE;
786         }
787
788         return FALSE;
789 }
790
791 /**
792  * soup_session_get_async_context:
793  * @session: a #SoupSession
794  *
795  * Gets @session's async_context. This does not add a ref to the
796  * context, so you will need to ref it yourself if you want it to
797  * outlive its session.
798  *
799  * If #SoupSession:use-thread-context is true, this will return the
800  * current thread-default main context.
801  *
802  * Return value: (transfer none): @session's #GMainContext, which may
803  * be %NULL
804  **/
805 GMainContext *
806 soup_session_get_async_context (SoupSession *session)
807 {
808         SoupSessionPrivate *priv;
809
810         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
811         priv = SOUP_SESSION_GET_PRIVATE (session);
812
813         if (priv->use_thread_context)
814                 return g_main_context_get_thread_default ();
815         else
816                 return priv->async_context;
817 }
818
819 /* Hosts */
820
821 /* Note that we can't use soup_uri_host_hash() and soup_uri_host_equal()
822  * because we want to ignore the protocol; http://example.com and
823  * webcal://example.com are the same host.
824  */
825 static guint
826 soup_host_uri_hash (gconstpointer key)
827 {
828         const SoupURI *uri = key;
829
830         g_return_val_if_fail (uri != NULL && uri->host != NULL, 0);
831
832         return uri->port + soup_str_case_hash (uri->host);
833 }
834
835 static gboolean
836 soup_host_uri_equal (gconstpointer v1, gconstpointer v2)
837 {
838         const SoupURI *one = v1;
839         const SoupURI *two = v2;
840
841         g_return_val_if_fail (one != NULL && two != NULL, one == two);
842         g_return_val_if_fail (one->host != NULL && two->host != NULL, one->host == two->host);
843
844         if (one->port != two->port)
845                 return FALSE;
846
847         return g_ascii_strcasecmp (one->host, two->host) == 0;
848 }
849
850
851 static SoupSessionHost *
852 soup_session_host_new (SoupSession *session, SoupURI *uri)
853 {
854         SoupSessionHost *host;
855
856         host = g_slice_new0 (SoupSessionHost);
857         host->uri = soup_uri_copy_host (uri);
858         if (host->uri->scheme != SOUP_URI_SCHEME_HTTP &&
859             host->uri->scheme != SOUP_URI_SCHEME_HTTPS) {
860                 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
861
862                 if (uri_is_https (priv, host->uri))
863                         host->uri->scheme = SOUP_URI_SCHEME_HTTPS;
864                 else
865                         host->uri->scheme = SOUP_URI_SCHEME_HTTP;
866         }
867
868         host->addr = g_object_new (SOUP_TYPE_ADDRESS,
869                                    SOUP_ADDRESS_NAME, host->uri->host,
870                                    SOUP_ADDRESS_PORT, host->uri->port,
871                                    SOUP_ADDRESS_PROTOCOL, host->uri->scheme,
872                                    NULL);
873         host->keep_alive_src = NULL;
874         host->session = session;
875
876         return host;
877 }
878
879 /* Requires conn_lock to be locked */
880 static SoupSessionHost *
881 get_host_for_uri (SoupSession *session, SoupURI *uri)
882 {
883         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
884         SoupSessionHost *host;
885
886         if (uri_is_https (priv, uri))
887                 host = g_hash_table_lookup (priv->https_hosts, uri);
888         else
889                 host = g_hash_table_lookup (priv->http_hosts, uri);
890         if (host)
891                 return host;
892
893         host = soup_session_host_new (session, uri);
894
895         if (uri_is_https (priv, uri))
896                 g_hash_table_insert (priv->https_hosts, host->uri, host);
897         else
898                 g_hash_table_insert (priv->http_hosts, host->uri, host);
899
900         return host;
901 }
902
903 /* Requires conn_lock to be locked */
904 static SoupSessionHost *
905 get_host_for_message (SoupSession *session, SoupMessage *msg)
906 {
907         return get_host_for_uri (session, soup_message_get_uri (msg));
908 }
909
910 static void
911 free_host (SoupSessionHost *host)
912 {
913         g_warn_if_fail (host->connections == NULL);
914
915         if (host->keep_alive_src) {
916                 g_source_destroy (host->keep_alive_src);
917                 g_source_unref (host->keep_alive_src);
918         }
919
920         soup_uri_free (host->uri);
921         g_object_unref (host->addr);
922         g_slice_free (SoupSessionHost, host);
923 }
924
925 static void
926 soup_session_real_auth_required (SoupSession *session, SoupMessage *msg,
927                SoupAuth *auth, gboolean retrying)
928 {
929         g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
930 }
931
932 static void
933 auth_manager_authenticate (SoupAuthManager *manager, SoupMessage *msg,
934                            SoupAuth *auth, gboolean retrying,
935                            gpointer session)
936 {
937         SOUP_SESSION_GET_CLASS (session)->auth_required (
938                 session, msg, auth, retrying);
939 }
940
941 #define SOUP_SESSION_WOULD_REDIRECT_AS_GET(session, msg) \
942         ((msg)->status_code == SOUP_STATUS_SEE_OTHER || \
943          ((msg)->status_code == SOUP_STATUS_FOUND && \
944           !SOUP_METHOD_IS_SAFE ((msg)->method)) || \
945          ((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY && \
946           (msg)->method == SOUP_METHOD_POST))
947
948 #define SOUP_SESSION_WOULD_REDIRECT_AS_SAFE(session, msg) \
949         (((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY || \
950           (msg)->status_code == SOUP_STATUS_TEMPORARY_REDIRECT || \
951           (msg)->status_code == SOUP_STATUS_FOUND) && \
952          SOUP_METHOD_IS_SAFE ((msg)->method))
953
954 static inline SoupURI *
955 redirection_uri (SoupMessage *msg)
956 {
957         const char *new_loc;
958         SoupURI *new_uri;
959
960         new_loc = soup_message_headers_get_one (msg->response_headers,
961                                                 "Location");
962         if (!new_loc)
963                 return NULL;
964         new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
965         if (!new_uri || !new_uri->host) {
966                 if (new_uri)
967                         soup_uri_free (new_uri);
968                 return NULL;
969         }
970
971         return new_uri;
972 }
973
974 /**
975  * soup_session_would_redirect:
976  * @session: a #SoupSession
977  * @msg: a #SoupMessage that has response headers
978  *
979  * Checks if @msg contains a response that would cause @session to
980  * redirect it to a new URL (ignoring @msg's %SOUP_MESSAGE_NO_REDIRECT
981  * flag, and the number of times it has already been redirected).
982  *
983  * Return value: whether @msg would be redirected
984  *
985  * Since: 2.38
986  */
987 gboolean
988 soup_session_would_redirect (SoupSession *session, SoupMessage *msg)
989 {
990         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
991         SoupURI *new_uri;
992
993         /* It must have an appropriate status code and method */
994         if (!SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg) &&
995             !SOUP_SESSION_WOULD_REDIRECT_AS_SAFE (session, msg))
996                 return FALSE;
997
998         /* and a Location header that parses to an http URI */
999         if (!soup_message_headers_get_one (msg->response_headers, "Location"))
1000                 return FALSE;
1001         new_uri = redirection_uri (msg);
1002         if (!new_uri)
1003                 return FALSE;
1004         if (!new_uri->host || !*new_uri->host ||
1005             (!uri_is_http (priv, new_uri) && !uri_is_https (priv, new_uri))) {
1006                 soup_uri_free (new_uri);
1007                 return FALSE;
1008         }
1009
1010         soup_uri_free (new_uri);
1011         return TRUE;
1012 }
1013
1014 /**
1015  * soup_session_redirect_message:
1016  * @session: the session
1017  * @msg: a #SoupMessage that has received a 3xx response
1018  *
1019  * Updates @msg's URI according to its status code and "Location"
1020  * header, and requeues it on @session. Use this when you have set
1021  * %SOUP_MESSAGE_NO_REDIRECT on a message, but have decided to allow a
1022  * particular redirection to occur, or if you want to allow a
1023  * redirection that #SoupSession will not perform automatically (eg,
1024  * redirecting a non-safe method such as DELETE).
1025  *
1026  * If @msg's status code indicates that it should be retried as a GET
1027  * request, then @msg will be modified accordingly.
1028  *
1029  * If @msg has already been redirected too many times, this will
1030  * cause it to fail with %SOUP_STATUS_TOO_MANY_REDIRECTS.
1031  *
1032  * Return value: %TRUE if a redirection was applied, %FALSE if not
1033  * (eg, because there was no Location header, or it could not be
1034  * parsed).
1035  *
1036  * Since: 2.38
1037  */
1038 gboolean
1039 soup_session_redirect_message (SoupSession *session, SoupMessage *msg)
1040 {
1041         SoupMessageQueueItem *item;
1042         SoupURI *new_uri;
1043
1044         new_uri = redirection_uri (msg);
1045         if (!new_uri)
1046                 return FALSE;
1047
1048         item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
1049         if (!item) {
1050                 soup_uri_free (new_uri);
1051                 return FALSE;
1052         }
1053         if (item->redirection_count >= SOUP_SESSION_MAX_REDIRECTION_COUNT) {
1054                 soup_uri_free (new_uri);
1055                 soup_session_cancel_message (session, msg, SOUP_STATUS_TOO_MANY_REDIRECTS);
1056                 soup_message_queue_item_unref (item);
1057                 return FALSE;
1058         }
1059         item->redirection_count++;
1060         soup_message_queue_item_unref (item);
1061
1062         if (SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg)) {
1063                 if (msg->method != SOUP_METHOD_HEAD) {
1064                         g_object_set (msg,
1065                                       SOUP_MESSAGE_METHOD, SOUP_METHOD_GET,
1066                                       NULL);
1067                 }
1068                 soup_message_set_request (msg, NULL,
1069                                           SOUP_MEMORY_STATIC, NULL, 0);
1070                 soup_message_headers_set_encoding (msg->request_headers,
1071                                                    SOUP_ENCODING_NONE);
1072         }
1073
1074         soup_message_set_uri (msg, new_uri);
1075         soup_uri_free (new_uri);
1076
1077         soup_session_requeue_message (session, msg);
1078         return TRUE;
1079 }
1080
1081 static void
1082 redirect_handler (SoupMessage *msg, gpointer user_data)
1083 {
1084         SoupMessageQueueItem *item = user_data;
1085         SoupSession *session = item->session;
1086
1087         if (!soup_session_would_redirect (session, msg)) {
1088                 SoupURI *new_uri = redirection_uri (msg);
1089                 gboolean invalid = !new_uri || !new_uri->host;
1090
1091                 if (new_uri)
1092                         soup_uri_free (new_uri);
1093                 if (invalid && !item->new_api) {
1094                         soup_message_set_status_full (msg,
1095                                                       SOUP_STATUS_MALFORMED,
1096                                                       "Invalid Redirect URL");
1097                 }
1098                 return;
1099         }
1100
1101         soup_session_redirect_message (session, msg);
1102 }
1103
1104 static void
1105 proxy_connection_event (SoupConnection      *conn,
1106                         GSocketClientEvent   event,
1107                         GIOStream           *connection,
1108                         gpointer             user_data)
1109 {
1110         SoupMessageQueueItem *item = user_data;
1111
1112         soup_message_network_event (item->msg, event, connection);
1113 }
1114
1115 static void
1116 soup_session_set_item_connection (SoupSession          *session,
1117                                   SoupMessageQueueItem *item,
1118                                   SoupConnection       *conn)
1119 {
1120         if (item->conn) {
1121                 g_signal_handlers_disconnect_by_func (item->conn, proxy_connection_event, item);
1122                 g_object_unref (item->conn);
1123         }
1124
1125         item->conn = conn;
1126
1127         if (item->conn) {
1128                 g_object_ref (item->conn);
1129                 g_signal_connect (item->conn, "event",
1130                                   G_CALLBACK (proxy_connection_event), item);
1131         }
1132 }
1133
1134 static void
1135 message_restarted (SoupMessage *msg, gpointer user_data)
1136 {
1137         SoupMessageQueueItem *item = user_data;
1138
1139         if (item->conn &&
1140             (!soup_message_is_keepalive (msg) ||
1141              SOUP_STATUS_IS_REDIRECTION (msg->status_code))) {
1142                 if (soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE)
1143                         soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
1144                 soup_session_set_item_connection (item->session, item, NULL);
1145         }
1146
1147         soup_message_cleanup_response (msg);
1148 }
1149
1150 SoupMessageQueueItem *
1151 soup_session_append_queue_item (SoupSession *session, SoupMessage *msg,
1152                                 gboolean async, gboolean new_api,
1153                                 SoupSessionCallback callback, gpointer user_data)
1154 {
1155         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1156         SoupMessageQueueItem *item;
1157         SoupSessionHost *host;
1158
1159         soup_message_cleanup_response (msg);
1160
1161         item = soup_message_queue_append (priv->queue, msg, callback, user_data);
1162         item->async = async;
1163         item->new_api = new_api;
1164
1165         g_mutex_lock (&priv->conn_lock);
1166         host = get_host_for_message (session, item->msg);
1167         host->num_messages++;
1168         g_mutex_unlock (&priv->conn_lock);
1169
1170         if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
1171                 soup_message_add_header_handler (
1172                         msg, "got_body", "Location",
1173                         G_CALLBACK (redirect_handler), item);
1174         }
1175         g_signal_connect (msg, "restarted",
1176                           G_CALLBACK (message_restarted), item);
1177
1178         g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
1179
1180         soup_message_queue_item_ref (item);
1181         return item;
1182 }
1183
1184 static void
1185 soup_session_send_queue_item (SoupSession *session,
1186                               SoupMessageQueueItem *item,
1187                               SoupMessageCompletionFn completion_cb)
1188 {
1189         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1190         const char *conn_header;
1191
1192         if (priv->user_agent) {
1193                 soup_message_headers_replace (item->msg->request_headers,
1194                                               "User-Agent", priv->user_agent);
1195         }
1196
1197         if (priv->accept_language &&
1198             !soup_message_headers_get_list (item->msg->request_headers,
1199                                             "Accept-Language")) {
1200                 soup_message_headers_append (item->msg->request_headers,
1201                                              "Accept-Language",
1202                                              priv->accept_language);
1203         }
1204
1205         /* Force keep alive connections for HTTP 1.0. Performance will
1206          * improve when issuing multiple requests to the same host in
1207          * a short period of time, as we wouldn't need to establish
1208          * new connections. Keep alive is implicit for HTTP 1.1.
1209          */
1210         conn_header = soup_message_headers_get_list (item->msg->request_headers, "Connection");
1211         if (!conn_header ||
1212             (!soup_header_contains (conn_header, "Keep-Alive") &&
1213              !soup_header_contains (conn_header, "close")))
1214                 soup_message_headers_append (item->msg->request_headers,
1215                                              "Connection", "Keep-Alive");
1216
1217         g_signal_emit (session, signals[REQUEST_STARTED], 0,
1218                        item->msg, soup_connection_get_socket (item->conn));
1219         if (item->state == SOUP_MESSAGE_RUNNING)
1220                 soup_connection_send_request (item->conn, item, completion_cb, item);
1221 }
1222
1223 static gboolean
1224 soup_session_cleanup_connections (SoupSession *session,
1225                                   gboolean     cleanup_idle)
1226 {
1227         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1228         GSList *conns = NULL, *c;
1229         GHashTableIter iter;
1230         gpointer conn, host;
1231         SoupConnectionState state;
1232
1233         g_mutex_lock (&priv->conn_lock);
1234         g_hash_table_iter_init (&iter, priv->conns);
1235         while (g_hash_table_iter_next (&iter, &conn, &host)) {
1236                 state = soup_connection_get_state (conn);
1237                 if (state == SOUP_CONNECTION_REMOTE_DISCONNECTED ||
1238                     (cleanup_idle && state == SOUP_CONNECTION_IDLE)) {
1239                         conns = g_slist_prepend (conns, g_object_ref (conn));
1240                         g_hash_table_iter_remove (&iter);
1241                         drop_connection (session, host, conn);
1242                 }
1243         }
1244         g_mutex_unlock (&priv->conn_lock);
1245
1246         if (!conns)
1247                 return FALSE;
1248
1249         for (c = conns; c; c = c->next) {
1250                 conn = c->data;
1251                 soup_connection_disconnect (conn);
1252                 g_object_unref (conn);
1253         }
1254         g_slist_free (conns);
1255
1256         return TRUE;
1257 }
1258
1259 static gboolean
1260 free_unused_host (gpointer user_data)
1261 {
1262         SoupSessionHost *host = (SoupSessionHost *) user_data;
1263         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (host->session);
1264
1265         g_mutex_lock (&priv->conn_lock);
1266
1267         /* In a multithreaded session, a connection might have been
1268          * added while we were waiting for conn_lock.
1269          */
1270         if (host->connections) {
1271                 g_mutex_unlock (&priv->conn_lock);
1272                 return FALSE;
1273         }
1274
1275         /* This will free the host in addition to removing it from the
1276          * hash table
1277          */
1278         if (host->uri->scheme == SOUP_URI_SCHEME_HTTPS)
1279                 g_hash_table_remove (priv->https_hosts, host->uri);
1280         else
1281                 g_hash_table_remove (priv->http_hosts, host->uri);
1282         g_mutex_unlock (&priv->conn_lock);
1283
1284         return FALSE;
1285 }
1286
1287 static void
1288 drop_connection (SoupSession *session, SoupSessionHost *host, SoupConnection *conn)
1289 {
1290         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1291
1292         /* Note: caller must hold conn_lock, and must remove @conn
1293          * from priv->conns itself.
1294          */
1295
1296         if (host) {
1297                 host->connections = g_slist_remove (host->connections, conn);
1298                 host->num_conns--;
1299
1300                 /* Free the SoupHost (and its SoupAddress) if there
1301                  * has not been any new connection to the host during
1302                  * the last HOST_KEEP_ALIVE msecs.
1303                  */
1304                 if (host->num_conns == 0) {
1305                         g_assert (host->keep_alive_src == NULL);
1306                         host->keep_alive_src = soup_add_timeout (priv->async_context,
1307                                                                  HOST_KEEP_ALIVE,
1308                                                                  free_unused_host,
1309                                                                  host);
1310                         host->keep_alive_src = g_source_ref (host->keep_alive_src);
1311                 }
1312
1313                 if (soup_connection_get_ssl_fallback (conn))
1314                         host->ssl_fallback = TRUE;
1315         }
1316
1317         g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
1318         g_signal_handlers_disconnect_by_func (conn, connection_state_changed, session);
1319         priv->num_conns--;
1320
1321         g_object_unref (conn);
1322 }
1323
1324 static void
1325 connection_disconnected (SoupConnection *conn, gpointer user_data)
1326 {
1327         SoupSession *session = user_data;
1328         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1329         SoupSessionHost *host;
1330
1331         g_mutex_lock (&priv->conn_lock);
1332
1333         host = g_hash_table_lookup (priv->conns, conn);
1334         if (host)
1335                 g_hash_table_remove (priv->conns, conn);
1336         drop_connection (session, host, conn);
1337
1338         g_mutex_unlock (&priv->conn_lock);
1339
1340         soup_session_kick_queue (session);
1341 }
1342
1343 static void
1344 connection_state_changed (GObject *object, GParamSpec *param, gpointer user_data)
1345 {
1346         SoupSession *session = user_data;
1347         SoupConnection *conn = SOUP_CONNECTION (object);
1348
1349         if (soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE)
1350                 soup_session_kick_queue (session);
1351 }
1352
1353 SoupMessageQueue *
1354 soup_session_get_queue (SoupSession *session)
1355 {
1356         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1357
1358         return priv->queue;
1359 }
1360
1361 static void
1362 soup_session_unqueue_item (SoupSession          *session,
1363                            SoupMessageQueueItem *item)
1364 {
1365         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1366         SoupSessionHost *host;
1367
1368         if (item->conn) {
1369                 if (item->msg->method != SOUP_METHOD_CONNECT ||
1370                     !SOUP_STATUS_IS_SUCCESSFUL (item->msg->status_code))
1371                         soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
1372                 soup_session_set_item_connection (session, item, NULL);
1373         }
1374
1375         if (item->state != SOUP_MESSAGE_FINISHED) {
1376                 g_warning ("finished an item with state %d", item->state);
1377                 return;
1378         }
1379
1380         soup_message_queue_remove (priv->queue, item);
1381
1382         g_mutex_lock (&priv->conn_lock);
1383         host = get_host_for_message (session, item->msg);
1384         host->num_messages--;
1385         g_mutex_unlock (&priv->conn_lock);
1386
1387         /* g_signal_handlers_disconnect_by_func doesn't work if you
1388          * have a metamarshal, meaning it doesn't work with
1389          * soup_message_add_header_handler()
1390          */
1391         g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
1392                                               0, 0, NULL, NULL, item);
1393         g_signal_emit (session, signals[REQUEST_UNQUEUED], 0, item->msg);
1394         soup_message_queue_item_unref (item);
1395 }
1396
1397 static void
1398 soup_session_set_item_status (SoupSession          *session,
1399                               SoupMessageQueueItem *item,
1400                               guint                 status_code)
1401 {
1402         SoupURI *uri;
1403         char *msg;
1404
1405         switch (status_code) {
1406         case SOUP_STATUS_CANT_RESOLVE:
1407         case SOUP_STATUS_CANT_CONNECT:
1408                 uri = soup_message_get_uri (item->msg);
1409                 msg = g_strdup_printf ("%s (%s)",
1410                                        soup_status_get_phrase (status_code),
1411                                        uri->host);
1412                 soup_message_set_status_full (item->msg, status_code, msg);
1413                 g_free (msg);
1414                 break;
1415
1416         case SOUP_STATUS_CANT_RESOLVE_PROXY:
1417         case SOUP_STATUS_CANT_CONNECT_PROXY:
1418                 if (item->proxy_uri && item->proxy_uri->host) {
1419                         msg = g_strdup_printf ("%s (%s)",
1420                                                soup_status_get_phrase (status_code),
1421                                                item->proxy_uri->host);
1422                         soup_message_set_status_full (item->msg, status_code, msg);
1423                         g_free (msg);
1424                         break;
1425                 }
1426                 soup_message_set_status (item->msg, status_code);
1427                 break;
1428
1429         case SOUP_STATUS_SSL_FAILED:
1430                 if (!g_tls_backend_supports_tls (g_tls_backend_get_default ())) {
1431                         soup_message_set_status_full (item->msg, status_code,
1432                                                       "TLS/SSL support not available; install glib-networking");
1433                 } else
1434                         soup_message_set_status (item->msg, status_code);
1435                 break;
1436
1437         default:
1438                 soup_message_set_status (item->msg, status_code);
1439                 break;
1440         }
1441 }
1442
1443
1444 static void
1445 message_completed (SoupMessage *msg, gpointer user_data)
1446 {
1447         SoupMessageQueueItem *item = user_data;
1448
1449         if (item->async)
1450                 soup_session_kick_queue (item->session);
1451
1452         if (item->state != SOUP_MESSAGE_RESTARTING) {
1453                 item->state = SOUP_MESSAGE_FINISHING;
1454
1455                 if (item->new_api && !item->async)
1456                         soup_session_process_queue_item (item->session, item, NULL, TRUE);
1457         }
1458 }
1459
1460 static void
1461 tunnel_complete (SoupConnection *conn, guint status, gpointer user_data)
1462 {
1463         SoupMessageQueueItem *tunnel_item = user_data;
1464         SoupMessageQueueItem *item = tunnel_item->related;
1465         SoupSession *session = tunnel_item->session;
1466
1467         soup_message_finished (tunnel_item->msg);
1468         soup_message_queue_item_unref (tunnel_item);
1469
1470         if (item->msg->status_code)
1471                 item->state = SOUP_MESSAGE_FINISHING;
1472         else
1473                 soup_message_set_https_status (item->msg, item->conn);
1474
1475         if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
1476                 soup_session_set_item_connection (session, item, NULL);
1477                 soup_session_set_item_status (session, item, status);
1478         }
1479
1480         item->state = SOUP_MESSAGE_READY;
1481         if (item->async)
1482                 soup_session_kick_queue (session);
1483         soup_message_queue_item_unref (item);
1484 }
1485
1486 static void
1487 tunnel_message_completed (SoupMessage *msg, gpointer user_data)
1488 {
1489         SoupMessageQueueItem *tunnel_item = user_data;
1490         SoupMessageQueueItem *item = tunnel_item->related;
1491         SoupSession *session = tunnel_item->session;
1492         guint status;
1493
1494         if (tunnel_item->state == SOUP_MESSAGE_RESTARTING) {
1495                 soup_message_restarted (msg);
1496                 if (tunnel_item->conn) {
1497                         tunnel_item->state = SOUP_MESSAGE_RUNNING;
1498                         soup_session_send_queue_item (session, tunnel_item,
1499                                                       tunnel_message_completed);
1500                         return;
1501                 }
1502
1503                 soup_message_set_status (msg, SOUP_STATUS_TRY_AGAIN);
1504         }
1505
1506         tunnel_item->state = SOUP_MESSAGE_FINISHED;
1507         soup_session_unqueue_item (session, tunnel_item);
1508
1509         status = tunnel_item->msg->status_code;
1510         if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
1511                 tunnel_complete (item->conn, status, tunnel_item);
1512                 return;
1513         }
1514
1515         if (tunnel_item->async) {
1516                 soup_connection_start_ssl_async (item->conn, item->cancellable,
1517                                                  tunnel_complete, tunnel_item);
1518         } else {
1519                 status = soup_connection_start_ssl_sync (item->conn, item->cancellable);
1520                 tunnel_complete (item->conn, status, tunnel_item);
1521         }
1522 }
1523
1524 static void
1525 tunnel_connect (SoupMessageQueueItem *item)
1526 {
1527         SoupSession *session = item->session;
1528         SoupMessageQueueItem *tunnel_item;
1529         SoupURI *uri;
1530         SoupMessage *msg;
1531
1532         item->state = SOUP_MESSAGE_TUNNELING;
1533
1534         uri = soup_connection_get_remote_uri (item->conn);
1535         msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT, uri);
1536         soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
1537
1538         tunnel_item = soup_session_append_queue_item (session, msg,
1539                                                       item->async, FALSE,
1540                                                       NULL, NULL);
1541         g_object_unref (msg);
1542         tunnel_item->related = item;
1543         soup_message_queue_item_ref (item);
1544         soup_session_set_item_connection (session, tunnel_item, item->conn);
1545         tunnel_item->state = SOUP_MESSAGE_RUNNING;
1546
1547         g_signal_emit (session, signals[TUNNELING], 0, tunnel_item->conn);
1548
1549         soup_session_send_queue_item (session, tunnel_item,
1550                                       tunnel_message_completed);
1551 }
1552
1553 static void
1554 got_connection (SoupConnection *conn, guint status, gpointer user_data)
1555 {
1556         SoupMessageQueueItem *item = user_data;
1557         SoupSession *session = item->session;
1558
1559         if (status != SOUP_STATUS_OK) {
1560                 if (item->state == SOUP_MESSAGE_CONNECTING) {
1561                         soup_session_set_item_status (session, item, status);
1562                         soup_session_set_item_connection (session, item, NULL);
1563                         item->state = SOUP_MESSAGE_READY;
1564                 }
1565         } else
1566                 item->state = SOUP_MESSAGE_CONNECTED;
1567
1568         if (item->async) {
1569                 if (item->state == SOUP_MESSAGE_CONNECTED ||
1570                     item->state == SOUP_MESSAGE_READY)
1571                         async_run_queue (item->session);
1572                 else
1573                         soup_session_kick_queue (item->session);
1574
1575                 soup_message_queue_item_unref (item);
1576         }
1577 }
1578
1579 /* requires conn_lock */
1580 static SoupConnection *
1581 get_connection_for_host (SoupSession *session,
1582                          SoupMessageQueueItem *item,
1583                          SoupSessionHost *host,
1584                          gboolean need_new_connection,
1585                          gboolean *try_cleanup)
1586 {
1587         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1588         SoupConnection *conn;
1589         GSList *conns;
1590         int num_pending = 0;
1591
1592         if (priv->disposed)
1593                 return FALSE;
1594
1595         if (item->conn) {
1596                 g_return_val_if_fail (soup_connection_get_state (item->conn) != SOUP_CONNECTION_DISCONNECTED, FALSE);
1597                 return item->conn;
1598         }
1599
1600         for (conns = host->connections; conns; conns = conns->next) {
1601                 conn = conns->data;
1602
1603                 if (!need_new_connection && soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE) {
1604                         soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
1605                         return conn;
1606                 } else if (soup_connection_get_state (conn) == SOUP_CONNECTION_CONNECTING)
1607                         num_pending++;
1608         }
1609
1610         /* Limit the number of pending connections; num_messages / 2
1611          * is somewhat arbitrary...
1612          */
1613         if (num_pending > host->num_messages / 2)
1614                 return NULL;
1615
1616         if (host->num_conns >= priv->max_conns_per_host) {
1617                 if (need_new_connection)
1618                         *try_cleanup = TRUE;
1619                 return NULL;
1620         }
1621
1622         if (priv->num_conns >= priv->max_conns) {
1623                 *try_cleanup = TRUE;
1624                 return NULL;
1625         }
1626
1627         conn = g_object_new (
1628                 SOUP_TYPE_CONNECTION,
1629                 SOUP_CONNECTION_REMOTE_URI, host->uri,
1630                 SOUP_CONNECTION_PROXY_RESOLVER, soup_session_get_feature (session, SOUP_TYPE_PROXY_URI_RESOLVER),
1631                 SOUP_CONNECTION_SSL, uri_is_https (priv, soup_message_get_uri (item->msg)),
1632                 SOUP_CONNECTION_SSL_CREDENTIALS, priv->tlsdb,
1633                 SOUP_CONNECTION_SSL_STRICT, (priv->tlsdb != NULL) && priv->ssl_strict,
1634                 SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
1635                 SOUP_CONNECTION_USE_THREAD_CONTEXT, priv->use_thread_context,
1636                 SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
1637                 SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
1638                 SOUP_CONNECTION_SSL_FALLBACK, host->ssl_fallback,
1639                 NULL);
1640         g_signal_connect (conn, "disconnected",
1641                           G_CALLBACK (connection_disconnected),
1642                           session);
1643         g_signal_connect (conn, "notify::state",
1644                           G_CALLBACK (connection_state_changed),
1645                           session);
1646
1647         /* This is a debugging-related signal, and so can ignore the
1648          * usual rule about not emitting signals while holding
1649          * conn_lock.
1650          */
1651         g_signal_emit (session, signals[CONNECTION_CREATED], 0, conn);
1652
1653         g_hash_table_insert (priv->conns, conn, host);
1654
1655         priv->num_conns++;
1656         host->num_conns++;
1657         host->connections = g_slist_prepend (host->connections, conn);
1658
1659         if (host->keep_alive_src) {
1660                 g_source_destroy (host->keep_alive_src);
1661                 g_source_unref (host->keep_alive_src);
1662                 host->keep_alive_src = NULL;
1663         }
1664
1665         return conn;
1666 }
1667
1668 static gboolean
1669 get_connection (SoupMessageQueueItem *item, gboolean *should_cleanup)
1670 {
1671         SoupSession *session = item->session;
1672         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1673         SoupSessionHost *host;
1674         SoupConnection *conn = NULL;
1675         gboolean my_should_cleanup = FALSE;
1676         gboolean need_new_connection;
1677
1678         need_new_connection =
1679                 (soup_message_get_flags (item->msg) & SOUP_MESSAGE_NEW_CONNECTION) ||
1680                 (!(soup_message_get_flags (item->msg) & SOUP_MESSAGE_IDEMPOTENT) &&
1681                  !SOUP_METHOD_IS_IDEMPOTENT (item->msg->method));
1682
1683         g_mutex_lock (&priv->conn_lock);
1684         host = get_host_for_message (session, item->msg);
1685         while (TRUE) {
1686                 conn = get_connection_for_host (session, item, host,
1687                                                 need_new_connection,
1688                                                 &my_should_cleanup);
1689                 if (conn || item->async)
1690                         break;
1691
1692                 if (my_should_cleanup) {
1693                         g_mutex_unlock (&priv->conn_lock);
1694                         soup_session_cleanup_connections (session, TRUE);
1695                         g_mutex_lock (&priv->conn_lock);
1696
1697                         my_should_cleanup = FALSE;
1698                         continue;
1699                 }
1700
1701                 g_cond_wait (&priv->conn_cond, &priv->conn_lock);
1702         }
1703         g_mutex_unlock (&priv->conn_lock);
1704
1705         if (!conn) {
1706                 if (should_cleanup)
1707                         *should_cleanup = my_should_cleanup;
1708                 return FALSE;
1709         }
1710
1711         soup_session_set_item_connection (session, item, conn);
1712         soup_message_set_https_status (item->msg, item->conn);
1713
1714         if (soup_connection_get_state (item->conn) != SOUP_CONNECTION_NEW) {
1715                 item->state = SOUP_MESSAGE_READY;
1716                 return TRUE;
1717         }
1718
1719         item->state = SOUP_MESSAGE_CONNECTING;
1720
1721         if (item->async) {
1722                 soup_message_queue_item_ref (item);
1723                 soup_connection_connect_async (item->conn, item->cancellable,
1724                                                got_connection, item);
1725                 return FALSE;
1726         } else {
1727                 guint status;
1728
1729                 status = soup_connection_connect_sync (item->conn, item->cancellable);
1730                 got_connection (item->conn, status, item);
1731
1732                 return TRUE;
1733         }
1734 }
1735
1736 void
1737 soup_session_process_queue_item (SoupSession          *session,
1738                                  SoupMessageQueueItem *item,
1739                                  gboolean             *should_cleanup,
1740                                  gboolean              loop)
1741 {
1742         g_assert (item->session == session);
1743
1744         do {
1745                 if (item->paused)
1746                         return;
1747
1748                 switch (item->state) {
1749                 case SOUP_MESSAGE_STARTING:
1750                         if (!get_connection (item, should_cleanup))
1751                                 return;
1752                         break;
1753
1754                 case SOUP_MESSAGE_CONNECTED:
1755                         if (soup_connection_is_tunnelled (item->conn))
1756                                 tunnel_connect (item);
1757                         else
1758                                 item->state = SOUP_MESSAGE_READY;
1759                         break;
1760
1761                 case SOUP_MESSAGE_READY:
1762                         soup_message_set_https_status (item->msg, item->conn);
1763                         if (item->msg->status_code) {
1764                                 if (item->msg->status_code == SOUP_STATUS_TRY_AGAIN) {
1765                                         soup_message_cleanup_response (item->msg);
1766                                         item->state = SOUP_MESSAGE_STARTING;
1767                                 } else
1768                                         item->state = SOUP_MESSAGE_FINISHING;
1769                                 break;
1770                         }
1771
1772                         item->state = SOUP_MESSAGE_RUNNING;
1773
1774                         soup_session_send_queue_item (session, item, message_completed);
1775
1776                         if (item->new_api) {
1777                                 if (item->async)
1778                                         async_send_request_running (session, item);
1779                                 return;
1780                         }
1781                         break;
1782
1783                 case SOUP_MESSAGE_RUNNING:
1784                         if (item->async)
1785                                 return;
1786
1787                         g_warn_if_fail (item->new_api);
1788                         item->state = SOUP_MESSAGE_FINISHING;
1789                         break;
1790
1791                 case SOUP_MESSAGE_RESTARTING:
1792                         item->state = SOUP_MESSAGE_STARTING;
1793                         soup_message_restarted (item->msg);
1794                         break;
1795
1796                 case SOUP_MESSAGE_FINISHING:
1797                         item->state = SOUP_MESSAGE_FINISHED;
1798                         soup_message_finished (item->msg);
1799                         if (item->state != SOUP_MESSAGE_FINISHED) {
1800                                 g_return_if_fail (!item->new_api);
1801                                 break;
1802                         }
1803
1804                         soup_session_unqueue_item (session, item);
1805                         if (item->async && item->callback)
1806                                 item->callback (session, item->msg, item->callback_data);
1807                         return;
1808
1809                 default:
1810                         /* Nothing to do with this message in any
1811                          * other state.
1812                          */
1813                         g_warn_if_fail (item->async);
1814                         return;
1815                 }
1816         } while (loop && item->state != SOUP_MESSAGE_FINISHED);
1817 }
1818
1819 static void
1820 async_run_queue (SoupSession *session)
1821 {
1822         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1823         SoupMessageQueueItem *item;
1824         SoupMessage *msg;
1825         gboolean try_cleanup = TRUE, should_cleanup = FALSE;
1826
1827         g_object_ref (session);
1828         soup_session_cleanup_connections (session, FALSE);
1829
1830  try_again:
1831         for (item = soup_message_queue_first (priv->queue);
1832              item;
1833              item = soup_message_queue_next (priv->queue, item)) {
1834                 msg = item->msg;
1835
1836                 /* CONNECT messages are handled specially */
1837                 if (msg->method == SOUP_METHOD_CONNECT)
1838                         continue;
1839
1840                 if (item->async_context != soup_session_get_async_context (session))
1841                         continue;
1842
1843                 soup_session_process_queue_item (session, item, &should_cleanup, TRUE);
1844         }
1845
1846         if (try_cleanup && should_cleanup) {
1847                 /* There is at least one message in the queue that
1848                  * could be sent if we cleanupd an idle connection from
1849                  * some other server.
1850                  */
1851                 if (soup_session_cleanup_connections (session, TRUE)) {
1852                         try_cleanup = should_cleanup = FALSE;
1853                         goto try_again;
1854                 }
1855         }
1856
1857         g_object_unref (session);
1858 }
1859
1860 static gboolean
1861 idle_run_queue (gpointer user_data)
1862 {
1863         SoupSessionPrivate *priv = user_data;
1864         GSource *source;
1865
1866         if (priv->disposed)
1867                 return FALSE;
1868
1869         source = g_main_current_source ();
1870         priv->run_queue_sources = g_slist_remove (priv->run_queue_sources, source);
1871
1872         /* Ensure that the source is destroyed before running the queue */
1873         g_source_destroy (source);
1874         g_source_unref (source);
1875
1876         g_assert (priv->session);
1877         async_run_queue (priv->session);
1878         return FALSE;
1879 }
1880
1881 /**
1882  * SoupSessionCallback:
1883  * @session: the session
1884  * @msg: the message that has finished
1885  * @user_data: the data passed to soup_session_queue_message
1886  *
1887  * Prototype for the callback passed to soup_session_queue_message(),
1888  * qv.
1889  **/
1890
1891 /**
1892  * soup_session_queue_message:
1893  * @session: a #SoupSession
1894  * @msg: (transfer full): the message to queue
1895  * @callback: (allow-none) (scope async): a #SoupSessionCallback which will
1896  * be called after the message completes or when an unrecoverable error occurs.
1897  * @user_data: (allow-none): a pointer passed to @callback.
1898  * 
1899  * Queues the message @msg for sending. All messages are processed
1900  * while the glib main loop runs. If @msg has been processed before,
1901  * any resources related to the time it was last sent are freed.
1902  *
1903  * Upon message completion, the callback specified in @callback will
1904  * be invoked (in the thread associated with @session's async
1905  * context). If after returning from this callback the message has not
1906  * been requeued, @msg will be unreffed.
1907  */
1908 void
1909 soup_session_queue_message (SoupSession *session, SoupMessage *msg,
1910                             SoupSessionCallback callback, gpointer user_data)
1911 {
1912         g_return_if_fail (SOUP_IS_SESSION_ASYNC (session) || SOUP_IS_SESSION_SYNC (session));
1913         g_return_if_fail (SOUP_IS_MESSAGE (msg));
1914
1915         SOUP_SESSION_GET_CLASS (session)->queue_message (session, msg,
1916                                                          callback, user_data);
1917         /* The SoupMessageQueueItem will hold a ref on @msg until it is
1918          * finished, so we can drop the ref adopted from the caller now.
1919          */
1920         g_object_unref (msg);
1921 }
1922
1923 static void
1924 soup_session_real_requeue_message (SoupSession *session, SoupMessage *msg)
1925 {
1926         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1927         SoupMessageQueueItem *item;
1928
1929         item = soup_message_queue_lookup (priv->queue, msg);
1930         g_return_if_fail (item != NULL);
1931         item->state = SOUP_MESSAGE_RESTARTING;
1932         soup_message_queue_item_unref (item);
1933 }
1934
1935 /**
1936  * soup_session_requeue_message:
1937  * @session: a #SoupSession
1938  * @msg: the message to requeue
1939  *
1940  * This causes @msg to be placed back on the queue to be attempted
1941  * again.
1942  **/
1943 void
1944 soup_session_requeue_message (SoupSession *session, SoupMessage *msg)
1945 {
1946         g_return_if_fail (SOUP_IS_SESSION (session));
1947         g_return_if_fail (SOUP_IS_MESSAGE (msg));
1948
1949         SOUP_SESSION_GET_CLASS (session)->requeue_message (session, msg);
1950 }
1951
1952 /**
1953  * soup_session_send_message:
1954  * @session: a #SoupSession
1955  * @msg: the message to send
1956  * 
1957  * Synchronously send @msg. This call will not return until the
1958  * transfer is finished successfully or there is an unrecoverable
1959  * error.
1960  *
1961  * @msg is not freed upon return.
1962  *
1963  * Return value: the HTTP status code of the response
1964  */
1965 guint
1966 soup_session_send_message (SoupSession *session, SoupMessage *msg)
1967 {
1968         g_return_val_if_fail (SOUP_IS_SESSION_ASYNC (session) || SOUP_IS_SESSION_SYNC (session), SOUP_STATUS_MALFORMED);
1969         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_STATUS_MALFORMED);
1970
1971         return SOUP_SESSION_GET_CLASS (session)->send_message (session, msg);
1972 }
1973
1974
1975 /**
1976  * soup_session_pause_message:
1977  * @session: a #SoupSession
1978  * @msg: a #SoupMessage currently running on @session
1979  *
1980  * Pauses HTTP I/O on @msg. Call soup_session_unpause_message() to
1981  * resume I/O.
1982  **/
1983 void
1984 soup_session_pause_message (SoupSession *session,
1985                             SoupMessage *msg)
1986 {
1987         SoupSessionPrivate *priv;
1988         SoupMessageQueueItem *item;
1989
1990         g_return_if_fail (SOUP_IS_SESSION (session));
1991         g_return_if_fail (SOUP_IS_MESSAGE (msg));
1992
1993         priv = SOUP_SESSION_GET_PRIVATE (session);
1994         item = soup_message_queue_lookup (priv->queue, msg);
1995         g_return_if_fail (item != NULL);
1996
1997         item->paused = TRUE;
1998         if (item->state == SOUP_MESSAGE_RUNNING)
1999                 soup_message_io_pause (msg);
2000         soup_message_queue_item_unref (item);
2001 }
2002
2003 static void
2004 soup_session_real_kick_queue (SoupSession *session)
2005 {
2006         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2007         SoupMessageQueueItem *item;
2008         gboolean have_sync_items = FALSE;
2009
2010         if (priv->disposed)
2011                 return;
2012
2013         for (item = soup_message_queue_first (priv->queue);
2014              item;
2015              item = soup_message_queue_next (priv->queue, item)) {
2016                 if (item->async) {
2017                         GSource *source;
2018
2019                         /* We use priv rather than session as the
2020                          * source data, because other parts of libsoup
2021                          * (or the calling app) may have sources using
2022                          * the session as the source data.
2023                          */
2024                         source = g_main_context_find_source_by_user_data (item->async_context, priv);
2025                         if (!source) {
2026                                 source = soup_add_completion_reffed (item->async_context,
2027                                                                      idle_run_queue, priv);
2028                                 priv->run_queue_sources = g_slist_prepend (priv->run_queue_sources,
2029                                                                            source);
2030                         }
2031                 } else
2032                         have_sync_items = TRUE;
2033         }
2034
2035         if (have_sync_items)
2036                 g_cond_broadcast (&priv->conn_cond);
2037 }
2038
2039 void
2040 soup_session_kick_queue (SoupSession *session)
2041 {
2042         SOUP_SESSION_GET_CLASS (session)->kick (session);
2043 }
2044
2045 /**
2046  * soup_session_unpause_message:
2047  * @session: a #SoupSession
2048  * @msg: a #SoupMessage currently running on @session
2049  *
2050  * Resumes HTTP I/O on @msg. Use this to resume after calling
2051  * soup_session_pause_message().
2052  *
2053  * If @msg is being sent via blocking I/O, this will resume reading or
2054  * writing immediately. If @msg is using non-blocking I/O, then
2055  * reading or writing won't resume until you return to the main loop.
2056  **/
2057 void
2058 soup_session_unpause_message (SoupSession *session,
2059                               SoupMessage *msg)
2060 {
2061         SoupSessionPrivate *priv;
2062         SoupMessageQueueItem *item;
2063
2064         g_return_if_fail (SOUP_IS_SESSION (session));
2065         g_return_if_fail (SOUP_IS_MESSAGE (msg));
2066
2067         priv = SOUP_SESSION_GET_PRIVATE (session);
2068         item = soup_message_queue_lookup (priv->queue, msg);
2069         g_return_if_fail (item != NULL);
2070
2071         item->paused = FALSE;
2072         if (item->state == SOUP_MESSAGE_RUNNING)
2073                 soup_message_io_unpause (msg);
2074         soup_message_queue_item_unref (item);
2075
2076         soup_session_kick_queue (session);
2077 }
2078
2079
2080 static void
2081 soup_session_real_cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
2082 {
2083         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2084         SoupMessageQueueItem *item;
2085
2086         item = soup_message_queue_lookup (priv->queue, msg);
2087         g_return_if_fail (item != NULL);
2088
2089         item->paused = FALSE;
2090         soup_message_set_status (msg, status_code);
2091         g_cancellable_cancel (item->cancellable);
2092
2093         soup_session_kick_queue (item->session);
2094         soup_message_queue_item_unref (item);
2095 }
2096
2097 /**
2098  * soup_session_cancel_message:
2099  * @session: a #SoupSession
2100  * @msg: the message to cancel
2101  * @status_code: status code to set on @msg (generally
2102  * %SOUP_STATUS_CANCELLED)
2103  *
2104  * Causes @session to immediately finish processing @msg (regardless
2105  * of its current state) with a final status_code of @status_code. You
2106  * may call this at any time after handing @msg off to @session; if
2107  * @session has started sending the request but has not yet received
2108  * the complete response, then it will close the request's connection.
2109  * Note that with non-idempotent requests (eg,
2110  * <literal>POST</literal>, <literal>PUT</literal>,
2111  * <literal>DELETE</literal>) it is possible that you might cancel the
2112  * request after the server acts on it, but before it returns a
2113  * response, leaving the remote resource in an unknown state.
2114  *
2115  * If the message is cancelled while its response body is being read,
2116  * then the response body in @msg will be left partially-filled-in.
2117  * The response headers, on the other hand, will always be either
2118  * empty or complete.
2119  *
2120  * For messages queued with soup_session_queue_message() (and
2121  * cancelled from the same thread), the callback will be invoked
2122  * before soup_session_cancel_message() returns.
2123  **/
2124 void
2125 soup_session_cancel_message (SoupSession *session, SoupMessage *msg,
2126                              guint status_code)
2127 {
2128         SoupSessionPrivate *priv;
2129         SoupMessageQueueItem *item;
2130
2131         g_return_if_fail (SOUP_IS_SESSION (session));
2132         g_return_if_fail (SOUP_IS_MESSAGE (msg));
2133
2134         priv = SOUP_SESSION_GET_PRIVATE (session);
2135         item = soup_message_queue_lookup (priv->queue, msg);
2136         /* If the message is already ending, don't do anything */
2137         if (!item)
2138                 return;
2139         if (item->state == SOUP_MESSAGE_FINISHED) {
2140                 soup_message_queue_item_unref (item);
2141                 return;
2142         }
2143
2144         SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg, status_code);
2145         soup_message_queue_item_unref (item);
2146 }
2147
2148 static void
2149 soup_session_real_flush_queue (SoupSession *session)
2150 {
2151         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2152         SoupMessageQueueItem *item;
2153         GHashTable *current = NULL;
2154         gboolean done = FALSE;
2155
2156         if (SOUP_IS_SESSION_SYNC (session)) {
2157                 /* Record the current contents of the queue */
2158                 current = g_hash_table_new (NULL, NULL);
2159                 for (item = soup_message_queue_first (priv->queue);
2160                      item;
2161                      item = soup_message_queue_next (priv->queue, item))
2162                         g_hash_table_insert (current, item, item);
2163         }
2164
2165         /* Cancel everything */
2166         for (item = soup_message_queue_first (priv->queue);
2167              item;
2168              item = soup_message_queue_next (priv->queue, item)) {
2169                 soup_session_cancel_message (session, item->msg,
2170                                              SOUP_STATUS_CANCELLED);
2171         }
2172
2173         if (SOUP_IS_SESSION_SYNC (session)) {
2174                 /* Wait until all of the items in @current have been
2175                  * removed from the queue. (This is not the same as
2176                  * "wait for the queue to be empty", because the app
2177                  * may queue new requests in response to the
2178                  * cancellation of the old ones. We don't try to
2179                  * cancel those requests as well, since we'd likely
2180                  * just end up looping forever.)
2181                  */
2182                 g_mutex_lock (&priv->conn_lock);
2183                 do {
2184                         done = TRUE;
2185                         for (item = soup_message_queue_first (priv->queue);
2186                              item;
2187                              item = soup_message_queue_next (priv->queue, item)) {
2188                                 if (g_hash_table_lookup (current, item))
2189                                         done = FALSE;
2190                         }
2191
2192                         if (!done)
2193                                 g_cond_wait (&priv->conn_cond, &priv->conn_lock);
2194                 } while (!done);
2195                 g_mutex_unlock (&priv->conn_lock);
2196
2197                 g_hash_table_destroy (current);
2198         }
2199 }
2200
2201 /**
2202  * soup_session_abort:
2203  * @session: the session
2204  *
2205  * Cancels all pending requests in @session and closes all idle
2206  * persistent connections.
2207  **/
2208 void
2209 soup_session_abort (SoupSession *session)
2210 {
2211         SoupSessionPrivate *priv;
2212         GSList *conns, *c;
2213         GHashTableIter iter;
2214         gpointer conn, host;
2215
2216         g_return_if_fail (SOUP_IS_SESSION (session));
2217         priv = SOUP_SESSION_GET_PRIVATE (session);
2218
2219         SOUP_SESSION_GET_CLASS (session)->flush_queue (session);
2220
2221         /* Close all connections */
2222         g_mutex_lock (&priv->conn_lock);
2223         conns = NULL;
2224         g_hash_table_iter_init (&iter, priv->conns);
2225         while (g_hash_table_iter_next (&iter, &conn, &host)) {
2226                 conns = g_slist_prepend (conns, g_object_ref (conn));
2227                 g_hash_table_iter_remove (&iter);
2228                 drop_connection (session, host, conn);
2229         }
2230         g_mutex_unlock (&priv->conn_lock);
2231
2232         for (c = conns; c; c = c->next) {
2233                 soup_connection_disconnect (c->data);
2234                 g_object_unref (c->data);
2235         }
2236
2237         g_slist_free (conns);
2238 }
2239
2240 static void
2241 prefetch_uri (SoupSession *session, SoupURI *uri,
2242               GCancellable *cancellable,
2243               SoupAddressCallback callback, gpointer user_data)
2244 {
2245         SoupSessionPrivate *priv;
2246         SoupSessionHost *host;
2247         SoupAddress *addr;
2248
2249         priv = SOUP_SESSION_GET_PRIVATE (session);
2250
2251         g_mutex_lock (&priv->conn_lock);
2252         host = get_host_for_uri (session, uri);
2253         addr = g_object_ref (host->addr);
2254         g_mutex_unlock (&priv->conn_lock);
2255
2256         soup_address_resolve_async (addr,
2257                                     soup_session_get_async_context (session),
2258                                     cancellable, callback, user_data);
2259         g_object_unref (addr);
2260 }
2261
2262 /**
2263  * soup_session_prepare_for_uri:
2264  * @session: a #SoupSession
2265  * @uri: a #SoupURI which may be required
2266  *
2267  * Tells @session that @uri may be requested shortly, and so the
2268  * session can try to prepare (resolving the domain name, obtaining
2269  * proxy address, etc.) in order to work more quickly once the URI is
2270  * actually requested.
2271  *
2272  * This method acts asynchronously, in @session's
2273  * #SoupSession:async_context. If you are using #SoupSessionSync and do
2274  * not have a main loop running, then you can't use this method.
2275  *
2276  * Since: 2.30
2277  *
2278  * Deprecated: 2.38: use soup_session_prefetch_dns() instead
2279  **/
2280 void
2281 soup_session_prepare_for_uri (SoupSession *session, SoupURI *uri)
2282 {
2283         g_return_if_fail (SOUP_IS_SESSION (session));
2284         g_return_if_fail (uri != NULL);
2285
2286         if (!uri->host)
2287                 return;
2288
2289         prefetch_uri (session, uri, NULL, NULL, NULL);
2290 }
2291
2292 /**
2293 * soup_session_prefetch_dns:
2294 * @session: a #SoupSession
2295 * @hostname: a hostname to be resolved
2296 * @cancellable: (allow-none): a #GCancellable object, or %NULL
2297 * @callback: (scope async) (allow-none): callback to call with the
2298 *     result, or %NULL
2299 * @user_data: data for @callback
2300 *
2301 * Tells @session that an URI from the given @hostname may be requested
2302 * shortly, and so the session can try to prepare by resolving the
2303 * domain name in advance, in order to work more quickly once the URI
2304 * is actually requested.
2305 *
2306 * If @cancellable is non-%NULL, it can be used to cancel the
2307 * resolution. @callback will still be invoked in this case, with a
2308 * status of %SOUP_STATUS_CANCELLED.
2309 *
2310 * This method acts asynchronously, in @session's
2311 * #SoupSession:async_context. If you are using #SoupSessionSync and do
2312 * not have a main loop running, then you can't use this method.
2313 *
2314 * Since: 2.38
2315 **/
2316 void
2317 soup_session_prefetch_dns (SoupSession *session, const char *hostname,
2318                            GCancellable *cancellable,
2319                            SoupAddressCallback callback, gpointer user_data)
2320 {
2321         SoupURI *uri;
2322
2323         g_return_if_fail (SOUP_IS_SESSION (session));
2324         g_return_if_fail (hostname != NULL);
2325
2326         /* FIXME: Prefetching should work for both HTTP and HTTPS */
2327         uri = soup_uri_new (NULL);
2328         soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP);
2329         soup_uri_set_host (uri, hostname);
2330         soup_uri_set_path (uri, "");
2331
2332         prefetch_uri (session, uri, cancellable, callback, user_data);
2333         soup_uri_free (uri);
2334 }
2335
2336 /**
2337  * soup_session_add_feature:
2338  * @session: a #SoupSession
2339  * @feature: an object that implements #SoupSessionFeature
2340  *
2341  * Adds @feature's functionality to @session. You can also add a
2342  * feature to the session at construct time by using the
2343  * %SOUP_SESSION_ADD_FEATURE property.
2344  *
2345  * Since: 2.24
2346  **/
2347 void
2348 soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature)
2349 {
2350         SoupSessionPrivate *priv;
2351
2352         g_return_if_fail (SOUP_IS_SESSION (session));
2353         g_return_if_fail (SOUP_IS_SESSION_FEATURE (feature));
2354
2355         priv = SOUP_SESSION_GET_PRIVATE (session);
2356         priv->features = g_slist_prepend (priv->features, g_object_ref (feature));
2357         g_hash_table_remove_all (priv->features_cache);
2358         soup_session_feature_attach (feature, session);
2359 }
2360
2361 /**
2362  * soup_session_add_feature_by_type:
2363  * @session: a #SoupSession
2364  * @feature_type: a #GType
2365  *
2366  * If @feature_type is the type of a class that implements
2367  * #SoupSessionFeature, this creates a new feature of that type and
2368  * adds it to @session as with soup_session_add_feature(). You can use
2369  * this when you don't need to customize the new feature in any way.
2370  *
2371  * If @feature_type is not a #SoupSessionFeature type, this gives each
2372  * existing feature on @session the chance to accept @feature_type as
2373  * a "subfeature". This can be used to add new #SoupAuth or
2374  * #SoupRequest types, for instance.
2375  *
2376  * You can also add a feature to the session at construct time by
2377  * using the %SOUP_SESSION_ADD_FEATURE_BY_TYPE property.
2378  *
2379  * Since: 2.24
2380  **/
2381 void
2382 soup_session_add_feature_by_type (SoupSession *session, GType feature_type)
2383 {
2384         SoupSessionPrivate *priv;
2385
2386         g_return_if_fail (SOUP_IS_SESSION (session));
2387
2388         priv = SOUP_SESSION_GET_PRIVATE (session);
2389
2390         if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2391                 SoupSessionFeature *feature;
2392
2393                 feature = g_object_new (feature_type, NULL);
2394                 soup_session_add_feature (session, feature);
2395                 g_object_unref (feature);
2396         } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
2397                 SoupRequestClass *request_class;
2398                 int i;
2399
2400                 request_class = g_type_class_ref (feature_type);
2401                 for (i = 0; request_class->schemes[i]; i++) {
2402                         g_hash_table_insert (priv->request_types,
2403                                              (char *)request_class->schemes[i],
2404                                              GSIZE_TO_POINTER (feature_type));
2405                 }
2406         } else {
2407                 GSList *f;
2408
2409                 for (f = priv->features; f; f = f->next) {
2410                         if (soup_session_feature_add_feature (f->data, feature_type))
2411                                 return;
2412                 }
2413                 g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
2414         }
2415 }
2416
2417 /**
2418  * soup_session_remove_feature:
2419  * @session: a #SoupSession
2420  * @feature: a feature that has previously been added to @session
2421  *
2422  * Removes @feature's functionality from @session.
2423  *
2424  * Since: 2.24
2425  **/
2426 void
2427 soup_session_remove_feature (SoupSession *session, SoupSessionFeature *feature)
2428 {
2429         SoupSessionPrivate *priv;
2430
2431         g_return_if_fail (SOUP_IS_SESSION (session));
2432
2433         priv = SOUP_SESSION_GET_PRIVATE (session);
2434         if (g_slist_find (priv->features, feature)) {
2435                 priv->features = g_slist_remove (priv->features, feature);
2436                 g_hash_table_remove_all (priv->features_cache);
2437                 soup_session_feature_detach (feature, session);
2438                 g_object_unref (feature);
2439         }
2440 }
2441
2442 /**
2443  * soup_session_remove_feature_by_type:
2444  * @session: a #SoupSession
2445  * @feature_type: a #GType
2446  *
2447  * Removes all features of type @feature_type (or any subclass of
2448  * @feature_type) from @session. You can also remove standard features
2449  * from the session at construct time by using the
2450  * %SOUP_SESSION_REMOVE_FEATURE_BY_TYPE property.
2451  *
2452  * Since: 2.24
2453  **/
2454 void
2455 soup_session_remove_feature_by_type (SoupSession *session, GType feature_type)
2456 {
2457         SoupSessionPrivate *priv;
2458         GSList *f;
2459
2460         g_return_if_fail (SOUP_IS_SESSION (session));
2461
2462         priv = SOUP_SESSION_GET_PRIVATE (session);
2463
2464         if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2465         restart:
2466                 for (f = priv->features; f; f = f->next) {
2467                         if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type)) {
2468                                 soup_session_remove_feature (session, f->data);
2469                                 goto restart;
2470                         }
2471                 }
2472         } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
2473                 SoupRequestClass *request_class;
2474                 int i;
2475
2476                 request_class = g_type_class_peek (feature_type);
2477                 if (!request_class)
2478                         return;
2479                 for (i = 0; request_class->schemes[i]; i++) {
2480                         g_hash_table_remove (priv->request_types,
2481                                              request_class->schemes[i]);
2482                 }
2483         } else {
2484                 for (f = priv->features; f; f = f->next) {
2485                         if (soup_session_feature_remove_feature (f->data, feature_type))
2486                                 return;
2487                 }
2488                 g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
2489         }
2490 }
2491
2492 /**
2493  * soup_session_has_feature:
2494  * @session: a #SoupSession
2495  * @feature_type: the #GType of the class of features to check for
2496  *
2497  * Tests if @session has at a feature of type @feature_type (which can
2498  * be the type of either a #SoupSessionFeature, or else a subtype of
2499  * some class managed by another feature, such as #SoupAuth or
2500  * #SoupRequest).
2501  *
2502  * Return value: %TRUE or %FALSE
2503  *
2504  * Since: 2.42
2505  **/
2506 gboolean
2507 soup_session_has_feature (SoupSession *session,
2508                           GType        feature_type)
2509 {
2510         SoupSessionPrivate *priv;
2511         GSList *f;
2512
2513         g_return_val_if_fail (SOUP_IS_SESSION (session), FALSE);
2514
2515         priv = SOUP_SESSION_GET_PRIVATE (session);
2516
2517         if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2518                 for (f = priv->features; f; f = f->next) {
2519                         if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
2520                                 return TRUE;
2521                 }
2522         } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
2523                 return g_hash_table_lookup (priv->request_types,
2524                                             GSIZE_TO_POINTER (feature_type)) != NULL;
2525         } else {
2526                 for (f = priv->features; f; f = f->next) {
2527                         if (soup_session_feature_has_feature (f->data, feature_type))
2528                                 return TRUE;
2529                 }
2530         }
2531
2532         return FALSE;
2533 }
2534
2535 /**
2536  * soup_session_get_features:
2537  * @session: a #SoupSession
2538  * @feature_type: the #GType of the class of features to get
2539  *
2540  * Generates a list of @session's features of type @feature_type. (If
2541  * you want to see all features, you can pass %SOUP_TYPE_SESSION_FEATURE
2542  * for @feature_type.)
2543  *
2544  * Return value: (transfer container) (element-type Soup.SessionFeature):
2545  * a list of features. You must free the list, but not its contents
2546  *
2547  * Since: 2.26
2548  **/
2549 GSList *
2550 soup_session_get_features (SoupSession *session, GType feature_type)
2551 {
2552         SoupSessionPrivate *priv;
2553         GSList *f, *ret;
2554
2555         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
2556
2557         priv = SOUP_SESSION_GET_PRIVATE (session);
2558         for (f = priv->features, ret = NULL; f; f = f->next) {
2559                 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
2560                         ret = g_slist_prepend (ret, f->data);
2561         }
2562         return g_slist_reverse (ret);
2563 }
2564
2565 /**
2566  * soup_session_get_feature:
2567  * @session: a #SoupSession
2568  * @feature_type: the #GType of the feature to get
2569  *
2570  * Gets the first feature in @session of type @feature_type. For
2571  * features where there may be more than one feature of a given type,
2572  * use soup_session_get_features().
2573  *
2574  * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
2575  * feature is owned by @session.
2576  *
2577  * Since: 2.26
2578  **/
2579 SoupSessionFeature *
2580 soup_session_get_feature (SoupSession *session, GType feature_type)
2581 {
2582         SoupSessionPrivate *priv;
2583         SoupSessionFeature *feature;
2584         GSList *f;
2585
2586         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
2587
2588         priv = SOUP_SESSION_GET_PRIVATE (session);
2589
2590         feature = g_hash_table_lookup (priv->features_cache,
2591                                        GSIZE_TO_POINTER (feature_type));
2592         if (feature)
2593                 return feature;
2594
2595         for (f = priv->features; f; f = f->next) {
2596                 feature = f->data;
2597                 if (G_TYPE_CHECK_INSTANCE_TYPE (feature, feature_type)) {
2598                         g_hash_table_insert (priv->features_cache,
2599                                              GSIZE_TO_POINTER (feature_type),
2600                                              feature);
2601                         return feature;
2602                 }
2603         }
2604         return NULL;
2605 }
2606
2607 /**
2608  * soup_session_get_feature_for_message:
2609  * @session: a #SoupSession
2610  * @feature_type: the #GType of the feature to get
2611  * @msg: a #SoupMessage
2612  *
2613  * Gets the first feature in @session of type @feature_type, provided
2614  * that it is not disabled for @msg. As with
2615  * soup_session_get_feature(), this should only be used for features
2616  * where @feature_type is only expected to match a single feature. In
2617  * particular, if there are two matching features, and the first is
2618  * disabled on @msg, and the second is not, then this will return
2619  * %NULL, not the second feature.
2620  *
2621  * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
2622  * feature is owned by @session.
2623  *
2624  * Since: 2.28
2625  **/
2626 SoupSessionFeature *
2627 soup_session_get_feature_for_message (SoupSession *session, GType feature_type,
2628                                       SoupMessage *msg)
2629 {
2630         SoupSessionFeature *feature;
2631
2632         feature = soup_session_get_feature (session, feature_type);
2633         if (feature && soup_message_disables_feature (msg, feature))
2634                 return NULL;
2635         return feature;
2636 }
2637
2638 static void
2639 soup_session_class_init (SoupSessionClass *session_class)
2640 {
2641         GObjectClass *object_class = G_OBJECT_CLASS (session_class);
2642
2643         g_type_class_add_private (session_class, sizeof (SoupSessionPrivate));
2644
2645         /* virtual method definition */
2646         session_class->requeue_message = soup_session_real_requeue_message;
2647         session_class->cancel_message = soup_session_real_cancel_message;
2648         session_class->auth_required = soup_session_real_auth_required;
2649         session_class->flush_queue = soup_session_real_flush_queue;
2650         session_class->kick = soup_session_real_kick_queue;
2651
2652         /* virtual method override */
2653         object_class->constructor = soup_session_constructor;
2654         object_class->dispose = soup_session_dispose;
2655         object_class->finalize = soup_session_finalize;
2656         object_class->set_property = soup_session_set_property;
2657         object_class->get_property = soup_session_get_property;
2658
2659         /* signals */
2660
2661         /**
2662          * SoupSession::request-queued:
2663          * @session: the session
2664          * @msg: the request that was queued
2665          *
2666          * Emitted when a request is queued on @session. (Note that
2667          * "queued" doesn't just mean soup_session_queue_message();
2668          * soup_session_send_message() implicitly queues the message
2669          * as well.)
2670          *
2671          * When sending a request, first #SoupSession::request_queued
2672          * is emitted, indicating that the session has become aware of
2673          * the request.
2674          *
2675          * Once a connection is available to send the request on, the
2676          * session emits #SoupSession::request_started. Then, various
2677          * #SoupMessage signals are emitted as the message is
2678          * processed. If the message is requeued, it will emit
2679          * #SoupMessage::restarted, which will then be followed by
2680          * another #SoupSession::request_started and another set of
2681          * #SoupMessage signals when the message is re-sent.
2682          *
2683          * Eventually, the message will emit #SoupMessage::finished.
2684          * Normally, this signals the completion of message
2685          * processing. However, it is possible that the application
2686          * will requeue the message from the "finished" handler (or
2687          * equivalently, from the soup_session_queue_message()
2688          * callback). In that case, the process will loop back to
2689          * #SoupSession::request_started.
2690          *
2691          * Eventually, a message will reach "finished" and not be
2692          * requeued. At that point, the session will emit
2693          * #SoupSession::request_unqueued to indicate that it is done
2694          * with the message.
2695          *
2696          * To sum up: #SoupSession::request_queued and
2697          * #SoupSession::request_unqueued are guaranteed to be emitted
2698          * exactly once, but #SoupSession::request_started and
2699          * #SoupMessage::finished (and all of the other #SoupMessage
2700          * signals) may be invoked multiple times for a given message.
2701          *
2702          * Since: 2.24
2703          **/
2704         signals[REQUEST_QUEUED] =
2705                 g_signal_new ("request-queued",
2706                               G_OBJECT_CLASS_TYPE (object_class),
2707                               G_SIGNAL_RUN_FIRST,
2708                               0, /* FIXME? */
2709                               NULL, NULL,
2710                               _soup_marshal_NONE__OBJECT,
2711                               G_TYPE_NONE, 1,
2712                               SOUP_TYPE_MESSAGE);
2713
2714         /**
2715          * SoupSession::request-started:
2716          * @session: the session
2717          * @msg: the request being sent
2718          * @socket: the socket the request is being sent on
2719          *
2720          * Emitted just before a request is sent. See
2721          * #SoupSession::request_queued for a detailed description of
2722          * the message lifecycle within a session.
2723          **/
2724         signals[REQUEST_STARTED] =
2725                 g_signal_new ("request-started",
2726                               G_OBJECT_CLASS_TYPE (object_class),
2727                               G_SIGNAL_RUN_FIRST,
2728                               G_STRUCT_OFFSET (SoupSessionClass, request_started),
2729                               NULL, NULL,
2730                               _soup_marshal_NONE__OBJECT_OBJECT,
2731                               G_TYPE_NONE, 2,
2732                               SOUP_TYPE_MESSAGE,
2733                               SOUP_TYPE_SOCKET);
2734
2735         /**
2736          * SoupSession::request-unqueued:
2737          * @session: the session
2738          * @msg: the request that was unqueued
2739          *
2740          * Emitted when a request is removed from @session's queue,
2741          * indicating that @session is done with it. See
2742          * #SoupSession::request_queued for a detailed description of the
2743          * message lifecycle within a session.
2744          *
2745          * Since: 2.24
2746          **/
2747         signals[REQUEST_UNQUEUED] =
2748                 g_signal_new ("request-unqueued",
2749                               G_OBJECT_CLASS_TYPE (object_class),
2750                               G_SIGNAL_RUN_FIRST,
2751                               0, /* FIXME? */
2752                               NULL, NULL,
2753                               _soup_marshal_NONE__OBJECT,
2754                               G_TYPE_NONE, 1,
2755                               SOUP_TYPE_MESSAGE);
2756
2757         /**
2758          * SoupSession::authenticate:
2759          * @session: the session
2760          * @msg: the #SoupMessage being sent
2761          * @auth: the #SoupAuth to authenticate
2762          * @retrying: %TRUE if this is the second (or later) attempt
2763          *
2764          * Emitted when the session requires authentication. If
2765          * credentials are available call soup_auth_authenticate() on
2766          * @auth. If these credentials fail, the signal will be
2767          * emitted again, with @retrying set to %TRUE, which will
2768          * continue until you return without calling
2769          * soup_auth_authenticate() on @auth.
2770          *
2771          * Note that this may be emitted before @msg's body has been
2772          * fully read.
2773          *
2774          * If you call soup_session_pause_message() on @msg before
2775          * returning, then you can authenticate @auth asynchronously
2776          * (as long as you g_object_ref() it to make sure it doesn't
2777          * get destroyed), and then unpause @msg when you are ready
2778          * for it to continue.
2779          **/
2780         signals[AUTHENTICATE] =
2781                 g_signal_new ("authenticate",
2782                               G_OBJECT_CLASS_TYPE (object_class),
2783                               G_SIGNAL_RUN_FIRST,
2784                               G_STRUCT_OFFSET (SoupSessionClass, authenticate),
2785                               NULL, NULL,
2786                               _soup_marshal_NONE__OBJECT_OBJECT_BOOLEAN,
2787                               G_TYPE_NONE, 3,
2788                               SOUP_TYPE_MESSAGE,
2789                               SOUP_TYPE_AUTH,
2790                               G_TYPE_BOOLEAN);
2791
2792         /**
2793          * SoupSession::connection-created:
2794          * @session: the #SoupSession
2795          * @connection: the connection
2796          *
2797          * Emitted when a new connection is created. This is an
2798          * internal signal intended only to be used for debugging
2799          * purposes, and may go away in the future.
2800          *
2801          * Since: 2.30
2802          */
2803         signals[CONNECTION_CREATED] =
2804                 g_signal_new ("connection-created",
2805                               G_OBJECT_CLASS_TYPE (object_class),
2806                               G_SIGNAL_RUN_FIRST,
2807                               0,
2808                               NULL, NULL,
2809                               _soup_marshal_NONE__OBJECT,
2810                               G_TYPE_NONE, 1,
2811                               /* SoupConnection is private, so we can't use
2812                                * SOUP_TYPE_CONNECTION here.
2813                                */
2814                               G_TYPE_OBJECT);
2815
2816         /**
2817          * SoupSession::tunneling:
2818          * @session: the #SoupSession
2819          * @connection: the connection
2820          *
2821          * Emitted when an SSL tunnel is being created on a proxy
2822          * connection. This is an internal signal intended only to be
2823          * used for debugging purposes, and may go away in the future.
2824          *
2825          * Since: 2.30
2826          */
2827         signals[TUNNELING] =
2828                 g_signal_new ("tunneling",
2829                               G_OBJECT_CLASS_TYPE (object_class),
2830                               G_SIGNAL_RUN_FIRST,
2831                               0,
2832                               NULL, NULL,
2833                               _soup_marshal_NONE__OBJECT,
2834                               G_TYPE_NONE, 1,
2835                               /* SoupConnection is private, so we can't use
2836                                * SOUP_TYPE_CONNECTION here.
2837                                */
2838                               G_TYPE_OBJECT);
2839
2840
2841         /* properties */
2842         /**
2843          * SOUP_SESSION_PROXY_URI:
2844          *
2845          * Alias for the #SoupSession:proxy-uri property. (The HTTP
2846          * proxy to use for this session.)
2847          **/
2848         g_object_class_install_property (
2849                 object_class, PROP_PROXY_URI,
2850                 g_param_spec_boxed (SOUP_SESSION_PROXY_URI,
2851                                     "Proxy URI",
2852                                     "The HTTP Proxy to use for this session",
2853                                     SOUP_TYPE_URI,
2854                                     G_PARAM_READWRITE));
2855         /**
2856          * SOUP_SESSION_MAX_CONNS:
2857          *
2858          * Alias for the #SoupSession:max-conns property. (The maximum
2859          * number of connections that the session can open at once.)
2860          **/
2861         g_object_class_install_property (
2862                 object_class, PROP_MAX_CONNS,
2863                 g_param_spec_int (SOUP_SESSION_MAX_CONNS,
2864                                   "Max Connection Count",
2865                                   "The maximum number of connections that the session can open at once",
2866                                   1,
2867                                   G_MAXINT,
2868                                   SOUP_SESSION_MAX_CONNS_DEFAULT,
2869                                   G_PARAM_READWRITE));
2870         /**
2871          * SOUP_SESSION_MAX_CONNS_PER_HOST:
2872          *
2873          * Alias for the #SoupSession:max-conns-per-host property.
2874          * (The maximum number of connections that the session can
2875          * open at once to a given host.)
2876          **/
2877         g_object_class_install_property (
2878                 object_class, PROP_MAX_CONNS_PER_HOST,
2879                 g_param_spec_int (SOUP_SESSION_MAX_CONNS_PER_HOST,
2880                                   "Max Per-Host Connection Count",
2881                                   "The maximum number of connections that the session can open at once to a given host",
2882                                   1,
2883                                   G_MAXINT,
2884                                   SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT,
2885                                   G_PARAM_READWRITE));
2886         /**
2887          * SoupSession:idle-timeout:
2888          *
2889          * Connection lifetime (in seconds) when idle. Any connection
2890          * left idle longer than this will be closed.
2891          *
2892          * Although you can change this property at any time, it will
2893          * only affect newly-created connections, not currently-open
2894          * ones. You can call soup_session_abort() after setting this
2895          * if you want to ensure that all future connections will have
2896          * this timeout value.
2897          *
2898          * Since: 2.24
2899          **/
2900         /**
2901          * SOUP_SESSION_IDLE_TIMEOUT:
2902          *
2903          * Alias for the #SoupSession:idle-timeout property. (The idle
2904          * connection lifetime.)
2905          *
2906          * Since: 2.24
2907          **/
2908         g_object_class_install_property (
2909                 object_class, PROP_IDLE_TIMEOUT,
2910                 g_param_spec_uint (SOUP_SESSION_IDLE_TIMEOUT,
2911                                    "Idle Timeout",
2912                                    "Connection lifetime when idle",
2913                                    0, G_MAXUINT, 0,
2914                                    G_PARAM_READWRITE));
2915         /**
2916          * SoupSession:use-ntlm:
2917          *
2918          * Whether or not to use NTLM authentication.
2919          *
2920          * Deprecated: use soup_session_add_feature_by_type() with
2921          * #SOUP_TYPE_AUTH_NTLM.
2922          **/
2923         /**
2924          * SOUP_SESSION_USE_NTLM:
2925          *
2926          * Alias for the #SoupSession:use-ntlm property. (Whether or
2927          * not to use NTLM authentication.)
2928          **/
2929         g_object_class_install_property (
2930                 object_class, PROP_USE_NTLM,
2931                 g_param_spec_boolean (SOUP_SESSION_USE_NTLM,
2932                                       "Use NTLM",
2933                                       "Whether or not to use NTLM authentication",
2934                                       FALSE,
2935                                       G_PARAM_READWRITE | G_PARAM_DEPRECATED));
2936         /**
2937          * SoupSession:ssl-ca-file:
2938          *
2939          * File containing SSL CA certificates.
2940          *
2941          * Deprecated: use #SoupSession:ssl-use-system-ca-file or
2942          * #SoupSession:tls-database instead
2943          **/
2944         /**
2945          * SOUP_SESSION_SSL_CA_FILE:
2946          *
2947          * Alias for the #SoupSession:ssl-ca-file property. (File
2948          * containing SSL CA certificates.).
2949          *
2950          * Deprecated: use #SoupSession:ssl-use-system-ca-file or
2951          * #SoupSession:tls-database instead
2952          **/
2953         g_object_class_install_property (
2954                 object_class, PROP_SSL_CA_FILE,
2955                 g_param_spec_string (SOUP_SESSION_SSL_CA_FILE,
2956                                      "SSL CA file",
2957                                      "File containing SSL CA certificates",
2958                                      NULL,
2959                                      G_PARAM_READWRITE | G_PARAM_DEPRECATED));
2960         /**
2961          * SOUP_SESSION_USE_SYSTEM_CA_FILE:
2962          *
2963          * Alias for the #SoupSession:ssl-use-system-ca-file property,
2964          * qv.
2965          *
2966          * Since: 2.38
2967          **/
2968         /**
2969          * SoupSession:ssl-use-system-ca-file:
2970          *
2971          * Setting this to %TRUE is equivalent to setting
2972          * #SoupSession:tls-database to the default system CA database.
2973          * (and likewise, setting #SoupSession:tls-database to the
2974          * default database by hand will cause this property to
2975          * become %TRUE).
2976          *
2977          * Setting this to %FALSE (when it was previously %TRUE) will
2978          * clear the #SoupSession:tls-database field.
2979          *
2980          * See #SoupSession:ssl-strict for more information on how
2981          * https certificate validation is handled.
2982          *
2983          * Since: 2.38
2984          **/
2985         g_object_class_install_property (
2986                 object_class, PROP_SSL_USE_SYSTEM_CA_FILE,
2987                 g_param_spec_boolean (SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE,
2988                                       "Use system CA file",
2989                                       "Use the system certificate database",
2990                                       FALSE,
2991                                       G_PARAM_READWRITE));
2992         /**
2993          * SOUP_SESSION_TLS_DATABASE:
2994          *
2995          * Alias for the #SoupSession:tls-database property, qv.
2996          *
2997          * Since: 2.38
2998          **/
2999         /**
3000          * SoupSession:tls-database:
3001          *
3002          * Sets the #GTlsDatabase to use for validating SSL/TLS
3003          * certificates.
3004          *
3005          * Note that setting the #SoupSession:ssl-ca-file or
3006          * #SoupSession:ssl-use-system-ca-file property will cause
3007          * this property to be set to a #GTlsDatabase corresponding to
3008          * the indicated file or system default.
3009          *
3010          * See #SoupSession:ssl-strict for more information on how
3011          * https certificate validation is handled.
3012          *
3013          * Since: 2.38
3014          **/
3015         g_object_class_install_property (
3016                 object_class, PROP_TLS_DATABASE,
3017                 g_param_spec_object (SOUP_SESSION_TLS_DATABASE,
3018                                      "TLS Database",
3019                                      "TLS database to use",
3020                                      G_TYPE_TLS_DATABASE,
3021                                      G_PARAM_READWRITE));
3022         /**
3023          * SOUP_SESSION_SSL_STRICT:
3024          *
3025          * Alias for the #SoupSession:ssl-strict property, qv.
3026          *
3027          * Since: 2.30
3028          **/
3029         /**
3030          * SoupSession:ssl-strict:
3031          *
3032          * Normally, if #SoupSession:tlsdb is set (including if it was
3033          * set via #SoupSession:ssl-use-system-ca-file or
3034          * #SoupSession:ssl-ca-file), then libsoup will reject any
3035          * certificate that is invalid (ie, expired) or that is not
3036          * signed by one of the given CA certificates, and the
3037          * #SoupMessage will fail with the status
3038          * %SOUP_STATUS_SSL_FAILED.
3039          *
3040          * If you set #SoupSession:ssl-strict to %FALSE, then all
3041          * certificates will be accepted, and you will need to call
3042          * soup_message_get_https_status() to distinguish valid from
3043          * invalid certificates. (This can be used, eg, if you want to
3044          * accept invalid certificates after giving some sort of
3045          * warning.)
3046          *
3047          * If the session has no CA file or TLS database, then all
3048          * certificates are always accepted, and this property has no
3049          * effect.
3050          *
3051          * Since: 2.30
3052          */
3053         g_object_class_install_property (
3054                 object_class, PROP_SSL_STRICT,
3055                 g_param_spec_boolean (SOUP_SESSION_SSL_STRICT,
3056                                       "Strictly validate SSL certificates",
3057                                       "Whether certificate errors should be considered a connection error",
3058                                       TRUE,
3059                                       G_PARAM_READWRITE));
3060         /**
3061          * SOUP_SESSION_ASYNC_CONTEXT:
3062          *
3063          * Alias for the #SoupSession:async-context property. (The
3064          * session's #GMainContext.)
3065          */
3066         g_object_class_install_property (
3067                 object_class, PROP_ASYNC_CONTEXT,
3068                 g_param_spec_pointer (SOUP_SESSION_ASYNC_CONTEXT,
3069                                       "Async GMainContext",
3070                                       "The GMainContext to dispatch async I/O in",
3071                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
3072         /**
3073          * SOUP_SESSION_USE_THREAD_CONTEXT:
3074          *
3075          * Alias for the #SoupSession:use-thread-context property, qv.
3076          *
3077          * Since: 2.38
3078          */
3079         /**
3080          * SoupSession:use-thread-context:
3081          *
3082          * If set, asynchronous operations in this session will run in
3083          * whatever the thread-default #GMainContext is at the time
3084          * they are started, rather than always occurring in a context
3085          * fixed at the session's construction time. "Bookkeeping"
3086          * tasks (like expiring idle connections) will happen in the
3087          * context that was thread-default at the time the session was
3088          * created.
3089          *
3090          * Since: 2.38
3091          */
3092         g_object_class_install_property (
3093                 object_class, PROP_USE_THREAD_CONTEXT,
3094                 g_param_spec_boolean (SOUP_SESSION_USE_THREAD_CONTEXT,
3095                                       "Use thread-default GMainContext",
3096                                       "Whether to use thread-default main contexts",
3097                                       FALSE,
3098                                       G_PARAM_READWRITE));
3099         /**
3100          * SoupSession:timeout:
3101          *
3102          * The timeout (in seconds) for socket I/O operations
3103          * (including connecting to a server, and waiting for a reply
3104          * to an HTTP request).
3105          *
3106          * Although you can change this property at any time, it will
3107          * only affect newly-created connections, not currently-open
3108          * ones. You can call soup_session_abort() after setting this
3109          * if you want to ensure that all future connections will have
3110          * this timeout value.
3111          */
3112         /**
3113          * SOUP_SESSION_TIMEOUT:
3114          *
3115          * Alias for the #SoupSession:timeout property. (The timeout
3116          * in seconds for socket I/O operations.)
3117          **/
3118         g_object_class_install_property (
3119                 object_class, PROP_TIMEOUT,
3120                 g_param_spec_uint (SOUP_SESSION_TIMEOUT,
3121                                    "Timeout value",
3122                                    "Value in seconds to timeout a blocking I/O",
3123                                    0, G_MAXUINT, 0,
3124                                    G_PARAM_READWRITE));
3125
3126         /**
3127          * SoupSession:user-agent:
3128          *
3129          * If non-%NULL, the value to use for the "User-Agent" header
3130          * on #SoupMessage<!-- -->s sent from this session.
3131          *
3132          * RFC 2616 says: "The User-Agent request-header field
3133          * contains information about the user agent originating the
3134          * request. This is for statistical purposes, the tracing of
3135          * protocol violations, and automated recognition of user
3136          * agents for the sake of tailoring responses to avoid
3137          * particular user agent limitations. User agents SHOULD
3138          * include this field with requests."
3139          *
3140          * The User-Agent header contains a list of one or more
3141          * product tokens, separated by whitespace, with the most
3142          * significant product token coming first. The tokens must be
3143          * brief, ASCII, and mostly alphanumeric (although "-", "_",
3144          * and "." are also allowed), and may optionally include a "/"
3145          * followed by a version string. You may also put comments,
3146          * enclosed in parentheses, between or after the tokens.
3147          *
3148          * If you set a #SoupSession:user_agent property that has trailing
3149          * whitespace, #SoupSession will append its own product token
3150          * (eg, "<literal>libsoup/2.3.2</literal>") to the end of the
3151          * header for you.
3152          **/
3153         /**
3154          * SOUP_SESSION_USER_AGENT:
3155          *
3156          * Alias for the #SoupSession:user-agent property, qv.
3157          **/
3158         g_object_class_install_property (
3159                 object_class, PROP_USER_AGENT,
3160                 g_param_spec_string (SOUP_SESSION_USER_AGENT,
3161                                      "User-Agent string",
3162                                      "User-Agent string",
3163                                      NULL,
3164                                      G_PARAM_READWRITE));
3165
3166         /**
3167          * SoupSession:accept-language:
3168          *
3169          * If non-%NULL, the value to use for the "Accept-Language" header
3170          * on #SoupMessage<!-- -->s sent from this session.
3171          *
3172          * Setting this will disable
3173          * #SoupSession:accept-language-auto.
3174          *
3175          * Since: 2.30
3176          **/
3177         /**
3178          * SOUP_SESSION_ACCEPT_LANGUAGE:
3179          *
3180          * Alias for the #SoupSession:accept-language property, qv.
3181          *
3182          * Since: 2.30
3183          **/
3184         g_object_class_install_property (
3185                 object_class, PROP_ACCEPT_LANGUAGE,
3186                 g_param_spec_string (SOUP_SESSION_ACCEPT_LANGUAGE,
3187                                      "Accept-Language string",
3188                                      "Accept-Language string",
3189                                      NULL,
3190                                      G_PARAM_READWRITE));
3191
3192         /**
3193          * SoupSession:accept-language-auto:
3194          *
3195          * If %TRUE, #SoupSession will automatically set the string
3196          * for the "Accept-Language" header on every #SoupMessage
3197          * sent, based on the return value of g_get_language_names().
3198          *
3199          * Setting this will override any previous value of
3200          * #SoupSession:accept-language.
3201          *
3202          * Since: 2.30
3203          **/
3204         /**
3205          * SOUP_SESSION_ACCEPT_LANGUAGE_AUTO:
3206          *
3207          * Alias for the #SoupSession:accept-language-auto property, qv.
3208          *
3209          * Since: 2.30
3210          **/
3211         g_object_class_install_property (
3212                 object_class, PROP_ACCEPT_LANGUAGE_AUTO,
3213                 g_param_spec_boolean (SOUP_SESSION_ACCEPT_LANGUAGE_AUTO,
3214                                       "Accept-Language automatic mode",
3215                                       "Accept-Language automatic mode",
3216                                       FALSE,
3217                                       G_PARAM_READWRITE));
3218
3219         /**
3220          * SoupSession:add-feature: (skip)
3221          *
3222          * Add a feature object to the session. (Shortcut for calling
3223          * soup_session_add_feature().)
3224          *
3225          * Since: 2.24
3226          **/
3227         /**
3228          * SOUP_SESSION_ADD_FEATURE: (skip)
3229          *
3230          * Alias for the #SoupSession:add-feature property. (Shortcut
3231          * for calling soup_session_add_feature().
3232          *
3233          * Since: 2.24
3234          **/
3235         g_object_class_install_property (
3236                 object_class, PROP_ADD_FEATURE,
3237                 g_param_spec_object (SOUP_SESSION_ADD_FEATURE,
3238                                      "Add Feature",
3239                                      "Add a feature object to the session",
3240                                      SOUP_TYPE_SESSION_FEATURE,
3241                                      G_PARAM_READWRITE));
3242         /**
3243          * SoupSession:add-feature-by-type: (skip)
3244          *
3245          * Add a feature object of the given type to the session.
3246          * (Shortcut for calling soup_session_add_feature_by_type().)
3247          *
3248          * Since: 2.24
3249          **/
3250         /**
3251          * SOUP_SESSION_ADD_FEATURE_BY_TYPE: (skip)
3252          *
3253          * Alias for the #SoupSession:add-feature-by-type property.
3254          * (Shortcut for calling soup_session_add_feature_by_type().
3255          *
3256          * Since: 2.24
3257          **/
3258         g_object_class_install_property (
3259                 object_class, PROP_ADD_FEATURE_BY_TYPE,
3260                 g_param_spec_gtype (SOUP_SESSION_ADD_FEATURE_BY_TYPE,
3261                                     "Add Feature By Type",
3262                                     "Add a feature object of the given type to the session",
3263                                     SOUP_TYPE_SESSION_FEATURE,
3264                                     G_PARAM_READWRITE));
3265         /**
3266          * SoupSession:remove-feature-by-type: (skip)
3267          *
3268          * Remove feature objects from the session. (Shortcut for
3269          * calling soup_session_remove_feature_by_type().)
3270          *
3271          * Since: 2.24
3272          **/
3273         /**
3274          * SOUP_SESSION_REMOVE_FEATURE_BY_TYPE: (skip)
3275          *
3276          * Alias for the #SoupSession:remove-feature-by-type
3277          * property. (Shortcut for calling
3278          * soup_session_remove_feature_by_type().
3279          *
3280          * Since: 2.24
3281          **/
3282         g_object_class_install_property (
3283                 object_class, PROP_REMOVE_FEATURE_BY_TYPE,
3284                 g_param_spec_gtype (SOUP_SESSION_REMOVE_FEATURE_BY_TYPE,
3285                                     "Remove Feature By Type",
3286                                     "Remove features of the given type from the session",
3287                                     SOUP_TYPE_SESSION_FEATURE,
3288                                     G_PARAM_READWRITE));
3289         /**
3290          * SoupSession:http-aliases:
3291          *
3292          * A %NULL-terminated array of URI schemes that should be
3293          * considered to be aliases for "http". Eg, if this included
3294          * <literal>"dav"</literal>, than a URI of
3295          * <literal>dav://example.com/path</literal> would be treated
3296          * identically to <literal>http://example.com/path</literal>.
3297          * If the value is %NULL, then only "http" is recognized as
3298          * meaning "http".
3299          *
3300          * For backward-compatibility reasons, the default value for
3301          * this property is an array containing the single element
3302          * <literal>"*"</literal>, a special value which means that
3303          * any scheme except "https" is considered to be an alias for
3304          * "http".
3305          *
3306          * See also #SoupSession:https-aliases.
3307          *
3308          * Since: 2.38
3309          */
3310         /**
3311          * SOUP_SESSION_HTTP_ALIASES:
3312          *
3313          * Alias for the #SoupSession:http-aliases property. (URI
3314          * schemes that will be considered aliases for "http".)
3315          *
3316          * Since: 2.38
3317          */
3318         g_object_class_install_property (
3319                 object_class, PROP_HTTP_ALIASES,
3320                 g_param_spec_boxed (SOUP_SESSION_HTTP_ALIASES,
3321                                     "http aliases",
3322                                     "URI schemes that are considered aliases for 'http'",
3323                                     G_TYPE_STRV,
3324                                     G_PARAM_READWRITE));
3325         /**
3326          * SoupSession:https-aliases:
3327          *
3328          * A comma-delimited list of URI schemes that should be
3329          * considered to be aliases for "https". See
3330          * #SoupSession:http-aliases for more information.
3331          *
3332          * The default value is %NULL, meaning that no URI schemes
3333          * are considered aliases for "https".
3334          *
3335          * Since: 2.38
3336          */
3337         /**
3338          * SOUP_SESSION_HTTPS_ALIASES:
3339          *
3340          * Alias for the #SoupSession:https-aliases property. (URI
3341          * schemes that will be considered aliases for "https".)
3342          *
3343          * Since: 2.38
3344          **/
3345         g_object_class_install_property (
3346                 object_class, PROP_HTTPS_ALIASES,
3347                 g_param_spec_boxed (SOUP_SESSION_HTTPS_ALIASES,
3348                                     "https aliases",
3349                                     "URI schemes that are considered aliases for 'https'",
3350                                     G_TYPE_STRV,
3351                                     G_PARAM_READWRITE));
3352 }
3353
3354
3355 /* send_request_async */
3356
3357 static void
3358 async_send_request_return_result (SoupMessageQueueItem *item,
3359                                   gpointer stream, GError *error)
3360 {
3361         GTask *task;
3362
3363         g_return_if_fail (item->task != NULL);
3364
3365         g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
3366                                               0, 0, NULL, NULL, item);
3367
3368         task = item->task;
3369         item->task = NULL;
3370
3371         if (item->io_source) {
3372                 g_source_destroy (item->io_source);
3373                 g_clear_pointer (&item->io_source, g_source_unref);
3374         }
3375
3376         if (error)
3377                 g_task_return_error (task, error);
3378         else if (SOUP_STATUS_IS_TRANSPORT_ERROR (item->msg->status_code)) {
3379                 if (stream)
3380                         g_object_unref (stream);
3381                 g_task_return_new_error (task, SOUP_HTTP_ERROR,
3382                                          item->msg->status_code,
3383                                          "%s",
3384                                          item->msg->reason_phrase);
3385         } else
3386                 g_task_return_pointer (task, stream, g_object_unref);
3387         g_object_unref (task);
3388 }
3389
3390 static void
3391 async_send_request_restarted (SoupMessage *msg, gpointer user_data)
3392 {
3393         SoupMessageQueueItem *item = user_data;
3394
3395         /* We won't be needing this, then. */
3396         g_object_set_data (G_OBJECT (item->msg), "SoupSession:ostream", NULL);
3397         item->io_started = FALSE;
3398 }
3399
3400 static void
3401 async_send_request_finished (SoupMessage *msg, gpointer user_data)
3402 {
3403         SoupMessageQueueItem *item = user_data;
3404         GMemoryOutputStream *mostream;
3405         GInputStream *istream = NULL;
3406         GError *error = NULL;
3407
3408         if (!item->task) {
3409                 /* Something else already took care of it. */
3410                 return;
3411         }
3412
3413         mostream = g_object_get_data (G_OBJECT (item->task), "SoupSession:ostream");
3414         if (mostream) {
3415                 gpointer data;
3416                 gssize size;
3417
3418                 /* We thought it would be requeued, but it wasn't, so
3419                  * return the original body.
3420                  */
3421                 size = g_memory_output_stream_get_data_size (mostream);
3422                 data = size ? g_memory_output_stream_steal_data (mostream) : g_strdup ("");
3423                 istream = g_memory_input_stream_new_from_data (data, size, g_free);
3424         } else if (item->io_started) {
3425                 /* The message finished before becoming readable. This
3426                  * will happen, eg, if it's cancelled from got-headers.
3427                  * Do nothing; the op will complete via read_ready_cb()
3428                  * after we return;
3429                  */
3430                 return;
3431         } else {
3432                 /* The message finished before even being started;
3433                  * probably a tunnel connect failure.
3434                  */
3435                 istream = g_memory_input_stream_new ();
3436         }
3437
3438         async_send_request_return_result (item, istream, error);
3439 }
3440
3441 static void
3442 send_async_spliced (GObject *source, GAsyncResult *result, gpointer user_data)
3443 {
3444         SoupMessageQueueItem *item = user_data;
3445         GInputStream *istream = g_object_get_data (source, "istream");
3446         GError *error = NULL;
3447
3448         /* It should be safe to call the sync close() method here since
3449          * the message body has already been written.
3450          */
3451         g_input_stream_close (istream, NULL, NULL);
3452         g_object_unref (istream);
3453
3454         /* If the message was cancelled, it will be completed via other means */
3455         if (g_cancellable_is_cancelled (item->cancellable) ||
3456             !item->task) {
3457                 soup_message_queue_item_unref (item);
3458                 return;
3459         }
3460
3461         if (g_output_stream_splice_finish (G_OUTPUT_STREAM (source),
3462                                            result, &error) == -1) {
3463                 async_send_request_return_result (item, NULL, error);
3464                 soup_message_queue_item_unref (item);
3465                 return;
3466         }
3467
3468         /* Otherwise either restarted or finished will eventually be called. */
3469         soup_session_kick_queue (item->session);
3470         soup_message_queue_item_unref (item);
3471 }
3472
3473 static void
3474 send_async_maybe_complete (SoupMessageQueueItem *item,
3475                            GInputStream         *stream)
3476 {
3477         if (item->msg->status_code == SOUP_STATUS_UNAUTHORIZED ||
3478             item->msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED ||
3479             soup_session_would_redirect (item->session, item->msg)) {
3480                 GOutputStream *ostream;
3481
3482                 /* Message may be requeued, so gather the current message body... */
3483                 ostream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
3484                 g_object_set_data_full (G_OBJECT (item->task), "SoupSession:ostream",
3485                                         ostream, g_object_unref);
3486
3487                 g_object_set_data (G_OBJECT (ostream), "istream", stream);
3488
3489                 /* Give the splice op its own ref on item */
3490                 soup_message_queue_item_ref (item);
3491                 g_output_stream_splice_async (ostream, stream,
3492                                               /* We can't use CLOSE_SOURCE because it
3493                                                * might get closed in the wrong thread.
3494                                                */
3495                                               G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
3496                                               G_PRIORITY_DEFAULT,
3497                                               item->cancellable,
3498                                               send_async_spliced, item);
3499                 return;
3500         }
3501
3502         async_send_request_return_result (item, stream, NULL);
3503 }
3504
3505 static void try_run_until_read (SoupMessageQueueItem *item);
3506
3507 static gboolean
3508 read_ready_cb (SoupMessage *msg, gpointer user_data)
3509 {
3510         SoupMessageQueueItem *item = user_data;
3511
3512         g_clear_pointer (&item->io_source, g_source_unref);
3513         try_run_until_read (item);
3514         return FALSE;
3515 }
3516
3517 static void
3518 try_run_until_read (SoupMessageQueueItem *item)
3519 {
3520         GError *error = NULL;
3521         GInputStream *stream = NULL;
3522
3523         if (soup_message_io_run_until_read (item->msg, item->cancellable, &error))
3524                 stream = soup_message_io_get_response_istream (item->msg, &error);
3525         if (stream) {
3526                 send_async_maybe_complete (item, stream);
3527                 return;
3528         }
3529
3530         if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) {
3531                 item->state = SOUP_MESSAGE_RESTARTING;
3532                 soup_message_io_finished (item->msg);
3533                 g_error_free (error);
3534                 return;
3535         }
3536
3537         if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
3538                 if (item->state != SOUP_MESSAGE_FINISHED) {
3539                         if (soup_message_io_in_progress (item->msg))
3540                                 soup_message_io_finished (item->msg);
3541                         item->state = SOUP_MESSAGE_FINISHING;
3542                         soup_session_process_queue_item (item->session, item, NULL, FALSE);
3543                 }
3544                 async_send_request_return_result (item, NULL, error);
3545                 return;
3546         }
3547
3548         g_clear_error (&error);
3549         item->io_source = soup_message_io_get_source (item->msg, item->cancellable,
3550                                                       read_ready_cb, item);
3551         g_source_attach (item->io_source, soup_session_get_async_context (item->session));
3552 }
3553
3554 static void
3555 async_send_request_running (SoupSession *session, SoupMessageQueueItem *item)
3556 {
3557         item->io_started = TRUE;
3558         try_run_until_read (item);
3559 }
3560
3561 void
3562 soup_session_send_request_async (SoupSession         *session,
3563                                  SoupMessage         *msg,
3564                                  GCancellable        *cancellable,
3565                                  GAsyncReadyCallback  callback,
3566                                  gpointer             user_data)
3567 {
3568         SoupMessageQueueItem *item;
3569         gboolean use_thread_context;
3570
3571         g_return_if_fail (SOUP_IS_SESSION (session));
3572         g_return_if_fail (!SOUP_IS_SESSION_SYNC (session));
3573
3574         g_object_get (G_OBJECT (session),
3575                       SOUP_SESSION_USE_THREAD_CONTEXT, &use_thread_context,
3576                       NULL);
3577         g_return_if_fail (use_thread_context);
3578
3579         item = soup_session_append_queue_item (session, msg, TRUE, TRUE,
3580                                                NULL, NULL);
3581         g_signal_connect (msg, "restarted",
3582                           G_CALLBACK (async_send_request_restarted), item);
3583         g_signal_connect (msg, "finished",
3584                           G_CALLBACK (async_send_request_finished), item);
3585
3586         item->new_api = TRUE;
3587         item->task = g_task_new (session, cancellable, callback, user_data);
3588         g_task_set_task_data (item->task, item, (GDestroyNotify) soup_message_queue_item_unref);
3589
3590         if (cancellable) {
3591                 g_object_unref (item->cancellable);
3592                 item->cancellable = g_object_ref (cancellable);
3593         }
3594
3595         soup_session_kick_queue (session);
3596 }
3597
3598 GInputStream *
3599 soup_session_send_request_finish (SoupSession   *session,
3600                                   GAsyncResult  *result,
3601                                   GError       **error)
3602 {
3603         GTask *task;
3604
3605         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
3606         g_return_val_if_fail (!SOUP_IS_SESSION_SYNC (session), NULL);
3607         g_return_val_if_fail (g_task_is_valid (result, session), NULL);
3608
3609         task = G_TASK (result);
3610         if (g_task_had_error (task)) {
3611                 SoupMessageQueueItem *item = g_task_get_task_data (task);
3612
3613                 if (soup_message_io_in_progress (item->msg))
3614                         soup_message_io_finished (item->msg);
3615                 else if (item->state != SOUP_MESSAGE_FINISHED)
3616                         item->state = SOUP_MESSAGE_FINISHING;
3617
3618                 if (item->state != SOUP_MESSAGE_FINISHED)
3619                         soup_session_process_queue_item (session, item, NULL, FALSE);
3620         }
3621
3622         return g_task_propagate_pointer (task, error);
3623 }
3624
3625 GInputStream *
3626 soup_session_send_request (SoupSession   *session,
3627                            SoupMessage   *msg,
3628                            GCancellable  *cancellable,
3629                            GError       **error)
3630 {
3631         SoupMessageQueueItem *item;
3632         GInputStream *stream = NULL;
3633         GOutputStream *ostream;
3634         GMemoryOutputStream *mostream;
3635         gssize size;
3636         GError *my_error = NULL;
3637
3638         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
3639         g_return_val_if_fail (!SOUP_IS_SESSION_ASYNC (session), NULL);
3640
3641         item = soup_session_append_queue_item (session, msg, FALSE, TRUE,
3642                                                NULL, NULL);
3643
3644         item->new_api = TRUE;
3645         if (cancellable) {
3646                 g_object_unref (item->cancellable);
3647                 item->cancellable = g_object_ref (cancellable);
3648         }
3649
3650         while (!stream) {
3651                 /* Get a connection, etc */
3652                 soup_session_process_queue_item (session, item, NULL, TRUE);
3653                 if (item->state != SOUP_MESSAGE_RUNNING)
3654                         break;
3655
3656                 /* Send request, read headers */
3657                 if (!soup_message_io_run_until_read (msg, item->cancellable, &my_error)) {
3658                         if (g_error_matches (my_error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) {
3659                                 item->state = SOUP_MESSAGE_RESTARTING;
3660                                 soup_message_io_finished (item->msg);
3661                                 g_clear_error (&my_error);
3662                                 continue;
3663                         } else
3664                                 break;
3665                 }
3666
3667                 stream = soup_message_io_get_response_istream (msg, &my_error);
3668                 if (!stream)
3669                         break;
3670
3671                 /* Break if the message doesn't look likely-to-be-requeued */
3672                 if (msg->status_code != SOUP_STATUS_UNAUTHORIZED &&
3673                     msg->status_code != SOUP_STATUS_PROXY_UNAUTHORIZED &&
3674                     !soup_session_would_redirect (session, msg))
3675                         break;
3676
3677                 /* Gather the current message body... */
3678                 ostream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
3679                 if (g_output_stream_splice (ostream, stream,
3680                                             G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
3681                                             G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
3682                                             item->cancellable, &my_error) == -1) {
3683                         g_object_unref (stream);
3684                         g_object_unref (ostream);
3685                         stream = NULL;
3686                         break;
3687                 }
3688                 g_object_unref (stream);
3689                 stream = NULL;
3690
3691                 /* If the message was requeued, loop */
3692                 if (item->state == SOUP_MESSAGE_RESTARTING) {
3693                         g_object_unref (ostream);
3694                         continue;
3695                 }
3696
3697                 /* Not requeued, so return the original body */
3698                 mostream = G_MEMORY_OUTPUT_STREAM (ostream);
3699                 size = g_memory_output_stream_get_data_size (mostream);
3700                 stream = g_memory_input_stream_new ();
3701                 if (size) {
3702                         g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (stream),
3703                                                         g_memory_output_stream_steal_data (mostream),
3704                                                         size, g_free);
3705                 }
3706                 g_object_unref (ostream);
3707         }
3708
3709         if (my_error)
3710                 g_propagate_error (error, my_error);
3711         else if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
3712                 if (stream) {
3713                         g_object_unref (stream);
3714                         stream = NULL;
3715                 }
3716                 g_set_error_literal (error, SOUP_HTTP_ERROR, msg->status_code,
3717                                      msg->reason_phrase);
3718         } else if (!stream)
3719                 stream = g_memory_input_stream_new ();
3720
3721         if (!stream) {
3722                 if (soup_message_io_in_progress (msg))
3723                         soup_message_io_finished (msg);
3724                 else if (item->state != SOUP_MESSAGE_FINISHED)
3725                         item->state = SOUP_MESSAGE_FINISHING;
3726
3727                 if (item->state != SOUP_MESSAGE_FINISHED)
3728                         soup_session_process_queue_item (session, item, NULL, TRUE);
3729         }
3730
3731         soup_message_queue_item_unref (item);
3732         return stream;
3733 }
3734
3735 /**
3736  * soup_session_request:
3737  * @session: a #SoupSession
3738  * @uri_string: a URI, in string form
3739  * @error: return location for a #GError, or %NULL
3740  *
3741  * Creates a #SoupRequest for retrieving @uri_string.
3742  *
3743  * Return value: (transfer full): a new #SoupRequest, or
3744  *   %NULL on error.
3745  *
3746  * Since: 2.42
3747  */
3748 SoupRequest *
3749 soup_session_request (SoupSession *session, const char *uri_string,
3750                       GError **error)
3751 {
3752         SoupURI *uri;
3753         SoupRequest *req;
3754
3755         uri = soup_uri_new (uri_string);
3756         if (!uri) {
3757                 g_set_error (error, SOUP_REQUEST_ERROR,
3758                              SOUP_REQUEST_ERROR_BAD_URI,
3759                              _("Could not parse URI '%s'"), uri_string);
3760                 return NULL;
3761         }
3762
3763         req = soup_session_request_uri (session, uri, error);
3764         soup_uri_free (uri);
3765         return req;
3766 }
3767
3768 /**
3769  * soup_session_request_uri:
3770  * @session: a #SoupSession
3771  * @uri: a #SoupURI representing the URI to retrieve
3772  * @error: return location for a #GError, or %NULL
3773  *
3774  * Creates a #SoupRequest for retrieving @uri.
3775  *
3776  * Return value: (transfer full): a new #SoupRequest, or
3777  *   %NULL on error.
3778  *
3779  * Since: 2.42
3780  */
3781 SoupRequest *
3782 soup_session_request_uri (SoupSession *session, SoupURI *uri,
3783                           GError **error)
3784 {
3785         SoupSessionPrivate *priv;
3786         GType request_type;
3787
3788         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
3789
3790         priv = SOUP_SESSION_GET_PRIVATE (session);
3791
3792         request_type = (GType)GPOINTER_TO_SIZE (g_hash_table_lookup (priv->request_types, uri->scheme));
3793         if (!request_type) {
3794                 g_set_error (error, SOUP_REQUEST_ERROR,
3795                              SOUP_REQUEST_ERROR_UNSUPPORTED_URI_SCHEME,
3796                              _("Unsupported URI scheme '%s'"), uri->scheme);
3797                 return NULL;
3798         }
3799
3800         return g_initable_new (request_type, NULL, error,
3801                                "uri", uri,
3802                                "session", session,
3803                                NULL);
3804 }
3805
3806 /**
3807  * SOUP_REQUEST_ERROR:
3808  *
3809  * A #GError domain for #SoupRequest-related errors. Used with
3810  * #SoupRequestError.
3811  *
3812  * Since: 2.42
3813  */
3814 /**
3815  * SoupRequestError:
3816  * @SOUP_REQUEST_ERROR_BAD_URI: the URI could not be parsed
3817  * @SOUP_REQUEST_ERROR_UNSUPPORTED_URI_SCHEME: the URI scheme is not
3818  *   supported by this #SoupSession
3819  *
3820  * A #SoupRequest error.
3821  *
3822  * Since: 2.42
3823  */
3824
3825 GQuark
3826 soup_request_error_quark (void)
3827 {
3828         static GQuark error;
3829         if (!error)
3830                 error = g_quark_from_static_string ("soup_request_error_quark");
3831         return error;
3832 }