Remove extern "C" wrapping other includes
[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.h"
17 #include "soup-cache-private.h"
18 #include "soup-connection.h"
19 #include "soup-message-private.h"
20 #include "soup-misc-private.h"
21 #include "soup-message-queue.h"
22 #include "soup-proxy-resolver-wrapper.h"
23 #include "soup-session-private.h"
24 #include "soup-socket-private.h"
25 #include "soup-websocket.h"
26 #include "soup-websocket-connection.h"
27 #include "soup-websocket-extension-manager-private.h"
28
29 #define HOST_KEEP_ALIVE 5 * 60 * 1000 /* 5 min in msecs */
30
31 /**
32  * SECTION:soup-session
33  * @short_description: Soup session state object
34  *
35  * #SoupSession is the object that controls client-side HTTP. A
36  * #SoupSession encapsulates all of the state that libsoup is keeping
37  * on behalf of your program; cached HTTP connections, authentication
38  * information, etc. It also keeps track of various global options
39  * and features that you are using.
40  *
41  * Most applications will only need a single #SoupSession; the primary
42  * reason you might need multiple sessions is if you need to have
43  * multiple independent authentication contexts. (Eg, you are
44  * connecting to a server and authenticating as two different users at
45  * different times; the easiest way to ensure that each #SoupMessage
46  * is sent with the authentication information you intended is to use
47  * one session for the first user, and a second session for the other
48  * user.)
49  *
50  * In the past, #SoupSession was an abstract class, and users needed
51  * to choose between #SoupSessionAsync (which always uses
52  * #GMainLoop<!-- -->-based I/O), or #SoupSessionSync (which always uses
53  * blocking I/O and can be used from multiple threads simultaneously).
54  * This is no longer necessary; you can (and should) use a plain
55  * #SoupSession, which supports both synchronous and asynchronous use.
56  * (When using a plain #SoupSession, soup_session_queue_message()
57  * behaves like it traditionally did on a #SoupSessionAsync, and
58  * soup_session_send_message() behaves like it traditionally did on a
59  * #SoupSessionSync.)
60  *
61  * Additional #SoupSession functionality is provided by
62  * #SoupSessionFeature objects, which can be added to a session with
63  * soup_session_add_feature() or soup_session_add_feature_by_type()
64  * (or at construct time with the %SOUP_SESSION_ADD_FEATURE_BY_TYPE
65  * pseudo-property). For example, #SoupLogger provides support for
66  * logging HTTP traffic, #SoupContentDecoder provides support for
67  * compressed response handling, and #SoupContentSniffer provides
68  * support for HTML5-style response body content sniffing.
69  * Additionally, subtypes of #SoupAuth and #SoupRequest can be added
70  * as features, to add support for additional authentication and URI
71  * types.
72  *
73  * All #SoupSessions are created with a #SoupAuthManager, and support
74  * for %SOUP_TYPE_AUTH_BASIC and %SOUP_TYPE_AUTH_DIGEST. For
75  * #SoupRequest types, #SoupRequestHTTP, #SoupRequestFile, and
76  * #SoupRequestData are supported. Additionally, sessions using the
77  * plain #SoupSession class (rather than one of its deprecated
78  * subtypes) have a #SoupContentDecoder by default.
79  **/
80
81 typedef struct {
82         SoupURI     *uri;
83         SoupAddress *addr;
84
85         GSList      *connections;      /* CONTAINS: SoupConnection */
86         guint        num_conns;
87
88         guint        num_messages;
89
90         GSource     *keep_alive_src;
91         SoupSession *session;
92 } SoupSessionHost;
93 static guint soup_host_uri_hash (gconstpointer key);
94 static gboolean soup_host_uri_equal (gconstpointer v1, gconstpointer v2);
95
96 typedef struct {
97         gboolean disposed;
98
99         GTlsDatabase *tlsdb;
100         GTlsInteraction *tls_interaction;
101         char *ssl_ca_file;
102         gboolean ssl_strict;
103         gboolean tlsdb_use_default;
104
105         guint io_timeout, idle_timeout;
106         SoupAddress *local_addr;
107
108         GResolver *resolver;
109         GProxyResolver *proxy_resolver;
110         gboolean proxy_use_default;
111         SoupURI *proxy_uri;
112
113         SoupSocketProperties *socket_props;
114
115         SoupMessageQueue *queue;
116
117         char *user_agent;
118         char *accept_language;
119         gboolean accept_language_auto;
120
121         GSList *features;
122         GHashTable *features_cache;
123
124         GHashTable *http_hosts, *https_hosts; /* char* -> SoupSessionHost */
125         GHashTable *conns; /* SoupConnection -> SoupSessionHost */
126         guint num_conns;
127         guint max_conns, max_conns_per_host;
128
129         /* Must hold the conn_lock before potentially creating a new
130          * SoupSessionHost, adding/removing a connection,
131          * disconnecting a connection, moving a connection from
132          * IDLE to IN_USE, or when updating socket properties.
133          * Must not emit signals or destroy objects while holding it.
134          * The conn_cond is signaled when it may be possible for
135          * a previously-blocked message to continue.
136          */
137         GMutex conn_lock;
138         GCond conn_cond;
139
140         GMainContext *async_context;
141         gboolean use_thread_context;
142
143         char **http_aliases, **https_aliases;
144
145         GHashTable *request_types;
146 } SoupSessionPrivate;
147
148 #define SOUP_IS_PLAIN_SESSION(o) (G_TYPE_FROM_INSTANCE (o) == SOUP_TYPE_SESSION)
149
150 static void free_host (SoupSessionHost *host);
151 static void connection_state_changed (GObject *object, GParamSpec *param,
152                                       gpointer user_data);
153 static void connection_disconnected (SoupConnection *conn, gpointer user_data);
154 static void drop_connection (SoupSession *session, SoupSessionHost *host,
155                              SoupConnection *conn);
156
157 static void auth_manager_authenticate (SoupAuthManager *manager,
158                                        SoupMessage *msg, SoupAuth *auth,
159                                        gboolean retrying, gpointer user_data);
160
161 static void async_run_queue (SoupSession *session);
162
163 static void async_send_request_running (SoupSession *session, SoupMessageQueueItem *item);
164
165 #define SOUP_SESSION_MAX_CONNS_DEFAULT 10
166 #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2
167
168 #define SOUP_SESSION_MAX_RESEND_COUNT 20
169
170 #define SOUP_SESSION_USER_AGENT_BASE "libsoup/" PACKAGE_VERSION
171
172 G_DEFINE_TYPE_WITH_PRIVATE (SoupSession, soup_session, G_TYPE_OBJECT)
173
174 enum {
175         REQUEST_QUEUED,
176         REQUEST_STARTED,
177         REQUEST_UNQUEUED,
178         AUTHENTICATE,
179         CONNECTION_CREATED,
180         TUNNELING,
181         LAST_SIGNAL
182 };
183
184 static guint signals[LAST_SIGNAL] = { 0 };
185
186 enum {
187         PROP_0,
188
189         PROP_PROXY_URI,
190         PROP_PROXY_RESOLVER,
191         PROP_MAX_CONNS,
192         PROP_MAX_CONNS_PER_HOST,
193         PROP_USE_NTLM,
194         PROP_SSL_CA_FILE,
195         PROP_SSL_USE_SYSTEM_CA_FILE,
196         PROP_TLS_DATABASE,
197         PROP_SSL_STRICT,
198         PROP_ASYNC_CONTEXT,
199         PROP_USE_THREAD_CONTEXT,
200         PROP_TIMEOUT,
201         PROP_USER_AGENT,
202         PROP_ACCEPT_LANGUAGE,
203         PROP_ACCEPT_LANGUAGE_AUTO,
204         PROP_IDLE_TIMEOUT,
205         PROP_ADD_FEATURE,
206         PROP_ADD_FEATURE_BY_TYPE,
207         PROP_REMOVE_FEATURE_BY_TYPE,
208         PROP_HTTP_ALIASES,
209         PROP_HTTPS_ALIASES,
210         PROP_LOCAL_ADDRESS,
211         PROP_TLS_INTERACTION,
212
213         LAST_PROP
214 };
215
216 static void
217 soup_session_init (SoupSession *session)
218 {
219         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
220         SoupAuthManager *auth_manager;
221
222         priv->queue = soup_message_queue_new (session);
223
224         g_mutex_init (&priv->conn_lock);
225         g_cond_init (&priv->conn_cond);
226         priv->http_hosts = g_hash_table_new_full (soup_host_uri_hash,
227                                                   soup_host_uri_equal,
228                                                   NULL, (GDestroyNotify)free_host);
229         priv->https_hosts = g_hash_table_new_full (soup_host_uri_hash,
230                                                    soup_host_uri_equal,
231                                                    NULL, (GDestroyNotify)free_host);
232         priv->conns = g_hash_table_new (NULL, NULL);
233
234         priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
235         priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
236
237         priv->features_cache = g_hash_table_new (NULL, NULL);
238
239         auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER, NULL);
240         g_signal_connect (auth_manager, "authenticate",
241                           G_CALLBACK (auth_manager_authenticate), session);
242         soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
243                                           SOUP_TYPE_AUTH_BASIC);
244         soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
245                                           SOUP_TYPE_AUTH_DIGEST);
246         soup_session_add_feature (session, SOUP_SESSION_FEATURE (auth_manager));
247         g_object_unref (auth_manager);
248
249         /* We'll be doing DNS continuously-ish while the session is active,
250          * so hold a ref on the default GResolver.
251          */
252         priv->resolver = g_resolver_get_default ();
253
254         priv->ssl_strict = TRUE;
255
256         priv->http_aliases = g_new (char *, 2);
257         priv->http_aliases[0] = (char *)g_intern_string ("*");
258         priv->http_aliases[1] = NULL;
259
260         priv->request_types = g_hash_table_new (soup_str_case_hash,
261                                                 soup_str_case_equal);
262         soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_HTTP);
263         soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_FILE);
264         soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_DATA);
265 }
266
267 static GObject *
268 soup_session_constructor (GType                  type,
269                           guint                  n_construct_properties,
270                           GObjectConstructParam *construct_params)
271 {
272         GObject *object;
273
274         object = G_OBJECT_CLASS (soup_session_parent_class)->constructor (type, n_construct_properties, construct_params);
275
276         /* If this is a "plain" SoupSession, fix up the default
277          * properties values, etc.
278          */
279         if (type == SOUP_TYPE_SESSION) {
280                 SoupSession *session = SOUP_SESSION (object);
281                 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
282
283                 g_clear_pointer (&priv->async_context, g_main_context_unref);
284                 priv->async_context = g_main_context_ref_thread_default ();
285                 priv->use_thread_context = TRUE;
286
287                 priv->io_timeout = priv->idle_timeout = 60;
288
289                 priv->http_aliases[0] = NULL;
290
291                 /* If the user overrides the proxy or tlsdb during construction,
292                  * we don't want to needlessly resolve the extension point. So
293                  * we just set flags saying to do it later.
294                  */
295                 priv->proxy_use_default = TRUE;
296                 priv->tlsdb_use_default = TRUE;
297
298                 soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_DECODER);
299         }
300
301         return object;
302 }
303
304 static void
305 soup_session_dispose (GObject *object)
306 {
307         SoupSession *session = SOUP_SESSION (object);
308         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
309
310         priv->disposed = TRUE;
311         soup_session_abort (session);
312         g_warn_if_fail (g_hash_table_size (priv->conns) == 0);
313
314         while (priv->features)
315                 soup_session_remove_feature (session, priv->features->data);
316
317         G_OBJECT_CLASS (soup_session_parent_class)->dispose (object);
318 }
319
320 static void
321 soup_session_finalize (GObject *object)
322 {
323         SoupSession *session = SOUP_SESSION (object);
324         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
325
326         soup_message_queue_destroy (priv->queue);
327
328         g_mutex_clear (&priv->conn_lock);
329         g_cond_clear (&priv->conn_cond);
330         g_hash_table_destroy (priv->http_hosts);
331         g_hash_table_destroy (priv->https_hosts);
332         g_hash_table_destroy (priv->conns);
333
334         g_free (priv->user_agent);
335         g_free (priv->accept_language);
336
337         g_clear_object (&priv->tlsdb);
338         g_clear_object (&priv->tls_interaction);
339         g_free (priv->ssl_ca_file);
340
341         g_clear_pointer (&priv->async_context, g_main_context_unref);
342         g_clear_object (&priv->local_addr);
343
344         g_hash_table_destroy (priv->features_cache);
345
346         g_object_unref (priv->resolver);
347         g_clear_object (&priv->proxy_resolver);
348         g_clear_pointer (&priv->proxy_uri, soup_uri_free);
349
350         g_free (priv->http_aliases);
351         g_free (priv->https_aliases);
352
353         g_hash_table_destroy (priv->request_types);
354
355         g_clear_pointer (&priv->socket_props, soup_socket_properties_unref);
356
357         G_OBJECT_CLASS (soup_session_parent_class)->finalize (object);
358 }
359
360 /* requires conn_lock */
361 static void
362 ensure_socket_props (SoupSession *session)
363 {
364         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
365         gboolean ssl_strict;
366
367         if (priv->socket_props)
368                 return;
369
370         if (priv->proxy_use_default) {
371                 priv->proxy_resolver = g_object_ref (g_proxy_resolver_get_default ());
372                 priv->proxy_use_default = FALSE;
373         }
374         if (priv->tlsdb_use_default) {
375                 priv->tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
376                 priv->tlsdb_use_default = FALSE;
377         }
378
379         ssl_strict = priv->ssl_strict && (priv->tlsdb != NULL ||
380                                           SOUP_IS_PLAIN_SESSION (session));
381
382         priv->socket_props = soup_socket_properties_new (priv->async_context,
383                                                          priv->use_thread_context,
384                                                          priv->proxy_resolver,
385                                                          priv->local_addr,
386                                                          priv->tlsdb,
387                                                          priv->tls_interaction,
388                                                          ssl_strict,
389                                                          priv->io_timeout,
390                                                          priv->idle_timeout);
391 }
392
393 /* Converts a language in POSIX format and to be RFC2616 compliant    */
394 /* Based on code from epiphany-webkit (ephy_langs_append_languages()) */
395 static gchar *
396 posix_lang_to_rfc2616 (const gchar *language)
397 {
398         /* Don't include charset variants, etc */
399         if (strchr (language, '.') || strchr (language, '@'))
400                 return NULL;
401
402         /* Ignore "C" locale, which g_get_language_names() always
403          * includes as a fallback.
404          */
405         if (!strcmp (language, "C"))
406                 return NULL;
407
408         return g_strdelimit (g_ascii_strdown (language, -1), "_", '-');
409 }
410
411 /* Converts @quality from 0-100 to 0.0-1.0 and appends to @str */
412 static gchar *
413 add_quality_value (const gchar *str, int quality)
414 {
415         g_return_val_if_fail (str != NULL, NULL);
416
417         if (quality >= 0 && quality < 100) {
418                 /* We don't use %.02g because of "." vs "," locale issues */
419                 if (quality % 10)
420                         return g_strdup_printf ("%s;q=0.%02d", str, quality);
421                 else
422                         return g_strdup_printf ("%s;q=0.%d", str, quality / 10);
423         } else
424                 return g_strdup (str);
425 }
426
427 /* Returns a RFC2616 compliant languages list from system locales */
428 static gchar *
429 accept_languages_from_system (void)
430 {
431         const char * const * lang_names;
432         GPtrArray *langs = NULL;
433         char *lang, *langs_str;
434         int delta;
435         guint i;
436
437         lang_names = g_get_language_names ();
438         g_return_val_if_fail (lang_names != NULL, NULL);
439
440         /* Build the array of languages */
441         langs = g_ptr_array_new_with_free_func (g_free);
442         for (i = 0; lang_names[i] != NULL; i++) {
443                 lang = posix_lang_to_rfc2616 (lang_names[i]);
444                 if (lang)
445                         g_ptr_array_add (langs, lang);
446         }
447
448         /* Add quality values */
449         if (langs->len < 10)
450                 delta = 10;
451         else if (langs->len < 20)
452                 delta = 5;
453         else
454                 delta = 1;
455
456         for (i = 0; i < langs->len; i++) {
457                 lang = langs->pdata[i];
458                 langs->pdata[i] = add_quality_value (lang, 100 - i * delta);
459                 g_free (lang);
460         }
461
462         /* Fallback: add "en" if list is empty */
463         if (langs->len == 0)
464                 g_ptr_array_add (langs, g_strdup ("en"));
465
466         g_ptr_array_add (langs, NULL);
467         langs_str = g_strjoinv (", ", (char **)langs->pdata);
468         g_ptr_array_free (langs, TRUE);
469
470         return langs_str;
471 }
472
473 static void
474 set_tlsdb (SoupSession *session, GTlsDatabase *tlsdb)
475 {
476         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
477         GTlsDatabase *system_default;
478
479         priv->tlsdb_use_default = FALSE;
480         if (tlsdb == priv->tlsdb)
481                 return;
482
483         g_object_freeze_notify (G_OBJECT (session));
484
485         system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
486         if (system_default) {
487                 if (priv->tlsdb == system_default || tlsdb == system_default) {
488                         g_object_notify (G_OBJECT (session), "ssl-use-system-ca-file");
489                 }
490                 g_object_unref (system_default);
491         }
492
493         if (priv->ssl_ca_file) {
494                 g_free (priv->ssl_ca_file);
495                 priv->ssl_ca_file = NULL;
496                 g_object_notify (G_OBJECT (session), "ssl-ca-file");
497         }
498
499         if (priv->tlsdb)
500                 g_object_unref (priv->tlsdb);
501         priv->tlsdb = tlsdb;
502         if (priv->tlsdb)
503                 g_object_ref (priv->tlsdb);
504
505         g_object_notify (G_OBJECT (session), "tls-database");
506         g_object_thaw_notify (G_OBJECT (session));
507 }
508
509 static void
510 set_use_system_ca_file (SoupSession *session, gboolean use_system_ca_file)
511 {
512         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
513         GTlsDatabase *system_default;
514
515         priv->tlsdb_use_default = FALSE;
516
517         system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
518
519         if (use_system_ca_file)
520                 set_tlsdb (session, system_default);
521         else if (priv->tlsdb == system_default)
522                 set_tlsdb (session, NULL);
523
524         g_clear_object (&system_default);
525 }
526
527 static void
528 set_ssl_ca_file (SoupSession *session, const char *ssl_ca_file)
529 {
530         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
531         GTlsDatabase *tlsdb;
532         GError *error = NULL;
533
534         priv->tlsdb_use_default = FALSE;
535         if (!g_strcmp0 (priv->ssl_ca_file, ssl_ca_file))
536                 return;
537
538         g_object_freeze_notify (G_OBJECT (session));
539
540         if (g_path_is_absolute (ssl_ca_file))
541                 tlsdb = g_tls_file_database_new (ssl_ca_file, &error);
542         else {
543                 char *path, *cwd;
544
545                 cwd = g_get_current_dir ();
546                 path = g_build_filename (cwd, ssl_ca_file, NULL);
547                 tlsdb = g_tls_file_database_new (path, &error);
548                 g_free (path);
549                 g_free (cwd);
550         }
551
552         if (error) {
553                 if (!g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_UNAVAILABLE)) {
554                         g_warning ("Could not set SSL credentials from '%s': %s",
555                                    ssl_ca_file, error->message);
556
557                         tlsdb = g_tls_file_database_new ("/dev/null", NULL);
558                 }
559                 g_error_free (error);
560         }
561
562         set_tlsdb (session, tlsdb);
563         if (tlsdb) {
564                 g_object_unref (tlsdb);
565
566                 priv->ssl_ca_file = g_strdup (ssl_ca_file);
567                 g_object_notify (G_OBJECT (session), "ssl-ca-file");
568         } else if (priv->ssl_ca_file) {
569                 g_clear_pointer (&priv->ssl_ca_file, g_free);
570                 g_object_notify (G_OBJECT (session), "ssl-ca-file");
571         }
572
573         g_object_thaw_notify (G_OBJECT (session));
574 }
575
576 /* priv->http_aliases and priv->https_aliases are stored as arrays of
577  * *interned* strings, so we can't just use g_strdupv() to set them.
578  */
579 static void
580 set_aliases (char ***variable, char **value)
581 {
582         int len, i;
583
584         if (*variable)
585                 g_free (*variable);
586
587         if (!value) {
588                 *variable = NULL;
589                 return;
590         }
591
592         len = g_strv_length (value);
593         *variable = g_new (char *, len + 1);
594         for (i = 0; i < len; i++)
595                 (*variable)[i] = (char *)g_intern_string (value[i]);
596         (*variable)[i] = NULL;
597 }
598
599 static void
600 set_proxy_resolver (SoupSession *session, SoupURI *uri,
601                     SoupProxyURIResolver *soup_resolver,
602                     GProxyResolver *g_resolver)
603 {
604         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
605
606         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
607         soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_URI_RESOLVER);
608         G_GNUC_END_IGNORE_DEPRECATIONS;
609         g_clear_object (&priv->proxy_resolver);
610         g_clear_pointer (&priv->proxy_uri, soup_uri_free);
611         priv->proxy_use_default = FALSE;
612
613         if (uri) {
614                 char *uri_string;
615
616                 priv->proxy_uri = soup_uri_copy (uri);
617                 uri_string = soup_uri_to_string_internal (uri, FALSE, TRUE, TRUE);
618                 priv->proxy_resolver = g_simple_proxy_resolver_new (uri_string, NULL);
619                 g_free (uri_string);
620         } else if (soup_resolver) {
621                 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
622                 if (SOUP_IS_PROXY_RESOLVER_DEFAULT (soup_resolver))
623                         priv->proxy_resolver = g_object_ref (g_proxy_resolver_get_default ());
624                 else
625                         priv->proxy_resolver = soup_proxy_resolver_wrapper_new (soup_resolver);
626                 G_GNUC_END_IGNORE_DEPRECATIONS;
627         } else if (g_resolver)
628                 priv->proxy_resolver = g_object_ref (g_resolver);
629 }
630
631 static void
632 soup_session_set_property (GObject *object, guint prop_id,
633                            const GValue *value, GParamSpec *pspec)
634 {
635         SoupSession *session = SOUP_SESSION (object);
636         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
637         const char *user_agent;
638         SoupSessionFeature *feature;
639         GMainContext *async_context;
640         gboolean socket_props_changed = FALSE;
641
642         switch (prop_id) {
643         case PROP_LOCAL_ADDRESS:
644                 priv->local_addr = g_value_dup_object (value);
645                 socket_props_changed = TRUE;
646                 break;
647         case PROP_PROXY_URI:
648                 set_proxy_resolver (session, g_value_get_boxed (value),
649                                     NULL, NULL);
650                 soup_session_abort (session);
651                 socket_props_changed = TRUE;
652                 break;
653         case PROP_PROXY_RESOLVER:
654                 set_proxy_resolver (session, NULL, NULL,
655                                     g_value_get_object (value));
656                 socket_props_changed = TRUE;
657                 break;
658         case PROP_MAX_CONNS:
659                 priv->max_conns = g_value_get_int (value);
660                 break;
661         case PROP_MAX_CONNS_PER_HOST:
662                 priv->max_conns_per_host = g_value_get_int (value);
663                 break;
664         case PROP_USE_NTLM:
665                 g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
666                 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
667                 if (feature) {
668                         if (g_value_get_boolean (value))
669                                 soup_session_feature_add_feature (feature, SOUP_TYPE_AUTH_NTLM);
670                         else
671                                 soup_session_feature_remove_feature (feature, SOUP_TYPE_AUTH_NTLM);
672                 } else
673                         g_warning ("Trying to set use-ntlm on session with no auth-manager");
674                 break;
675         case PROP_SSL_CA_FILE:
676                 set_ssl_ca_file (session, g_value_get_string (value));
677                 socket_props_changed = TRUE;
678                 break;
679         case PROP_SSL_USE_SYSTEM_CA_FILE:
680                 set_use_system_ca_file (session, g_value_get_boolean (value));
681                 socket_props_changed = TRUE;
682                 break;
683         case PROP_TLS_DATABASE:
684                 set_tlsdb (session, g_value_get_object (value));
685                 socket_props_changed = TRUE;
686                 break;
687         case PROP_TLS_INTERACTION:
688                 g_clear_object(&priv->tls_interaction);
689                 priv->tls_interaction = g_value_dup_object (value);
690                 socket_props_changed = TRUE;
691                 break;
692         case PROP_SSL_STRICT:
693                 priv->ssl_strict = g_value_get_boolean (value);
694                 socket_props_changed = TRUE;
695                 break;
696         case PROP_ASYNC_CONTEXT:
697                 async_context = g_value_get_pointer (value);
698                 if (async_context && async_context != g_main_context_get_thread_default ())
699                         g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
700                 priv->async_context = async_context;
701                 if (priv->async_context)
702                         g_main_context_ref (priv->async_context);
703                 socket_props_changed = TRUE;
704                 break;
705         case PROP_USE_THREAD_CONTEXT:
706                 if (!g_value_get_boolean (value))
707                         g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
708                 priv->use_thread_context = g_value_get_boolean (value);
709                 if (priv->use_thread_context) {
710                         if (priv->async_context)
711                                 g_main_context_unref (priv->async_context);
712                         priv->async_context = g_main_context_get_thread_default ();
713                         if (priv->async_context)
714                                 g_main_context_ref (priv->async_context);
715                 }
716                 socket_props_changed = TRUE;
717                 break;
718         case PROP_TIMEOUT:
719                 priv->io_timeout = g_value_get_uint (value);
720                 socket_props_changed = TRUE;
721                 break;
722         case PROP_USER_AGENT:
723                 g_free (priv->user_agent);
724                 user_agent = g_value_get_string (value);
725                 if (!user_agent)
726                         priv->user_agent = NULL;
727                 else if (!*user_agent) {
728                         priv->user_agent =
729                                 g_strdup (SOUP_SESSION_USER_AGENT_BASE);
730                 } else if (g_str_has_suffix (user_agent, " ")) {
731                         priv->user_agent =
732                                 g_strdup_printf ("%s%s", user_agent,
733                                                  SOUP_SESSION_USER_AGENT_BASE);
734                 } else
735                         priv->user_agent = g_strdup (user_agent);
736                 break;
737         case PROP_ACCEPT_LANGUAGE:
738                 g_free (priv->accept_language);
739                 priv->accept_language = g_strdup (g_value_get_string (value));
740                 priv->accept_language_auto = FALSE;
741                 break;
742         case PROP_ACCEPT_LANGUAGE_AUTO:
743                 priv->accept_language_auto = g_value_get_boolean (value);
744                 if (priv->accept_language) {
745                         g_free (priv->accept_language);
746                         priv->accept_language = NULL;
747                 }
748
749                 /* Get languages from system if needed */
750                 if (priv->accept_language_auto)
751                         priv->accept_language = accept_languages_from_system ();
752                 break;
753         case PROP_IDLE_TIMEOUT:
754                 priv->idle_timeout = g_value_get_uint (value);
755                 socket_props_changed = TRUE;
756                 break;
757         case PROP_ADD_FEATURE:
758                 soup_session_add_feature (session, g_value_get_object (value));
759                 break;
760         case PROP_ADD_FEATURE_BY_TYPE:
761                 soup_session_add_feature_by_type (session, g_value_get_gtype (value));
762                 break;
763         case PROP_REMOVE_FEATURE_BY_TYPE:
764                 soup_session_remove_feature_by_type (session, g_value_get_gtype (value));
765                 break;
766         case PROP_HTTP_ALIASES:
767                 set_aliases (&priv->http_aliases, g_value_get_boxed (value));
768                 break;
769         case PROP_HTTPS_ALIASES:
770                 set_aliases (&priv->https_aliases, g_value_get_boxed (value));
771                 break;
772         default:
773                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
774                 break;
775         }
776
777         g_mutex_lock (&priv->conn_lock);
778         if (priv->socket_props && socket_props_changed) {
779                 soup_socket_properties_unref (priv->socket_props);
780                 priv->socket_props = NULL;
781                 ensure_socket_props (session);
782         }
783         g_mutex_unlock (&priv->conn_lock);
784 }
785
786 static void
787 soup_session_get_property (GObject *object, guint prop_id,
788                            GValue *value, GParamSpec *pspec)
789 {
790         SoupSession *session = SOUP_SESSION (object);
791         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
792         SoupSessionFeature *feature;
793         GTlsDatabase *tlsdb;
794
795         switch (prop_id) {
796         case PROP_LOCAL_ADDRESS:
797                 g_value_set_object (value, priv->local_addr);
798                 break;
799         case PROP_PROXY_URI:
800                 g_value_set_boxed (value, priv->proxy_uri);
801                 break;
802         case PROP_PROXY_RESOLVER:
803                 g_mutex_lock (&priv->conn_lock);
804                 ensure_socket_props (session);
805                 g_mutex_unlock (&priv->conn_lock);
806                 g_value_set_object (value, priv->proxy_resolver);
807                 break;
808         case PROP_MAX_CONNS:
809                 g_value_set_int (value, priv->max_conns);
810                 break;
811         case PROP_MAX_CONNS_PER_HOST:
812                 g_value_set_int (value, priv->max_conns_per_host);
813                 break;
814         case PROP_USE_NTLM:
815                 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
816                 if (feature)
817                         g_value_set_boolean (value, soup_session_feature_has_feature (feature, SOUP_TYPE_AUTH_NTLM));
818                 else
819                         g_value_set_boolean (value, FALSE);
820                 break;
821         case PROP_SSL_CA_FILE:
822                 g_value_set_string (value, priv->ssl_ca_file);
823                 break;
824         case PROP_SSL_USE_SYSTEM_CA_FILE:
825                 tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
826                 g_mutex_lock (&priv->conn_lock);
827                 ensure_socket_props (session);
828                 g_mutex_unlock (&priv->conn_lock);
829                 g_value_set_boolean (value, priv->tlsdb == tlsdb);
830                 g_clear_object (&tlsdb);
831                 break;
832         case PROP_TLS_DATABASE:
833                 g_mutex_lock (&priv->conn_lock);
834                 ensure_socket_props (session);
835                 g_mutex_unlock (&priv->conn_lock);
836                 g_value_set_object (value, priv->tlsdb);
837                 break;
838         case PROP_TLS_INTERACTION:
839                 g_value_set_object (value, priv->tls_interaction);
840                 break;
841         case PROP_SSL_STRICT:
842                 g_value_set_boolean (value, priv->ssl_strict);
843                 break;
844         case PROP_ASYNC_CONTEXT:
845                 g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
846                 break;
847         case PROP_USE_THREAD_CONTEXT:
848                 g_value_set_boolean (value, priv->use_thread_context);
849                 break;
850         case PROP_TIMEOUT:
851                 g_value_set_uint (value, priv->io_timeout);
852                 break;
853         case PROP_USER_AGENT:
854                 g_value_set_string (value, priv->user_agent);
855                 break;
856         case PROP_ACCEPT_LANGUAGE:
857                 g_value_set_string (value, priv->accept_language);
858                 break;
859         case PROP_ACCEPT_LANGUAGE_AUTO:
860                 g_value_set_boolean (value, priv->accept_language_auto);
861                 break;
862         case PROP_IDLE_TIMEOUT:
863                 g_value_set_uint (value, priv->idle_timeout);
864                 break;
865         case PROP_HTTP_ALIASES:
866                 g_value_set_boxed (value, priv->http_aliases);
867                 break;
868         case PROP_HTTPS_ALIASES:
869                 g_value_set_boxed (value, priv->https_aliases);
870                 break;
871         default:
872                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
873                 break;
874         }
875 }
876
877 /**
878  * soup_session_new:
879  *
880  * Creates a #SoupSession with the default options.
881  *
882  * Return value: the new session.
883  *
884  * Since: 2.42
885  */
886 SoupSession *
887 soup_session_new (void)
888 {
889         return g_object_new (SOUP_TYPE_SESSION, NULL);
890 }
891
892 /**
893  * soup_session_new_with_options:
894  * @optname1: name of first property to set
895  * @...: value of @optname1, followed by additional property/value pairs
896  *
897  * Creates a #SoupSession with the specified options.
898  *
899  * Return value: the new session.
900  *
901  * Since: 2.42
902  */
903 SoupSession *
904 soup_session_new_with_options (const char *optname1,
905                                ...)
906 {
907         SoupSession *session;
908         va_list ap;
909
910         va_start (ap, optname1);
911         session = (SoupSession *)g_object_new_valist (SOUP_TYPE_SESSION,
912                                                       optname1, ap);
913         va_end (ap);
914
915         return session;
916 }
917
918 /**
919  * soup_session_get_async_context:
920  * @session: a #SoupSession
921  *
922  * Gets @session's #SoupSession:async-context. This does not add a ref
923  * to the context, so you will need to ref it yourself if you want it
924  * to outlive its session.
925  *
926  * For a modern #SoupSession, this will always just return the
927  * thread-default #GMainContext, and so is not especially useful.
928  *
929  * Return value: (nullable) (transfer none): @session's #GMainContext,
930  * which may be %NULL
931  **/
932 GMainContext *
933 soup_session_get_async_context (SoupSession *session)
934 {
935         SoupSessionPrivate *priv;
936
937         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
938         priv = soup_session_get_instance_private (session);
939
940         if (priv->use_thread_context)
941                 return g_main_context_get_thread_default ();
942         else
943                 return priv->async_context;
944 }
945
946 /* Hosts */
947
948 /* Note that we can't use soup_uri_host_hash() and soup_uri_host_equal()
949  * because we want to ignore the protocol; http://example.com and
950  * webcal://example.com are the same host.
951  */
952 static guint
953 soup_host_uri_hash (gconstpointer key)
954 {
955         const SoupURI *uri = key;
956
957         g_return_val_if_fail (uri != NULL && uri->host != NULL, 0);
958
959         return uri->port + soup_str_case_hash (uri->host);
960 }
961
962 static gboolean
963 soup_host_uri_equal (gconstpointer v1, gconstpointer v2)
964 {
965         const SoupURI *one = v1;
966         const SoupURI *two = v2;
967
968         g_return_val_if_fail (one != NULL && two != NULL, one == two);
969         g_return_val_if_fail (one->host != NULL && two->host != NULL, one->host == two->host);
970
971         if (one->port != two->port)
972                 return FALSE;
973
974         return g_ascii_strcasecmp (one->host, two->host) == 0;
975 }
976
977
978 static SoupSessionHost *
979 soup_session_host_new (SoupSession *session, SoupURI *uri)
980 {
981         SoupSessionHost *host;
982
983         host = g_slice_new0 (SoupSessionHost);
984         host->uri = soup_uri_copy_host (uri);
985         if (host->uri->scheme != SOUP_URI_SCHEME_HTTP &&
986             host->uri->scheme != SOUP_URI_SCHEME_HTTPS) {
987                 SoupSessionPrivate *priv = soup_session_get_instance_private (session);
988
989                 if (soup_uri_is_https (host->uri, priv->https_aliases))
990                         host->uri->scheme = SOUP_URI_SCHEME_HTTPS;
991                 else
992                         host->uri->scheme = SOUP_URI_SCHEME_HTTP;
993         }
994
995         host->addr = g_object_new (SOUP_TYPE_ADDRESS,
996                                    SOUP_ADDRESS_NAME, host->uri->host,
997                                    SOUP_ADDRESS_PORT, host->uri->port,
998                                    SOUP_ADDRESS_PROTOCOL, host->uri->scheme,
999                                    NULL);
1000         host->keep_alive_src = NULL;
1001         host->session = session;
1002
1003         return host;
1004 }
1005
1006 /* Requires conn_lock to be locked */
1007 static SoupSessionHost *
1008 get_host_for_uri (SoupSession *session, SoupURI *uri)
1009 {
1010         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1011         SoupSessionHost *host;
1012         gboolean https;
1013         SoupURI *uri_tmp = NULL;
1014
1015         https = soup_uri_is_https (uri, priv->https_aliases);
1016         if (https)
1017                 host = g_hash_table_lookup (priv->https_hosts, uri);
1018         else
1019                 host = g_hash_table_lookup (priv->http_hosts, uri);
1020         if (host)
1021                 return host;
1022
1023         if (uri->scheme != SOUP_URI_SCHEME_HTTP &&
1024             uri->scheme != SOUP_URI_SCHEME_HTTPS) {
1025                 uri = uri_tmp = soup_uri_copy (uri);
1026                 uri->scheme = https ? SOUP_URI_SCHEME_HTTPS : SOUP_URI_SCHEME_HTTP;
1027         }
1028         host = soup_session_host_new (session, uri);
1029         if (uri_tmp)
1030                 soup_uri_free (uri_tmp);
1031
1032         if (https)
1033                 g_hash_table_insert (priv->https_hosts, host->uri, host);
1034         else
1035                 g_hash_table_insert (priv->http_hosts, host->uri, host);
1036
1037         return host;
1038 }
1039
1040 /* Requires conn_lock to be locked */
1041 static SoupSessionHost *
1042 get_host_for_message (SoupSession *session, SoupMessage *msg)
1043 {
1044         return get_host_for_uri (session, soup_message_get_uri (msg));
1045 }
1046
1047 static void
1048 free_host (SoupSessionHost *host)
1049 {
1050         g_warn_if_fail (host->connections == NULL);
1051
1052         if (host->keep_alive_src) {
1053                 g_source_destroy (host->keep_alive_src);
1054                 g_source_unref (host->keep_alive_src);
1055         }
1056
1057         soup_uri_free (host->uri);
1058         g_object_unref (host->addr);
1059         g_slice_free (SoupSessionHost, host);
1060 }
1061
1062 static void
1063 auth_manager_authenticate (SoupAuthManager *manager, SoupMessage *msg,
1064                            SoupAuth *auth, gboolean retrying,
1065                            gpointer session)
1066 {
1067         g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
1068 }
1069
1070 #define SOUP_SESSION_WOULD_REDIRECT_AS_GET(session, msg) \
1071         ((msg)->status_code == SOUP_STATUS_SEE_OTHER || \
1072          ((msg)->status_code == SOUP_STATUS_FOUND && \
1073           !SOUP_METHOD_IS_SAFE ((msg)->method)) || \
1074          ((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY && \
1075           (msg)->method == SOUP_METHOD_POST))
1076
1077 #define SOUP_SESSION_WOULD_REDIRECT_AS_SAFE(session, msg) \
1078         (((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY || \
1079           (msg)->status_code == SOUP_STATUS_PERMANENT_REDIRECT || \
1080           (msg)->status_code == SOUP_STATUS_TEMPORARY_REDIRECT || \
1081           (msg)->status_code == SOUP_STATUS_FOUND) && \
1082          SOUP_METHOD_IS_SAFE ((msg)->method))
1083
1084 static inline SoupURI *
1085 redirection_uri (SoupMessage *msg)
1086 {
1087         const char *new_loc;
1088         SoupURI *new_uri;
1089
1090         new_loc = soup_message_headers_get_one (msg->response_headers,
1091                                                 "Location");
1092         if (!new_loc)
1093                 return NULL;
1094         new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
1095         if (!new_uri || !new_uri->host) {
1096                 if (new_uri)
1097                         soup_uri_free (new_uri);
1098                 return NULL;
1099         }
1100
1101         return new_uri;
1102 }
1103
1104 /**
1105  * soup_session_would_redirect:
1106  * @session: a #SoupSession
1107  * @msg: a #SoupMessage that has response headers
1108  *
1109  * Checks if @msg contains a response that would cause @session to
1110  * redirect it to a new URL (ignoring @msg's %SOUP_MESSAGE_NO_REDIRECT
1111  * flag, and the number of times it has already been redirected).
1112  *
1113  * Return value: whether @msg would be redirected
1114  *
1115  * Since: 2.38
1116  */
1117 gboolean
1118 soup_session_would_redirect (SoupSession *session, SoupMessage *msg)
1119 {
1120         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1121         SoupURI *new_uri;
1122
1123         /* It must have an appropriate status code and method */
1124         if (!SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg) &&
1125             !SOUP_SESSION_WOULD_REDIRECT_AS_SAFE (session, msg))
1126                 return FALSE;
1127
1128         /* and a Location header that parses to an http URI */
1129         if (!soup_message_headers_get_one (msg->response_headers, "Location"))
1130                 return FALSE;
1131         new_uri = redirection_uri (msg);
1132         if (!new_uri)
1133                 return FALSE;
1134         if (!new_uri->host || !*new_uri->host ||
1135             (!soup_uri_is_http (new_uri, priv->http_aliases) &&
1136              !soup_uri_is_https (new_uri, priv->https_aliases))) {
1137                 soup_uri_free (new_uri);
1138                 return FALSE;
1139         }
1140
1141         soup_uri_free (new_uri);
1142         return TRUE;
1143 }
1144
1145 /**
1146  * soup_session_redirect_message:
1147  * @session: the session
1148  * @msg: a #SoupMessage that has received a 3xx response
1149  *
1150  * Updates @msg's URI according to its status code and "Location"
1151  * header, and requeues it on @session. Use this when you have set
1152  * %SOUP_MESSAGE_NO_REDIRECT on a message, but have decided to allow a
1153  * particular redirection to occur, or if you want to allow a
1154  * redirection that #SoupSession will not perform automatically (eg,
1155  * redirecting a non-safe method such as DELETE).
1156  *
1157  * If @msg's status code indicates that it should be retried as a GET
1158  * request, then @msg will be modified accordingly.
1159  *
1160  * If @msg has already been redirected too many times, this will
1161  * cause it to fail with %SOUP_STATUS_TOO_MANY_REDIRECTS.
1162  *
1163  * Return value: %TRUE if a redirection was applied, %FALSE if not
1164  * (eg, because there was no Location header, or it could not be
1165  * parsed).
1166  *
1167  * Since: 2.38
1168  */
1169 gboolean
1170 soup_session_redirect_message (SoupSession *session, SoupMessage *msg)
1171 {
1172         SoupURI *new_uri;
1173
1174         new_uri = redirection_uri (msg);
1175         if (!new_uri)
1176                 return FALSE;
1177
1178         if (SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg)) {
1179                 if (msg->method != SOUP_METHOD_HEAD) {
1180                         g_object_set (msg,
1181                                       SOUP_MESSAGE_METHOD, SOUP_METHOD_GET,
1182                                       NULL);
1183                 }
1184                 soup_message_set_request (msg, NULL,
1185                                           SOUP_MEMORY_STATIC, NULL, 0);
1186                 soup_message_headers_set_encoding (msg->request_headers,
1187                                                    SOUP_ENCODING_NONE);
1188         }
1189
1190         soup_message_set_uri (msg, new_uri);
1191         soup_uri_free (new_uri);
1192
1193         soup_session_requeue_message (session, msg);
1194         return TRUE;
1195 }
1196
1197 static void
1198 redirect_handler (SoupMessage *msg, gpointer user_data)
1199 {
1200         SoupMessageQueueItem *item = user_data;
1201         SoupSession *session = item->session;
1202
1203         if (!soup_session_would_redirect (session, msg)) {
1204                 SoupURI *new_uri = redirection_uri (msg);
1205                 gboolean invalid = !new_uri || !new_uri->host;
1206
1207                 if (new_uri)
1208                         soup_uri_free (new_uri);
1209                 if (invalid && !item->new_api) {
1210                         soup_message_set_status_full (msg,
1211                                                       SOUP_STATUS_MALFORMED,
1212                                                       "Invalid Redirect URL");
1213                 }
1214                 return;
1215         }
1216
1217         soup_session_redirect_message (session, msg);
1218 }
1219
1220 static void
1221 re_emit_connection_event (SoupConnection      *conn,
1222                           GSocketClientEvent   event,
1223                           GIOStream           *connection,
1224                           gpointer             user_data)
1225 {
1226         SoupMessageQueueItem *item = user_data;
1227
1228         soup_message_network_event (item->msg, event, connection);
1229 }
1230
1231 static void
1232 soup_session_set_item_connection (SoupSession          *session,
1233                                   SoupMessageQueueItem *item,
1234                                   SoupConnection       *conn)
1235 {
1236         if (item->conn) {
1237                 g_signal_handlers_disconnect_by_func (item->conn, re_emit_connection_event, item);
1238                 g_object_unref (item->conn);
1239         }
1240
1241         item->conn = conn;
1242         item->conn_is_dedicated = FALSE;
1243         soup_message_set_connection (item->msg, conn);
1244
1245         if (item->conn) {
1246                 g_object_ref (item->conn);
1247                 g_signal_connect (item->conn, "event",
1248                                   G_CALLBACK (re_emit_connection_event), item);
1249         }
1250 }
1251
1252 static void
1253 message_restarted (SoupMessage *msg, gpointer user_data)
1254 {
1255         SoupMessageQueueItem *item = user_data;
1256
1257         if (item->conn &&
1258             (!soup_message_is_keepalive (msg) ||
1259              SOUP_STATUS_IS_REDIRECTION (msg->status_code))) {
1260                 if (soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE)
1261                         soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
1262                 soup_session_set_item_connection (item->session, item, NULL);
1263         }
1264
1265         soup_message_cleanup_response (msg);
1266 }
1267
1268 SoupMessageQueueItem *
1269 soup_session_append_queue_item (SoupSession *session, SoupMessage *msg,
1270                                 gboolean async, gboolean new_api,
1271                                 SoupSessionCallback callback, gpointer user_data)
1272 {
1273         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1274         SoupMessageQueueItem *item;
1275         SoupSessionHost *host;
1276
1277         soup_message_cleanup_response (msg);
1278
1279         item = soup_message_queue_append (priv->queue, msg, callback, user_data);
1280         item->async = async;
1281         item->new_api = new_api;
1282
1283         g_mutex_lock (&priv->conn_lock);
1284         host = get_host_for_message (session, item->msg);
1285         host->num_messages++;
1286         g_mutex_unlock (&priv->conn_lock);
1287
1288         if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
1289                 soup_message_add_header_handler (
1290                         msg, "got_body", "Location",
1291                         G_CALLBACK (redirect_handler), item);
1292         }
1293         g_signal_connect (msg, "restarted",
1294                           G_CALLBACK (message_restarted), item);
1295
1296         g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
1297
1298         soup_message_queue_item_ref (item);
1299         return item;
1300 }
1301
1302 static void
1303 soup_session_send_queue_item (SoupSession *session,
1304                               SoupMessageQueueItem *item,
1305                               SoupMessageCompletionFn completion_cb)
1306 {
1307         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1308
1309         if (priv->user_agent) {
1310                 soup_message_headers_replace (item->msg->request_headers,
1311                                               "User-Agent", priv->user_agent);
1312         }
1313
1314         if (priv->accept_language &&
1315             !soup_message_headers_get_list (item->msg->request_headers,
1316                                             "Accept-Language")) {
1317                 soup_message_headers_append (item->msg->request_headers,
1318                                              "Accept-Language",
1319                                              priv->accept_language);
1320         }
1321
1322         /* Force keep alive connections for HTTP 1.0. Performance will
1323          * improve when issuing multiple requests to the same host in
1324          * a short period of time, as we wouldn't need to establish
1325          * new connections. Keep alive is implicit for HTTP 1.1.
1326          */
1327         if (!soup_message_headers_header_contains (item->msg->request_headers,
1328                                                    "Connection", "Keep-Alive") &&
1329             !soup_message_headers_header_contains (item->msg->request_headers,
1330                                                    "Connection", "close") &&
1331             !soup_message_headers_header_contains (item->msg->request_headers,
1332                                                    "Connection", "Upgrade")) {
1333                 soup_message_headers_append (item->msg->request_headers,
1334                                              "Connection", "Keep-Alive");
1335         }
1336
1337         g_signal_emit (session, signals[REQUEST_STARTED], 0,
1338                        item->msg, soup_connection_get_socket (item->conn));
1339         soup_message_starting (item->msg);
1340         if (item->state == SOUP_MESSAGE_RUNNING)
1341                 soup_connection_send_request (item->conn, item, completion_cb, item);
1342 }
1343
1344 static gboolean
1345 soup_session_cleanup_connections (SoupSession *session,
1346                                   gboolean     cleanup_idle)
1347 {
1348         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1349         GSList *conns = NULL, *c;
1350         GHashTableIter iter;
1351         gpointer conn, host;
1352         SoupConnectionState state;
1353
1354         g_mutex_lock (&priv->conn_lock);
1355         g_hash_table_iter_init (&iter, priv->conns);
1356         while (g_hash_table_iter_next (&iter, &conn, &host)) {
1357                 state = soup_connection_get_state (conn);
1358                 if (state == SOUP_CONNECTION_REMOTE_DISCONNECTED ||
1359                     (cleanup_idle && state == SOUP_CONNECTION_IDLE)) {
1360                         conns = g_slist_prepend (conns, g_object_ref (conn));
1361                         g_hash_table_iter_remove (&iter);
1362                         drop_connection (session, host, conn);
1363                 }
1364         }
1365         g_mutex_unlock (&priv->conn_lock);
1366
1367         if (!conns)
1368                 return FALSE;
1369
1370         for (c = conns; c; c = c->next) {
1371                 conn = c->data;
1372                 soup_connection_disconnect (conn);
1373                 g_object_unref (conn);
1374         }
1375         g_slist_free (conns);
1376
1377         return TRUE;
1378 }
1379
1380 static gboolean
1381 free_unused_host (gpointer user_data)
1382 {
1383         SoupSessionHost *host = (SoupSessionHost *) user_data;
1384         SoupSessionPrivate *priv = soup_session_get_instance_private (host->session);
1385
1386         g_mutex_lock (&priv->conn_lock);
1387
1388         /* In a multithreaded session, a connection might have been
1389          * added while we were waiting for conn_lock.
1390          */
1391         if (host->connections) {
1392                 g_mutex_unlock (&priv->conn_lock);
1393                 return FALSE;
1394         }
1395
1396         /* This will free the host in addition to removing it from the
1397          * hash table
1398          */
1399         if (host->uri->scheme == SOUP_URI_SCHEME_HTTPS)
1400                 g_hash_table_remove (priv->https_hosts, host->uri);
1401         else
1402                 g_hash_table_remove (priv->http_hosts, host->uri);
1403         g_mutex_unlock (&priv->conn_lock);
1404
1405         return FALSE;
1406 }
1407
1408 static void
1409 drop_connection (SoupSession *session, SoupSessionHost *host, SoupConnection *conn)
1410 {
1411         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1412
1413         /* Note: caller must hold conn_lock, and must remove @conn
1414          * from priv->conns itself.
1415          */
1416
1417         if (host) {
1418                 host->connections = g_slist_remove (host->connections, conn);
1419                 host->num_conns--;
1420
1421                 /* Free the SoupHost (and its SoupAddress) if there
1422                  * has not been any new connection to the host during
1423                  * the last HOST_KEEP_ALIVE msecs.
1424                  */
1425                 if (host->num_conns == 0) {
1426                         g_assert (host->keep_alive_src == NULL);
1427                         host->keep_alive_src = soup_add_timeout (priv->async_context,
1428                                                                  HOST_KEEP_ALIVE,
1429                                                                  free_unused_host,
1430                                                                  host);
1431                         host->keep_alive_src = g_source_ref (host->keep_alive_src);
1432                 }
1433         }
1434
1435         g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
1436         g_signal_handlers_disconnect_by_func (conn, connection_state_changed, session);
1437         priv->num_conns--;
1438
1439         g_object_unref (conn);
1440 }
1441
1442 static void
1443 connection_disconnected (SoupConnection *conn, gpointer user_data)
1444 {
1445         SoupSession *session = user_data;
1446         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1447         SoupSessionHost *host;
1448
1449         g_mutex_lock (&priv->conn_lock);
1450
1451         host = g_hash_table_lookup (priv->conns, conn);
1452         if (host)
1453                 g_hash_table_remove (priv->conns, conn);
1454         drop_connection (session, host, conn);
1455
1456         g_mutex_unlock (&priv->conn_lock);
1457
1458         soup_session_kick_queue (session);
1459 }
1460
1461 static void
1462 connection_state_changed (GObject *object, GParamSpec *param, gpointer user_data)
1463 {
1464         SoupSession *session = user_data;
1465         SoupConnection *conn = SOUP_CONNECTION (object);
1466
1467         if (soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE)
1468                 soup_session_kick_queue (session);
1469 }
1470
1471 SoupMessageQueue *
1472 soup_session_get_queue (SoupSession *session)
1473 {
1474         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1475
1476         return priv->queue;
1477 }
1478
1479 static void
1480 soup_session_unqueue_item (SoupSession          *session,
1481                            SoupMessageQueueItem *item)
1482 {
1483         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1484         SoupSessionHost *host;
1485         SoupConnection *dedicated_conn = NULL;
1486
1487         if (item->conn) {
1488                 if (item->conn_is_dedicated)
1489                         dedicated_conn = g_object_ref (item->conn);
1490                 else if (item->msg->method != SOUP_METHOD_CONNECT ||
1491                          !SOUP_STATUS_IS_SUCCESSFUL (item->msg->status_code))
1492                         soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
1493                 soup_session_set_item_connection (session, item, NULL);
1494         }
1495
1496         if (item->state != SOUP_MESSAGE_FINISHED) {
1497                 g_warning ("finished an item with state %d", item->state);
1498                 return;
1499         }
1500
1501         soup_message_queue_remove (priv->queue, item);
1502
1503         g_mutex_lock (&priv->conn_lock);
1504         host = get_host_for_message (session, item->msg);
1505         host->num_messages--;
1506         if (dedicated_conn) {
1507                 /* FIXME: Do not drop the connection if current number of connections
1508                  * is no longer over the limits, just mark it as IDLE so it can be reused.
1509                  */
1510                 g_hash_table_remove (priv->conns, dedicated_conn);
1511                 drop_connection (session, host, dedicated_conn);
1512         }
1513         g_cond_broadcast (&priv->conn_cond);
1514         g_mutex_unlock (&priv->conn_lock);
1515
1516         if (dedicated_conn) {
1517                 soup_connection_disconnect (dedicated_conn);
1518                 g_object_unref (dedicated_conn);
1519         }
1520
1521         /* g_signal_handlers_disconnect_by_func doesn't work if you
1522          * have a metamarshal, meaning it doesn't work with
1523          * soup_message_add_header_handler()
1524          */
1525         g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
1526                                               0, 0, NULL, NULL, item);
1527         g_signal_emit (session, signals[REQUEST_UNQUEUED], 0, item->msg);
1528         soup_message_queue_item_unref (item);
1529 }
1530
1531 static void
1532 soup_session_set_item_status (SoupSession          *session,
1533                               SoupMessageQueueItem *item,
1534                               guint                 status_code,
1535                               GError               *error)
1536 {
1537         SoupURI *uri = NULL;
1538
1539         switch (status_code) {
1540         case SOUP_STATUS_CANT_RESOLVE:
1541         case SOUP_STATUS_CANT_CONNECT:
1542                 uri = soup_message_get_uri (item->msg);
1543                 break;
1544
1545         case SOUP_STATUS_CANT_RESOLVE_PROXY:
1546         case SOUP_STATUS_CANT_CONNECT_PROXY:
1547                 if (item->conn)
1548                         uri = soup_connection_get_proxy_uri (item->conn);
1549                 break;
1550
1551         case SOUP_STATUS_SSL_FAILED:
1552                 if (!g_tls_backend_supports_tls (g_tls_backend_get_default ())) {
1553                         soup_message_set_status_full (item->msg, status_code,
1554                                                       "TLS/SSL support not available; install glib-networking");
1555                         return;
1556                 }
1557                 break;
1558
1559         default:
1560                 break;
1561         }
1562
1563         if (error)
1564                 soup_message_set_status_full (item->msg, status_code, error->message);
1565         else if (uri && uri->host) {
1566                 char *msg = g_strdup_printf ("%s (%s)",
1567                                              soup_status_get_phrase (status_code),
1568                                              uri->host);
1569                 soup_message_set_status_full (item->msg, status_code, msg);
1570                 g_free (msg);
1571         } else
1572                 soup_message_set_status (item->msg, status_code);
1573 }
1574
1575
1576 static void
1577 message_completed (SoupMessage *msg, SoupMessageIOCompletion completion, gpointer user_data)
1578 {
1579         SoupMessageQueueItem *item = user_data;
1580
1581         if (item->async)
1582                 soup_session_kick_queue (item->session);
1583
1584         if (completion == SOUP_MESSAGE_IO_STOLEN) {
1585                 item->state = SOUP_MESSAGE_FINISHED;
1586                 soup_session_unqueue_item (item->session, item);
1587                 return;
1588         }
1589
1590         if (item->state != SOUP_MESSAGE_RESTARTING) {
1591                 item->state = SOUP_MESSAGE_FINISHING;
1592
1593                 if (item->new_api && !item->async)
1594                         soup_session_process_queue_item (item->session, item, NULL, TRUE);
1595         }
1596 }
1597
1598 static guint
1599 status_from_connect_error (SoupMessageQueueItem *item, GError *error)
1600 {
1601         guint status;
1602
1603         if (!error)
1604                 return SOUP_STATUS_OK;
1605
1606         if (error->domain == G_TLS_ERROR)
1607                 status = SOUP_STATUS_SSL_FAILED;
1608         else if (error->domain == G_RESOLVER_ERROR)
1609                 status = SOUP_STATUS_CANT_RESOLVE;
1610         else if (error->domain == G_IO_ERROR) {
1611                 if (error->code == G_IO_ERROR_CANCELLED)
1612                         status = SOUP_STATUS_CANCELLED;
1613                 else if (error->code == G_IO_ERROR_HOST_UNREACHABLE ||
1614                          error->code == G_IO_ERROR_NETWORK_UNREACHABLE ||
1615                          error->code == G_IO_ERROR_CONNECTION_REFUSED)
1616                         status = SOUP_STATUS_CANT_CONNECT;
1617                 else if (error->code == G_IO_ERROR_PROXY_FAILED ||
1618                          error->code == G_IO_ERROR_PROXY_AUTH_FAILED ||
1619                          error->code == G_IO_ERROR_PROXY_NEED_AUTH ||
1620                          error->code == G_IO_ERROR_PROXY_NOT_ALLOWED)
1621                         status = SOUP_STATUS_CANT_CONNECT_PROXY;
1622                 else
1623                         status = SOUP_STATUS_IO_ERROR;
1624         } else
1625                 status = SOUP_STATUS_IO_ERROR;
1626
1627         if (item->conn && soup_connection_is_via_proxy (item->conn))
1628                 return soup_status_proxify (status);
1629         else
1630                 return status;
1631 }
1632
1633 static void
1634 tunnel_complete (SoupMessageQueueItem *tunnel_item,
1635                  guint status, GError *error)
1636 {
1637         SoupMessageQueueItem *item = tunnel_item->related;
1638         SoupSession *session = tunnel_item->session;
1639
1640         soup_message_finished (tunnel_item->msg);
1641         soup_message_queue_item_unref (tunnel_item);
1642
1643         if (item->msg->status_code)
1644                 item->state = SOUP_MESSAGE_FINISHING;
1645         soup_message_set_https_status (item->msg, item->conn);
1646
1647         item->error = error;
1648         if (!status)
1649                 status = status_from_connect_error (item, error);
1650         if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
1651                 soup_connection_disconnect (item->conn);
1652                 soup_session_set_item_connection (session, item, NULL);
1653                 if (!item->new_api || item->msg->status_code == 0)
1654                         soup_session_set_item_status (session, item, status, error);
1655         }
1656
1657         item->state = SOUP_MESSAGE_READY;
1658         if (item->async)
1659                 soup_session_kick_queue (session);
1660         soup_message_queue_item_unref (item);
1661 }
1662
1663 static void
1664 tunnel_handshake_complete (GObject      *object,
1665                            GAsyncResult *result,
1666                            gpointer      user_data)
1667 {
1668         SoupConnection *conn = SOUP_CONNECTION (object);
1669         SoupMessageQueueItem *tunnel_item = user_data;
1670         GError *error = NULL;
1671
1672         soup_connection_start_ssl_finish (conn, result, &error);
1673         tunnel_complete (tunnel_item, 0, error);
1674 }
1675
1676 static void
1677 tunnel_message_completed (SoupMessage *msg, SoupMessageIOCompletion completion,
1678                           gpointer user_data)
1679 {
1680         SoupMessageQueueItem *tunnel_item = user_data;
1681         SoupMessageQueueItem *item = tunnel_item->related;
1682         SoupSession *session = tunnel_item->session;
1683         guint status;
1684
1685         if (tunnel_item->state == SOUP_MESSAGE_RESTARTING) {
1686                 soup_message_restarted (msg);
1687                 if (tunnel_item->conn) {
1688                         tunnel_item->state = SOUP_MESSAGE_RUNNING;
1689                         soup_session_send_queue_item (session, tunnel_item,
1690                                                       tunnel_message_completed);
1691                         return;
1692                 }
1693
1694                 soup_message_set_status (msg, SOUP_STATUS_TRY_AGAIN);
1695         }
1696
1697         tunnel_item->state = SOUP_MESSAGE_FINISHED;
1698         soup_session_unqueue_item (session, tunnel_item);
1699
1700         status = tunnel_item->msg->status_code;
1701         if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
1702                 tunnel_complete (tunnel_item, status, NULL);
1703                 return;
1704         }
1705
1706         if (tunnel_item->async) {
1707                 soup_connection_start_ssl_async (item->conn, item->cancellable,
1708                                                  tunnel_handshake_complete,
1709                                                  tunnel_item);
1710         } else {
1711                 GError *error = NULL;
1712
1713                 soup_connection_start_ssl_sync (item->conn, item->cancellable, &error);
1714                 tunnel_complete (tunnel_item, 0, error);
1715         }
1716 }
1717
1718 static void
1719 tunnel_connect (SoupMessageQueueItem *item)
1720 {
1721         SoupSession *session = item->session;
1722         SoupMessageQueueItem *tunnel_item;
1723         SoupURI *uri;
1724         SoupMessage *msg;
1725
1726         item->state = SOUP_MESSAGE_TUNNELING;
1727
1728         uri = soup_connection_get_remote_uri (item->conn);
1729         msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT, uri);
1730         soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
1731
1732         tunnel_item = soup_session_append_queue_item (session, msg,
1733                                                       item->async, FALSE,
1734                                                       NULL, NULL);
1735         g_object_unref (msg);
1736         tunnel_item->related = item;
1737         soup_message_queue_item_ref (item);
1738         soup_session_set_item_connection (session, tunnel_item, item->conn);
1739         tunnel_item->state = SOUP_MESSAGE_RUNNING;
1740
1741         g_signal_emit (session, signals[TUNNELING], 0, tunnel_item->conn);
1742
1743         soup_session_send_queue_item (session, tunnel_item,
1744                                       tunnel_message_completed);
1745 }
1746
1747 static void
1748 connect_complete (SoupMessageQueueItem *item, SoupConnection *conn, GError *error)
1749 {
1750         SoupSession *session = item->session;
1751         guint status;
1752
1753         soup_message_set_https_status (item->msg, item->conn);
1754
1755         if (!error) {
1756                 item->state = SOUP_MESSAGE_CONNECTED;
1757                 return;
1758         }
1759
1760         item->error = error;
1761         status = status_from_connect_error (item, error);
1762         soup_connection_disconnect (conn);
1763         if (item->state == SOUP_MESSAGE_CONNECTING) {
1764                 if (!item->new_api || item->msg->status_code == 0)
1765                         soup_session_set_item_status (session, item, status, error);
1766                 soup_session_set_item_connection (session, item, NULL);
1767                 item->state = SOUP_MESSAGE_READY;
1768         }
1769 }
1770
1771 static void
1772 connect_async_complete (GObject      *object,
1773                         GAsyncResult *result,
1774                         gpointer      user_data)
1775 {
1776         SoupConnection *conn = SOUP_CONNECTION (object);
1777         SoupMessageQueueItem *item = user_data;
1778         GError *error = NULL;
1779
1780         soup_connection_connect_finish (conn, result, &error);
1781         connect_complete (item, conn, error);
1782
1783         if (item->state == SOUP_MESSAGE_CONNECTED ||
1784             item->state == SOUP_MESSAGE_READY)
1785                 async_run_queue (item->session);
1786         else
1787                 soup_session_kick_queue (item->session);
1788
1789         soup_message_queue_item_unref (item);
1790 }
1791
1792 /* requires conn_lock */
1793 static SoupConnection *
1794 get_connection_for_host (SoupSession *session,
1795                          SoupMessageQueueItem *item,
1796                          SoupSessionHost *host,
1797                          gboolean need_new_connection,
1798                          gboolean ignore_connection_limits,
1799                          gboolean *try_cleanup,
1800                          gboolean *is_dedicated_connection)
1801 {
1802         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1803         SoupConnection *conn;
1804         GSList *conns;
1805         guint num_pending = 0;
1806
1807         if (priv->disposed)
1808                 return NULL;
1809
1810         if (item->conn) {
1811                 g_return_val_if_fail (soup_connection_get_state (item->conn) != SOUP_CONNECTION_DISCONNECTED, FALSE);
1812                 return item->conn;
1813         }
1814
1815         for (conns = host->connections; conns; conns = conns->next) {
1816                 conn = conns->data;
1817
1818                 if (!need_new_connection && soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE) {
1819                         soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
1820                         return conn;
1821                 } else if (soup_connection_get_state (conn) == SOUP_CONNECTION_CONNECTING)
1822                         num_pending++;
1823         }
1824
1825         /* Limit the number of pending connections; num_messages / 2
1826          * is somewhat arbitrary...
1827          */
1828         if (num_pending > host->num_messages / 2) {
1829                 if (!ignore_connection_limits)
1830                         return NULL;
1831
1832                 *is_dedicated_connection = TRUE;
1833         }
1834
1835         if (host->num_conns >= priv->max_conns_per_host) {
1836                 if (!ignore_connection_limits) {
1837                         if (need_new_connection)
1838                                 *try_cleanup = TRUE;
1839                         return NULL;
1840                 }
1841
1842                 *is_dedicated_connection = TRUE;
1843         }
1844
1845         if (priv->num_conns >= priv->max_conns) {
1846                 if (!ignore_connection_limits) {
1847                         *try_cleanup = TRUE;
1848                         return NULL;
1849                 }
1850
1851                 *is_dedicated_connection = TRUE;
1852         }
1853
1854         ensure_socket_props (session);
1855         conn = g_object_new (SOUP_TYPE_CONNECTION,
1856                              SOUP_CONNECTION_REMOTE_URI, host->uri,
1857                              SOUP_CONNECTION_SSL, soup_uri_is_https (host->uri, priv->https_aliases),
1858                              SOUP_CONNECTION_SOCKET_PROPERTIES, priv->socket_props,
1859                              NULL);
1860
1861         g_signal_connect (conn, "disconnected",
1862                           G_CALLBACK (connection_disconnected),
1863                           session);
1864         g_signal_connect (conn, "notify::state",
1865                           G_CALLBACK (connection_state_changed),
1866                           session);
1867
1868         /* This is a debugging-related signal, and so can ignore the
1869          * usual rule about not emitting signals while holding
1870          * conn_lock.
1871          */
1872         g_signal_emit (session, signals[CONNECTION_CREATED], 0, conn);
1873
1874         g_hash_table_insert (priv->conns, conn, host);
1875
1876         priv->num_conns++;
1877         host->num_conns++;
1878         host->connections = g_slist_prepend (host->connections, conn);
1879
1880         if (host->keep_alive_src) {
1881                 g_source_destroy (host->keep_alive_src);
1882                 g_source_unref (host->keep_alive_src);
1883                 host->keep_alive_src = NULL;
1884         }
1885
1886         return conn;
1887 }
1888
1889 static gboolean
1890 get_connection (SoupMessageQueueItem *item, gboolean *should_cleanup)
1891 {
1892         SoupSession *session = item->session;
1893         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
1894         SoupSessionHost *host;
1895         SoupConnection *conn = NULL;
1896         gboolean my_should_cleanup = FALSE;
1897         gboolean need_new_connection;
1898         gboolean ignore_connection_limits;
1899         gboolean is_dedicated_connection = FALSE;
1900
1901         soup_session_cleanup_connections (session, FALSE);
1902
1903         need_new_connection =
1904                 (soup_message_get_flags (item->msg) & SOUP_MESSAGE_NEW_CONNECTION) ||
1905                 (!(soup_message_get_flags (item->msg) & SOUP_MESSAGE_IDEMPOTENT) &&
1906                  !SOUP_METHOD_IS_IDEMPOTENT (item->msg->method));
1907         ignore_connection_limits =
1908                 (soup_message_get_flags (item->msg) & SOUP_MESSAGE_IGNORE_CONNECTION_LIMITS);
1909
1910         g_mutex_lock (&priv->conn_lock);
1911         host = get_host_for_message (session, item->msg);
1912         while (TRUE) {
1913                 conn = get_connection_for_host (session, item, host,
1914                                                 need_new_connection,
1915                                                 ignore_connection_limits,
1916                                                 &my_should_cleanup,
1917                                                 &is_dedicated_connection);
1918                 if (conn || item->async)
1919                         break;
1920
1921                 if (my_should_cleanup) {
1922                         g_mutex_unlock (&priv->conn_lock);
1923                         soup_session_cleanup_connections (session, TRUE);
1924                         g_mutex_lock (&priv->conn_lock);
1925
1926                         my_should_cleanup = FALSE;
1927                         continue;
1928                 }
1929
1930                 g_cond_wait (&priv->conn_cond, &priv->conn_lock);
1931         }
1932         g_mutex_unlock (&priv->conn_lock);
1933
1934         if (!conn) {
1935                 if (should_cleanup)
1936                         *should_cleanup = my_should_cleanup;
1937                 return FALSE;
1938         }
1939
1940         soup_session_set_item_connection (session, item, conn);
1941         item->conn_is_dedicated = is_dedicated_connection;
1942
1943         if (soup_connection_get_state (item->conn) != SOUP_CONNECTION_NEW) {
1944                 item->state = SOUP_MESSAGE_READY;
1945                 soup_message_set_https_status (item->msg, item->conn);
1946                 return TRUE;
1947         }
1948
1949         item->state = SOUP_MESSAGE_CONNECTING;
1950
1951         if (item->async) {
1952                 soup_message_queue_item_ref (item);
1953                 soup_connection_connect_async (item->conn, item->cancellable,
1954                                                connect_async_complete, item);
1955                 return FALSE;
1956         } else {
1957                 GError *error = NULL;
1958
1959                 soup_connection_connect_sync (item->conn, item->cancellable, &error);
1960                 connect_complete (item, conn, error);
1961
1962                 return TRUE;
1963         }
1964 }
1965
1966 void
1967 soup_session_process_queue_item (SoupSession          *session,
1968                                  SoupMessageQueueItem *item,
1969                                  gboolean             *should_cleanup,
1970                                  gboolean              loop)
1971 {
1972         g_assert (item->session == session);
1973
1974         do {
1975                 if (item->paused)
1976                         return;
1977
1978                 switch (item->state) {
1979                 case SOUP_MESSAGE_STARTING:
1980                         if (!get_connection (item, should_cleanup))
1981                                 return;
1982                         break;
1983
1984                 case SOUP_MESSAGE_CONNECTED:
1985                         if (soup_connection_is_tunnelled (item->conn))
1986                                 tunnel_connect (item);
1987                         else
1988                                 item->state = SOUP_MESSAGE_READY;
1989                         break;
1990
1991                 case SOUP_MESSAGE_READY:
1992                         if (item->connect_only) {
1993                                 item->state = SOUP_MESSAGE_FINISHING;
1994                                 break;
1995                         }
1996
1997                         if (item->msg->status_code) {
1998                                 if (item->msg->status_code == SOUP_STATUS_TRY_AGAIN) {
1999                                         soup_message_cleanup_response (item->msg);
2000                                         item->state = SOUP_MESSAGE_STARTING;
2001                                 } else
2002                                         item->state = SOUP_MESSAGE_FINISHING;
2003                                 break;
2004                         }
2005
2006                         item->state = SOUP_MESSAGE_RUNNING;
2007
2008                         soup_session_send_queue_item (session, item, message_completed);
2009
2010                         if (item->new_api) {
2011                                 if (item->async)
2012                                         async_send_request_running (session, item);
2013                                 return;
2014                         }
2015                         break;
2016
2017                 case SOUP_MESSAGE_RUNNING:
2018                         if (item->async)
2019                                 return;
2020
2021                         g_warn_if_fail (item->new_api);
2022                         item->state = SOUP_MESSAGE_FINISHING;
2023                         break;
2024
2025                 case SOUP_MESSAGE_CACHED:
2026                         /* Will be handled elsewhere */
2027                         return;
2028
2029                 case SOUP_MESSAGE_RESTARTING:
2030                         item->state = SOUP_MESSAGE_STARTING;
2031                         soup_message_restarted (item->msg);
2032                         break;
2033
2034                 case SOUP_MESSAGE_FINISHING:
2035                         item->state = SOUP_MESSAGE_FINISHED;
2036                         soup_message_finished (item->msg);
2037                         if (item->state != SOUP_MESSAGE_FINISHED) {
2038                                 g_return_if_fail (!item->new_api);
2039                                 break;
2040                         }
2041
2042                         soup_message_queue_item_ref (item);
2043                         soup_session_unqueue_item (session, item);
2044                         if (item->async && item->callback)
2045                                 item->callback (session, item->msg, item->callback_data);
2046                         soup_message_queue_item_unref (item);
2047                         return;
2048
2049                 default:
2050                         /* Nothing to do with this message in any
2051                          * other state.
2052                          */
2053                         g_warn_if_fail (item->async);
2054                         return;
2055                 }
2056         } while (loop && item->state != SOUP_MESSAGE_FINISHED);
2057 }
2058
2059 static void
2060 async_run_queue (SoupSession *session)
2061 {
2062         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
2063         SoupMessageQueueItem *item;
2064         SoupMessage *msg;
2065         gboolean try_cleanup = TRUE, should_cleanup = FALSE;
2066
2067         g_object_ref (session);
2068         soup_session_cleanup_connections (session, FALSE);
2069
2070  try_again:
2071         for (item = soup_message_queue_first (priv->queue);
2072              item;
2073              item = soup_message_queue_next (priv->queue, item)) {
2074                 msg = item->msg;
2075
2076                 /* CONNECT messages are handled specially */
2077                 if (msg->method == SOUP_METHOD_CONNECT)
2078                         continue;
2079
2080                 if (!item->async ||
2081                     item->async_context != soup_session_get_async_context (session))
2082                         continue;
2083
2084                 item->async_pending = FALSE;
2085                 soup_session_process_queue_item (session, item, &should_cleanup, TRUE);
2086         }
2087
2088         if (try_cleanup && should_cleanup) {
2089                 /* There is at least one message in the queue that
2090                  * could be sent if we cleanupd an idle connection from
2091                  * some other server.
2092                  */
2093                 if (soup_session_cleanup_connections (session, TRUE)) {
2094                         try_cleanup = should_cleanup = FALSE;
2095                         goto try_again;
2096                 }
2097         }
2098
2099         g_object_unref (session);
2100 }
2101
2102 static gboolean
2103 idle_run_queue (gpointer user_data)
2104 {
2105         GWeakRef *wref = user_data;
2106         SoupSession *session;
2107
2108         session = g_weak_ref_get (wref);
2109         if (!session)
2110                 return FALSE;
2111
2112         async_run_queue (session);
2113         g_object_unref (session);
2114         return FALSE;
2115 }
2116
2117 static void
2118 idle_run_queue_dnotify (gpointer user_data)
2119 {
2120         GWeakRef *wref = user_data;
2121
2122         g_weak_ref_clear (wref);
2123         g_slice_free (GWeakRef, wref);
2124 }
2125
2126 /**
2127  * SoupSessionCallback:
2128  * @session: the session
2129  * @msg: the message that has finished
2130  * @user_data: the data passed to soup_session_queue_message
2131  *
2132  * Prototype for the callback passed to soup_session_queue_message(),
2133  * qv.
2134  **/
2135
2136 static void
2137 soup_session_real_queue_message (SoupSession *session, SoupMessage *msg,
2138                                  SoupSessionCallback callback, gpointer user_data)
2139 {
2140         SoupMessageQueueItem *item;
2141
2142         item = soup_session_append_queue_item (session, msg, TRUE, FALSE,
2143                                                callback, user_data);
2144         soup_session_kick_queue (session);
2145         soup_message_queue_item_unref (item);
2146 }
2147
2148 /**
2149  * soup_session_queue_message:
2150  * @session: a #SoupSession
2151  * @msg: (transfer full): the message to queue
2152  * @callback: (allow-none) (scope async): a #SoupSessionCallback which will
2153  * be called after the message completes or when an unrecoverable error occurs.
2154  * @user_data: (allow-none): a pointer passed to @callback.
2155  * 
2156  * Queues the message @msg for asynchronously sending the request and
2157  * receiving a response in the current thread-default #GMainContext.
2158  * If @msg has been processed before, any resources related to the
2159  * time it was last sent are freed.
2160  *
2161  * Upon message completion, the callback specified in @callback will
2162  * be invoked. If after returning from this callback the message has not
2163  * been requeued, @msg will be unreffed.
2164  *
2165  * (The behavior above applies to a plain #SoupSession; if you are
2166  * using #SoupSessionAsync or #SoupSessionSync, then the #GMainContext
2167  * that is used depends on the settings of #SoupSession:async-context
2168  * and #SoupSession:use-thread-context, and for #SoupSessionSync, the
2169  * message will actually be sent and processed in another thread, with
2170  * only the final callback occurring in the indicated #GMainContext.)
2171  *
2172  * Contrast this method with soup_session_send_async(), which also
2173  * asynchronously sends a message, but returns before reading the
2174  * response body, and allows you to read the response via a
2175  * #GInputStream.
2176  */
2177 void
2178 soup_session_queue_message (SoupSession *session, SoupMessage *msg,
2179                             SoupSessionCallback callback, gpointer user_data)
2180 {
2181         g_return_if_fail (SOUP_IS_SESSION (session));
2182         g_return_if_fail (SOUP_IS_MESSAGE (msg));
2183
2184         SOUP_SESSION_GET_CLASS (session)->queue_message (session, msg,
2185                                                          callback, user_data);
2186         /* The SoupMessageQueueItem will hold a ref on @msg until it is
2187          * finished, so we can drop the ref adopted from the caller now.
2188          */
2189         g_object_unref (msg);
2190 }
2191
2192 static void
2193 soup_session_real_requeue_message (SoupSession *session, SoupMessage *msg)
2194 {
2195         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
2196         SoupMessageQueueItem *item;
2197
2198         item = soup_message_queue_lookup (priv->queue, msg);
2199         g_return_if_fail (item != NULL);
2200
2201         if (item->resend_count >= SOUP_SESSION_MAX_RESEND_COUNT) {
2202                 if (SOUP_STATUS_IS_REDIRECTION (msg->status_code))
2203                         soup_message_set_status (msg, SOUP_STATUS_TOO_MANY_REDIRECTS);
2204                 else
2205                         g_warning ("SoupMessage %p stuck in infinite loop?", msg);
2206         } else {
2207                 item->resend_count++;
2208                 item->state = SOUP_MESSAGE_RESTARTING;
2209         }
2210
2211         soup_message_queue_item_unref (item);
2212 }
2213
2214 /**
2215  * soup_session_requeue_message:
2216  * @session: a #SoupSession
2217  * @msg: the message to requeue
2218  *
2219  * This causes @msg to be placed back on the queue to be attempted
2220  * again.
2221  **/
2222 void
2223 soup_session_requeue_message (SoupSession *session, SoupMessage *msg)
2224 {
2225         g_return_if_fail (SOUP_IS_SESSION (session));
2226         g_return_if_fail (SOUP_IS_MESSAGE (msg));
2227
2228         SOUP_SESSION_GET_CLASS (session)->requeue_message (session, msg);
2229 }
2230
2231 static guint
2232 soup_session_real_send_message (SoupSession *session, SoupMessage *msg)
2233 {
2234         SoupMessageQueueItem *item;
2235         guint status;
2236
2237         item = soup_session_append_queue_item (session, msg, FALSE, FALSE,
2238                                                NULL, NULL);
2239         soup_session_process_queue_item (session, item, NULL, TRUE);
2240         status = msg->status_code;
2241         soup_message_queue_item_unref (item);
2242         return status;
2243 }
2244
2245 /**
2246  * soup_session_send_message:
2247  * @session: a #SoupSession
2248  * @msg: the message to send
2249  * 
2250  * Synchronously send @msg. This call will not return until the
2251  * transfer is finished successfully or there is an unrecoverable
2252  * error.
2253  *
2254  * Unlike with soup_session_queue_message(), @msg is not freed upon
2255  * return.
2256  *
2257  * (Note that if you call this method on a #SoupSessionAsync, it will
2258  * still use asynchronous I/O internally, running the glib main loop
2259  * to process the message, which may also cause other events to be
2260  * processed.)
2261  *
2262  * Contrast this method with soup_session_send(), which also
2263  * synchronously sends a message, but returns before reading the
2264  * response body, and allows you to read the response via a
2265  * #GInputStream.
2266  *
2267  * Return value: the HTTP status code of the response
2268  */
2269 guint
2270 soup_session_send_message (SoupSession *session, SoupMessage *msg)
2271 {
2272         g_return_val_if_fail (SOUP_IS_SESSION (session), SOUP_STATUS_MALFORMED);
2273         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_STATUS_MALFORMED);
2274
2275         return SOUP_SESSION_GET_CLASS (session)->send_message (session, msg);
2276 }
2277
2278
2279 /**
2280  * soup_session_pause_message:
2281  * @session: a #SoupSession
2282  * @msg: a #SoupMessage currently running on @session
2283  *
2284  * Pauses HTTP I/O on @msg. Call soup_session_unpause_message() to
2285  * resume I/O.
2286  *
2287  * This may only be called for asynchronous messages (those sent on a
2288  * #SoupSessionAsync or using soup_session_queue_message()).
2289  **/
2290 void
2291 soup_session_pause_message (SoupSession *session,
2292                             SoupMessage *msg)
2293 {
2294         SoupSessionPrivate *priv;
2295         SoupMessageQueueItem *item;
2296
2297         g_return_if_fail (SOUP_IS_SESSION (session));
2298         g_return_if_fail (SOUP_IS_MESSAGE (msg));
2299
2300         priv = soup_session_get_instance_private (session);
2301         item = soup_message_queue_lookup (priv->queue, msg);
2302         g_return_if_fail (item != NULL);
2303         g_return_if_fail (item->async);
2304
2305         item->paused = TRUE;
2306         if (item->state == SOUP_MESSAGE_RUNNING)
2307                 soup_message_io_pause (msg);
2308         soup_message_queue_item_unref (item);
2309 }
2310
2311 static void
2312 soup_session_real_kick_queue (SoupSession *session)
2313 {
2314         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
2315         SoupMessageQueueItem *item;
2316         GHashTable *async_pending;
2317         gboolean have_sync_items = FALSE;
2318
2319         if (priv->disposed)
2320                 return;
2321
2322         async_pending = g_hash_table_new (NULL, NULL);
2323         for (item = soup_message_queue_first (priv->queue);
2324              item;
2325              item = soup_message_queue_next (priv->queue, item)) {
2326                 if (item->async) {
2327                         GMainContext *context = item->async_context ? item->async_context : g_main_context_default ();
2328
2329                         if (!g_hash_table_contains (async_pending, context)) {
2330                                 if (!item->async_pending) {
2331                                         GWeakRef *wref = g_slice_new (GWeakRef);
2332                                         GSource *source;
2333
2334                                         g_weak_ref_init (wref, session);
2335                                         source = soup_add_completion_reffed (context, idle_run_queue, wref, idle_run_queue_dnotify);
2336                                         g_source_unref (source);
2337                                 }
2338                                 g_hash_table_add (async_pending, context);
2339                         }
2340                         item->async_pending = TRUE;
2341                 } else
2342                         have_sync_items = TRUE;
2343         }
2344         g_hash_table_unref (async_pending);
2345
2346         if (have_sync_items) {
2347                 g_mutex_lock (&priv->conn_lock);
2348                 g_cond_broadcast (&priv->conn_cond);
2349                 g_mutex_unlock (&priv->conn_lock);
2350         }
2351 }
2352
2353 void
2354 soup_session_kick_queue (SoupSession *session)
2355 {
2356         SOUP_SESSION_GET_CLASS (session)->kick (session);
2357 }
2358
2359 /**
2360  * soup_session_unpause_message:
2361  * @session: a #SoupSession
2362  * @msg: a #SoupMessage currently running on @session
2363  *
2364  * Resumes HTTP I/O on @msg. Use this to resume after calling
2365  * soup_session_pause_message().
2366  *
2367  * If @msg is being sent via blocking I/O, this will resume reading or
2368  * writing immediately. If @msg is using non-blocking I/O, then
2369  * reading or writing won't resume until you return to the main loop.
2370  *
2371  * This may only be called for asynchronous messages (those sent on a
2372  * #SoupSessionAsync or using soup_session_queue_message()).
2373  **/
2374 void
2375 soup_session_unpause_message (SoupSession *session,
2376                               SoupMessage *msg)
2377 {
2378         SoupSessionPrivate *priv;
2379         SoupMessageQueueItem *item;
2380
2381         g_return_if_fail (SOUP_IS_SESSION (session));
2382         g_return_if_fail (SOUP_IS_MESSAGE (msg));
2383
2384         priv = soup_session_get_instance_private (session);
2385         item = soup_message_queue_lookup (priv->queue, msg);
2386         g_return_if_fail (item != NULL);
2387         g_return_if_fail (item->async);
2388
2389         item->paused = FALSE;
2390         if (item->state == SOUP_MESSAGE_RUNNING)
2391                 soup_message_io_unpause (msg);
2392         soup_message_queue_item_unref (item);
2393
2394         soup_session_kick_queue (session);
2395 }
2396
2397
2398 static void
2399 soup_session_real_cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
2400 {
2401         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
2402         SoupMessageQueueItem *item;
2403
2404         item = soup_message_queue_lookup (priv->queue, msg);
2405         g_return_if_fail (item != NULL);
2406
2407         if (item->paused) {
2408                 item->paused = FALSE;
2409
2410                 if (soup_message_io_in_progress (msg))
2411                         soup_message_io_unpause (msg);
2412         }
2413
2414         soup_message_set_status (msg, status_code);
2415         g_cancellable_cancel (item->cancellable);
2416
2417         soup_session_kick_queue (item->session);
2418         soup_message_queue_item_unref (item);
2419 }
2420
2421 /**
2422  * soup_session_cancel_message:
2423  * @session: a #SoupSession
2424  * @msg: the message to cancel
2425  * @status_code: status code to set on @msg (generally
2426  * %SOUP_STATUS_CANCELLED)
2427  *
2428  * Causes @session to immediately finish processing @msg (regardless
2429  * of its current state) with a final status_code of @status_code. You
2430  * may call this at any time after handing @msg off to @session; if
2431  * @session has started sending the request but has not yet received
2432  * the complete response, then it will close the request's connection.
2433  * Note that with requests that have side effects (eg,
2434  * <literal>POST</literal>, <literal>PUT</literal>,
2435  * <literal>DELETE</literal>) it is possible that you might cancel the
2436  * request after the server acts on it, but before it returns a
2437  * response, leaving the remote resource in an unknown state.
2438  *
2439  * If the message is cancelled while its response body is being read,
2440  * then the response body in @msg will be left partially-filled-in.
2441  * The response headers, on the other hand, will always be either
2442  * empty or complete.
2443  *
2444  * Beware that with the deprecated #SoupSessionAsync, messages queued
2445  * with soup_session_queue_message() will have their callbacks invoked
2446  * before soup_session_cancel_message() returns. The plain
2447  * #SoupSession does not have this behavior; cancelling an
2448  * asynchronous message will merely queue its callback to be run after
2449  * returning to the main loop.
2450  **/
2451 void
2452 soup_session_cancel_message (SoupSession *session, SoupMessage *msg,
2453                              guint status_code)
2454 {
2455         SoupSessionPrivate *priv;
2456         SoupMessageQueueItem *item;
2457
2458         g_return_if_fail (SOUP_IS_SESSION (session));
2459         g_return_if_fail (SOUP_IS_MESSAGE (msg));
2460
2461         priv = soup_session_get_instance_private (session);
2462         item = soup_message_queue_lookup (priv->queue, msg);
2463         /* If the message is already ending, don't do anything */
2464         if (!item)
2465                 return;
2466         if (item->state == SOUP_MESSAGE_FINISHED) {
2467                 soup_message_queue_item_unref (item);
2468                 return;
2469         }
2470
2471         SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg, status_code);
2472         soup_message_queue_item_unref (item);
2473 }
2474
2475 static void
2476 soup_session_real_flush_queue (SoupSession *session)
2477 {
2478         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
2479         SoupMessageQueueItem *item;
2480         GHashTable *current = NULL;
2481         gboolean done = FALSE;
2482
2483         if (SOUP_IS_SESSION_SYNC (session)) {
2484                 /* Record the current contents of the queue */
2485                 current = g_hash_table_new (NULL, NULL);
2486                 for (item = soup_message_queue_first (priv->queue);
2487                      item;
2488                      item = soup_message_queue_next (priv->queue, item))
2489                         g_hash_table_insert (current, item, item);
2490         }
2491
2492         /* Cancel everything */
2493         for (item = soup_message_queue_first (priv->queue);
2494              item;
2495              item = soup_message_queue_next (priv->queue, item)) {
2496                 soup_session_cancel_message (session, item->msg,
2497                                              SOUP_STATUS_CANCELLED);
2498         }
2499
2500         if (SOUP_IS_SESSION_SYNC (session)) {
2501                 /* Wait until all of the items in @current have been
2502                  * removed from the queue. (This is not the same as
2503                  * "wait for the queue to be empty", because the app
2504                  * may queue new requests in response to the
2505                  * cancellation of the old ones. We don't try to
2506                  * cancel those requests as well, since we'd likely
2507                  * just end up looping forever.)
2508                  */
2509                 g_mutex_lock (&priv->conn_lock);
2510                 do {
2511                         done = TRUE;
2512                         for (item = soup_message_queue_first (priv->queue);
2513                              item;
2514                              item = soup_message_queue_next (priv->queue, item)) {
2515                                 if (g_hash_table_lookup (current, item))
2516                                         done = FALSE;
2517                         }
2518
2519                         if (!done)
2520                                 g_cond_wait (&priv->conn_cond, &priv->conn_lock);
2521                 } while (!done);
2522                 g_mutex_unlock (&priv->conn_lock);
2523
2524                 g_hash_table_destroy (current);
2525         }
2526 }
2527
2528 /**
2529  * soup_session_abort:
2530  * @session: the session
2531  *
2532  * Cancels all pending requests in @session and closes all idle
2533  * persistent connections.
2534  *
2535  * The message cancellation has the same semantics as with
2536  * soup_session_cancel_message(); asynchronous requests on a
2537  * #SoupSessionAsync will have their callback called before
2538  * soup_session_abort() returns. Requests on a plain #SoupSession will
2539  * not.
2540  **/
2541 void
2542 soup_session_abort (SoupSession *session)
2543 {
2544         SoupSessionPrivate *priv;
2545         GSList *conns, *c;
2546         GHashTableIter iter;
2547         gpointer conn, host;
2548
2549         g_return_if_fail (SOUP_IS_SESSION (session));
2550         priv = soup_session_get_instance_private (session);
2551
2552         SOUP_SESSION_GET_CLASS (session)->flush_queue (session);
2553
2554         /* Close all idle connections */
2555         g_mutex_lock (&priv->conn_lock);
2556         conns = NULL;
2557         g_hash_table_iter_init (&iter, priv->conns);
2558         while (g_hash_table_iter_next (&iter, &conn, &host)) {
2559                 SoupConnectionState state;
2560
2561                 state = soup_connection_get_state (conn);
2562                 if (state == SOUP_CONNECTION_IDLE ||
2563                     state == SOUP_CONNECTION_REMOTE_DISCONNECTED) {
2564                         conns = g_slist_prepend (conns, g_object_ref (conn));
2565                         g_hash_table_iter_remove (&iter);
2566                         drop_connection (session, host, conn);
2567                 }
2568         }
2569         g_mutex_unlock (&priv->conn_lock);
2570
2571         for (c = conns; c; c = c->next) {
2572                 soup_connection_disconnect (c->data);
2573                 g_object_unref (c->data);
2574         }
2575
2576         g_slist_free (conns);
2577 }
2578
2579 static void
2580 prefetch_uri (SoupSession *session, SoupURI *uri,
2581               GCancellable *cancellable,
2582               SoupAddressCallback callback, gpointer user_data)
2583 {
2584         SoupSessionPrivate *priv;
2585         SoupSessionHost *host;
2586         SoupAddress *addr;
2587
2588         priv = soup_session_get_instance_private (session);
2589
2590         g_mutex_lock (&priv->conn_lock);
2591         host = get_host_for_uri (session, uri);
2592         addr = g_object_ref (host->addr);
2593         g_mutex_unlock (&priv->conn_lock);
2594
2595         soup_address_resolve_async (addr,
2596                                     soup_session_get_async_context (session),
2597                                     cancellable, callback, user_data);
2598         g_object_unref (addr);
2599 }
2600
2601 /**
2602  * soup_session_prepare_for_uri:
2603  * @session: a #SoupSession
2604  * @uri: a #SoupURI which may be required
2605  *
2606  * Tells @session that @uri may be requested shortly, and so the
2607  * session can try to prepare (resolving the domain name, obtaining
2608  * proxy address, etc.) in order to work more quickly once the URI is
2609  * actually requested.
2610  *
2611  * Since: 2.30
2612  *
2613  * Deprecated: 2.38: use soup_session_prefetch_dns() instead
2614  **/
2615 void
2616 soup_session_prepare_for_uri (SoupSession *session, SoupURI *uri)
2617 {
2618         g_return_if_fail (SOUP_IS_SESSION (session));
2619         g_return_if_fail (uri != NULL);
2620
2621         if (!uri->host)
2622                 return;
2623
2624         prefetch_uri (session, uri, NULL, NULL, NULL);
2625 }
2626
2627 /**
2628 * soup_session_prefetch_dns:
2629 * @session: a #SoupSession
2630 * @hostname: a hostname to be resolved
2631 * @cancellable: (allow-none): a #GCancellable object, or %NULL
2632 * @callback: (scope async) (allow-none): callback to call with the
2633 *     result, or %NULL
2634 * @user_data: data for @callback
2635 *
2636 * Tells @session that an URI from the given @hostname may be requested
2637 * shortly, and so the session can try to prepare by resolving the
2638 * domain name in advance, in order to work more quickly once the URI
2639 * is actually requested.
2640 *
2641 * If @cancellable is non-%NULL, it can be used to cancel the
2642 * resolution. @callback will still be invoked in this case, with a
2643 * status of %SOUP_STATUS_CANCELLED.
2644 *
2645 * Since: 2.38
2646 **/
2647 void
2648 soup_session_prefetch_dns (SoupSession *session, const char *hostname,
2649                            GCancellable *cancellable,
2650                            SoupAddressCallback callback, gpointer user_data)
2651 {
2652         SoupURI *uri;
2653
2654         g_return_if_fail (SOUP_IS_SESSION (session));
2655         g_return_if_fail (hostname != NULL);
2656
2657         /* FIXME: Prefetching should work for both HTTP and HTTPS */
2658         uri = soup_uri_new (NULL);
2659         soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP);
2660         soup_uri_set_host (uri, hostname);
2661         soup_uri_set_path (uri, "");
2662
2663         prefetch_uri (session, uri, cancellable, callback, user_data);
2664         soup_uri_free (uri);
2665 }
2666
2667 /**
2668  * soup_session_add_feature:
2669  * @session: a #SoupSession
2670  * @feature: an object that implements #SoupSessionFeature
2671  *
2672  * Adds @feature's functionality to @session. You can also add a
2673  * feature to the session at construct time by using the
2674  * %SOUP_SESSION_ADD_FEATURE property.
2675  *
2676  * See the main #SoupSession documentation for information on what
2677  * features are present in sessions by default.
2678  *
2679  * Since: 2.24
2680  **/
2681 void
2682 soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature)
2683 {
2684         SoupSessionPrivate *priv;
2685
2686         g_return_if_fail (SOUP_IS_SESSION (session));
2687         g_return_if_fail (SOUP_IS_SESSION_FEATURE (feature));
2688
2689         priv = soup_session_get_instance_private (session);
2690
2691         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2692         if (SOUP_IS_PROXY_URI_RESOLVER (feature)) {
2693                 set_proxy_resolver (session, NULL,
2694                                     SOUP_PROXY_URI_RESOLVER (feature),
2695                                     NULL);
2696         }
2697         G_GNUC_END_IGNORE_DEPRECATIONS;
2698
2699         priv->features = g_slist_prepend (priv->features, g_object_ref (feature));
2700         g_hash_table_remove_all (priv->features_cache);
2701         soup_session_feature_attach (feature, session);
2702 }
2703
2704 /**
2705  * soup_session_add_feature_by_type:
2706  * @session: a #SoupSession
2707  * @feature_type: a #GType
2708  *
2709  * If @feature_type is the type of a class that implements
2710  * #SoupSessionFeature, this creates a new feature of that type and
2711  * adds it to @session as with soup_session_add_feature(). You can use
2712  * this when you don't need to customize the new feature in any way.
2713  *
2714  * If @feature_type is not a #SoupSessionFeature type, this gives each
2715  * existing feature on @session the chance to accept @feature_type as
2716  * a "subfeature". This can be used to add new #SoupAuth or
2717  * #SoupRequest types, for instance.
2718  *
2719  * You can also add a feature to the session at construct time by
2720  * using the %SOUP_SESSION_ADD_FEATURE_BY_TYPE property.
2721  *
2722  * See the main #SoupSession documentation for information on what
2723  * features are present in sessions by default.
2724  *
2725  * Since: 2.24
2726  **/
2727 void
2728 soup_session_add_feature_by_type (SoupSession *session, GType feature_type)
2729 {
2730         SoupSessionPrivate *priv;
2731
2732         g_return_if_fail (SOUP_IS_SESSION (session));
2733
2734         priv = soup_session_get_instance_private (session);
2735
2736         if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2737                 SoupSessionFeature *feature;
2738
2739                 feature = g_object_new (feature_type, NULL);
2740                 soup_session_add_feature (session, feature);
2741                 g_object_unref (feature);
2742         } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
2743                 SoupRequestClass *request_class;
2744                 int i;
2745
2746                 request_class = g_type_class_ref (feature_type);
2747                 for (i = 0; request_class->schemes[i]; i++) {
2748                         g_hash_table_insert (priv->request_types,
2749                                              (char *)request_class->schemes[i],
2750                                              GSIZE_TO_POINTER (feature_type));
2751                 }
2752         } else {
2753                 GSList *f;
2754
2755                 for (f = priv->features; f; f = f->next) {
2756                         if (soup_session_feature_add_feature (f->data, feature_type))
2757                                 return;
2758                 }
2759                 g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
2760         }
2761 }
2762
2763 /**
2764  * soup_session_remove_feature:
2765  * @session: a #SoupSession
2766  * @feature: a feature that has previously been added to @session
2767  *
2768  * Removes @feature's functionality from @session.
2769  *
2770  * Since: 2.24
2771  **/
2772 void
2773 soup_session_remove_feature (SoupSession *session, SoupSessionFeature *feature)
2774 {
2775         SoupSessionPrivate *priv;
2776
2777         g_return_if_fail (SOUP_IS_SESSION (session));
2778
2779         priv = soup_session_get_instance_private (session);
2780         if (g_slist_find (priv->features, feature)) {
2781                 priv->features = g_slist_remove (priv->features, feature);
2782                 g_hash_table_remove_all (priv->features_cache);
2783                 soup_session_feature_detach (feature, session);
2784
2785                 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2786                 if (SOUP_IS_PROXY_URI_RESOLVER (feature)) {
2787                         if (SOUP_IS_PROXY_RESOLVER_WRAPPER (priv->proxy_resolver) &&
2788                             SOUP_PROXY_RESOLVER_WRAPPER (priv->proxy_resolver)->soup_resolver == SOUP_PROXY_URI_RESOLVER (feature))
2789                                 g_clear_object (&priv->proxy_resolver);
2790                 }
2791                 G_GNUC_END_IGNORE_DEPRECATIONS;
2792
2793                 g_object_unref (feature);
2794         }
2795 }
2796
2797 /**
2798  * soup_session_remove_feature_by_type:
2799  * @session: a #SoupSession
2800  * @feature_type: a #GType
2801  *
2802  * Removes all features of type @feature_type (or any subclass of
2803  * @feature_type) from @session. You can also remove standard features
2804  * from the session at construct time by using the
2805  * %SOUP_SESSION_REMOVE_FEATURE_BY_TYPE property.
2806  *
2807  * Since: 2.24
2808  **/
2809 void
2810 soup_session_remove_feature_by_type (SoupSession *session, GType feature_type)
2811 {
2812         SoupSessionPrivate *priv;
2813         GSList *f;
2814
2815         g_return_if_fail (SOUP_IS_SESSION (session));
2816
2817         priv = soup_session_get_instance_private (session);
2818
2819         if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2820         restart:
2821                 for (f = priv->features; f; f = f->next) {
2822                         if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type)) {
2823                                 soup_session_remove_feature (session, f->data);
2824                                 goto restart;
2825                         }
2826                 }
2827                 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2828                 if (g_type_is_a (feature_type, SOUP_TYPE_PROXY_URI_RESOLVER))
2829                         priv->proxy_use_default = FALSE;
2830                 G_GNUC_END_IGNORE_DEPRECATIONS;
2831         } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
2832                 SoupRequestClass *request_class;
2833                 int i;
2834
2835                 request_class = g_type_class_peek (feature_type);
2836                 if (!request_class)
2837                         return;
2838                 for (i = 0; request_class->schemes[i]; i++) {
2839                         g_hash_table_remove (priv->request_types,
2840                                              request_class->schemes[i]);
2841                 }
2842         } else {
2843                 for (f = priv->features; f; f = f->next) {
2844                         if (soup_session_feature_remove_feature (f->data, feature_type))
2845                                 return;
2846                 }
2847                 g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
2848         }
2849 }
2850
2851 /**
2852  * soup_session_has_feature:
2853  * @session: a #SoupSession
2854  * @feature_type: the #GType of the class of features to check for
2855  *
2856  * Tests if @session has at a feature of type @feature_type (which can
2857  * be the type of either a #SoupSessionFeature, or else a subtype of
2858  * some class managed by another feature, such as #SoupAuth or
2859  * #SoupRequest).
2860  *
2861  * Return value: %TRUE or %FALSE
2862  *
2863  * Since: 2.42
2864  **/
2865 gboolean
2866 soup_session_has_feature (SoupSession *session,
2867                           GType        feature_type)
2868 {
2869         SoupSessionPrivate *priv;
2870         GSList *f;
2871
2872         g_return_val_if_fail (SOUP_IS_SESSION (session), FALSE);
2873
2874         priv = soup_session_get_instance_private (session);
2875
2876         if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2877                 for (f = priv->features; f; f = f->next) {
2878                         if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
2879                                 return TRUE;
2880                 }
2881         } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
2882                 SoupRequestClass *request_class;
2883                 int i;
2884
2885                 request_class = g_type_class_peek (feature_type);
2886                 if (!request_class)
2887                         return FALSE;
2888
2889                 for (i = 0; request_class->schemes[i]; i++) {
2890                         gpointer type;
2891
2892                         type = g_hash_table_lookup (priv->request_types,
2893                                                     request_class->schemes[i]);
2894                         if (type && g_type_is_a (GPOINTER_TO_SIZE (type), feature_type))
2895                                 return TRUE;
2896                 }
2897         } else {
2898                 for (f = priv->features; f; f = f->next) {
2899                         if (soup_session_feature_has_feature (f->data, feature_type))
2900                                 return TRUE;
2901                 }
2902         }
2903
2904         return FALSE;
2905 }
2906
2907 /**
2908  * soup_session_get_features:
2909  * @session: a #SoupSession
2910  * @feature_type: the #GType of the class of features to get
2911  *
2912  * Generates a list of @session's features of type @feature_type. (If
2913  * you want to see all features, you can pass %SOUP_TYPE_SESSION_FEATURE
2914  * for @feature_type.)
2915  *
2916  * Return value: (transfer container) (element-type Soup.SessionFeature):
2917  * a list of features. You must free the list, but not its contents
2918  *
2919  * Since: 2.26
2920  **/
2921 GSList *
2922 soup_session_get_features (SoupSession *session, GType feature_type)
2923 {
2924         SoupSessionPrivate *priv;
2925         GSList *f, *ret;
2926
2927         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
2928
2929         priv = soup_session_get_instance_private (session);
2930         for (f = priv->features, ret = NULL; f; f = f->next) {
2931                 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
2932                         ret = g_slist_prepend (ret, f->data);
2933         }
2934         return g_slist_reverse (ret);
2935 }
2936
2937 /**
2938  * soup_session_get_feature:
2939  * @session: a #SoupSession
2940  * @feature_type: the #GType of the feature to get
2941  *
2942  * Gets the first feature in @session of type @feature_type. For
2943  * features where there may be more than one feature of a given type,
2944  * use soup_session_get_features().
2945  *
2946  * Return value: (nullable) (transfer none): a #SoupSessionFeature, or
2947  * %NULL. The feature is owned by @session.
2948  *
2949  * Since: 2.26
2950  **/
2951 SoupSessionFeature *
2952 soup_session_get_feature (SoupSession *session, GType feature_type)
2953 {
2954         SoupSessionPrivate *priv;
2955         SoupSessionFeature *feature;
2956         GSList *f;
2957
2958         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
2959
2960         priv = soup_session_get_instance_private (session);
2961
2962         feature = g_hash_table_lookup (priv->features_cache,
2963                                        GSIZE_TO_POINTER (feature_type));
2964         if (feature)
2965                 return feature;
2966
2967         for (f = priv->features; f; f = f->next) {
2968                 feature = f->data;
2969                 if (G_TYPE_CHECK_INSTANCE_TYPE (feature, feature_type)) {
2970                         g_hash_table_insert (priv->features_cache,
2971                                              GSIZE_TO_POINTER (feature_type),
2972                                              feature);
2973                         return feature;
2974                 }
2975         }
2976         return NULL;
2977 }
2978
2979 /**
2980  * soup_session_get_feature_for_message:
2981  * @session: a #SoupSession
2982  * @feature_type: the #GType of the feature to get
2983  * @msg: a #SoupMessage
2984  *
2985  * Gets the first feature in @session of type @feature_type, provided
2986  * that it is not disabled for @msg. As with
2987  * soup_session_get_feature(), this should only be used for features
2988  * where @feature_type is only expected to match a single feature. In
2989  * particular, if there are two matching features, and the first is
2990  * disabled on @msg, and the second is not, then this will return
2991  * %NULL, not the second feature.
2992  *
2993  * Return value: (nullable) (transfer none): a #SoupSessionFeature, or %NULL. The
2994  * feature is owned by @session.
2995  *
2996  * Since: 2.28
2997  **/
2998 SoupSessionFeature *
2999 soup_session_get_feature_for_message (SoupSession *session, GType feature_type,
3000                                       SoupMessage *msg)
3001 {
3002         SoupSessionFeature *feature;
3003
3004         feature = soup_session_get_feature (session, feature_type);
3005         if (feature && soup_message_disables_feature (msg, feature))
3006                 return NULL;
3007         return feature;
3008 }
3009
3010 static void
3011 soup_session_class_init (SoupSessionClass *session_class)
3012 {
3013         GObjectClass *object_class = G_OBJECT_CLASS (session_class);
3014
3015         /* virtual method definition */
3016         session_class->queue_message = soup_session_real_queue_message;
3017         session_class->send_message = soup_session_real_send_message;
3018         session_class->requeue_message = soup_session_real_requeue_message;
3019         session_class->cancel_message = soup_session_real_cancel_message;
3020         session_class->flush_queue = soup_session_real_flush_queue;
3021         session_class->kick = soup_session_real_kick_queue;
3022
3023         /* virtual method override */
3024         object_class->constructor = soup_session_constructor;
3025         object_class->dispose = soup_session_dispose;
3026         object_class->finalize = soup_session_finalize;
3027         object_class->set_property = soup_session_set_property;
3028         object_class->get_property = soup_session_get_property;
3029
3030         /* signals */
3031
3032         /**
3033          * SoupSession::request-queued:
3034          * @session: the session
3035          * @msg: the request that was queued
3036          *
3037          * Emitted when a request is queued on @session. (Note that
3038          * "queued" doesn't just mean soup_session_queue_message();
3039          * soup_session_send_message() implicitly queues the message
3040          * as well.)
3041          *
3042          * When sending a request, first #SoupSession::request_queued
3043          * is emitted, indicating that the session has become aware of
3044          * the request.
3045          *
3046          * Once a connection is available to send the request on, the
3047          * session emits #SoupSession::request_started. Then, various
3048          * #SoupMessage signals are emitted as the message is
3049          * processed. If the message is requeued, it will emit
3050          * #SoupMessage::restarted, which will then be followed by
3051          * another #SoupSession::request_started and another set of
3052          * #SoupMessage signals when the message is re-sent.
3053          *
3054          * Eventually, the message will emit #SoupMessage::finished.
3055          * Normally, this signals the completion of message
3056          * processing. However, it is possible that the application
3057          * will requeue the message from the "finished" handler (or
3058          * equivalently, from the soup_session_queue_message()
3059          * callback). In that case, the process will loop back to
3060          * #SoupSession::request_started.
3061          *
3062          * Eventually, a message will reach "finished" and not be
3063          * requeued. At that point, the session will emit
3064          * #SoupSession::request_unqueued to indicate that it is done
3065          * with the message.
3066          *
3067          * To sum up: #SoupSession::request_queued and
3068          * #SoupSession::request_unqueued are guaranteed to be emitted
3069          * exactly once, but #SoupSession::request_started and
3070          * #SoupMessage::finished (and all of the other #SoupMessage
3071          * signals) may be invoked multiple times for a given message.
3072          *
3073          * Since: 2.24
3074          **/
3075         signals[REQUEST_QUEUED] =
3076                 g_signal_new ("request-queued",
3077                               G_OBJECT_CLASS_TYPE (object_class),
3078                               G_SIGNAL_RUN_FIRST,
3079                               0, /* FIXME? */
3080                               NULL, NULL,
3081                               NULL,
3082                               G_TYPE_NONE, 1,
3083                               SOUP_TYPE_MESSAGE);
3084
3085         /**
3086          * SoupSession::request-started:
3087          * @session: the session
3088          * @msg: the request being sent
3089          * @socket: the socket the request is being sent on
3090          *
3091          * Emitted just before a request is sent. See
3092          * #SoupSession::request_queued for a detailed description of
3093          * the message lifecycle within a session.
3094          *
3095          * Deprecated: 2.50. Use #SoupMessage::starting instead.
3096          **/
3097         signals[REQUEST_STARTED] =
3098                 g_signal_new ("request-started",
3099                               G_OBJECT_CLASS_TYPE (object_class),
3100                               G_SIGNAL_RUN_FIRST,
3101                               G_STRUCT_OFFSET (SoupSessionClass, request_started),
3102                               NULL, NULL,
3103                               NULL,
3104                               G_TYPE_NONE, 2,
3105                               SOUP_TYPE_MESSAGE,
3106                               SOUP_TYPE_SOCKET);
3107
3108         /**
3109          * SoupSession::request-unqueued:
3110          * @session: the session
3111          * @msg: the request that was unqueued
3112          *
3113          * Emitted when a request is removed from @session's queue,
3114          * indicating that @session is done with it. See
3115          * #SoupSession::request_queued for a detailed description of the
3116          * message lifecycle within a session.
3117          *
3118          * Since: 2.24
3119          **/
3120         signals[REQUEST_UNQUEUED] =
3121                 g_signal_new ("request-unqueued",
3122                               G_OBJECT_CLASS_TYPE (object_class),
3123                               G_SIGNAL_RUN_FIRST,
3124                               0, /* FIXME? */
3125                               NULL, NULL,
3126                               NULL,
3127                               G_TYPE_NONE, 1,
3128                               SOUP_TYPE_MESSAGE);
3129
3130         /**
3131          * SoupSession::authenticate:
3132          * @session: the session
3133          * @msg: the #SoupMessage being sent
3134          * @auth: the #SoupAuth to authenticate
3135          * @retrying: %TRUE if this is the second (or later) attempt
3136          *
3137          * Emitted when the session requires authentication. If
3138          * credentials are available call soup_auth_authenticate() on
3139          * @auth. If these credentials fail, the signal will be
3140          * emitted again, with @retrying set to %TRUE, which will
3141          * continue until you return without calling
3142          * soup_auth_authenticate() on @auth.
3143          *
3144          * Note that this may be emitted before @msg's body has been
3145          * fully read.
3146          *
3147          * If you call soup_session_pause_message() on @msg before
3148          * returning, then you can authenticate @auth asynchronously
3149          * (as long as you g_object_ref() it to make sure it doesn't
3150          * get destroyed), and then unpause @msg when you are ready
3151          * for it to continue.
3152          **/
3153         signals[AUTHENTICATE] =
3154                 g_signal_new ("authenticate",
3155                               G_OBJECT_CLASS_TYPE (object_class),
3156                               G_SIGNAL_RUN_FIRST,
3157                               G_STRUCT_OFFSET (SoupSessionClass, authenticate),
3158                               NULL, NULL,
3159                               NULL,
3160                               G_TYPE_NONE, 3,
3161                               SOUP_TYPE_MESSAGE,
3162                               SOUP_TYPE_AUTH,
3163                               G_TYPE_BOOLEAN);
3164
3165         /**
3166          * SoupSession::connection-created:
3167          * @session: the #SoupSession
3168          * @connection: the connection
3169          *
3170          * Emitted when a new connection is created. This is an
3171          * internal signal intended only to be used for debugging
3172          * purposes, and may go away in the future.
3173          *
3174          * Since: 2.30
3175          */
3176         signals[CONNECTION_CREATED] =
3177                 g_signal_new ("connection-created",
3178                               G_OBJECT_CLASS_TYPE (object_class),
3179                               G_SIGNAL_RUN_FIRST,
3180                               0,
3181                               NULL, NULL,
3182                               NULL,
3183                               G_TYPE_NONE, 1,
3184                               /* SoupConnection is private, so we can't use
3185                                * SOUP_TYPE_CONNECTION here.
3186                                */
3187                               G_TYPE_OBJECT);
3188
3189         /**
3190          * SoupSession::tunneling:
3191          * @session: the #SoupSession
3192          * @connection: the connection
3193          *
3194          * Emitted when an SSL tunnel is being created on a proxy
3195          * connection. This is an internal signal intended only to be
3196          * used for debugging purposes, and may go away in the future.
3197          *
3198          * Since: 2.30
3199          */
3200         signals[TUNNELING] =
3201                 g_signal_new ("tunneling",
3202                               G_OBJECT_CLASS_TYPE (object_class),
3203                               G_SIGNAL_RUN_FIRST,
3204                               0,
3205                               NULL, NULL,
3206                               NULL,
3207                               G_TYPE_NONE, 1,
3208                               /* SoupConnection is private, so we can't use
3209                                * SOUP_TYPE_CONNECTION here.
3210                                */
3211                               G_TYPE_OBJECT);
3212
3213
3214         /* properties */
3215         /**
3216          * SoupSession:proxy-uri:
3217          *
3218          * A proxy to use for all http and https requests in this
3219          * session. Setting this will clear the
3220          * #SoupSession:proxy-resolver property, and remove any
3221          * <type>SoupProxyURIResolver</type> features that have been
3222          * added to the session. Setting this property will also
3223          * cancel all currently pending messages.
3224          *
3225          * Note that #SoupSession will normally handle looking up the
3226          * user's proxy settings for you; you should only use
3227          * #SoupSession:proxy-uri if you need to override the user's
3228          * normal proxy settings.
3229          *
3230          * Also note that this proxy will be used for
3231          * <emphasis>all</emphasis> requests; even requests to
3232          * <literal>localhost</literal>. If you need more control over
3233          * proxies, you can create a #GSimpleProxyResolver and set the
3234          * #SoupSession:proxy-resolver property.
3235          *
3236          * Deprecated: 2.70: Use SoupSession:proxy-resolver along with #GSimpleProxyResolver.
3237          */
3238         /**
3239          * SOUP_SESSION_PROXY_URI:
3240          *
3241          * Alias for the #SoupSession:proxy-uri property, qv.
3242          **/
3243         g_object_class_install_property (
3244                 object_class, PROP_PROXY_URI,
3245                 g_param_spec_boxed (SOUP_SESSION_PROXY_URI,
3246                                     "Proxy URI",
3247                                     "The HTTP Proxy to use for this session",
3248                                     SOUP_TYPE_URI,
3249                                     G_PARAM_READWRITE |
3250                                     G_PARAM_STATIC_STRINGS |
3251                                     G_PARAM_DEPRECATED));
3252         /**
3253          * SoupSession:proxy-resolver:
3254          *
3255          * A #GProxyResolver to use with this session. Setting this
3256          * will clear the #SoupSession:proxy-uri property, and remove
3257          * any <type>SoupProxyURIResolver</type> features that have
3258          * been added to the session.
3259          *
3260          * By default, in a plain #SoupSession, this is set to the
3261          * default #GProxyResolver, but you can set it to %NULL if you
3262          * don't want to use proxies, or set it to your own
3263          * #GProxyResolver if you want to control what proxies get
3264          * used.
3265          *
3266          * Since: 2.42
3267          */
3268         /**
3269          * SOUP_SESSION_PROXY_RESOLVER:
3270          *
3271          * Alias for the #SoupSession:proxy-resolver property, qv.
3272          **/
3273         g_object_class_install_property (
3274                 object_class, PROP_PROXY_RESOLVER,
3275                 g_param_spec_object (SOUP_SESSION_PROXY_RESOLVER,
3276                                      "Proxy Resolver",
3277                                      "The GProxyResolver to use for this session",
3278                                      G_TYPE_PROXY_RESOLVER,
3279                                      G_PARAM_READWRITE |
3280                                      G_PARAM_STATIC_STRINGS));
3281         /**
3282          * SOUP_SESSION_MAX_CONNS:
3283          *
3284          * Alias for the #SoupSession:max-conns property, qv.
3285          **/
3286         g_object_class_install_property (
3287                 object_class, PROP_MAX_CONNS,
3288                 g_param_spec_int (SOUP_SESSION_MAX_CONNS,
3289                                   "Max Connection Count",
3290                                   "The maximum number of connections that the session can open at once",
3291                                   1,
3292                                   G_MAXINT,
3293                                   SOUP_SESSION_MAX_CONNS_DEFAULT,
3294                                   G_PARAM_READWRITE |
3295                                   G_PARAM_STATIC_STRINGS));
3296         /**
3297          * SOUP_SESSION_MAX_CONNS_PER_HOST:
3298          *
3299          * Alias for the #SoupSession:max-conns-per-host property, qv.
3300          **/
3301         g_object_class_install_property (
3302                 object_class, PROP_MAX_CONNS_PER_HOST,
3303                 g_param_spec_int (SOUP_SESSION_MAX_CONNS_PER_HOST,
3304                                   "Max Per-Host Connection Count",
3305                                   "The maximum number of connections that the session can open at once to a given host",
3306                                   1,
3307                                   G_MAXINT,
3308                                   SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT,
3309                                   G_PARAM_READWRITE |
3310                                   G_PARAM_STATIC_STRINGS));
3311         /**
3312          * SoupSession:idle-timeout:
3313          *
3314          * Connection lifetime (in seconds) when idle. Any connection
3315          * left idle longer than this will be closed.
3316          *
3317          * Although you can change this property at any time, it will
3318          * only affect newly-created connections, not currently-open
3319          * ones. You can call soup_session_abort() after setting this
3320          * if you want to ensure that all future connections will have
3321          * this timeout value.
3322          *
3323          * Note that the default value of 60 seconds only applies to
3324          * plain #SoupSessions. If you are using #SoupSessionAsync or
3325          * #SoupSessionSync, the default value is 0 (meaning idle
3326          * connections will never time out).
3327          *
3328          * Since: 2.24
3329          **/
3330         /**
3331          * SOUP_SESSION_IDLE_TIMEOUT:
3332          *
3333          * Alias for the #SoupSession:idle-timeout property, qv.
3334          *
3335          * Since: 2.24
3336          **/
3337         g_object_class_install_property (
3338                 object_class, PROP_IDLE_TIMEOUT,
3339                 g_param_spec_uint (SOUP_SESSION_IDLE_TIMEOUT,
3340                                    "Idle Timeout",
3341                                    "Connection lifetime when idle",
3342                                    0, G_MAXUINT, 60,
3343                                    G_PARAM_READWRITE |
3344                                    G_PARAM_STATIC_STRINGS));
3345         /**
3346          * SoupSession:use-ntlm:
3347          *
3348          * Whether or not to use NTLM authentication.
3349          *
3350          * Deprecated: use soup_session_add_feature_by_type() with
3351          * #SOUP_TYPE_AUTH_NTLM.
3352          **/
3353         /**
3354          * SOUP_SESSION_USE_NTLM:
3355          *
3356          * Alias for the #SoupSession:use-ntlm property, qv.
3357          **/
3358         g_object_class_install_property (
3359                 object_class, PROP_USE_NTLM,
3360                 g_param_spec_boolean (SOUP_SESSION_USE_NTLM,
3361                                       "Use NTLM",
3362                                       "Whether or not to use NTLM authentication",
3363                                       FALSE,
3364                                       G_PARAM_READWRITE | G_PARAM_DEPRECATED |
3365                                       G_PARAM_STATIC_STRINGS));
3366         /**
3367          * SoupSession:ssl-ca-file:
3368          *
3369          * File containing SSL CA certificates.
3370          *
3371          * If the specified file does not exist or cannot be read,
3372          * then libsoup will print a warning, and then behave as
3373          * though it had read in a empty CA file, meaning that all SSL
3374          * certificates will be considered invalid.
3375          *
3376          * Deprecated: use #SoupSession:ssl-use-system-ca-file, or
3377          * else #SoupSession:tls-database with a #GTlsFileDatabase
3378          * (which allows you to do explicit error handling).
3379          **/
3380         /**
3381          * SOUP_SESSION_SSL_CA_FILE:
3382          *
3383          * Alias for the #SoupSession:ssl-ca-file property, qv.
3384          **/
3385         g_object_class_install_property (
3386                 object_class, PROP_SSL_CA_FILE,
3387                 g_param_spec_string (SOUP_SESSION_SSL_CA_FILE,
3388                                      "SSL CA file",
3389                                      "File containing SSL CA certificates",
3390                                      NULL,
3391                                      G_PARAM_READWRITE | G_PARAM_DEPRECATED |
3392                                      G_PARAM_STATIC_STRINGS));
3393         /**
3394          * SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE:
3395          *
3396          * Alias for the #SoupSession:ssl-use-system-ca-file property,
3397          * qv.
3398          *
3399          * Since: 2.38
3400          **/
3401         /**
3402          * SoupSession:ssl-use-system-ca-file:
3403          *
3404          * Setting this to %TRUE is equivalent to setting
3405          * #SoupSession:tls-database to the default system CA database.
3406          * (and likewise, setting #SoupSession:tls-database to the
3407          * default database by hand will cause this property to
3408          * become %TRUE).
3409          *
3410          * Setting this to %FALSE (when it was previously %TRUE) will
3411          * clear the #SoupSession:tls-database field.
3412          *
3413          * See #SoupSession:ssl-strict for more information on how
3414          * https certificate validation is handled.
3415          *
3416          * Note that the default value of %TRUE only applies to plain
3417          * #SoupSessions. If you are using #SoupSessionAsync or
3418          * #SoupSessionSync, the default value is %FALSE, for backward
3419          * compatibility.
3420          *
3421          * Since: 2.38
3422          **/
3423         g_object_class_install_property (
3424                 object_class, PROP_SSL_USE_SYSTEM_CA_FILE,
3425                 g_param_spec_boolean (SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE,
3426                                       "Use system CA file",
3427                                       "Use the system certificate database",
3428                                       TRUE,
3429                                       G_PARAM_READWRITE |
3430                                       G_PARAM_STATIC_STRINGS));
3431         /**
3432          * SOUP_SESSION_TLS_DATABASE:
3433          *
3434          * Alias for the #SoupSession:tls-database property, qv.
3435          *
3436          * Since: 2.38
3437          **/
3438         /**
3439          * SoupSession:tls-database:
3440          *
3441          * Sets the #GTlsDatabase to use for validating SSL/TLS
3442          * certificates.
3443          *
3444          * Note that setting the #SoupSession:ssl-ca-file or
3445          * #SoupSession:ssl-use-system-ca-file property will cause
3446          * this property to be set to a #GTlsDatabase corresponding to
3447          * the indicated file or system default.
3448          *
3449          * See #SoupSession:ssl-strict for more information on how
3450          * https certificate validation is handled.
3451          *
3452          * If you are using a plain #SoupSession then
3453          * #SoupSession:ssl-use-system-ca-file will be %TRUE by
3454          * default, and so this property will be a copy of the system
3455          * CA database. If you are using #SoupSessionAsync or
3456          * #SoupSessionSync, this property will be %NULL by default.
3457          *
3458          * Since: 2.38
3459          **/
3460         g_object_class_install_property (
3461                 object_class, PROP_TLS_DATABASE,
3462                 g_param_spec_object (SOUP_SESSION_TLS_DATABASE,
3463                                      "TLS Database",
3464                                      "TLS database to use",
3465                                      G_TYPE_TLS_DATABASE,
3466                                      G_PARAM_READWRITE |
3467                                      G_PARAM_STATIC_STRINGS));
3468         /**
3469          * SOUP_SESSION_SSL_STRICT:
3470          *
3471          * Alias for the #SoupSession:ssl-strict property, qv.
3472          *
3473          * Since: 2.30
3474          **/
3475         /**
3476          * SoupSession:ssl-strict:
3477          *
3478          * Normally, if #SoupSession:tls-database is set (including if
3479          * it was set via #SoupSession:ssl-use-system-ca-file or
3480          * #SoupSession:ssl-ca-file), then libsoup will reject any
3481          * certificate that is invalid (ie, expired) or that is not
3482          * signed by one of the given CA certificates, and the
3483          * #SoupMessage will fail with the status
3484          * %SOUP_STATUS_SSL_FAILED.
3485          *
3486          * If you set #SoupSession:ssl-strict to %FALSE, then all
3487          * certificates will be accepted, and you will need to call
3488          * soup_message_get_https_status() to distinguish valid from
3489          * invalid certificates. (This can be used, eg, if you want to
3490          * accept invalid certificates after giving some sort of
3491          * warning.)
3492          *
3493          * For a plain #SoupSession, if the session has no CA file or
3494          * TLS database, and this property is %TRUE, then all
3495          * certificates will be rejected. However, beware that the
3496          * deprecated #SoupSession subclasses (#SoupSessionAsync and
3497          * #SoupSessionSync) have the opposite behavior: if there is
3498          * no CA file or TLS database, then all certificates are always
3499          * accepted, and this property has no effect.
3500          *
3501          * Since: 2.30
3502          */
3503         g_object_class_install_property (
3504                 object_class, PROP_SSL_STRICT,
3505                 g_param_spec_boolean (SOUP_SESSION_SSL_STRICT,
3506                                       "Strictly validate SSL certificates",
3507                                       "Whether certificate errors should be considered a connection error",
3508                                       TRUE,
3509                                       G_PARAM_READWRITE |
3510                                       G_PARAM_STATIC_STRINGS));
3511         /**
3512          * SoupSession:async-context:
3513          *
3514          * The #GMainContext that miscellaneous session-related
3515          * asynchronous callbacks are invoked on. (Eg, setting
3516          * #SoupSession:idle-timeout will add a timeout source on this
3517          * context.)
3518          *
3519          * For a plain #SoupSession, this property is always set to
3520          * the #GMainContext that is the thread-default at the time
3521          * the session was created, and cannot be overridden. For the
3522          * deprecated #SoupSession subclasses, the default value is
3523          * %NULL, meaning to use the global default #GMainContext.
3524          *
3525          * If #SoupSession:use-thread-context is %FALSE, this context
3526          * will also be used for asynchronous HTTP I/O.
3527          */
3528         /**
3529          * SOUP_SESSION_ASYNC_CONTEXT:
3530          *
3531          * Alias for the #SoupSession:async-context property, qv.
3532          */
3533         g_object_class_install_property (
3534                 object_class, PROP_ASYNC_CONTEXT,
3535                 g_param_spec_pointer (SOUP_SESSION_ASYNC_CONTEXT,
3536                                       "Async GMainContext",
3537                                       "The GMainContext to dispatch async I/O in",
3538                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
3539                                       G_PARAM_STATIC_STRINGS));
3540         /**
3541          * SOUP_SESSION_USE_THREAD_CONTEXT:
3542          *
3543          * Alias for the #SoupSession:use-thread-context property, qv.
3544          *
3545          * Since: 2.38
3546          */
3547         /**
3548          * SoupSession:use-thread-context:
3549          *
3550          * If %TRUE (which it always is on a plain #SoupSession),
3551          * asynchronous HTTP requests in this session will run in
3552          * whatever the thread-default #GMainContext is at the time
3553          * they are started, rather than always occurring in
3554          * #SoupSession:async-context.
3555          *
3556          * Since: 2.38
3557          */
3558         g_object_class_install_property (
3559                 object_class, PROP_USE_THREAD_CONTEXT,
3560                 g_param_spec_boolean (SOUP_SESSION_USE_THREAD_CONTEXT,
3561                                       "Use thread-default GMainContext",
3562                                       "Whether to use thread-default main contexts",
3563                                       FALSE,
3564                                       G_PARAM_READWRITE |
3565                                       G_PARAM_STATIC_STRINGS));
3566         /**
3567          * SoupSession:timeout:
3568          *
3569          * The timeout (in seconds) for socket I/O operations
3570          * (including connecting to a server, and waiting for a reply
3571          * to an HTTP request).
3572          *
3573          * Although you can change this property at any time, it will
3574          * only affect newly-created connections, not currently-open
3575          * ones. You can call soup_session_abort() after setting this
3576          * if you want to ensure that all future connections will have
3577          * this timeout value.
3578          *
3579          * Note that the default value of 60 seconds only applies to
3580          * plain #SoupSessions. If you are using #SoupSessionAsync or
3581          * #SoupSessionSync, the default value is 0 (meaning socket I/O
3582          * will not time out).
3583          *
3584          * Not to be confused with #SoupSession:idle-timeout (which is
3585          * the length of time that idle persistent connections will be
3586          * kept open).
3587          */
3588         /**
3589          * SOUP_SESSION_TIMEOUT:
3590          *
3591          * Alias for the #SoupSession:timeout property, qv.
3592          **/
3593         g_object_class_install_property (
3594                 object_class, PROP_TIMEOUT,
3595                 g_param_spec_uint (SOUP_SESSION_TIMEOUT,
3596                                    "Timeout value",
3597                                    "Value in seconds to timeout a blocking I/O",
3598                                    0, G_MAXUINT, 0,
3599                                    G_PARAM_READWRITE |
3600                                    G_PARAM_STATIC_STRINGS));
3601
3602         /**
3603          * SoupSession:user-agent:
3604          *
3605          * If non-%NULL, the value to use for the "User-Agent" header
3606          * on #SoupMessage<!-- -->s sent from this session.
3607          *
3608          * RFC 2616 says: "The User-Agent request-header field
3609          * contains information about the user agent originating the
3610          * request. This is for statistical purposes, the tracing of
3611          * protocol violations, and automated recognition of user
3612          * agents for the sake of tailoring responses to avoid
3613          * particular user agent limitations. User agents SHOULD
3614          * include this field with requests."
3615          *
3616          * The User-Agent header contains a list of one or more
3617          * product tokens, separated by whitespace, with the most
3618          * significant product token coming first. The tokens must be
3619          * brief, ASCII, and mostly alphanumeric (although "-", "_",
3620          * and "." are also allowed), and may optionally include a "/"
3621          * followed by a version string. You may also put comments,
3622          * enclosed in parentheses, between or after the tokens.
3623          *
3624          * If you set a #SoupSession:user_agent property that has trailing
3625          * whitespace, #SoupSession will append its own product token
3626          * (eg, "<literal>libsoup/2.3.2</literal>") to the end of the
3627          * header for you.
3628          **/
3629         /**
3630          * SOUP_SESSION_USER_AGENT:
3631          *
3632          * Alias for the #SoupSession:user-agent property, qv.
3633          **/
3634         g_object_class_install_property (
3635                 object_class, PROP_USER_AGENT,
3636                 g_param_spec_string (SOUP_SESSION_USER_AGENT,
3637                                      "User-Agent string",
3638                                      "User-Agent string",
3639                                      NULL,
3640                                      G_PARAM_READWRITE |
3641                                      G_PARAM_STATIC_STRINGS));
3642
3643         /**
3644          * SoupSession:accept-language:
3645          *
3646          * If non-%NULL, the value to use for the "Accept-Language" header
3647          * on #SoupMessage<!-- -->s sent from this session.
3648          *
3649          * Setting this will disable
3650          * #SoupSession:accept-language-auto.
3651          *
3652          * Since: 2.30
3653          **/
3654         /**
3655          * SOUP_SESSION_ACCEPT_LANGUAGE:
3656          *
3657          * Alias for the #SoupSession:accept-language property, qv.
3658          *
3659          * Since: 2.30
3660          **/
3661         g_object_class_install_property (
3662                 object_class, PROP_ACCEPT_LANGUAGE,
3663                 g_param_spec_string (SOUP_SESSION_ACCEPT_LANGUAGE,
3664                                      "Accept-Language string",
3665                                      "Accept-Language string",
3666                                      NULL,
3667                                      G_PARAM_READWRITE |
3668                                      G_PARAM_STATIC_STRINGS));
3669
3670         /**
3671          * SoupSession:accept-language-auto:
3672          *
3673          * If %TRUE, #SoupSession will automatically set the string
3674          * for the "Accept-Language" header on every #SoupMessage
3675          * sent, based on the return value of g_get_language_names().
3676          *
3677          * Setting this will override any previous value of
3678          * #SoupSession:accept-language.
3679          *
3680          * Since: 2.30
3681          **/
3682         /**
3683          * SOUP_SESSION_ACCEPT_LANGUAGE_AUTO:
3684          *
3685          * Alias for the #SoupSession:accept-language-auto property, qv.
3686          *
3687          * Since: 2.30
3688          **/
3689         g_object_class_install_property (
3690                 object_class, PROP_ACCEPT_LANGUAGE_AUTO,
3691                 g_param_spec_boolean (SOUP_SESSION_ACCEPT_LANGUAGE_AUTO,
3692                                       "Accept-Language automatic mode",
3693                                       "Accept-Language automatic mode",
3694                                       FALSE,
3695                                       G_PARAM_READWRITE |
3696                                       G_PARAM_STATIC_STRINGS));
3697
3698         /**
3699          * SoupSession:add-feature: (skip)
3700          *
3701          * Add a feature object to the session. (Shortcut for calling
3702          * soup_session_add_feature().)
3703          *
3704          * Since: 2.24
3705          **/
3706         /**
3707          * SOUP_SESSION_ADD_FEATURE: (skip)
3708          *
3709          * Alias for the #SoupSession:add-feature property, qv.
3710          *
3711          * Since: 2.24
3712          **/
3713         g_object_class_install_property (
3714                 object_class, PROP_ADD_FEATURE,
3715                 g_param_spec_object (SOUP_SESSION_ADD_FEATURE,
3716                                      "Add Feature",
3717                                      "Add a feature object to the session",
3718                                      SOUP_TYPE_SESSION_FEATURE,
3719                                      G_PARAM_READWRITE |
3720                                      G_PARAM_STATIC_STRINGS));
3721         /**
3722          * SoupSession:add-feature-by-type: (skip)
3723          *
3724          * Add a feature object of the given type to the session.
3725          * (Shortcut for calling soup_session_add_feature_by_type().)
3726          *
3727          * Since: 2.24
3728          **/
3729         /**
3730          * SOUP_SESSION_ADD_FEATURE_BY_TYPE: (skip)
3731          *
3732          * Alias for the #SoupSession:add-feature-by-type property, qv.
3733          *
3734          * Since: 2.24
3735          **/
3736         g_object_class_install_property (
3737                 object_class, PROP_ADD_FEATURE_BY_TYPE,
3738                 g_param_spec_gtype (SOUP_SESSION_ADD_FEATURE_BY_TYPE,
3739                                     "Add Feature By Type",
3740                                     "Add a feature object of the given type to the session",
3741                                     G_TYPE_OBJECT,
3742                                     G_PARAM_READWRITE |
3743                                     G_PARAM_STATIC_STRINGS));
3744         /**
3745          * SoupSession:remove-feature-by-type: (skip)
3746          *
3747          * Remove feature objects from the session. (Shortcut for
3748          * calling soup_session_remove_feature_by_type().)
3749          *
3750          * Since: 2.24
3751          **/
3752         /**
3753          * SOUP_SESSION_REMOVE_FEATURE_BY_TYPE: (skip)
3754          *
3755          * Alias for the #SoupSession:remove-feature-by-type property,
3756          * qv.
3757          *
3758          * Since: 2.24
3759          **/
3760         g_object_class_install_property (
3761                 object_class, PROP_REMOVE_FEATURE_BY_TYPE,
3762                 g_param_spec_gtype (SOUP_SESSION_REMOVE_FEATURE_BY_TYPE,
3763                                     "Remove Feature By Type",
3764                                     "Remove features of the given type from the session",
3765                                     G_TYPE_OBJECT,
3766                                     G_PARAM_READWRITE |
3767                                     G_PARAM_STATIC_STRINGS));
3768         /**
3769          * SoupSession:http-aliases:
3770          *
3771          * A %NULL-terminated array of URI schemes that should be
3772          * considered to be aliases for "http". Eg, if this included
3773          * <literal>"dav"</literal>, than a URI of
3774          * <literal>dav://example.com/path</literal> would be treated
3775          * identically to <literal>http://example.com/path</literal>.
3776          *
3777          * In a plain #SoupSession, the default value is %NULL,
3778          * meaning that only "http" is recognized as meaning "http".
3779          * In #SoupSessionAsync and #SoupSessionSync, for backward
3780          * compatibility, the default value is an array containing the
3781          * single element <literal>"*"</literal>, a special value
3782          * which means that any scheme except "https" is considered to
3783          * be an alias for "http".
3784          *
3785          * See also #SoupSession:https-aliases.
3786          *
3787          * Since: 2.38
3788          */
3789         /**
3790          * SOUP_SESSION_HTTP_ALIASES:
3791          *
3792          * Alias for the #SoupSession:http-aliases property, qv.
3793          *
3794          * Since: 2.38
3795          */
3796         g_object_class_install_property (
3797                 object_class, PROP_HTTP_ALIASES,
3798                 g_param_spec_boxed (SOUP_SESSION_HTTP_ALIASES,
3799                                     "http aliases",
3800                                     "URI schemes that are considered aliases for 'http'",
3801                                     G_TYPE_STRV,
3802                                     G_PARAM_READWRITE |
3803                                     G_PARAM_STATIC_STRINGS));
3804         /**
3805          * SoupSession:https-aliases:
3806          *
3807          * A comma-delimited list of URI schemes that should be
3808          * considered to be aliases for "https". See
3809          * #SoupSession:http-aliases for more information.
3810          *
3811          * The default value is %NULL, meaning that no URI schemes
3812          * are considered aliases for "https".
3813          *
3814          * Since: 2.38
3815          */
3816         /**
3817          * SOUP_SESSION_HTTPS_ALIASES:
3818          *
3819          * Alias for the #SoupSession:https-aliases property, qv.
3820          *
3821          * Since: 2.38
3822          **/
3823         g_object_class_install_property (
3824                 object_class, PROP_HTTPS_ALIASES,
3825                 g_param_spec_boxed (SOUP_SESSION_HTTPS_ALIASES,
3826                                     "https aliases",
3827                                     "URI schemes that are considered aliases for 'https'",
3828                                     G_TYPE_STRV,
3829                                     G_PARAM_READWRITE |
3830                                     G_PARAM_STATIC_STRINGS));
3831
3832         /**
3833          * SOUP_SESSION_LOCAL_ADDRESS:
3834          *
3835          * Alias for the #SoupSession:local-address property, qv.
3836          *
3837          * Since: 2.42
3838          **/
3839         /**
3840          * SoupSession:local-address:
3841          *
3842          * Sets the #SoupAddress to use for the client side of
3843          * the connection.
3844          *
3845          * Use this property if you want for instance to bind the
3846          * local socket to a specific IP address.
3847          *
3848          * Since: 2.42
3849          **/
3850         g_object_class_install_property (
3851                 object_class, PROP_LOCAL_ADDRESS,
3852                 g_param_spec_object (SOUP_SESSION_LOCAL_ADDRESS,
3853                                      "Local address",
3854                                      "Address of local end of socket",
3855                                      SOUP_TYPE_ADDRESS,
3856                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
3857                                      G_PARAM_STATIC_STRINGS));
3858
3859         /**
3860          * SOUP_SESSION_TLS_INTERACTION:
3861          *
3862          * Alias for the #SoupSession:tls-interaction property, qv.
3863          *
3864          * Since: 2.48
3865          **/
3866         /**
3867          * SoupSession:tls-interaction:
3868          *
3869          * A #GTlsInteraction object that will be passed on to any
3870          * #GTlsConnections created by the session. (This can be used to
3871          * provide client-side certificates, for example.)
3872          *
3873          * Since: 2.48
3874          **/
3875         g_object_class_install_property (
3876                 object_class, PROP_TLS_INTERACTION,
3877                 g_param_spec_object (SOUP_SESSION_TLS_INTERACTION,
3878                                      "TLS Interaction",
3879                                      "TLS interaction to use",
3880                                      G_TYPE_TLS_INTERACTION,
3881                                      G_PARAM_READWRITE |
3882                                      G_PARAM_STATIC_STRINGS));
3883 }
3884
3885
3886 static gboolean
3887 expected_to_be_requeued (SoupSession *session, SoupMessage *msg)
3888 {
3889         if (msg->status_code == SOUP_STATUS_UNAUTHORIZED ||
3890             msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED) {
3891                 SoupSessionFeature *feature =
3892                         soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
3893                 return !feature || !soup_message_disables_feature (msg, feature);
3894         }
3895
3896         if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT))
3897                 return soup_session_would_redirect (session, msg);
3898
3899         return FALSE;
3900 }
3901
3902 /* send_request_async */
3903
3904 static void
3905 async_send_request_return_result (SoupMessageQueueItem *item,
3906                                   gpointer stream, GError *error)
3907 {
3908         GTask *task;
3909
3910         g_return_if_fail (item->task != NULL);
3911
3912         g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
3913                                               0, 0, NULL, NULL, item);
3914
3915         task = item->task;
3916         item->task = NULL;
3917
3918         if (item->io_source) {
3919                 g_source_destroy (item->io_source);
3920                 g_clear_pointer (&item->io_source, g_source_unref);
3921         }
3922
3923         if (error)
3924                 g_task_return_error (task, error);
3925         else if (item->error) {
3926                 if (stream)
3927                         g_object_unref (stream);
3928                 g_task_return_error (task, g_error_copy (item->error));
3929         } else if (SOUP_STATUS_IS_TRANSPORT_ERROR (item->msg->status_code)) {
3930                 if (stream)
3931                         g_object_unref (stream);
3932                 g_task_return_new_error (task, SOUP_HTTP_ERROR,
3933                                          item->msg->status_code,
3934                                          "%s",
3935                                          item->msg->reason_phrase);
3936         } else
3937                 g_task_return_pointer (task, stream, g_object_unref);
3938         g_object_unref (task);
3939 }
3940
3941 static void
3942 async_send_request_restarted (SoupMessage *msg, gpointer user_data)
3943 {
3944         SoupMessageQueueItem *item = user_data;
3945
3946         /* We won't be needing this, then. */
3947         if (item->task)
3948                 g_object_set_data (G_OBJECT (item->task), "SoupSession:ostream", NULL);
3949         item->io_started = FALSE;
3950 }
3951
3952 static void
3953 async_send_request_finished (SoupMessage *msg, gpointer user_data)
3954 {
3955         SoupMessageQueueItem *item = user_data;
3956         GMemoryOutputStream *mostream;
3957         GInputStream *istream = NULL;
3958         GError *error = NULL;
3959
3960         if (!item->task) {
3961                 /* Something else already took care of it. */
3962                 return;
3963         }
3964
3965         mostream = g_object_get_data (G_OBJECT (item->task), "SoupSession:ostream");
3966         if (mostream) {
3967                 gpointer data;
3968                 gssize size;
3969
3970                 /* We thought it would be requeued, but it wasn't, so
3971                  * return the original body.
3972                  */
3973                 size = g_memory_output_stream_get_data_size (mostream);
3974                 data = size ? g_memory_output_stream_steal_data (mostream) : g_strdup ("");
3975                 istream = g_memory_input_stream_new_from_data (data, size, g_free);
3976         } else if (item->io_started) {
3977                 /* The message finished before becoming readable. This
3978                  * will happen, eg, if it's cancelled from got-headers.
3979                  * Do nothing; the op will complete via read_ready_cb()
3980                  * after we return;
3981                  */
3982                 return;
3983         } else {
3984                 /* The message finished before even being started;
3985                  * probably a tunnel connect failure.
3986                  */
3987                 istream = g_memory_input_stream_new ();
3988         }
3989
3990         async_send_request_return_result (item, istream, error);
3991 }
3992
3993 static void
3994 send_async_spliced (GObject *source, GAsyncResult *result, gpointer user_data)
3995 {
3996         SoupMessageQueueItem *item = user_data;
3997         GInputStream *istream = g_object_get_data (source, "istream");
3998         GError *error = NULL;
3999
4000         /* It should be safe to call the sync close() method here since
4001          * the message body has already been written.
4002          */
4003         g_input_stream_close (istream, NULL, NULL);
4004         g_object_unref (istream);
4005
4006         /* If the message was cancelled, it will be completed via other means */
4007         if (g_cancellable_is_cancelled (item->cancellable) ||
4008             !item->task) {
4009                 soup_message_queue_item_unref (item);
4010                 return;
4011         }
4012
4013         if (g_output_stream_splice_finish (G_OUTPUT_STREAM (source),
4014                                            result, &error) == -1) {
4015                 async_send_request_return_result (item, NULL, error);
4016                 soup_message_queue_item_unref (item);
4017                 return;
4018         }
4019
4020         /* Otherwise either restarted or finished will eventually be called. */
4021         soup_session_kick_queue (item->session);
4022         soup_message_queue_item_unref (item);
4023 }
4024
4025 static void
4026 send_async_maybe_complete (SoupMessageQueueItem *item,
4027                            GInputStream         *stream)
4028 {
4029         if (expected_to_be_requeued (item->session, item->msg)) {
4030                 GOutputStream *ostream;
4031
4032                 /* Gather the current message body... */
4033                 ostream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
4034                 g_object_set_data_full (G_OBJECT (item->task), "SoupSession:ostream",
4035                                         ostream, g_object_unref);
4036
4037                 g_object_set_data (G_OBJECT (ostream), "istream", stream);
4038
4039                 /* Give the splice op its own ref on item */
4040                 soup_message_queue_item_ref (item);
4041                 /* We don't use CLOSE_SOURCE because we need to control when the
4042                  * side effects of closing the SoupClientInputStream happen.
4043                  */
4044                 g_output_stream_splice_async (ostream, stream,
4045                                               G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
4046                                               G_PRIORITY_DEFAULT,
4047                                               item->cancellable,
4048                                               send_async_spliced, item);
4049                 return;
4050         }
4051
4052         async_send_request_return_result (item, stream, NULL);
4053 }
4054
4055 static void try_run_until_read (SoupMessageQueueItem *item);
4056
4057 static gboolean
4058 read_ready_cb (SoupMessage *msg, gpointer user_data)
4059 {
4060         SoupMessageQueueItem *item = user_data;
4061
4062         g_clear_pointer (&item->io_source, g_source_unref);
4063         try_run_until_read (item);
4064         return FALSE;
4065 }
4066
4067 static void
4068 try_run_until_read (SoupMessageQueueItem *item)
4069 {
4070         GError *error = NULL;
4071         GInputStream *stream = NULL;
4072
4073         if (soup_message_io_run_until_read (item->msg, FALSE, item->cancellable, &error))
4074                 stream = soup_message_io_get_response_istream (item->msg, &error);
4075         if (stream) {
4076                 send_async_maybe_complete (item, stream);
4077                 return;
4078         }
4079
4080         if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) {
4081                 item->state = SOUP_MESSAGE_RESTARTING;
4082                 soup_message_io_finished (item->msg);
4083                 g_error_free (error);
4084                 return;
4085         }
4086
4087         if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
4088                 if (item->state != SOUP_MESSAGE_FINISHED) {
4089                         if (soup_message_io_in_progress (item->msg))
4090                                 soup_message_io_finished (item->msg);
4091                         item->state = SOUP_MESSAGE_FINISHING;
4092                         soup_session_process_queue_item (item->session, item, NULL, FALSE);
4093                 }
4094                 async_send_request_return_result (item, NULL, error);
4095                 return;
4096         }
4097
4098         g_clear_error (&error);
4099         item->io_source = soup_message_io_get_source (item->msg, item->cancellable,
4100                                                       read_ready_cb, item);
4101         g_source_attach (item->io_source, soup_session_get_async_context (item->session));
4102 }
4103
4104 static void
4105 async_send_request_running (SoupSession *session, SoupMessageQueueItem *item)
4106 {
4107         item->io_started = TRUE;
4108         try_run_until_read (item);
4109 }
4110
4111 static void
4112 cache_stream_finished (GInputStream         *stream,
4113                        SoupMessageQueueItem *item)
4114 {
4115         g_signal_handlers_disconnect_matched (stream, G_SIGNAL_MATCH_DATA,
4116                                               0, 0, NULL, NULL, item);
4117         item->state = SOUP_MESSAGE_FINISHING;
4118         soup_session_kick_queue (item->session);
4119         soup_message_queue_item_unref (item);
4120 }
4121
4122 static void
4123 async_return_from_cache (SoupMessageQueueItem *item,
4124                          GInputStream         *stream)
4125 {
4126         const char *content_type;
4127         GHashTable *params = NULL;
4128
4129         soup_message_got_headers (item->msg);
4130
4131         content_type = soup_message_headers_get_content_type (item->msg->response_headers, &params);
4132         if (content_type) {
4133                 soup_message_content_sniffed (item->msg, content_type, params);
4134                 g_hash_table_unref (params);
4135         }
4136
4137         soup_message_queue_item_ref (item);
4138         g_signal_connect (stream, "eof", G_CALLBACK (cache_stream_finished), item);
4139         g_signal_connect (stream, "closed", G_CALLBACK (cache_stream_finished), item);
4140
4141         async_send_request_return_result (item, g_object_ref (stream), NULL);
4142 }
4143
4144 typedef struct {
4145         SoupCache *cache;
4146         SoupMessage *conditional_msg;
4147 } AsyncCacheCancelData;
4148
4149
4150 static void
4151 free_async_cache_cancel_data (AsyncCacheCancelData *data)
4152 {
4153         g_object_unref (data->conditional_msg);
4154         g_object_unref (data->cache);
4155         g_slice_free (AsyncCacheCancelData, data);
4156 }
4157
4158 static void
4159 cancel_cache_response (SoupMessageQueueItem *item)
4160 {
4161         item->paused = FALSE;
4162         item->state = SOUP_MESSAGE_FINISHING;
4163         soup_message_set_status (item->msg, SOUP_STATUS_CANCELLED);
4164         soup_session_kick_queue (item->session);
4165 }
4166
4167 static void
4168 conditional_request_cancelled_cb (GCancellable *cancellable, AsyncCacheCancelData *data)
4169 {
4170         soup_cache_cancel_conditional_request (data->cache, data->conditional_msg);
4171 }
4172
4173 static void
4174 conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
4175 {
4176         SoupMessageQueueItem *item = user_data;
4177         GInputStream *stream;
4178         SoupCache *cache;
4179
4180         if (g_cancellable_is_cancelled (item->cancellable)) {
4181                 cancel_cache_response (item);
4182                 return;
4183         } else {
4184                 gulong handler_id = GPOINTER_TO_SIZE (g_object_get_data (G_OBJECT (msg), "SoupSession:handler-id"));
4185                 g_cancellable_disconnect (item->cancellable, handler_id);
4186         }
4187
4188         cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
4189         soup_cache_update_from_conditional_request (cache, msg);
4190
4191         if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) {
4192                 stream = soup_cache_send_response (cache, item->msg);
4193                 if (stream) {
4194                         async_return_from_cache (item, stream);
4195                         g_object_unref (stream);
4196                         return;
4197                 }
4198         }
4199
4200         /* The resource was modified or the server returned a 200
4201          * OK. Either way we reload it. FIXME.
4202          */
4203         item->state = SOUP_MESSAGE_STARTING;
4204         soup_session_kick_queue (session);
4205 }
4206
4207 static gboolean
4208 idle_return_from_cache_cb (gpointer data)
4209 {
4210         GTask *task = data;
4211         SoupMessageQueueItem *item = g_task_get_task_data (task);
4212         GInputStream *istream;
4213
4214         if (item->state == SOUP_MESSAGE_FINISHED) {
4215                 /* The original request was cancelled using
4216                  * soup_session_cancel_message () so it has been
4217                  * already handled by the cancellation code path.
4218                  */
4219                 return FALSE;
4220         } else if (g_cancellable_is_cancelled (item->cancellable)) {
4221                 /* Cancel original msg after g_cancellable_cancel(). */
4222                 cancel_cache_response (item);
4223                 return FALSE;
4224         }
4225
4226         istream = g_object_get_data (G_OBJECT (task), "SoupSession:istream");
4227         async_return_from_cache (item, istream);
4228
4229         return FALSE;
4230 }
4231
4232
4233 static gboolean
4234 async_respond_from_cache (SoupSession          *session,
4235                           SoupMessageQueueItem *item)
4236 {
4237         SoupCache *cache;
4238         SoupCacheResponse response;
4239
4240         cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
4241         if (!cache)
4242                 return FALSE;
4243
4244         response = soup_cache_has_response (cache, item->msg);
4245         if (response == SOUP_CACHE_RESPONSE_FRESH) {
4246                 GInputStream *stream;
4247                 GSource *source;
4248
4249                 stream = soup_cache_send_response (cache, item->msg);
4250                 if (!stream) {
4251                         /* Cached file was deleted? */
4252                         return FALSE;
4253                 }
4254                 g_object_set_data_full (G_OBJECT (item->task), "SoupSession:istream",
4255                                         stream, g_object_unref);
4256
4257                 source = g_timeout_source_new (0);
4258                 g_task_attach_source (item->task, source,
4259                                       (GSourceFunc) idle_return_from_cache_cb);
4260                 g_source_unref (source);
4261                 return TRUE;
4262         } else if (response == SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) {
4263                 SoupMessage *conditional_msg;
4264                 AsyncCacheCancelData *data;
4265                 gulong handler_id;
4266
4267                 conditional_msg = soup_cache_generate_conditional_request (cache, item->msg);
4268                 if (!conditional_msg)
4269                         return FALSE;
4270
4271                 /* Detect any quick cancellation before the cache is able to return data. */
4272                 data = g_slice_new0 (AsyncCacheCancelData);
4273                 data->cache = g_object_ref (cache);
4274                 data->conditional_msg = g_object_ref (conditional_msg);
4275                 handler_id = g_cancellable_connect (item->cancellable, G_CALLBACK (conditional_request_cancelled_cb),
4276                                                     data, (GDestroyNotify) free_async_cache_cancel_data);
4277
4278                 g_object_set_data (G_OBJECT (conditional_msg), "SoupSession:handler-id",
4279                                    GSIZE_TO_POINTER (handler_id));
4280                 soup_session_queue_message (session, conditional_msg,
4281                                             conditional_get_ready_cb,
4282                                             item);
4283
4284
4285                 return TRUE;
4286         } else
4287                 return FALSE;
4288 }
4289
4290 static void
4291 cancel_cancellable (G_GNUC_UNUSED GCancellable *cancellable, GCancellable *chained_cancellable)
4292 {
4293         g_cancellable_cancel (chained_cancellable);
4294 }
4295
4296 /**
4297  * soup_session_send_async:
4298  * @session: a #SoupSession
4299  * @msg: a #SoupMessage
4300  * @cancellable: a #GCancellable
4301  * @callback: the callback to invoke
4302  * @user_data: data for @callback
4303  *
4304  * Asynchronously sends @msg and waits for the beginning of a
4305  * response. When @callback is called, then either @msg has been sent,
4306  * and its response headers received, or else an error has occurred.
4307  * Call soup_session_send_finish() to get a #GInputStream for reading
4308  * the response body.
4309  *
4310  * See soup_session_send() for more details on the general semantics.
4311  *
4312  * Contrast this method with soup_session_queue_message(), which also
4313  * asynchronously sends a #SoupMessage, but doesn't invoke its
4314  * callback until the response has been completely read.
4315  *
4316  * (Note that this method cannot be called on the deprecated
4317  * #SoupSessionSync subclass, and can only be called on
4318  * #SoupSessionAsync if you have set the
4319  * #SoupSession:use-thread-context property.)
4320  *
4321  * Since: 2.42
4322  */
4323 void
4324 soup_session_send_async (SoupSession         *session,
4325                          SoupMessage         *msg,
4326                          GCancellable        *cancellable,
4327                          GAsyncReadyCallback  callback,
4328                          gpointer             user_data)
4329 {
4330         SoupMessageQueueItem *item;
4331         gboolean use_thread_context;
4332
4333         g_return_if_fail (SOUP_IS_SESSION (session));
4334         g_return_if_fail (!SOUP_IS_SESSION_SYNC (session));
4335
4336         g_object_get (G_OBJECT (session),
4337                       SOUP_SESSION_USE_THREAD_CONTEXT, &use_thread_context,
4338                       NULL);
4339         g_return_if_fail (use_thread_context);
4340
4341         item = soup_session_append_queue_item (session, msg, TRUE, TRUE,
4342                                                NULL, NULL);
4343         g_signal_connect (msg, "restarted",
4344                           G_CALLBACK (async_send_request_restarted), item);
4345         g_signal_connect (msg, "finished",
4346                           G_CALLBACK (async_send_request_finished), item);
4347
4348         if (cancellable) {
4349                 g_cancellable_connect (cancellable, G_CALLBACK (cancel_cancellable),
4350                                        g_object_ref (item->cancellable),
4351                                        (GDestroyNotify) g_object_unref);
4352         }
4353
4354         item->new_api = TRUE;
4355         item->task = g_task_new (session, item->cancellable, callback, user_data);
4356         g_task_set_task_data (item->task, item, (GDestroyNotify) soup_message_queue_item_unref);
4357
4358         /* Do not check for cancellations as we do not want to
4359          * overwrite custom error messages set during cancellations
4360          * (for example SOUP_HTTP_ERROR is set for cancelled messages
4361          * in async_send_request_return_result() (status_code==1
4362          * means CANCEL and is considered a TRANSPORT_ERROR)).
4363          */
4364         g_task_set_check_cancellable (item->task, FALSE);
4365
4366         if (async_respond_from_cache (session, item))
4367                 item->state = SOUP_MESSAGE_CACHED;
4368         else
4369                 soup_session_kick_queue (session);
4370 }
4371
4372 /**
4373  * soup_session_send_finish:
4374  * @session: a #SoupSession
4375  * @result: the #GAsyncResult passed to your callback
4376  * @error: return location for a #GError, or %NULL
4377  *
4378  * Gets the response to a soup_session_send_async() call and (if
4379  * successful), returns a #GInputStream that can be used to read the
4380  * response body.
4381  *
4382  * Return value: (transfer full): a #GInputStream for reading the
4383  *   response body, or %NULL on error.
4384  *
4385  * Since: 2.42
4386  */
4387 GInputStream *
4388 soup_session_send_finish (SoupSession   *session,
4389                           GAsyncResult  *result,
4390                           GError       **error)
4391 {
4392         GTask *task;
4393
4394         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
4395         g_return_val_if_fail (!SOUP_IS_SESSION_SYNC (session), NULL);
4396         g_return_val_if_fail (g_task_is_valid (result, session), NULL);
4397
4398         task = G_TASK (result);
4399         if (g_task_had_error (task)) {
4400                 SoupMessageQueueItem *item = g_task_get_task_data (task);
4401
4402                 if (soup_message_io_in_progress (item->msg))
4403                         soup_message_io_finished (item->msg);
4404                 else if (item->state != SOUP_MESSAGE_FINISHED)
4405                         item->state = SOUP_MESSAGE_FINISHING;
4406
4407                 if (item->state != SOUP_MESSAGE_FINISHED)
4408                         soup_session_process_queue_item (session, item, NULL, FALSE);
4409         }
4410
4411         return g_task_propagate_pointer (task, error);
4412 }
4413
4414 /**
4415  * soup_session_send:
4416  * @session: a #SoupSession
4417  * @msg: a #SoupMessage
4418  * @cancellable: a #GCancellable
4419  * @error: return location for a #GError, or %NULL
4420  *
4421  * Synchronously sends @msg and waits for the beginning of a response.
4422  * On success, a #GInputStream will be returned which you can use to
4423  * read the response body. ("Success" here means only that an HTTP
4424  * response was received and understood; it does not necessarily mean
4425  * that a 2xx class status code was received.)
4426  *
4427  * If non-%NULL, @cancellable can be used to cancel the request;
4428  * soup_session_send() will return a %G_IO_ERROR_CANCELLED error. Note
4429  * that with requests that have side effects (eg,
4430  * <literal>POST</literal>, <literal>PUT</literal>,
4431  * <literal>DELETE</literal>) it is possible that you might cancel the
4432  * request after the server acts on it, but before it returns a
4433  * response, leaving the remote resource in an unknown state.
4434  *
4435  * If @msg is requeued due to a redirect or authentication, the
4436  * initial (3xx/401/407) response body will be suppressed, and
4437  * soup_session_send() will only return once a final response has been
4438  * received.
4439  *
4440  * Contrast this method with soup_session_send_message(), which also
4441  * synchronously sends a #SoupMessage, but doesn't return until the
4442  * response has been completely read.
4443  *
4444  * (Note that this method cannot be called on the deprecated
4445  * #SoupSessionAsync subclass.)
4446  *
4447  * Return value: (transfer full): a #GInputStream for reading the
4448  *   response body, or %NULL on error.
4449  *
4450  * Since: 2.42
4451  */
4452 GInputStream *
4453 soup_session_send (SoupSession   *session,
4454                    SoupMessage   *msg,
4455                    GCancellable  *cancellable,
4456                    GError       **error)
4457 {
4458         SoupMessageQueueItem *item;
4459         GInputStream *stream = NULL;
4460         GOutputStream *ostream;
4461         GMemoryOutputStream *mostream;
4462         gssize size;
4463         GError *my_error = NULL;
4464
4465         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
4466         g_return_val_if_fail (!SOUP_IS_SESSION_ASYNC (session), NULL);
4467
4468         item = soup_session_append_queue_item (session, msg, FALSE, TRUE,
4469                                                NULL, NULL);
4470
4471         item->new_api = TRUE;
4472         if (cancellable) {
4473                 g_cancellable_connect (cancellable, G_CALLBACK (cancel_cancellable),
4474                                        g_object_ref (item->cancellable),
4475                                        (GDestroyNotify) g_object_unref);
4476         }
4477
4478         while (!stream) {
4479                 /* Get a connection, etc */
4480                 soup_session_process_queue_item (session, item, NULL, TRUE);
4481                 if (item->state != SOUP_MESSAGE_RUNNING)
4482                         break;
4483
4484                 /* Send request, read headers */
4485                 if (!soup_message_io_run_until_read (msg, TRUE, item->cancellable, &my_error)) {
4486                         if (g_error_matches (my_error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) {
4487                                 item->state = SOUP_MESSAGE_RESTARTING;
4488                                 soup_message_io_finished (item->msg);
4489                                 g_clear_error (&my_error);
4490                                 continue;
4491                         } else
4492                                 break;
4493                 }
4494
4495                 stream = soup_message_io_get_response_istream (msg, &my_error);
4496                 if (!stream)
4497                         break;
4498
4499                 if (!expected_to_be_requeued (session, msg))
4500                         break;
4501
4502                 /* Gather the current message body... */
4503                 ostream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
4504                 if (g_output_stream_splice (ostream, stream,
4505                                             G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
4506                                             G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
4507                                             item->cancellable, &my_error) == -1) {
4508                         g_object_unref (stream);
4509                         g_object_unref (ostream);
4510                         stream = NULL;
4511                         break;
4512                 }
4513                 g_object_unref (stream);
4514                 stream = NULL;
4515
4516                 /* If the message was requeued, loop */
4517                 if (item->state == SOUP_MESSAGE_RESTARTING) {
4518                         g_object_unref (ostream);
4519                         continue;
4520                 }
4521
4522                 /* Not requeued, so return the original body */
4523                 mostream = G_MEMORY_OUTPUT_STREAM (ostream);
4524                 size = g_memory_output_stream_get_data_size (mostream);
4525                 stream = g_memory_input_stream_new ();
4526                 if (size) {
4527                         g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (stream),
4528                                                         g_memory_output_stream_steal_data (mostream),
4529                                                         size, g_free);
4530                 }
4531                 g_object_unref (ostream);
4532         }
4533
4534         if (my_error)
4535                 g_propagate_error (error, my_error);
4536         else if (item->error) {
4537                 g_clear_object (&stream);
4538                 if (error)
4539                         *error = g_error_copy (item->error);
4540         } else if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
4541                 g_clear_object (&stream);
4542                 g_set_error_literal (error, SOUP_HTTP_ERROR, msg->status_code,
4543                                      msg->reason_phrase);
4544         } else if (!stream)
4545                 stream = g_memory_input_stream_new ();
4546
4547         if (!stream) {
4548                 if (soup_message_io_in_progress (msg))
4549                         soup_message_io_finished (msg);
4550                 else if (item->state != SOUP_MESSAGE_FINISHED)
4551                         item->state = SOUP_MESSAGE_FINISHING;
4552
4553                 if (item->state != SOUP_MESSAGE_FINISHED)
4554                         soup_session_process_queue_item (session, item, NULL, TRUE);
4555         }
4556
4557         soup_message_queue_item_unref (item);
4558         return stream;
4559 }
4560
4561 /**
4562  * soup_session_request:
4563  * @session: a #SoupSession
4564  * @uri_string: a URI, in string form
4565  * @error: return location for a #GError, or %NULL
4566  *
4567  * Creates a #SoupRequest for retrieving @uri_string.
4568  *
4569  * Return value: (transfer full): a new #SoupRequest, or
4570  *   %NULL on error.
4571  *
4572  * Since: 2.42
4573  */
4574 SoupRequest *
4575 soup_session_request (SoupSession *session, const char *uri_string,
4576                       GError **error)
4577 {
4578         SoupURI *uri;
4579         SoupRequest *req;
4580
4581         uri = soup_uri_new (uri_string);
4582         if (!uri) {
4583                 g_set_error (error, SOUP_REQUEST_ERROR,
4584                              SOUP_REQUEST_ERROR_BAD_URI,
4585                              _("Could not parse URI “%s”"), uri_string);
4586                 return NULL;
4587         }
4588
4589         req = soup_session_request_uri (session, uri, error);
4590         soup_uri_free (uri);
4591         return req;
4592 }
4593
4594 /**
4595  * soup_session_request_uri:
4596  * @session: a #SoupSession
4597  * @uri: a #SoupURI representing the URI to retrieve
4598  * @error: return location for a #GError, or %NULL
4599  *
4600  * Creates a #SoupRequest for retrieving @uri.
4601  *
4602  * Return value: (transfer full): a new #SoupRequest, or
4603  *   %NULL on error.
4604  *
4605  * Since: 2.42
4606  */
4607 SoupRequest *
4608 soup_session_request_uri (SoupSession *session, SoupURI *uri,
4609                           GError **error)
4610 {
4611         SoupSessionPrivate *priv;
4612         GType request_type;
4613
4614         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
4615
4616         priv = soup_session_get_instance_private (session);
4617
4618         request_type = (GType)GPOINTER_TO_SIZE (g_hash_table_lookup (priv->request_types, uri->scheme));
4619         if (!request_type) {
4620                 g_set_error (error, SOUP_REQUEST_ERROR,
4621                              SOUP_REQUEST_ERROR_UNSUPPORTED_URI_SCHEME,
4622                              _("Unsupported URI scheme “%s”"), uri->scheme);
4623                 return NULL;
4624         }
4625
4626         return g_initable_new (request_type, NULL, error,
4627                                "uri", uri,
4628                                "session", session,
4629                                NULL);
4630 }
4631
4632 static SoupRequestHTTP *
4633 initialize_http_request (SoupRequest  *req,
4634                          const char   *method,
4635                          GError      **error)
4636 {
4637         SoupRequestHTTP *http;
4638         SoupMessage *msg;
4639
4640         if (!SOUP_IS_REQUEST_HTTP (req)) {
4641                 g_object_unref (req);
4642                 g_set_error (error, SOUP_REQUEST_ERROR,
4643                              SOUP_REQUEST_ERROR_BAD_URI,
4644                              _("Not an HTTP URI"));
4645                 return NULL;
4646         }
4647
4648         http = SOUP_REQUEST_HTTP (req);
4649         msg = soup_request_http_get_message (http);
4650         g_object_set (G_OBJECT (msg),
4651                       SOUP_MESSAGE_METHOD, method,
4652                       NULL);
4653         g_object_unref (msg);
4654
4655         return http;
4656 }
4657
4658 /**
4659  * soup_session_request_http:
4660  * @session: a #SoupSession
4661  * @method: an HTTP method
4662  * @uri_string: a URI, in string form
4663  * @error: return location for a #GError, or %NULL
4664  *
4665  * Creates a #SoupRequest for retrieving @uri_string, which must be an
4666  * "http" or "https" URI (or another protocol listed in @session's
4667  * #SoupSession:http-aliases or #SoupSession:https-aliases).
4668  *
4669  * Return value: (transfer full): a new #SoupRequestHTTP, or
4670  *   %NULL on error.
4671  *
4672  * Since: 2.42
4673  */
4674 SoupRequestHTTP *
4675 soup_session_request_http (SoupSession  *session,
4676                            const char   *method,
4677                            const char   *uri_string,
4678                            GError      **error)
4679 {
4680         SoupRequest *req;
4681
4682         req = soup_session_request (session, uri_string, error);
4683         if (!req)
4684                 return NULL;
4685
4686         return initialize_http_request (req, method, error);
4687 }
4688
4689 /**
4690  * soup_session_request_http_uri:
4691  * @session: a #SoupSession
4692  * @method: an HTTP method
4693  * @uri: a #SoupURI representing the URI to retrieve
4694  * @error: return location for a #GError, or %NULL
4695  *
4696  * Creates a #SoupRequest for retrieving @uri, which must be an
4697  * "http" or "https" URI (or another protocol listed in @session's
4698  * #SoupSession:http-aliases or #SoupSession:https-aliases).
4699  *
4700  * Return value: (transfer full): a new #SoupRequestHTTP, or
4701  *   %NULL on error.
4702  *
4703  * Since: 2.42
4704  */
4705 SoupRequestHTTP *
4706 soup_session_request_http_uri (SoupSession  *session,
4707                                const char   *method,
4708                                SoupURI      *uri,
4709                                GError      **error)
4710 {
4711         SoupRequest *req;
4712
4713         req = soup_session_request_uri (session, uri, error);
4714         if (!req)
4715                 return NULL;
4716
4717         return initialize_http_request (req, method, error);
4718 }
4719
4720 /**
4721  * SOUP_REQUEST_ERROR:
4722  *
4723  * A #GError domain for #SoupRequest<!-- -->-related errors. Used with
4724  * #SoupRequestError.
4725  *
4726  * Since: 2.42
4727  */
4728 /**
4729  * SoupRequestError:
4730  * @SOUP_REQUEST_ERROR_BAD_URI: the URI could not be parsed
4731  * @SOUP_REQUEST_ERROR_UNSUPPORTED_URI_SCHEME: the URI scheme is not
4732  *   supported by this #SoupSession
4733  * @SOUP_REQUEST_ERROR_PARSING: the server's response could not
4734  *   be parsed
4735  * @SOUP_REQUEST_ERROR_ENCODING: the server's response was in an
4736  *   unsupported format
4737  *
4738  * A #SoupRequest error.
4739  *
4740  * Since: 2.42
4741  */
4742
4743 GQuark
4744 soup_request_error_quark (void)
4745 {
4746         static GQuark error;
4747         if (!error)
4748                 error = g_quark_from_static_string ("soup_request_error_quark");
4749         return error;
4750 }
4751
4752 static GIOStream *
4753 steal_connection (SoupSession          *session,
4754                   SoupMessageQueueItem *item)
4755 {
4756         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
4757         SoupConnection *conn;
4758         SoupSocket *sock;
4759         SoupSessionHost *host;
4760         GIOStream *stream;
4761
4762         conn = g_object_ref (item->conn);
4763         soup_session_set_item_connection (session, item, NULL);
4764
4765         g_mutex_lock (&priv->conn_lock);
4766         host = get_host_for_message (session, item->msg);
4767         g_hash_table_remove (priv->conns, conn);
4768         drop_connection (session, host, conn);
4769         g_mutex_unlock (&priv->conn_lock);
4770
4771         sock = soup_connection_get_socket (conn);
4772         g_object_set (sock,
4773                       SOUP_SOCKET_TIMEOUT, 0,
4774                       NULL);
4775
4776         if (item->connect_only)
4777                 stream = g_object_ref (soup_socket_get_connection (sock));
4778         else
4779                 stream = soup_message_io_steal (item->msg);
4780         g_object_set_data_full (G_OBJECT (stream), "GSocket",
4781                                 soup_socket_steal_gsocket (sock),
4782                                 g_object_unref);
4783         g_object_unref (conn);
4784
4785         return stream;
4786 }
4787
4788 /**
4789  * soup_session_steal_connection:
4790  * @session: a #SoupSession
4791  * @msg: the message whose connection is to be stolen
4792  *
4793  * "Steals" the HTTP connection associated with @msg from @session.
4794  * This happens immediately, regardless of the current state of the
4795  * connection, and @msg's callback will not be called. You can steal
4796  * the connection from a #SoupMessage signal handler if you need to
4797  * wait for part or all of the response to be received first.
4798  *
4799  * Calling this function may cause @msg to be freed if you are not
4800  * holding any other reference to it.
4801  *
4802  * Return value: (transfer full): the #GIOStream formerly associated
4803  *   with @msg (or %NULL if @msg was no longer associated with a
4804  *   connection). No guarantees are made about what kind of #GIOStream
4805  *   is returned.
4806  *
4807  * Since: 2.50
4808  **/
4809 GIOStream *
4810 soup_session_steal_connection (SoupSession *session,
4811                                SoupMessage *msg)
4812 {
4813         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
4814         SoupMessageQueueItem *item;
4815         GIOStream *stream = NULL;
4816
4817         item = soup_message_queue_lookup (priv->queue, msg);
4818         if (!item)
4819                 return NULL;
4820
4821         if (item->conn && soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE)
4822                 stream = steal_connection (session, item);
4823
4824         soup_message_queue_item_unref (item);
4825
4826         return stream;
4827 }
4828
4829 static GPtrArray *
4830 soup_session_get_supported_websocket_extensions_for_message (SoupSession *session,
4831                                                              SoupMessage *msg)
4832 {
4833         SoupSessionFeature *extension_manager;
4834
4835         extension_manager = soup_session_get_feature_for_message (session, SOUP_TYPE_WEBSOCKET_EXTENSION_MANAGER, msg);
4836         if (!extension_manager)
4837                 return NULL;
4838
4839         return soup_websocket_extension_manager_get_supported_extensions (SOUP_WEBSOCKET_EXTENSION_MANAGER (extension_manager));
4840 }
4841
4842 static void websocket_connect_async_stop (SoupMessage *msg, gpointer user_data);
4843
4844 static void
4845 websocket_connect_async_complete (SoupSession *session, SoupMessage *msg, gpointer user_data)
4846 {
4847         GTask *task = user_data;
4848
4849         /* Disconnect websocket_connect_async_stop() handler. */
4850         g_signal_handlers_disconnect_matched (msg, G_SIGNAL_MATCH_DATA,
4851                                               0, 0, NULL, NULL, task);
4852
4853         g_task_return_new_error (task,
4854                                  SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_NOT_WEBSOCKET,
4855                                  "%s", _("The server did not accept the WebSocket handshake."));
4856         g_object_unref (task);
4857 }
4858
4859 static void
4860 websocket_connect_async_stop (SoupMessage *msg, gpointer user_data)
4861 {
4862         GTask *task = user_data;
4863         SoupMessageQueueItem *item = g_task_get_task_data (task);
4864         GIOStream *stream;
4865         SoupWebsocketConnection *client;
4866         SoupSession *session = g_task_get_source_object (task);
4867         GPtrArray *supported_extensions;
4868         GList *accepted_extensions = NULL;
4869         GError *error = NULL;
4870
4871         /* Disconnect websocket_connect_async_stop() handler. */
4872         g_signal_handlers_disconnect_matched (msg, G_SIGNAL_MATCH_DATA,
4873                                               0, 0, NULL, NULL, task);
4874         /* Ensure websocket_connect_async_complete is not called either. */
4875         item->callback = NULL;
4876
4877         supported_extensions = soup_session_get_supported_websocket_extensions_for_message (session, msg);
4878         if (soup_websocket_client_verify_handshake_with_extensions (item->msg, supported_extensions, &accepted_extensions, &error)) {
4879                 stream = soup_session_steal_connection (item->session, item->msg);
4880                 client = soup_websocket_connection_new_with_extensions (stream,
4881                                                                         soup_message_get_uri (item->msg),
4882                                                                         SOUP_WEBSOCKET_CONNECTION_CLIENT,
4883                                                                         soup_message_headers_get_one (msg->request_headers, "Origin"),
4884                                                                         soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol"),
4885                                                                         accepted_extensions);
4886                 g_object_unref (stream);
4887                 g_task_return_pointer (task, client, g_object_unref);
4888                 g_object_unref (task);
4889
4890                 return;
4891         }
4892
4893         soup_message_io_finished (item->msg);
4894         g_task_return_error (task, error);
4895         g_object_unref (task);
4896 }
4897
4898 /**
4899  * soup_session_websocket_connect_async:
4900  * @session: a #SoupSession
4901  * @msg: #SoupMessage indicating the WebSocket server to connect to
4902  * @origin: (allow-none): origin of the connection
4903  * @protocols: (allow-none) (array zero-terminated=1): a
4904  *   %NULL-terminated array of protocols supported
4905  * @cancellable: a #GCancellable
4906  * @callback: the callback to invoke
4907  * @user_data: data for @callback
4908  *
4909  * Asynchronously creates a #SoupWebsocketConnection to communicate
4910  * with a remote server.
4911  *
4912  * All necessary WebSocket-related headers will be added to @msg, and
4913  * it will then be sent and asynchronously processed normally
4914  * (including handling of redirection and HTTP authentication).
4915  *
4916  * If the server returns "101 Switching Protocols", then @msg's status
4917  * code and response headers will be updated, and then the WebSocket
4918  * handshake will be completed. On success,
4919  * soup_session_websocket_connect_finish() will return a new
4920  * #SoupWebsocketConnection. On failure it will return a #GError.
4921  *
4922  * If the server returns a status other than "101 Switching
4923  * Protocols", then @msg will contain the complete response headers
4924  * and body from the server's response, and
4925  * soup_session_websocket_connect_finish() will return
4926  * %SOUP_WEBSOCKET_ERROR_NOT_WEBSOCKET.
4927  *
4928  * Since: 2.50
4929  */
4930 void
4931 soup_session_websocket_connect_async (SoupSession          *session,
4932                                       SoupMessage          *msg,
4933                                       const char           *origin,
4934                                       char                **protocols,
4935                                       GCancellable         *cancellable,
4936                                       GAsyncReadyCallback   callback,
4937                                       gpointer              user_data)
4938 {
4939         SoupSessionPrivate *priv = soup_session_get_instance_private (session);
4940         SoupMessageQueueItem *item;
4941         GTask *task;
4942         GPtrArray *supported_extensions;
4943         SoupMessageFlags flags;
4944
4945         g_return_if_fail (SOUP_IS_SESSION (session));
4946         g_return_if_fail (priv->use_thread_context);
4947         g_return_if_fail (SOUP_IS_MESSAGE (msg));
4948
4949         supported_extensions = soup_session_get_supported_websocket_extensions_for_message (session, msg);
4950         soup_websocket_client_prepare_handshake_with_extensions (msg, origin, protocols, supported_extensions);
4951
4952         /* When the client is to _Establish a WebSocket Connection_ given a set
4953          * of (/host/, /port/, /resource name/, and /secure/ flag), along with a
4954          * list of /protocols/ and /extensions/ to be used, and an /origin/ in
4955          * the case of web browsers, it MUST open a connection, send an opening
4956          * handshake, and read the server's handshake in response.
4957          */
4958         flags = soup_message_get_flags (msg);
4959         soup_message_set_flags (msg, flags | SOUP_MESSAGE_NEW_CONNECTION);
4960
4961         task = g_task_new (session, cancellable, callback, user_data);
4962         item = soup_session_append_queue_item (session, msg, TRUE, FALSE,
4963                                                websocket_connect_async_complete, task);
4964         g_task_set_task_data (task, item, (GDestroyNotify) soup_message_queue_item_unref);
4965
4966         soup_message_add_status_code_handler (msg, "got-informational",
4967                                               SOUP_STATUS_SWITCHING_PROTOCOLS,
4968                                               G_CALLBACK (websocket_connect_async_stop), task);
4969         soup_session_kick_queue (session);
4970 }
4971
4972 /**
4973  * soup_session_websocket_connect_finish:
4974  * @session: a #SoupSession
4975  * @result: the #GAsyncResult passed to your callback
4976  * @error: return location for a #GError, or %NULL
4977  *
4978  * Gets the #SoupWebsocketConnection response to a
4979  * soup_session_websocket_connect_async() call and (if successful),
4980  * returns a #SoupWebsocketConnection that can be used to communicate
4981  * with the server.
4982  *
4983  * Return value: (transfer full): a new #SoupWebsocketConnection, or
4984  *   %NULL on error.
4985  *
4986  * Since: 2.50
4987  */
4988 SoupWebsocketConnection *
4989 soup_session_websocket_connect_finish (SoupSession      *session,
4990                                        GAsyncResult     *result,
4991                                        GError          **error)
4992 {
4993         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
4994         g_return_val_if_fail (g_task_is_valid (result, session), NULL);
4995
4996         return g_task_propagate_pointer (G_TASK (result), error);
4997 }
4998
4999 /**
5000  * SoupSessionConnectProgressCallback:
5001  * @session: the #SoupSession
5002  * @event: a #GSocketClientEvent
5003  * @connection: the current state of the network connection
5004  * @user_data: the data passed to soup_session_connect_async().
5005  *
5006  * Prototype for the progress callback passed to soup_session_connect_async().
5007  *
5008  * Since: 2.62
5009  */
5010
5011 typedef struct {
5012         SoupMessageQueueItem *item;
5013         SoupSessionConnectProgressCallback progress_callback;
5014         gpointer user_data;
5015 } ConnectAsyncData;
5016
5017 static ConnectAsyncData *
5018 connect_async_data_new (SoupMessageQueueItem              *item,
5019                         SoupSessionConnectProgressCallback progress_callback,
5020                         gpointer                           user_data)
5021 {
5022         ConnectAsyncData *data;
5023
5024         soup_message_queue_item_ref (item);
5025
5026         data = g_slice_new (ConnectAsyncData);
5027         data->item = item;
5028         data->progress_callback = progress_callback;
5029         data->user_data = user_data;
5030
5031         return data;
5032 }
5033
5034 static void
5035 connect_async_data_free (ConnectAsyncData *data)
5036 {
5037         soup_message_queue_item_unref (data->item);
5038
5039         g_slice_free (ConnectAsyncData, data);
5040 }
5041
5042 static void
5043 connect_async_message_network_event (SoupMessage        *msg,
5044                                      GSocketClientEvent  event,
5045                                      GIOStream          *connection,
5046                                      GTask              *task)
5047 {
5048         ConnectAsyncData *data = g_task_get_task_data (task);
5049
5050         if (data->progress_callback)
5051                 data->progress_callback (data->item->session, event, connection, data->user_data);
5052 }
5053
5054 static void
5055 connect_async_message_finished (SoupMessage *msg,
5056                                 GTask       *task)
5057 {
5058         ConnectAsyncData *data = g_task_get_task_data (task);
5059         SoupMessageQueueItem *item = data->item;
5060
5061         if (!item->conn || item->error) {
5062                 g_task_return_error (task, g_error_copy (item->error));
5063         } else {
5064                 g_task_return_pointer (task,
5065                                        steal_connection (item->session, item),
5066                                        g_object_unref);
5067         }
5068         g_object_unref (task);
5069 }
5070
5071 /**
5072  * soup_session_connect_async:
5073  * @session: a #SoupSession
5074  * @uri: a #SoupURI to connect to
5075  * @cancellable: a #GCancellable
5076  * @progress_callback: (allow-none) (scope async): a #SoupSessionConnectProgressCallback which
5077  * will be called for every network event that occurs during the connection.
5078  * @callback: (allow-none) (scope async): the callback to invoke when the operation finishes
5079  * @user_data: data for @progress_callback and @callback
5080  *
5081  * Start a connection to @uri. The operation can be monitored by providing a @progress_callback
5082  * and finishes when the connection is done or an error ocurred.
5083  *
5084  * Call soup_session_connect_finish() to get the #GIOStream to communicate with the server.
5085  *
5086  * Since: 2.62
5087  */
5088 void
5089 soup_session_connect_async (SoupSession                       *session,
5090                             SoupURI                           *uri,
5091                             GCancellable                      *cancellable,
5092                             SoupSessionConnectProgressCallback progress_callback,
5093                             GAsyncReadyCallback                callback,
5094                             gpointer                           user_data)
5095 {
5096         SoupSessionPrivate *priv;
5097         SoupMessage *msg;
5098         SoupMessageQueueItem *item;
5099         ConnectAsyncData *data;
5100         GTask *task;
5101
5102         g_return_if_fail (SOUP_IS_SESSION (session));
5103         g_return_if_fail (!SOUP_IS_SESSION_SYNC (session));
5104         priv = soup_session_get_instance_private (session);
5105         g_return_if_fail (priv->use_thread_context);
5106         g_return_if_fail (uri != NULL);
5107
5108         task = g_task_new (session, cancellable, callback, user_data);
5109
5110         msg = soup_message_new_from_uri (SOUP_METHOD_HEAD, uri);
5111         soup_message_set_flags (msg, SOUP_MESSAGE_NEW_CONNECTION);
5112         g_signal_connect_object (msg, "finished",
5113                                  G_CALLBACK (connect_async_message_finished),
5114                                  task, 0);
5115         if (progress_callback) {
5116                 g_signal_connect_object (msg, "network-event",
5117                                          G_CALLBACK (connect_async_message_network_event),
5118                                          task, 0);
5119         }
5120
5121         item = soup_session_append_queue_item (session, msg, TRUE, FALSE, NULL, NULL);
5122         item->connect_only = TRUE;
5123         data = connect_async_data_new (item, progress_callback, user_data);
5124         g_task_set_task_data (task, data, (GDestroyNotify) connect_async_data_free);
5125         soup_session_kick_queue (session);
5126         soup_message_queue_item_unref (item);
5127         g_object_unref (msg);
5128 }
5129
5130 /**
5131  * soup_session_connect_finish:
5132  * @session: a #SoupSession
5133  * @result: the #GAsyncResult passed to your callback
5134  * @error: return location for a #GError, or %NULL
5135  *
5136  * Gets the #GIOStream created for the connection to communicate with the server.
5137  *
5138  * Return value: (transfer full): a new #GIOStream, or %NULL on error.
5139  *
5140  * Since: 2.62
5141  */
5142 GIOStream *
5143 soup_session_connect_finish (SoupSession  *session,
5144                              GAsyncResult *result,
5145                              GError      **error)
5146 {
5147         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
5148         g_return_val_if_fail (g_task_is_valid (result, session), NULL);
5149
5150         return g_task_propagate_pointer (G_TASK (result), error);
5151 }