Reapplying patch to disable attempts to use gtk-doc
[profile/ivi/libsoup2.4.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 <unistd.h>
13 #include <string.h>
14 #include <stdlib.h>
15
16 #include <glib/gi18n-lib.h>
17
18 #include "soup-auth.h"
19 #include "soup-auth-basic.h"
20 #include "soup-auth-digest.h"
21 #include "soup-auth-manager-ntlm.h"
22 #include "soup-connection.h"
23 #include "soup-marshal.h"
24 #include "soup-message-private.h"
25 #include "soup-message-queue.h"
26 #include "soup-misc.h"
27 #include "soup-proxy-resolver-static.h"
28 #include "soup-proxy-uri-resolver.h"
29 #include "soup-session.h"
30 #include "soup-session-feature.h"
31 #include "soup-session-private.h"
32 #include "soup-socket.h"
33 #include "soup-uri.h"
34
35 #define HOST_KEEP_ALIVE 5 * 60 * 1000 /* 5 min in msecs */
36
37 /**
38  * SECTION:soup-session
39  * @short_description: Soup session state object
40  *
41  * #SoupSession is the object that controls client-side HTTP. A
42  * #SoupSession encapsulates all of the state that libsoup is keeping
43  * on behalf of your program; cached HTTP connections, authentication
44  * information, etc.
45  *
46  * Most applications will only need a single #SoupSession; the primary
47  * reason you might need multiple sessions is if you need to have
48  * multiple independent authentication contexts. (Eg, you are
49  * connecting to a server and authenticating as two different users at
50  * different times; the easiest way to ensure that each #SoupMessage
51  * is sent with the authentication information you intended is to use
52  * one session for the first user, and a second session for the other
53  * user.)
54  *
55  * #SoupSession itself is an abstract class, with two subclasses. If
56  * you are using the glib main loop, you will generally want to use
57  * #SoupSessionAsync, which uses non-blocking I/O and callbacks. On
58  * the other hand, if your application is threaded and you want to do
59  * synchronous I/O in a separate thread from the UI, use
60  * #SoupSessionSync.
61  **/
62
63 static void
64 soup_init (void)
65 {
66         bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
67 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
68         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
69 #endif
70 }
71
72 typedef struct {
73         SoupURI     *uri;
74         SoupAddress *addr;
75
76         GSList      *connections;      /* CONTAINS: SoupConnection */
77         guint        num_conns;
78
79         guint        num_messages;
80
81         gboolean     ssl_fallback;
82
83         GSource     *keep_alive_src;
84         SoupSession *session;
85 } SoupSessionHost;
86 static guint soup_host_uri_hash (gconstpointer key);
87 gboolean soup_host_uri_equal (gconstpointer v1, gconstpointer v2);
88
89 typedef struct {
90         GTlsDatabase *tlsdb;
91         char *ssl_ca_file;
92         gboolean ssl_strict;
93
94         SoupMessageQueue *queue;
95
96         char *user_agent;
97         char *accept_language;
98         gboolean accept_language_auto;
99
100         GSList *features;
101         GHashTable *features_cache;
102
103         GHashTable *http_hosts, *https_hosts; /* char* -> SoupSessionHost */
104         GHashTable *conns; /* SoupConnection -> SoupSessionHost */
105         guint num_conns;
106         guint max_conns, max_conns_per_host;
107         guint io_timeout, idle_timeout;
108
109         /* Must hold the host_lock before potentially creating a
110          * new SoupSessionHost, or adding/removing a connection.
111          * Must not emit signals or destroy objects while holding it.
112          */
113         GMutex host_lock;
114
115         GMainContext *async_context;
116         gboolean use_thread_context;
117
118         GResolver *resolver;
119
120         char **http_aliases, **https_aliases;
121 } SoupSessionPrivate;
122 #define SOUP_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION, SoupSessionPrivate))
123
124 static void     free_host      (SoupSessionHost *host);
125
126 static void queue_message   (SoupSession *session, SoupMessage *msg,
127                              SoupSessionCallback callback, gpointer user_data);
128 static void requeue_message (SoupSession *session, SoupMessage *msg);
129 static void cancel_message  (SoupSession *session, SoupMessage *msg,
130                              guint status_code);
131 static void auth_required   (SoupSession *session, SoupMessage *msg,
132                              SoupAuth *auth, gboolean retrying);
133 static void flush_queue     (SoupSession *session);
134
135 static void auth_manager_authenticate (SoupAuthManager *manager,
136                                        SoupMessage *msg, SoupAuth *auth,
137                                        gboolean retrying, gpointer user_data);
138
139 #define SOUP_SESSION_MAX_CONNS_DEFAULT 10
140 #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2
141
142 #define SOUP_SESSION_MAX_REDIRECTION_COUNT 20
143
144 #define SOUP_SESSION_USER_AGENT_BASE "libsoup/" PACKAGE_VERSION
145
146 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (SoupSession, soup_session, G_TYPE_OBJECT,
147                                   soup_init ();
148                                   )
149
150 enum {
151         REQUEST_QUEUED,
152         REQUEST_STARTED,
153         REQUEST_UNQUEUED,
154         AUTHENTICATE,
155         CONNECTION_CREATED,
156         TUNNELING,
157         LAST_SIGNAL
158 };
159
160 static guint signals[LAST_SIGNAL] = { 0 };
161
162 enum {
163         PROP_0,
164
165         PROP_PROXY_URI,
166         PROP_MAX_CONNS,
167         PROP_MAX_CONNS_PER_HOST,
168         PROP_USE_NTLM,
169         PROP_SSL_CA_FILE,
170         PROP_SSL_USE_SYSTEM_CA_FILE,
171         PROP_TLS_DATABASE,
172         PROP_SSL_STRICT,
173         PROP_ASYNC_CONTEXT,
174         PROP_USE_THREAD_CONTEXT,
175         PROP_TIMEOUT,
176         PROP_USER_AGENT,
177         PROP_ACCEPT_LANGUAGE,
178         PROP_ACCEPT_LANGUAGE_AUTO,
179         PROP_IDLE_TIMEOUT,
180         PROP_ADD_FEATURE,
181         PROP_ADD_FEATURE_BY_TYPE,
182         PROP_REMOVE_FEATURE_BY_TYPE,
183         PROP_HTTP_ALIASES,
184         PROP_HTTPS_ALIASES,
185
186         LAST_PROP
187 };
188
189 static void set_property (GObject *object, guint prop_id,
190                           const GValue *value, GParamSpec *pspec);
191 static void get_property (GObject *object, guint prop_id,
192                           GValue *value, GParamSpec *pspec);
193
194 static void
195 soup_session_init (SoupSession *session)
196 {
197         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
198         SoupAuthManager *auth_manager;
199
200         priv->queue = soup_message_queue_new (session);
201
202         g_mutex_init (&priv->host_lock);
203         priv->http_hosts = g_hash_table_new_full (soup_host_uri_hash,
204                                                   soup_host_uri_equal,
205                                                   NULL, (GDestroyNotify)free_host);
206         priv->https_hosts = g_hash_table_new_full (soup_host_uri_hash,
207                                                    soup_host_uri_equal,
208                                                    NULL, (GDestroyNotify)free_host);
209         priv->conns = g_hash_table_new (NULL, NULL);
210
211         priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
212         priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
213
214         priv->features_cache = g_hash_table_new (NULL, NULL);
215
216         auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER_NTLM, NULL);
217         g_signal_connect (auth_manager, "authenticate",
218                           G_CALLBACK (auth_manager_authenticate), session);
219         soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
220                                           SOUP_TYPE_AUTH_BASIC);
221         soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
222                                           SOUP_TYPE_AUTH_DIGEST);
223         soup_session_add_feature (session, SOUP_SESSION_FEATURE (auth_manager));
224         g_object_unref (auth_manager);
225
226         /* We'll be doing DNS continuously-ish while the session is active,
227          * so hold a ref on the default GResolver.
228          */
229         priv->resolver = g_resolver_get_default ();
230
231         priv->ssl_strict = TRUE;
232
233         priv->http_aliases = g_new (char *, 2);
234         priv->http_aliases[0] = (char *)g_intern_string ("*");
235         priv->http_aliases[1] = NULL;
236 }
237
238 static void
239 dispose (GObject *object)
240 {
241         SoupSession *session = SOUP_SESSION (object);
242         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
243
244         soup_session_abort (session);
245
246         while (priv->features)
247                 soup_session_remove_feature (session, priv->features->data);
248
249         G_OBJECT_CLASS (soup_session_parent_class)->dispose (object);
250 }
251
252 static void
253 finalize (GObject *object)
254 {
255         SoupSession *session = SOUP_SESSION (object);
256         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
257
258         soup_message_queue_destroy (priv->queue);
259
260         g_mutex_clear (&priv->host_lock);
261         g_hash_table_destroy (priv->http_hosts);
262         g_hash_table_destroy (priv->https_hosts);
263         g_hash_table_destroy (priv->conns);
264
265         g_free (priv->user_agent);
266         g_free (priv->accept_language);
267
268         if (priv->tlsdb)
269                 g_object_unref (priv->tlsdb);
270         g_free (priv->ssl_ca_file);
271
272         if (priv->async_context)
273                 g_main_context_unref (priv->async_context);
274
275         g_hash_table_destroy (priv->features_cache);
276
277         g_object_unref (priv->resolver);
278
279         g_free (priv->http_aliases);
280         g_free (priv->https_aliases);
281
282         G_OBJECT_CLASS (soup_session_parent_class)->finalize (object);
283 }
284
285 static void
286 soup_session_class_init (SoupSessionClass *session_class)
287 {
288         GObjectClass *object_class = G_OBJECT_CLASS (session_class);
289
290         g_type_class_add_private (session_class, sizeof (SoupSessionPrivate));
291
292         /* virtual method definition */
293         session_class->queue_message = queue_message;
294         session_class->requeue_message = requeue_message;
295         session_class->cancel_message = cancel_message;
296         session_class->auth_required = auth_required;
297         session_class->flush_queue = flush_queue;
298
299         /* virtual method override */
300         object_class->dispose = dispose;
301         object_class->finalize = finalize;
302         object_class->set_property = set_property;
303         object_class->get_property = get_property;
304
305         /* signals */
306
307         /**
308          * SoupSession::request-queued:
309          * @session: the session
310          * @msg: the request that was queued
311          *
312          * Emitted when a request is queued on @session. (Note that
313          * "queued" doesn't just mean soup_session_queue_message();
314          * soup_session_send_message() implicitly queues the message
315          * as well.)
316          *
317          * When sending a request, first #SoupSession::request_queued
318          * is emitted, indicating that the session has become aware of
319          * the request.
320          *
321          * Once a connection is available to send the request on, the
322          * session emits #SoupSession::request_started. Then, various
323          * #SoupMessage signals are emitted as the message is
324          * processed. If the message is requeued, it will emit
325          * #SoupMessage::restarted, which will then be followed by
326          * another #SoupSession::request_started and another set of
327          * #SoupMessage signals when the message is re-sent.
328          *
329          * Eventually, the message will emit #SoupMessage::finished.
330          * Normally, this signals the completion of message
331          * processing. However, it is possible that the application
332          * will requeue the message from the "finished" handler (or
333          * equivalently, from the soup_session_queue_message()
334          * callback). In that case, the process will loop back to
335          * #SoupSession::request_started.
336          *
337          * Eventually, a message will reach "finished" and not be
338          * requeued. At that point, the session will emit
339          * #SoupSession::request_unqueued to indicate that it is done
340          * with the message.
341          *
342          * To sum up: #SoupSession::request_queued and
343          * #SoupSession::request_unqueued are guaranteed to be emitted
344          * exactly once, but #SoupSession::request_started and
345          * #SoupMessage::finished (and all of the other #SoupMessage
346          * signals) may be invoked multiple times for a given message.
347          *
348          * Since: 2.4.1
349          **/
350         signals[REQUEST_QUEUED] =
351                 g_signal_new ("request-queued",
352                               G_OBJECT_CLASS_TYPE (object_class),
353                               G_SIGNAL_RUN_FIRST,
354                               0, /* FIXME? */
355                               NULL, NULL,
356                               _soup_marshal_NONE__OBJECT,
357                               G_TYPE_NONE, 1,
358                               SOUP_TYPE_MESSAGE);
359
360         /**
361          * SoupSession::request-started:
362          * @session: the session
363          * @msg: the request being sent
364          * @socket: the socket the request is being sent on
365          *
366          * Emitted just before a request is sent. See
367          * #SoupSession::request_queued for a detailed description of
368          * the message lifecycle within a session.
369          **/
370         signals[REQUEST_STARTED] =
371                 g_signal_new ("request-started",
372                               G_OBJECT_CLASS_TYPE (object_class),
373                               G_SIGNAL_RUN_FIRST,
374                               G_STRUCT_OFFSET (SoupSessionClass, request_started),
375                               NULL, NULL,
376                               _soup_marshal_NONE__OBJECT_OBJECT,
377                               G_TYPE_NONE, 2,
378                               SOUP_TYPE_MESSAGE,
379                               SOUP_TYPE_SOCKET);
380
381         /**
382          * SoupSession::request-unqueued:
383          * @session: the session
384          * @msg: the request that was unqueued
385          *
386          * Emitted when a request is removed from @session's queue,
387          * indicating that @session is done with it. See
388          * #SoupSession::request_queued for a detailed description of the
389          * message lifecycle within a session.
390          *
391          * Since: 2.4.1
392          **/
393         signals[REQUEST_UNQUEUED] =
394                 g_signal_new ("request-unqueued",
395                               G_OBJECT_CLASS_TYPE (object_class),
396                               G_SIGNAL_RUN_FIRST,
397                               0, /* FIXME? */
398                               NULL, NULL,
399                               _soup_marshal_NONE__OBJECT,
400                               G_TYPE_NONE, 1,
401                               SOUP_TYPE_MESSAGE);
402
403         /**
404          * SoupSession::authenticate:
405          * @session: the session
406          * @msg: the #SoupMessage being sent
407          * @auth: the #SoupAuth to authenticate
408          * @retrying: %TRUE if this is the second (or later) attempt
409          *
410          * Emitted when the session requires authentication. If
411          * credentials are available call soup_auth_authenticate() on
412          * @auth. If these credentials fail, the signal will be
413          * emitted again, with @retrying set to %TRUE, which will
414          * continue until you return without calling
415          * soup_auth_authenticate() on @auth.
416          *
417          * Note that this may be emitted before @msg's body has been
418          * fully read.
419          *
420          * If you call soup_session_pause_message() on @msg before
421          * returning, then you can authenticate @auth asynchronously
422          * (as long as you g_object_ref() it to make sure it doesn't
423          * get destroyed), and then unpause @msg when you are ready
424          * for it to continue.
425          **/
426         signals[AUTHENTICATE] =
427                 g_signal_new ("authenticate",
428                               G_OBJECT_CLASS_TYPE (object_class),
429                               G_SIGNAL_RUN_FIRST,
430                               G_STRUCT_OFFSET (SoupSessionClass, authenticate),
431                               NULL, NULL,
432                               _soup_marshal_NONE__OBJECT_OBJECT_BOOLEAN,
433                               G_TYPE_NONE, 3,
434                               SOUP_TYPE_MESSAGE,
435                               SOUP_TYPE_AUTH,
436                               G_TYPE_BOOLEAN);
437
438         /**
439          * SoupSession::connection-created:
440          * @session: the #SoupSession
441          * @connection: the connection
442          *
443          * Emitted when a new connection is created. This is an
444          * internal signal intended only to be used for debugging
445          * purposes, and may go away in the future.
446          *
447          * Since: 2.30
448          */
449         signals[CONNECTION_CREATED] =
450                 g_signal_new ("connection-created",
451                               G_OBJECT_CLASS_TYPE (object_class),
452                               G_SIGNAL_RUN_FIRST,
453                               0,
454                               NULL, NULL,
455                               _soup_marshal_NONE__OBJECT,
456                               G_TYPE_NONE, 1,
457                               /* SoupConnection is private, so we can't use
458                                * SOUP_TYPE_CONNECTION here.
459                                */
460                               G_TYPE_OBJECT);
461
462         /**
463          * SoupSession::tunneling:
464          * @session: the #SoupSession
465          * @connection: the connection
466          *
467          * Emitted when an SSL tunnel is being created on a proxy
468          * connection. This is an internal signal intended only to be
469          * used for debugging purposes, and may go away in the future.
470          *
471          * Since: 2.30
472          */
473         signals[TUNNELING] =
474                 g_signal_new ("tunneling",
475                               G_OBJECT_CLASS_TYPE (object_class),
476                               G_SIGNAL_RUN_FIRST,
477                               0,
478                               NULL, NULL,
479                               _soup_marshal_NONE__OBJECT,
480                               G_TYPE_NONE, 1,
481                               /* SoupConnection is private, so we can't use
482                                * SOUP_TYPE_CONNECTION here.
483                                */
484                               G_TYPE_OBJECT);
485
486
487         /* properties */
488         /**
489          * SOUP_SESSION_PROXY_URI:
490          *
491          * Alias for the #SoupSession:proxy-uri property. (The HTTP
492          * proxy to use for this session.)
493          **/
494         g_object_class_install_property (
495                 object_class, PROP_PROXY_URI,
496                 g_param_spec_boxed (SOUP_SESSION_PROXY_URI,
497                                     "Proxy URI",
498                                     "The HTTP Proxy to use for this session",
499                                     SOUP_TYPE_URI,
500                                     G_PARAM_READWRITE));
501         /**
502          * SOUP_SESSION_MAX_CONNS:
503          *
504          * Alias for the #SoupSession:max-conns property. (The maximum
505          * number of connections that the session can open at once.)
506          **/
507         g_object_class_install_property (
508                 object_class, PROP_MAX_CONNS,
509                 g_param_spec_int (SOUP_SESSION_MAX_CONNS,
510                                   "Max Connection Count",
511                                   "The maximum number of connections that the session can open at once",
512                                   1,
513                                   G_MAXINT,
514                                   SOUP_SESSION_MAX_CONNS_DEFAULT,
515                                   G_PARAM_READWRITE));
516         /**
517          * SOUP_SESSION_MAX_CONNS_PER_HOST:
518          *
519          * Alias for the #SoupSession:max-conns-per-host property.
520          * (The maximum number of connections that the session can
521          * open at once to a given host.)
522          **/
523         g_object_class_install_property (
524                 object_class, PROP_MAX_CONNS_PER_HOST,
525                 g_param_spec_int (SOUP_SESSION_MAX_CONNS_PER_HOST,
526                                   "Max Per-Host Connection Count",
527                                   "The maximum number of connections that the session can open at once to a given host",
528                                   1,
529                                   G_MAXINT,
530                                   SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT,
531                                   G_PARAM_READWRITE));
532         /**
533          * SoupSession:idle-timeout:
534          *
535          * Connection lifetime when idle
536          *
537          * Since: 2.4.1
538          **/
539         /**
540          * SOUP_SESSION_IDLE_TIMEOUT:
541          *
542          * Alias for the #SoupSession:idle-timeout property. (The idle
543          * connection lifetime.)
544          *
545          * Since: 2.4.1
546          **/
547         g_object_class_install_property (
548                 object_class, PROP_IDLE_TIMEOUT,
549                 g_param_spec_uint (SOUP_SESSION_IDLE_TIMEOUT,
550                                    "Idle Timeout",
551                                    "Connection lifetime when idle",
552                                    0, G_MAXUINT, 0,
553                                    G_PARAM_READWRITE));
554         /**
555          * SoupSession:use-ntlm:
556          *
557          * Whether or not to use NTLM authentication.
558          *
559          * Deprecated: use soup_session_add_feature_by_type() with
560          * #SOUP_TYPE_AUTH_NTLM.
561          **/
562         /**
563          * SOUP_SESSION_USE_NTLM:
564          *
565          * Alias for the #SoupSession:use-ntlm property. (Whether or
566          * not to use NTLM authentication.)
567          **/
568         g_object_class_install_property (
569                 object_class, PROP_USE_NTLM,
570                 g_param_spec_boolean (SOUP_SESSION_USE_NTLM,
571                                       "Use NTLM",
572                                       "Whether or not to use NTLM authentication",
573                                       FALSE,
574                                       G_PARAM_READWRITE));
575         /**
576          * SoupSession:ssl-ca-file:
577          *
578          * File containing SSL CA certificates.
579          *
580          * Deprecated: use #SoupSession:ssl-use-system-ca-file or
581          * #SoupSession:tls-database instead
582          **/
583         /**
584          * SOUP_SESSION_SSL_CA_FILE:
585          *
586          * Alias for the #SoupSession:ssl-ca-file property. (File
587          * containing SSL CA certificates.).
588          *
589          * Deprecated: use #SoupSession:ssl-use-system-ca-file or
590          * #SoupSession:tls-database instead
591          **/
592         g_object_class_install_property (
593                 object_class, PROP_SSL_CA_FILE,
594                 g_param_spec_string (SOUP_SESSION_SSL_CA_FILE,
595                                      "SSL CA file",
596                                      "File containing SSL CA certificates",
597                                      NULL,
598                                      G_PARAM_READWRITE));
599         /**
600          * SOUP_SESSION_USE_SYSTEM_CA_FILE:
601          *
602          * Alias for the #SoupSession:ssl-use-system-ca-file property,
603          * qv.
604          *
605          * Since: 2.38
606          **/
607         /**
608          * SoupSession:ssl-use-system-ca-file:
609          *
610          * Setting this to %TRUE is equivalent to setting
611          * #SoupSession:tls-database to the default system CA database.
612          * (and likewise, setting #SoupSession:tls-database to the
613          * default database by hand will cause this property to
614          * become %TRUE).
615          *
616          * Setting this to %FALSE (when it was previously %TRUE) will
617          * clear the #SoupSession:tls-database field.
618          *
619          * See #SoupSession:ssl-strict for more information on how
620          * https certificate validation is handled.
621          *
622          * Since: 2.38
623          **/
624         g_object_class_install_property (
625                 object_class, PROP_SSL_USE_SYSTEM_CA_FILE,
626                 g_param_spec_boolean (SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE,
627                                       "Use system CA file",
628                                       "Use the system certificate database",
629                                       FALSE,
630                                       G_PARAM_READWRITE));
631         /**
632          * SOUP_SESSION_TLS_DATABASE:
633          *
634          * Alias for the #SoupSession:tls-database property, qv.
635          *
636          * Since: 2.38
637          **/
638         /**
639          * SoupSession:tls-database:
640          *
641          * Sets the #GTlsDatabase to use for validating SSL/TLS
642          * certificates.
643          *
644          * Note that setting the #SoupSession:ssl-ca-file or
645          * #SoupSession:ssl-use-system-ca-file property will cause
646          * this property to be set to a #GTlsDatabase corresponding to
647          * the indicated file or system default.
648          *
649          * See #SoupSession:ssl-strict for more information on how
650          * https certificate validation is handled.
651          *
652          * Since: 2.38
653          **/
654         g_object_class_install_property (
655                 object_class, PROP_TLS_DATABASE,
656                 g_param_spec_object (SOUP_SESSION_TLS_DATABASE,
657                                      "TLS Database",
658                                      "TLS database to use",
659                                      G_TYPE_TLS_DATABASE,
660                                      G_PARAM_READWRITE));
661         /**
662          * SOUP_SESSION_SSL_STRICT:
663          *
664          * Alias for the #SoupSession:ssl-strict property, qv.
665          *
666          * Since: 2.30
667          **/
668         /**
669          * SoupSession:ssl-strict:
670          *
671          * Normally, if #SoupSession:tlsdb is set (including if it was
672          * set via #SoupSession:ssl-use-system-ca-file or
673          * #SoupSession:ssl-ca-file), then libsoup will reject any
674          * certificate that is invalid (ie, expired) or that is not
675          * signed by one of the given CA certificates, and the
676          * #SoupMessage will fail with the status
677          * %SOUP_STATUS_SSL_FAILED.
678          *
679          * If you set #SoupSession:ssl-strict to %FALSE, then all
680          * certificates will be accepted, and you will need to call
681          * soup_message_get_https_status() to distinguish valid from
682          * invalid certificates. (This can be used, eg, if you want to
683          * accept invalid certificates after giving some sort of
684          * warning.)
685          *
686          * If the session has no CA file or TLS database, then all
687          * certificates are always accepted, and this property has no
688          * effect.
689          *
690          * Since: 2.30
691          */
692         g_object_class_install_property (
693                 object_class, PROP_SSL_STRICT,
694                 g_param_spec_boolean (SOUP_SESSION_SSL_STRICT,
695                                       "Strictly validate SSL certificates",
696                                       "Whether certificate errors should be considered a connection error",
697                                       TRUE,
698                                       G_PARAM_READWRITE));
699         /**
700          * SOUP_SESSION_ASYNC_CONTEXT:
701          *
702          * Alias for the #SoupSession:async-context property. (The
703          * session's #GMainContext.)
704          */
705         g_object_class_install_property (
706                 object_class, PROP_ASYNC_CONTEXT,
707                 g_param_spec_pointer (SOUP_SESSION_ASYNC_CONTEXT,
708                                       "Async GMainContext",
709                                       "The GMainContext to dispatch async I/O in",
710                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
711         /**
712          * SOUP_SESSION_USE_THREAD_CONTEXT:
713          *
714          * Alias for the #SoupSession:use-thread-context property, qv.
715          *
716          * Since: 2.38
717          */
718         /**
719          * SoupSession:use-thread-context:
720          *
721          * If set, asynchronous operations in this session will run in
722          * whatever the thread-default #GMainContext is at the time
723          * they are started, rather than always occurring in a context
724          * fixed at the session's construction time. "Bookkeeping"
725          * tasks (like expiring idle connections) will happen in the
726          * context that was thread-default at the time the session was
727          * created.
728          *
729          * Since: 2.38
730          */
731         g_object_class_install_property (
732                 object_class, PROP_USE_THREAD_CONTEXT,
733                 g_param_spec_boolean (SOUP_SESSION_USE_THREAD_CONTEXT,
734                                       "Use thread-default GMainContext",
735                                       "Whether to use thread-default main contexts",
736                                       FALSE,
737                                       G_PARAM_READWRITE));
738         /**
739          * SOUP_SESSION_TIMEOUT:
740          *
741          * Alias for the #SoupSession:timeout property. (The timeout
742          * in seconds for blocking socket I/O operations.)
743          **/
744         g_object_class_install_property (
745                 object_class, PROP_TIMEOUT,
746                 g_param_spec_uint (SOUP_SESSION_TIMEOUT,
747                                    "Timeout value",
748                                    "Value in seconds to timeout a blocking I/O",
749                                    0, G_MAXUINT, 0,
750                                    G_PARAM_READWRITE));
751
752         /**
753          * SoupSession:user-agent:
754          *
755          * If non-%NULL, the value to use for the "User-Agent" header
756          * on #SoupMessage<!-- -->s sent from this session.
757          *
758          * RFC 2616 says: "The User-Agent request-header field
759          * contains information about the user agent originating the
760          * request. This is for statistical purposes, the tracing of
761          * protocol violations, and automated recognition of user
762          * agents for the sake of tailoring responses to avoid
763          * particular user agent limitations. User agents SHOULD
764          * include this field with requests."
765          *
766          * The User-Agent header contains a list of one or more
767          * product tokens, separated by whitespace, with the most
768          * significant product token coming first. The tokens must be
769          * brief, ASCII, and mostly alphanumeric (although "-", "_",
770          * and "." are also allowed), and may optionally include a "/"
771          * followed by a version string. You may also put comments,
772          * enclosed in parentheses, between or after the tokens.
773          *
774          * If you set a #SoupSession:user_agent property that has trailing
775          * whitespace, #SoupSession will append its own product token
776          * (eg, "<literal>libsoup/2.3.2</literal>") to the end of the
777          * header for you.
778          **/
779         /**
780          * SOUP_SESSION_USER_AGENT:
781          *
782          * Alias for the #SoupSession:user-agent property, qv.
783          **/
784         g_object_class_install_property (
785                 object_class, PROP_USER_AGENT,
786                 g_param_spec_string (SOUP_SESSION_USER_AGENT,
787                                      "User-Agent string",
788                                      "User-Agent string",
789                                      NULL,
790                                      G_PARAM_READWRITE));
791
792         /**
793          * SoupSession:accept-language:
794          *
795          * If non-%NULL, the value to use for the "Accept-Language" header
796          * on #SoupMessage<!-- -->s sent from this session.
797          *
798          * Setting this will disable
799          * #SoupSession:accept-language-auto.
800          *
801          * Since: 2.30
802          **/
803         /**
804          * SOUP_SESSION_ACCEPT_LANGUAGE:
805          *
806          * Alias for the #SoupSession:accept-language property, qv.
807          *
808          * Since: 2.30
809          **/
810         g_object_class_install_property (
811                 object_class, PROP_ACCEPT_LANGUAGE,
812                 g_param_spec_string (SOUP_SESSION_ACCEPT_LANGUAGE,
813                                      "Accept-Language string",
814                                      "Accept-Language string",
815                                      NULL,
816                                      G_PARAM_READWRITE));
817
818         /**
819          * SoupSession:accept-language-auto:
820          *
821          * If %TRUE, #SoupSession will automatically set the string
822          * for the "Accept-Language" header on every #SoupMessage
823          * sent, based on the return value of g_get_language_names().
824          *
825          * Setting this will override any previous value of
826          * #SoupSession:accept-language.
827          *
828          * Since: 2.30
829          **/
830         /**
831          * SOUP_SESSION_ACCEPT_LANGUAGE_AUTO:
832          *
833          * Alias for the #SoupSession:accept-language-auto property, qv.
834          *
835          * Since: 2.30
836          **/
837         g_object_class_install_property (
838                 object_class, PROP_ACCEPT_LANGUAGE_AUTO,
839                 g_param_spec_boolean (SOUP_SESSION_ACCEPT_LANGUAGE_AUTO,
840                                       "Accept-Language automatic mode",
841                                       "Accept-Language automatic mode",
842                                       FALSE,
843                                       G_PARAM_READWRITE));
844
845         /**
846          * SoupSession:add-feature: (skip)
847          *
848          * Add a feature object to the session. (Shortcut for calling
849          * soup_session_add_feature().)
850          *
851          * Since: 2.24
852          **/
853         /**
854          * SOUP_SESSION_ADD_FEATURE: (skip)
855          *
856          * Alias for the #SoupSession:add-feature property. (Shortcut
857          * for calling soup_session_add_feature().
858          *
859          * Since: 2.24
860          **/
861         g_object_class_install_property (
862                 object_class, PROP_ADD_FEATURE,
863                 g_param_spec_object (SOUP_SESSION_ADD_FEATURE,
864                                      "Add Feature",
865                                      "Add a feature object to the session",
866                                      SOUP_TYPE_SESSION_FEATURE,
867                                      G_PARAM_READWRITE));
868         /**
869          * SoupSession:add-feature-by-type: (skip)
870          *
871          * Add a feature object of the given type to the session.
872          * (Shortcut for calling soup_session_add_feature_by_type().)
873          *
874          * Since: 2.24
875          **/
876         /**
877          * SOUP_SESSION_ADD_FEATURE_BY_TYPE: (skip)
878          *
879          * Alias for the #SoupSession:add-feature-by-type property.
880          * (Shortcut for calling soup_session_add_feature_by_type().
881          *
882          * Since: 2.24
883          **/
884         g_object_class_install_property (
885                 object_class, PROP_ADD_FEATURE_BY_TYPE,
886                 g_param_spec_gtype (SOUP_SESSION_ADD_FEATURE_BY_TYPE,
887                                     "Add Feature By Type",
888                                     "Add a feature object of the given type to the session",
889                                     SOUP_TYPE_SESSION_FEATURE,
890                                     G_PARAM_READWRITE));
891         /**
892          * SoupSession:remove-feature-by-type: (skip)
893          *
894          * Remove feature objects from the session. (Shortcut for
895          * calling soup_session_remove_feature_by_type().)
896          *
897          * Since: 2.24
898          **/
899         /**
900          * SOUP_SESSION_REMOVE_FEATURE_BY_TYPE: (skip)
901          *
902          * Alias for the #SoupSession:remove-feature-by-type
903          * property. (Shortcut for calling
904          * soup_session_remove_feature_by_type().
905          *
906          * Since: 2.24
907          **/
908         g_object_class_install_property (
909                 object_class, PROP_REMOVE_FEATURE_BY_TYPE,
910                 g_param_spec_gtype (SOUP_SESSION_REMOVE_FEATURE_BY_TYPE,
911                                     "Remove Feature By Type",
912                                     "Remove features of the given type from the session",
913                                     SOUP_TYPE_SESSION_FEATURE,
914                                     G_PARAM_READWRITE));
915         /**
916          * SoupSession:http-aliases:
917          *
918          * A %NULL-terminated array of URI schemes that should be
919          * considered to be aliases for "http". Eg, if this included
920          * <literal>"dav"</literal>, than a URI of
921          * <literal>dav://example.com/path</literal> would be treated
922          * identically to <literal>http://example.com/path</literal>.
923          * If the value is %NULL, then only "http" is recognized as
924          * meaning "http".
925          *
926          * For backward-compatibility reasons, the default value for
927          * this property is an array containing the single element
928          * <literal>"*"</literal>, a special value which means that
929          * any scheme except "https" is considered to be an alias for
930          * "http".
931          *
932          * See also #SoupSession:https-aliases.
933          *
934          * Since: 2.38
935          */
936         /**
937          * SOUP_SESSION_HTTP_ALIASES:
938          *
939          * Alias for the #SoupSession:http-aliases property. (URI
940          * schemes that will be considered aliases for "http".)
941          *
942          * Since: 2.38
943          */
944         g_object_class_install_property (
945                 object_class, PROP_HTTP_ALIASES,
946                 g_param_spec_boxed (SOUP_SESSION_HTTP_ALIASES,
947                                     "http aliases",
948                                     "URI schemes that are considered aliases for 'http'",
949                                     G_TYPE_STRV,
950                                     G_PARAM_READWRITE));
951         /**
952          * SoupSession:https-aliases:
953          *
954          * A comma-delimited list of URI schemes that should be
955          * considered to be aliases for "https". See
956          * #SoupSession:http-aliases for more information.
957          *
958          * The default value is %NULL, meaning that no URI schemes
959          * are considered aliases for "https".
960          *
961          * Since: 2.38
962          */
963         /**
964          * SOUP_SESSION_HTTPS_ALIASES:
965          *
966          * Alias for the #SoupSession:https-aliases property. (URI
967          * schemes that will be considered aliases for "https".)
968          *
969          * Since: 2.38
970          **/
971         g_object_class_install_property (
972                 object_class, PROP_HTTPS_ALIASES,
973                 g_param_spec_boxed (SOUP_SESSION_HTTPS_ALIASES,
974                                     "https aliases",
975                                     "URI schemes that are considered aliases for 'https'",
976                                     G_TYPE_STRV,
977                                     G_PARAM_READWRITE));
978 }
979
980 /* Converts a language in POSIX format and to be RFC2616 compliant    */
981 /* Based on code from epiphany-webkit (ephy_langs_append_languages()) */
982 static gchar *
983 posix_lang_to_rfc2616 (const gchar *language)
984 {
985         /* Don't include charset variants, etc */
986         if (strchr (language, '.') || strchr (language, '@'))
987                 return NULL;
988
989         /* Ignore "C" locale, which g_get_language_names() always
990          * includes as a fallback.
991          */
992         if (!strcmp (language, "C"))
993                 return NULL;
994
995         return g_strdelimit (g_ascii_strdown (language, -1), "_", '-');
996 }
997
998 /* Converts @quality from 0-100 to 0.0-1.0 and appends to @str */
999 static gchar *
1000 add_quality_value (const gchar *str, int quality)
1001 {
1002         g_return_val_if_fail (str != NULL, NULL);
1003
1004         if (quality >= 0 && quality < 100) {
1005                 /* We don't use %.02g because of "." vs "," locale issues */
1006                 if (quality % 10)
1007                         return g_strdup_printf ("%s;q=0.%02d", str, quality);
1008                 else
1009                         return g_strdup_printf ("%s;q=0.%d", str, quality / 10);
1010         } else
1011                 return g_strdup (str);
1012 }
1013
1014 /* Returns a RFC2616 compliant languages list from system locales */
1015 static gchar *
1016 accept_languages_from_system (void)
1017 {
1018         const char * const * lang_names;
1019         GPtrArray *langs = NULL;
1020         char *lang, **langs_array, *langs_str;
1021         int delta;
1022         int i;
1023
1024         lang_names = g_get_language_names ();
1025         g_return_val_if_fail (lang_names != NULL, NULL);
1026
1027         /* Build the array of languages */
1028         langs = g_ptr_array_new ();
1029         for (i = 0; lang_names[i] != NULL; i++) {
1030                 lang = posix_lang_to_rfc2616 (lang_names[i]);
1031                 if (lang)
1032                         g_ptr_array_add (langs, lang);
1033         }
1034
1035         /* Add quality values */
1036         if (langs->len < 10)
1037                 delta = 10;
1038         else if (langs->len < 20)
1039                 delta = 5;
1040         else
1041                 delta = 1;
1042
1043         for (i = 0; i < langs->len; i++) {
1044                 lang = langs->pdata[i];
1045                 langs->pdata[i] = add_quality_value (lang, 100 - i * delta);
1046                 g_free (lang);
1047         }
1048
1049         /* Fallback: add "en" if list is empty */
1050         if (langs->len == 0)
1051                 g_ptr_array_add (langs, g_strdup ("en"));
1052
1053         g_ptr_array_add (langs, NULL);
1054         langs_array = (char **)langs->pdata;
1055         langs_str = g_strjoinv (", ", langs_array);
1056
1057         g_strfreev (langs_array);
1058         g_ptr_array_free (langs, FALSE);
1059
1060         return langs_str;
1061 }
1062
1063 static void
1064 set_tlsdb (SoupSession *session, GTlsDatabase *tlsdb)
1065 {
1066         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1067         GTlsDatabase *system_default;
1068
1069         if (tlsdb == priv->tlsdb)
1070                 return;
1071
1072         g_object_freeze_notify (G_OBJECT (session));
1073
1074         system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
1075         if (priv->tlsdb == system_default || tlsdb == system_default) {
1076                 g_object_notify (G_OBJECT (session), "ssl-use-system-ca-file");
1077         }
1078         g_object_unref (system_default);
1079
1080         if (priv->ssl_ca_file) {
1081                 g_free (priv->ssl_ca_file);
1082                 priv->ssl_ca_file = NULL;
1083                 g_object_notify (G_OBJECT (session), "ssl-ca-file");
1084         }
1085
1086         if (priv->tlsdb)
1087                 g_object_unref (priv->tlsdb);
1088         priv->tlsdb = tlsdb;
1089         if (priv->tlsdb)
1090                 g_object_ref (priv->tlsdb);
1091
1092         g_object_notify (G_OBJECT (session), "tls-database");
1093         g_object_thaw_notify (G_OBJECT (session));
1094 }
1095
1096 static void
1097 set_use_system_ca_file (SoupSession *session, gboolean use_system_ca_file)
1098 {
1099         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1100         GTlsDatabase *system_default;
1101
1102         system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
1103
1104         if (use_system_ca_file)
1105                 set_tlsdb (session, system_default);
1106         else if (priv->tlsdb == system_default)
1107                 set_tlsdb (session, NULL);
1108
1109         g_object_unref (system_default);
1110 }
1111
1112 static void
1113 set_ssl_ca_file (SoupSession *session, const char *ssl_ca_file)
1114 {
1115         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1116         GTlsDatabase *tlsdb;
1117         GError *error = NULL;
1118
1119         if (!g_strcmp0 (priv->ssl_ca_file, ssl_ca_file))
1120                 return;
1121
1122         g_object_freeze_notify (G_OBJECT (session));
1123
1124         if (g_path_is_absolute (ssl_ca_file))
1125                 tlsdb = g_tls_file_database_new (ssl_ca_file, &error);
1126         else {
1127                 char *path, *cwd;
1128
1129                 cwd = g_get_current_dir ();
1130                 path = g_build_filename (cwd, ssl_ca_file, NULL);
1131                 tlsdb = g_tls_file_database_new (path, &error);
1132                 g_free (path);
1133         }
1134
1135         if (error) {
1136                 if (!g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_UNAVAILABLE)) {
1137                         g_warning ("Could not set SSL credentials from '%s': %s",
1138                                    ssl_ca_file, error->message);
1139
1140                         tlsdb = g_tls_file_database_new ("/dev/null", NULL);
1141                 }
1142                 g_error_free (error);
1143         }
1144
1145         set_tlsdb (session, tlsdb);
1146         g_object_unref (tlsdb);
1147
1148         priv->ssl_ca_file = g_strdup (ssl_ca_file);
1149         g_object_notify (G_OBJECT (session), "ssl-ca-file");
1150
1151         g_object_thaw_notify (G_OBJECT (session));
1152 }
1153
1154 /* priv->http_aliases and priv->https_aliases are stored as arrays of
1155  * *interned* strings, so we can't just use g_strdupv() to set them.
1156  */
1157 static void
1158 set_aliases (char ***variable, char **value)
1159 {
1160         int len, i;
1161
1162         if (*variable)
1163                 g_free (*variable);
1164
1165         if (!value) {
1166                 *variable = NULL;
1167                 return;
1168         }
1169
1170         len = g_strv_length (value);
1171         *variable = g_new (char *, len + 1);
1172         for (i = 0; i < len; i++)
1173                 (*variable)[i] = (char *)g_intern_string (value[i]);
1174         (*variable)[i] = NULL;
1175 }
1176
1177 static void
1178 set_property (GObject *object, guint prop_id,
1179               const GValue *value, GParamSpec *pspec)
1180 {
1181         SoupSession *session = SOUP_SESSION (object);
1182         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1183         SoupURI *uri;
1184         const char *user_agent;
1185         SoupSessionFeature *feature;
1186
1187         switch (prop_id) {
1188         case PROP_PROXY_URI:
1189                 uri = g_value_get_boxed (value);
1190
1191                 if (uri) {
1192                         soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER);
1193                         feature = SOUP_SESSION_FEATURE (soup_proxy_resolver_static_new (uri));
1194                         soup_session_add_feature (session, feature);
1195                         g_object_unref (feature);
1196                 } else
1197                         soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
1198
1199                 soup_session_abort (session);
1200                 break;
1201         case PROP_MAX_CONNS:
1202                 priv->max_conns = g_value_get_int (value);
1203                 break;
1204         case PROP_MAX_CONNS_PER_HOST:
1205                 priv->max_conns_per_host = g_value_get_int (value);
1206                 break;
1207         case PROP_USE_NTLM:
1208                 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
1209                 if (feature) {
1210                         if (g_value_get_boolean (value))
1211                                 soup_session_feature_add_feature (feature, SOUP_TYPE_AUTH_NTLM);
1212                         else
1213                                 soup_session_feature_remove_feature (feature, SOUP_TYPE_AUTH_NTLM);
1214                 } else
1215                         g_warning ("Trying to set use-ntlm on session with no auth-manager");
1216                 break;
1217         case PROP_SSL_CA_FILE:
1218                 set_ssl_ca_file (session, g_value_get_string (value));
1219                 break;
1220         case PROP_SSL_USE_SYSTEM_CA_FILE:
1221                 set_use_system_ca_file (session, g_value_get_boolean (value));
1222                 break;
1223         case PROP_TLS_DATABASE:
1224                 set_tlsdb (session, g_value_get_object (value));
1225                 break;
1226         case PROP_SSL_STRICT:
1227                 priv->ssl_strict = g_value_get_boolean (value);
1228                 break;
1229         case PROP_ASYNC_CONTEXT:
1230                 priv->async_context = g_value_get_pointer (value);
1231                 if (priv->async_context)
1232                         g_main_context_ref (priv->async_context);
1233                 break;
1234         case PROP_USE_THREAD_CONTEXT:
1235                 priv->use_thread_context = g_value_get_boolean (value);
1236                 if (priv->use_thread_context) {
1237                         if (priv->async_context)
1238                                 g_main_context_unref (priv->async_context);
1239                         priv->async_context = g_main_context_get_thread_default ();
1240                         if (priv->async_context)
1241                                 g_main_context_ref (priv->async_context);
1242                 }
1243                 break;
1244         case PROP_TIMEOUT:
1245                 priv->io_timeout = g_value_get_uint (value);
1246                 break;
1247         case PROP_USER_AGENT:
1248                 g_free (priv->user_agent);
1249                 user_agent = g_value_get_string (value);
1250                 if (!user_agent)
1251                         priv->user_agent = NULL;
1252                 else if (!*user_agent) {
1253                         priv->user_agent =
1254                                 g_strdup (SOUP_SESSION_USER_AGENT_BASE);
1255                 } else if (g_str_has_suffix (user_agent, " ")) {
1256                         priv->user_agent =
1257                                 g_strdup_printf ("%s%s", user_agent,
1258                                                  SOUP_SESSION_USER_AGENT_BASE);
1259                 } else
1260                         priv->user_agent = g_strdup (user_agent);
1261                 break;
1262         case PROP_ACCEPT_LANGUAGE:
1263                 g_free (priv->accept_language);
1264                 priv->accept_language = g_strdup (g_value_get_string (value));
1265                 priv->accept_language_auto = FALSE;
1266                 break;
1267         case PROP_ACCEPT_LANGUAGE_AUTO:
1268                 priv->accept_language_auto = g_value_get_boolean (value);
1269                 if (priv->accept_language) {
1270                         g_free (priv->accept_language);
1271                         priv->accept_language = NULL;
1272                 }
1273
1274                 /* Get languages from system if needed */
1275                 if (priv->accept_language_auto)
1276                         priv->accept_language = accept_languages_from_system ();
1277                 break;
1278         case PROP_IDLE_TIMEOUT:
1279                 priv->idle_timeout = g_value_get_uint (value);
1280                 break;
1281         case PROP_ADD_FEATURE:
1282                 soup_session_add_feature (session, g_value_get_object (value));
1283                 break;
1284         case PROP_ADD_FEATURE_BY_TYPE:
1285                 soup_session_add_feature_by_type (session, g_value_get_gtype (value));
1286                 break;
1287         case PROP_REMOVE_FEATURE_BY_TYPE:
1288                 soup_session_remove_feature_by_type (session, g_value_get_gtype (value));
1289                 break;
1290         case PROP_HTTP_ALIASES:
1291                 set_aliases (&priv->http_aliases, g_value_get_boxed (value));
1292                 break;
1293         case PROP_HTTPS_ALIASES:
1294                 set_aliases (&priv->https_aliases, g_value_get_boxed (value));
1295                 break;
1296         default:
1297                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1298                 break;
1299         }
1300 }
1301
1302 static void
1303 get_property (GObject *object, guint prop_id,
1304               GValue *value, GParamSpec *pspec)
1305 {
1306         SoupSession *session = SOUP_SESSION (object);
1307         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1308         SoupSessionFeature *feature;
1309         GTlsDatabase *tlsdb;
1310
1311         switch (prop_id) {
1312         case PROP_PROXY_URI:
1313                 feature = soup_session_get_feature (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
1314                 if (feature) {
1315                         g_object_get_property (G_OBJECT (feature),
1316                                                SOUP_PROXY_RESOLVER_STATIC_PROXY_URI,
1317                                                value);
1318                 } else
1319                         g_value_set_boxed (value, NULL);
1320                 break;
1321         case PROP_MAX_CONNS:
1322                 g_value_set_int (value, priv->max_conns);
1323                 break;
1324         case PROP_MAX_CONNS_PER_HOST:
1325                 g_value_set_int (value, priv->max_conns_per_host);
1326                 break;
1327         case PROP_USE_NTLM:
1328                 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
1329                 if (feature)
1330                         g_value_set_boolean (value, soup_session_feature_has_feature (feature, SOUP_TYPE_AUTH_NTLM));
1331                 else
1332                         g_value_set_boolean (value, FALSE);
1333                 break;
1334         case PROP_SSL_CA_FILE:
1335                 g_value_set_string (value, priv->ssl_ca_file);
1336                 break;
1337         case PROP_SSL_USE_SYSTEM_CA_FILE:
1338                 tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
1339                 g_value_set_boolean (value, priv->tlsdb == tlsdb);
1340                 g_object_unref (tlsdb);
1341                 break;
1342         case PROP_TLS_DATABASE:
1343                 g_value_set_object (value, priv->tlsdb);
1344                 break;
1345         case PROP_SSL_STRICT:
1346                 g_value_set_boolean (value, priv->ssl_strict);
1347                 break;
1348         case PROP_ASYNC_CONTEXT:
1349                 g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
1350                 break;
1351         case PROP_USE_THREAD_CONTEXT:
1352                 g_value_set_boolean (value, priv->use_thread_context);
1353                 break;
1354         case PROP_TIMEOUT:
1355                 g_value_set_uint (value, priv->io_timeout);
1356                 break;
1357         case PROP_USER_AGENT:
1358                 g_value_set_string (value, priv->user_agent);
1359                 break;
1360         case PROP_ACCEPT_LANGUAGE:
1361                 g_value_set_string (value, priv->accept_language);
1362                 break;
1363         case PROP_ACCEPT_LANGUAGE_AUTO:
1364                 g_value_set_boolean (value, priv->accept_language_auto);
1365                 break;
1366         case PROP_IDLE_TIMEOUT:
1367                 g_value_set_uint (value, priv->idle_timeout);
1368                 break;
1369         case PROP_HTTP_ALIASES:
1370                 g_value_set_boxed (value, priv->http_aliases);
1371                 break;
1372         case PROP_HTTPS_ALIASES:
1373                 g_value_set_boxed (value, priv->https_aliases);
1374                 break;
1375         default:
1376                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1377                 break;
1378         }
1379 }
1380
1381 static gboolean
1382 uri_is_http (SoupSessionPrivate *priv, SoupURI *uri)
1383 {
1384         int i;
1385
1386         if (uri->scheme == SOUP_URI_SCHEME_HTTP)
1387                 return TRUE;
1388         else if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
1389                 return FALSE;
1390         else if (!priv->http_aliases)
1391                 return FALSE;
1392
1393         for (i = 0; priv->http_aliases[i]; i++) {
1394                 if (uri->scheme == priv->http_aliases[i])
1395                         return TRUE;
1396         }
1397
1398         if (!priv->http_aliases[1] && !strcmp (priv->http_aliases[0], "*"))
1399                 return TRUE;
1400         else
1401                 return FALSE;
1402 }
1403
1404 static gboolean
1405 uri_is_https (SoupSessionPrivate *priv, SoupURI *uri)
1406 {
1407         int i;
1408
1409         if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
1410                 return TRUE;
1411         else if (uri->scheme == SOUP_URI_SCHEME_HTTP)
1412                 return FALSE;
1413         else if (!priv->https_aliases)
1414                 return FALSE;
1415
1416         for (i = 0; priv->https_aliases[i]; i++) {
1417                 if (uri->scheme == priv->https_aliases[i])
1418                         return TRUE;
1419         }
1420
1421         return FALSE;
1422 }
1423
1424 /**
1425  * soup_session_get_async_context:
1426  * @session: a #SoupSession
1427  *
1428  * Gets @session's async_context. This does not add a ref to the
1429  * context, so you will need to ref it yourself if you want it to
1430  * outlive its session.
1431  *
1432  * If #SoupSession:use-thread-context is true, this will return the
1433  * current thread-default main context.
1434  *
1435  * Return value: (transfer none): @session's #GMainContext, which may
1436  * be %NULL
1437  **/
1438 GMainContext *
1439 soup_session_get_async_context (SoupSession *session)
1440 {
1441         SoupSessionPrivate *priv;
1442
1443         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
1444         priv = SOUP_SESSION_GET_PRIVATE (session);
1445
1446         if (priv->use_thread_context)
1447                 return g_main_context_get_thread_default ();
1448         else
1449                 return priv->async_context;
1450 }
1451
1452 /* Hosts */
1453
1454 static guint
1455 soup_host_uri_hash (gconstpointer key)
1456 {
1457         const SoupURI *uri = key;
1458
1459         g_return_val_if_fail (uri != NULL && uri->host != NULL, 0);
1460
1461         return uri->port + soup_str_case_hash (uri->host);
1462 }
1463
1464 gboolean
1465 soup_host_uri_equal (gconstpointer v1, gconstpointer v2)
1466 {
1467         const SoupURI *one = v1;
1468         const SoupURI *two = v2;
1469
1470         g_return_val_if_fail (one != NULL && two != NULL, one == two);
1471         g_return_val_if_fail (one->host != NULL && two->host != NULL, one->host == two->host);
1472
1473         if (one->port != two->port)
1474                 return FALSE;
1475
1476         return g_ascii_strcasecmp (one->host, two->host) == 0;
1477 }
1478
1479
1480 static SoupSessionHost *
1481 soup_session_host_new (SoupSession *session, SoupURI *uri)
1482 {
1483         SoupSessionHost *host;
1484
1485         host = g_slice_new0 (SoupSessionHost);
1486         host->uri = soup_uri_copy_host (uri);
1487         if (host->uri->scheme != SOUP_URI_SCHEME_HTTP &&
1488             host->uri->scheme != SOUP_URI_SCHEME_HTTPS) {
1489                 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1490
1491                 if (uri_is_https (priv, host->uri))
1492                         host->uri->scheme = SOUP_URI_SCHEME_HTTPS;
1493                 else
1494                         host->uri->scheme = SOUP_URI_SCHEME_HTTP;
1495         }
1496
1497         host->addr = g_object_new (SOUP_TYPE_ADDRESS,
1498                                    SOUP_ADDRESS_NAME, host->uri->host,
1499                                    SOUP_ADDRESS_PORT, host->uri->port,
1500                                    SOUP_ADDRESS_PROTOCOL, host->uri->scheme,
1501                                    NULL);
1502         host->keep_alive_src = NULL;
1503         host->session = session;
1504
1505         return host;
1506 }
1507
1508 /* Requires host_lock to be locked */
1509 static SoupSessionHost *
1510 get_host_for_uri (SoupSession *session, SoupURI *uri)
1511 {
1512         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1513         SoupSessionHost *host;
1514
1515         if (uri_is_https (priv, uri))
1516                 host = g_hash_table_lookup (priv->https_hosts, uri);
1517         else
1518                 host = g_hash_table_lookup (priv->http_hosts, uri);
1519         if (host)
1520                 return host;
1521
1522         host = soup_session_host_new (session, uri);
1523
1524         if (uri_is_https (priv, uri))
1525                 g_hash_table_insert (priv->https_hosts, host->uri, host);
1526         else
1527                 g_hash_table_insert (priv->http_hosts, host->uri, host);
1528
1529         return host;
1530 }
1531
1532 /* Note: get_host_for_message doesn't lock the host_lock. The caller
1533  * must do it itself if there's a chance the host doesn't already
1534  * exist.
1535  */
1536 static SoupSessionHost *
1537 get_host_for_message (SoupSession *session, SoupMessage *msg)
1538 {
1539         return get_host_for_uri (session, soup_message_get_uri (msg));
1540 }
1541
1542 static void
1543 free_host (SoupSessionHost *host)
1544 {
1545         while (host->connections) {
1546                 SoupConnection *conn = host->connections->data;
1547
1548                 host->connections = g_slist_remove (host->connections, conn);
1549                 soup_connection_disconnect (conn);
1550         }
1551
1552         if (host->keep_alive_src) {
1553                 g_source_destroy (host->keep_alive_src);
1554                 g_source_unref (host->keep_alive_src);
1555         }
1556
1557         soup_uri_free (host->uri);
1558         g_object_unref (host->addr);
1559         g_slice_free (SoupSessionHost, host);
1560 }
1561
1562 static void
1563 auth_required (SoupSession *session, SoupMessage *msg,
1564                SoupAuth *auth, gboolean retrying)
1565 {
1566         g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
1567 }
1568
1569 static void
1570 auth_manager_authenticate (SoupAuthManager *manager, SoupMessage *msg,
1571                            SoupAuth *auth, gboolean retrying,
1572                            gpointer session)
1573 {
1574         SOUP_SESSION_GET_CLASS (session)->auth_required (
1575                 session, msg, auth, retrying);
1576 }
1577
1578 /* At some point it might be possible to mark additional methods
1579  * safe or idempotent...
1580  */
1581 #define SOUP_METHOD_IS_SAFE(method) (method == SOUP_METHOD_GET || \
1582                                      method == SOUP_METHOD_HEAD || \
1583                                      method == SOUP_METHOD_OPTIONS || \
1584                                      method == SOUP_METHOD_PROPFIND)
1585
1586 #define SOUP_METHOD_IS_IDEMPOTENT(method) (method == SOUP_METHOD_GET || \
1587                                            method == SOUP_METHOD_HEAD || \
1588                                            method == SOUP_METHOD_OPTIONS || \
1589                                            method == SOUP_METHOD_PROPFIND || \
1590                                            method == SOUP_METHOD_PUT || \
1591                                            method == SOUP_METHOD_DELETE)
1592
1593
1594 #define SOUP_SESSION_WOULD_REDIRECT_AS_GET(session, msg) \
1595         ((msg)->status_code == SOUP_STATUS_SEE_OTHER || \
1596          ((msg)->status_code == SOUP_STATUS_FOUND && \
1597           !SOUP_METHOD_IS_SAFE ((msg)->method)) || \
1598          ((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY && \
1599           (msg)->method == SOUP_METHOD_POST))
1600
1601 #define SOUP_SESSION_WOULD_REDIRECT_AS_SAFE(session, msg) \
1602         (((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY || \
1603           (msg)->status_code == SOUP_STATUS_TEMPORARY_REDIRECT || \
1604           (msg)->status_code == SOUP_STATUS_FOUND) && \
1605          SOUP_METHOD_IS_SAFE ((msg)->method))
1606
1607 static inline SoupURI *
1608 redirection_uri (SoupMessage *msg)
1609 {
1610         const char *new_loc;
1611         SoupURI *new_uri;
1612
1613         new_loc = soup_message_headers_get_one (msg->response_headers,
1614                                                 "Location");
1615         if (!new_loc)
1616                 return NULL;
1617         new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
1618         if (!new_uri || !new_uri->host) {
1619                 if (new_uri)
1620                         soup_uri_free (new_uri);
1621                 return NULL;
1622         }
1623
1624         return new_uri;
1625 }
1626
1627 /**
1628  * soup_session_would_redirect:
1629  * @session: a #SoupSession
1630  * @msg: a #SoupMessage that has response headers
1631  *
1632  * Checks if @msg contains a response that would cause @session to
1633  * redirect it to a new URL (ignoring @msg's %SOUP_MESSAGE_NO_REDIRECT
1634  * flag, and the number of times it has already been redirected).
1635  *
1636  * Return value: whether @msg would be redirected
1637  *
1638  * Since: 2.38
1639  */
1640 gboolean
1641 soup_session_would_redirect (SoupSession *session, SoupMessage *msg)
1642 {
1643         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1644         SoupURI *new_uri;
1645
1646         /* It must have an appropriate status code and method */
1647         if (!SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg) &&
1648             !SOUP_SESSION_WOULD_REDIRECT_AS_SAFE (session, msg))
1649                 return FALSE;
1650
1651         /* and a Location header that parses to an http URI */
1652         if (!soup_message_headers_get_one (msg->response_headers, "Location"))
1653                 return FALSE;
1654         new_uri = redirection_uri (msg);
1655         if (!new_uri)
1656                 return FALSE;
1657         if (!new_uri->host || !*new_uri->host ||
1658             (!uri_is_http (priv, new_uri) && !uri_is_https (priv, new_uri))) {
1659                 soup_uri_free (new_uri);
1660                 return FALSE;
1661         }
1662
1663         soup_uri_free (new_uri);
1664         return TRUE;
1665 }
1666
1667 /**
1668  * soup_session_redirect_message:
1669  * @session: the session
1670  * @msg: a #SoupMessage that has received a 3xx response
1671  *
1672  * Updates @msg's URI according to its status code and "Location"
1673  * header, and requeues it on @session. Use this when you have set
1674  * %SOUP_MESSAGE_NO_REDIRECT on a message, but have decided to allow a
1675  * particular redirection to occur, or if you want to allow a
1676  * redirection that #SoupSession will not perform automatically (eg,
1677  * redirecting a non-safe method such as DELETE).
1678  *
1679  * If @msg's status code indicates that it should be retried as a GET
1680  * request, then @msg will be modified accordingly.
1681  *
1682  * If @msg has already been redirected too many times, this will
1683  * cause it to fail with %SOUP_STATUS_TOO_MANY_REDIRECTS.
1684  *
1685  * Return value: %TRUE if a redirection was applied, %FALSE if not
1686  * (eg, because there was no Location header, or it could not be
1687  * parsed).
1688  *
1689  * Since: 2.38
1690  */
1691 gboolean
1692 soup_session_redirect_message (SoupSession *session, SoupMessage *msg)
1693 {
1694         SoupMessageQueueItem *item;
1695         SoupURI *new_uri;
1696
1697         new_uri = redirection_uri (msg);
1698         if (!new_uri)
1699                 return FALSE;
1700
1701         item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
1702         if (!item) {
1703                 soup_uri_free (new_uri);
1704                 return FALSE;
1705         }
1706         if (item->redirection_count >= SOUP_SESSION_MAX_REDIRECTION_COUNT) {
1707                 soup_uri_free (new_uri);
1708                 soup_session_cancel_message (session, msg, SOUP_STATUS_TOO_MANY_REDIRECTS);
1709                 soup_message_queue_item_unref (item);
1710                 return FALSE;
1711         }
1712         item->redirection_count++;
1713         soup_message_queue_item_unref (item);
1714
1715         if (SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg)) {
1716                 if (msg->method != SOUP_METHOD_HEAD) {
1717                         g_object_set (msg,
1718                                       SOUP_MESSAGE_METHOD, SOUP_METHOD_GET,
1719                                       NULL);
1720                 }
1721                 soup_message_set_request (msg, NULL,
1722                                           SOUP_MEMORY_STATIC, NULL, 0);
1723                 soup_message_headers_set_encoding (msg->request_headers,
1724                                                    SOUP_ENCODING_NONE);
1725         }
1726
1727         soup_message_set_uri (msg, new_uri);
1728         soup_uri_free (new_uri);
1729
1730         soup_session_requeue_message (session, msg);
1731         return TRUE;
1732 }
1733
1734 static void
1735 redirect_handler (SoupMessage *msg, gpointer user_data)
1736 {
1737         SoupMessageQueueItem *item = user_data;
1738         SoupSession *session = item->session;
1739
1740         if (!soup_session_would_redirect (session, msg)) {
1741                 SoupURI *new_uri = redirection_uri (msg);
1742                 gboolean invalid = !new_uri || !new_uri->host;
1743
1744                 if (new_uri)
1745                         soup_uri_free (new_uri);
1746                 if (invalid && !item->new_api) {
1747                         soup_message_set_status_full (msg,
1748                                                       SOUP_STATUS_MALFORMED,
1749                                                       "Invalid Redirect URL");
1750                 }
1751                 return;
1752         }
1753
1754         soup_session_redirect_message (session, msg);
1755 }
1756
1757 void
1758 soup_session_send_queue_item (SoupSession *session,
1759                               SoupMessageQueueItem *item,
1760                               SoupMessageCompletionFn completion_cb)
1761 {
1762         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1763         const char *conn_header;
1764
1765         if (priv->user_agent) {
1766                 soup_message_headers_replace (item->msg->request_headers,
1767                                               "User-Agent", priv->user_agent);
1768         }
1769
1770         if (priv->accept_language &&
1771             !soup_message_headers_get_list (item->msg->request_headers,
1772                                             "Accept-Language")) {
1773                 soup_message_headers_append (item->msg->request_headers,
1774                                              "Accept-Language",
1775                                              priv->accept_language);
1776         }
1777
1778         /* Force keep alive connections for HTTP 1.0. Performance will
1779          * improve when issuing multiple requests to the same host in
1780          * a short period of time, as we wouldn't need to establish
1781          * new connections. Keep alive is implicit for HTTP 1.1.
1782          */
1783         conn_header = soup_message_headers_get_list (item->msg->request_headers, "Connection");
1784         if (!conn_header ||
1785             (!soup_header_contains (conn_header, "Keep-Alive") &&
1786              !soup_header_contains (conn_header, "close")))
1787                 soup_message_headers_append (item->msg->request_headers,
1788                                              "Connection", "Keep-Alive");
1789
1790         g_signal_emit (session, signals[REQUEST_STARTED], 0,
1791                        item->msg, soup_connection_get_socket (item->conn));
1792         if (item->state == SOUP_MESSAGE_RUNNING)
1793                 soup_connection_send_request (item->conn, item, completion_cb, item);
1794 }
1795
1796 gboolean
1797 soup_session_cleanup_connections (SoupSession *session,
1798                                   gboolean     prune_idle)
1799 {
1800         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1801         GSList *conns = NULL, *c;
1802         GHashTableIter iter;
1803         gpointer conn, host;
1804         SoupConnectionState state;
1805
1806         g_mutex_lock (&priv->host_lock);
1807         g_hash_table_iter_init (&iter, priv->conns);
1808         while (g_hash_table_iter_next (&iter, &conn, &host)) {
1809                 state = soup_connection_get_state (conn);
1810                 if (state == SOUP_CONNECTION_REMOTE_DISCONNECTED ||
1811                     (prune_idle && state == SOUP_CONNECTION_IDLE))
1812                         conns = g_slist_prepend (conns, g_object_ref (conn));
1813         }
1814         g_mutex_unlock (&priv->host_lock);
1815
1816         if (!conns)
1817                 return FALSE;
1818
1819         for (c = conns; c; c = c->next) {
1820                 conn = c->data;
1821                 soup_connection_disconnect (conn);
1822                 g_object_unref (conn);
1823         }
1824         g_slist_free (conns);
1825
1826         return TRUE;
1827 }
1828
1829 static gboolean
1830 free_unused_host (gpointer user_data)
1831 {
1832         SoupSessionHost *host = (SoupSessionHost *) user_data;
1833         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (host->session);
1834
1835         g_mutex_lock (&priv->host_lock);
1836         /* This will free the host in addition to removing it from the
1837          * hash table
1838          */
1839         if (host->uri->scheme == SOUP_URI_SCHEME_HTTPS)
1840                 g_hash_table_remove (priv->https_hosts, host->uri);
1841         else
1842                 g_hash_table_remove (priv->http_hosts, host->uri);
1843         g_mutex_unlock (&priv->host_lock);
1844
1845         return FALSE;
1846 }
1847
1848 static void
1849 connection_disconnected (SoupConnection *conn, gpointer user_data)
1850 {
1851         SoupSession *session = user_data;
1852         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1853         SoupSessionHost *host;
1854
1855         g_mutex_lock (&priv->host_lock);
1856
1857         host = g_hash_table_lookup (priv->conns, conn);
1858         if (host) {
1859                 g_hash_table_remove (priv->conns, conn);
1860                 host->connections = g_slist_remove (host->connections, conn);
1861                 host->num_conns--;
1862
1863                 /* Free the SoupHost (and its SoupAddress) if there
1864                  * has not been any new connection to the host during
1865                  * the last HOST_KEEP_ALIVE msecs.
1866                  */
1867                 if (host->num_conns == 0) {
1868                         g_assert (host->keep_alive_src == NULL);
1869                         host->keep_alive_src = soup_add_timeout (priv->async_context,
1870                                                                  HOST_KEEP_ALIVE,
1871                                                                  free_unused_host,
1872                                                                  host);
1873                         host->keep_alive_src = g_source_ref (host->keep_alive_src);
1874                 }
1875
1876                 if (soup_connection_get_ssl_fallback (conn))
1877                         host->ssl_fallback = TRUE;
1878         }
1879
1880         g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
1881         priv->num_conns--;
1882
1883         g_mutex_unlock (&priv->host_lock);
1884         g_object_unref (conn);
1885 }
1886
1887 SoupMessageQueueItem *
1888 soup_session_make_connect_message (SoupSession    *session,
1889                                    SoupConnection *conn)
1890 {
1891         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1892         SoupURI *uri;
1893         SoupMessage *msg;
1894         SoupMessageQueueItem *item;
1895
1896         uri = soup_connection_get_remote_uri (conn);
1897         msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT, uri);
1898         soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
1899
1900         /* Call the base implementation of soup_session_queue_message
1901          * directly, to add msg to the SoupMessageQueue and cause all
1902          * the right signals to be emitted.
1903          */
1904         queue_message (session, msg, NULL, NULL);
1905         item = soup_message_queue_lookup (priv->queue, msg);
1906         soup_message_queue_item_set_connection (item, conn);
1907         g_object_unref (msg);
1908         item->state = SOUP_MESSAGE_RUNNING;
1909
1910         g_signal_emit (session, signals[TUNNELING], 0, conn);
1911         return item;
1912 }
1913
1914 gboolean
1915 soup_session_get_connection (SoupSession *session,
1916                              SoupMessageQueueItem *item,
1917                              gboolean *try_pruning)
1918 {
1919         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1920         SoupConnection *conn;
1921         SoupSessionHost *host;
1922         GSList *conns;
1923         int num_pending = 0;
1924         gboolean need_new_connection;
1925
1926         if (item->conn) {
1927                 g_return_val_if_fail (soup_connection_get_state (item->conn) != SOUP_CONNECTION_DISCONNECTED, FALSE);
1928                 return TRUE;
1929         }
1930
1931         need_new_connection =
1932                 (soup_message_get_flags (item->msg) & SOUP_MESSAGE_NEW_CONNECTION) ||
1933                 !SOUP_METHOD_IS_IDEMPOTENT (item->msg->method);
1934
1935         g_mutex_lock (&priv->host_lock);
1936
1937         host = get_host_for_message (session, item->msg);
1938         for (conns = host->connections; conns; conns = conns->next) {
1939                 if (!need_new_connection && soup_connection_get_state (conns->data) == SOUP_CONNECTION_IDLE) {
1940                         soup_connection_set_state (conns->data, SOUP_CONNECTION_IN_USE);
1941                         g_mutex_unlock (&priv->host_lock);
1942                         soup_message_queue_item_set_connection (item, conns->data);
1943                         soup_message_set_https_status (item->msg, item->conn);
1944                         return TRUE;
1945                 } else if (soup_connection_get_state (conns->data) == SOUP_CONNECTION_CONNECTING)
1946                         num_pending++;
1947         }
1948
1949         /* Limit the number of pending connections; num_messages / 2
1950          * is somewhat arbitrary...
1951          */
1952         if (num_pending > host->num_messages / 2) {
1953                 g_mutex_unlock (&priv->host_lock);
1954                 return FALSE;
1955         }
1956
1957         if (host->num_conns >= priv->max_conns_per_host) {
1958                 if (need_new_connection)
1959                         *try_pruning = TRUE;
1960                 g_mutex_unlock (&priv->host_lock);
1961                 return FALSE;
1962         }
1963
1964         if (priv->num_conns >= priv->max_conns) {
1965                 *try_pruning = TRUE;
1966                 g_mutex_unlock (&priv->host_lock);
1967                 return FALSE;
1968         }
1969
1970         conn = g_object_new (
1971                 SOUP_TYPE_CONNECTION,
1972                 SOUP_CONNECTION_REMOTE_URI, host->uri,
1973                 SOUP_CONNECTION_PROXY_RESOLVER, soup_session_get_feature (session, SOUP_TYPE_PROXY_URI_RESOLVER),
1974                 SOUP_CONNECTION_SSL, uri_is_https (priv, soup_message_get_uri (item->msg)),
1975                 SOUP_CONNECTION_SSL_CREDENTIALS, priv->tlsdb,
1976                 SOUP_CONNECTION_SSL_STRICT, (priv->tlsdb != NULL) && priv->ssl_strict,
1977                 SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
1978                 SOUP_CONNECTION_USE_THREAD_CONTEXT, priv->use_thread_context,
1979                 SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
1980                 SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
1981                 SOUP_CONNECTION_SSL_FALLBACK, host->ssl_fallback,
1982                 NULL);
1983         g_signal_connect (conn, "disconnected",
1984                           G_CALLBACK (connection_disconnected),
1985                           session);
1986
1987         g_signal_emit (session, signals[CONNECTION_CREATED], 0, conn);
1988
1989         g_hash_table_insert (priv->conns, conn, host);
1990
1991         priv->num_conns++;
1992         host->num_conns++;
1993         host->connections = g_slist_prepend (host->connections, conn);
1994
1995         if (host->keep_alive_src) {
1996                 g_source_destroy (host->keep_alive_src);
1997                 g_source_unref (host->keep_alive_src);
1998                 host->keep_alive_src = NULL;
1999         }
2000
2001         g_mutex_unlock (&priv->host_lock);
2002         soup_message_queue_item_set_connection (item, conn);
2003         return TRUE;
2004 }
2005
2006 SoupMessageQueue *
2007 soup_session_get_queue (SoupSession *session)
2008 {
2009         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2010
2011         return priv->queue;
2012 }
2013
2014 void
2015 soup_session_unqueue_item (SoupSession          *session,
2016                            SoupMessageQueueItem *item)
2017 {
2018         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2019         SoupSessionHost *host;
2020
2021         if (item->conn) {
2022                 if (soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE)
2023                         soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
2024                 soup_message_queue_item_set_connection (item, NULL);
2025         }
2026
2027         if (item->state != SOUP_MESSAGE_FINISHED) {
2028                 g_warning ("finished an item with state %d", item->state);
2029                 return;
2030         }
2031
2032         soup_message_queue_remove (priv->queue, item);
2033
2034         g_mutex_lock (&priv->host_lock);
2035         host = get_host_for_message (session, item->msg);
2036         host->num_messages--;
2037         g_mutex_unlock (&priv->host_lock);
2038
2039         /* g_signal_handlers_disconnect_by_func doesn't work if you
2040          * have a metamarshal, meaning it doesn't work with
2041          * soup_message_add_header_handler()
2042          */
2043         g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
2044                                               0, 0, NULL, NULL, item);
2045         g_signal_emit (session, signals[REQUEST_UNQUEUED], 0, item->msg);
2046         soup_message_queue_item_unref (item);
2047 }
2048
2049 void
2050 soup_session_set_item_status (SoupSession          *session,
2051                               SoupMessageQueueItem *item,
2052                               guint                 status_code)
2053 {
2054         SoupURI *uri;
2055         char *msg;
2056
2057         switch (status_code) {
2058         case SOUP_STATUS_CANT_RESOLVE:
2059         case SOUP_STATUS_CANT_CONNECT:
2060                 uri = soup_message_get_uri (item->msg);
2061                 msg = g_strdup_printf ("%s (%s)",
2062                                        soup_status_get_phrase (status_code),
2063                                        uri->host);
2064                 soup_message_set_status_full (item->msg, status_code, msg);
2065                 g_free (msg);
2066                 break;
2067
2068         case SOUP_STATUS_CANT_RESOLVE_PROXY:
2069         case SOUP_STATUS_CANT_CONNECT_PROXY:
2070                 if (item->proxy_uri && item->proxy_uri->host) {
2071                         msg = g_strdup_printf ("%s (%s)",
2072                                                soup_status_get_phrase (status_code),
2073                                                item->proxy_uri->host);
2074                         soup_message_set_status_full (item->msg, status_code, msg);
2075                         g_free (msg);
2076                         break;
2077                 }
2078                 soup_message_set_status (item->msg, status_code);
2079                 break;
2080
2081         case SOUP_STATUS_SSL_FAILED:
2082                 if (!g_tls_backend_supports_tls (g_tls_backend_get_default ())) {
2083                         soup_message_set_status_full (item->msg, status_code,
2084                                                       "TLS/SSL support not available; install glib-networking");
2085                 } else
2086                         soup_message_set_status (item->msg, status_code);
2087                 break;
2088
2089         default:
2090                 soup_message_set_status (item->msg, status_code);
2091                 break;
2092         }
2093 }
2094
2095 static void
2096 queue_message (SoupSession *session, SoupMessage *msg,
2097                SoupSessionCallback callback, gpointer user_data)
2098 {
2099         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2100         SoupMessageQueueItem *item;
2101         SoupSessionHost *host;
2102
2103         item = soup_message_queue_append (priv->queue, msg, callback, user_data);
2104
2105         g_mutex_lock (&priv->host_lock);
2106         host = get_host_for_message (session, item->msg);
2107         host->num_messages++;
2108         g_mutex_unlock (&priv->host_lock);
2109
2110         if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
2111                 soup_message_add_header_handler (
2112                         msg, "got_body", "Location",
2113                         G_CALLBACK (redirect_handler), item);
2114         }
2115
2116         g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
2117 }
2118
2119 /**
2120  * SoupSessionCallback:
2121  * @session: the session
2122  * @msg: the message that has finished
2123  * @user_data: the data passed to soup_session_queue_message
2124  *
2125  * Prototype for the callback passed to soup_session_queue_message(),
2126  * qv.
2127  **/
2128
2129 /**
2130  * soup_session_queue_message:
2131  * @session: a #SoupSession
2132  * @msg: (transfer full): the message to queue
2133  * @callback: (allow-none) (scope async): a #SoupSessionCallback which will
2134  * be called after the message completes or when an unrecoverable error occurs.
2135  * @user_data: (allow-none): a pointer passed to @callback.
2136  * 
2137  * Queues the message @msg for sending. All messages are processed
2138  * while the glib main loop runs. If @msg has been processed before,
2139  * any resources related to the time it was last sent are freed.
2140  *
2141  * Upon message completion, the callback specified in @callback will
2142  * be invoked (in the thread associated with @session's async
2143  * context). If after returning from this callback the message has not
2144  * been requeued, @msg will be unreffed.
2145  */
2146 void
2147 soup_session_queue_message (SoupSession *session, SoupMessage *msg,
2148                             SoupSessionCallback callback, gpointer user_data)
2149 {
2150         g_return_if_fail (SOUP_IS_SESSION (session));
2151         g_return_if_fail (SOUP_IS_MESSAGE (msg));
2152
2153         SOUP_SESSION_GET_CLASS (session)->queue_message (session, msg,
2154                                                          callback, user_data);
2155 }
2156
2157 static void
2158 requeue_message (SoupSession *session, SoupMessage *msg)
2159 {
2160         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2161         SoupMessageQueueItem *item;
2162
2163         item = soup_message_queue_lookup (priv->queue, msg);
2164         g_return_if_fail (item != NULL);
2165         item->state = SOUP_MESSAGE_RESTARTING;
2166         soup_message_queue_item_unref (item);
2167 }
2168
2169 /**
2170  * soup_session_requeue_message:
2171  * @session: a #SoupSession
2172  * @msg: the message to requeue
2173  *
2174  * This causes @msg to be placed back on the queue to be attempted
2175  * again.
2176  **/
2177 void
2178 soup_session_requeue_message (SoupSession *session, SoupMessage *msg)
2179 {
2180         g_return_if_fail (SOUP_IS_SESSION (session));
2181         g_return_if_fail (SOUP_IS_MESSAGE (msg));
2182
2183         SOUP_SESSION_GET_CLASS (session)->requeue_message (session, msg);
2184 }
2185
2186
2187 /**
2188  * soup_session_send_message:
2189  * @session: a #SoupSession
2190  * @msg: the message to send
2191  * 
2192  * Synchronously send @msg. This call will not return until the
2193  * transfer is finished successfully or there is an unrecoverable
2194  * error.
2195  *
2196  * @msg is not freed upon return.
2197  *
2198  * Return value: the HTTP status code of the response
2199  */
2200 guint
2201 soup_session_send_message (SoupSession *session, SoupMessage *msg)
2202 {
2203         g_return_val_if_fail (SOUP_IS_SESSION (session), SOUP_STATUS_MALFORMED);
2204         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_STATUS_MALFORMED);
2205
2206         return SOUP_SESSION_GET_CLASS (session)->send_message (session, msg);
2207 }
2208
2209
2210 /**
2211  * soup_session_pause_message:
2212  * @session: a #SoupSession
2213  * @msg: a #SoupMessage currently running on @session
2214  *
2215  * Pauses HTTP I/O on @msg. Call soup_session_unpause_message() to
2216  * resume I/O.
2217  **/
2218 void
2219 soup_session_pause_message (SoupSession *session,
2220                             SoupMessage *msg)
2221 {
2222         SoupSessionPrivate *priv;
2223         SoupMessageQueueItem *item;
2224
2225         g_return_if_fail (SOUP_IS_SESSION (session));
2226         g_return_if_fail (SOUP_IS_MESSAGE (msg));
2227
2228         priv = SOUP_SESSION_GET_PRIVATE (session);
2229         item = soup_message_queue_lookup (priv->queue, msg);
2230         g_return_if_fail (item != NULL);
2231
2232         item->paused = TRUE;
2233         if (item->state == SOUP_MESSAGE_RUNNING)
2234                 soup_message_io_pause (msg);
2235         soup_message_queue_item_unref (item);
2236 }
2237
2238 /**
2239  * soup_session_unpause_message:
2240  * @session: a #SoupSession
2241  * @msg: a #SoupMessage currently running on @session
2242  *
2243  * Resumes HTTP I/O on @msg. Use this to resume after calling
2244  * soup_session_pause_message().
2245  *
2246  * If @msg is being sent via blocking I/O, this will resume reading or
2247  * writing immediately. If @msg is using non-blocking I/O, then
2248  * reading or writing won't resume until you return to the main loop.
2249  **/
2250 void
2251 soup_session_unpause_message (SoupSession *session,
2252                               SoupMessage *msg)
2253 {
2254         SoupSessionPrivate *priv;
2255         SoupMessageQueueItem *item;
2256
2257         g_return_if_fail (SOUP_IS_SESSION (session));
2258         g_return_if_fail (SOUP_IS_MESSAGE (msg));
2259
2260         priv = SOUP_SESSION_GET_PRIVATE (session);
2261         item = soup_message_queue_lookup (priv->queue, msg);
2262         g_return_if_fail (item != NULL);
2263
2264         item->paused = FALSE;
2265         if (item->state == SOUP_MESSAGE_RUNNING)
2266                 soup_message_io_unpause (msg);
2267         soup_message_queue_item_unref (item);
2268
2269         SOUP_SESSION_GET_CLASS (session)->kick (session);
2270 }
2271
2272
2273 static void
2274 cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
2275 {
2276         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2277         SoupMessageQueueItem *item;
2278
2279         item = soup_message_queue_lookup (priv->queue, msg);
2280         g_return_if_fail (item != NULL);
2281
2282         item->paused = FALSE;
2283         soup_message_set_status (msg, status_code);
2284         g_cancellable_cancel (item->cancellable);
2285
2286         soup_message_queue_item_unref (item);
2287 }
2288
2289 /**
2290  * soup_session_cancel_message:
2291  * @session: a #SoupSession
2292  * @msg: the message to cancel
2293  * @status_code: status code to set on @msg (generally
2294  * %SOUP_STATUS_CANCELLED)
2295  *
2296  * Causes @session to immediately finish processing @msg (regardless
2297  * of its current state) with a final status_code of @status_code. You
2298  * may call this at any time after handing @msg off to @session; if
2299  * @session has started sending the request but has not yet received
2300  * the complete response, then it will close the request's connection.
2301  * Note that with non-idempotent requests (eg,
2302  * <literal>POST</literal>, <literal>PUT</literal>,
2303  * <literal>DELETE</literal>) it is possible that you might cancel the
2304  * request after the server acts on it, but before it returns a
2305  * response, leaving the remote resource in an unknown state.
2306  *
2307  * If the message is cancelled while its response body is being read,
2308  * then the response body in @msg will be left partially-filled-in.
2309  * The response headers, on the other hand, will always be either
2310  * empty or complete.
2311  *
2312  * For messages queued with soup_session_queue_message() (and
2313  * cancelled from the same thread), the callback will be invoked
2314  * before soup_session_cancel_message() returns.
2315  **/
2316 void
2317 soup_session_cancel_message (SoupSession *session, SoupMessage *msg,
2318                              guint status_code)
2319 {
2320         SoupSessionPrivate *priv;
2321         SoupMessageQueueItem *item;
2322
2323         g_return_if_fail (SOUP_IS_SESSION (session));
2324         g_return_if_fail (SOUP_IS_MESSAGE (msg));
2325
2326         priv = SOUP_SESSION_GET_PRIVATE (session);
2327         item = soup_message_queue_lookup (priv->queue, msg);
2328         /* If the message is already ending, don't do anything */
2329         if (!item)
2330                 return;
2331         if (item->state == SOUP_MESSAGE_FINISHED) {
2332                 soup_message_queue_item_unref (item);
2333                 return;
2334         }
2335
2336         SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg, status_code);
2337         soup_message_queue_item_unref (item);
2338 }
2339
2340 static void
2341 gather_conns (gpointer key, gpointer host, gpointer data)
2342 {
2343         SoupConnection *conn = key;
2344         GSList **conns = data;
2345
2346         *conns = g_slist_prepend (*conns, g_object_ref (conn));
2347 }
2348
2349 static void
2350 flush_queue (SoupSession *session)
2351 {
2352         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2353         SoupMessageQueueItem *item;
2354
2355         for (item = soup_message_queue_first (priv->queue);
2356              item;
2357              item = soup_message_queue_next (priv->queue, item)) {
2358                 soup_session_cancel_message (session, item->msg,
2359                                              SOUP_STATUS_CANCELLED);
2360         }
2361 }
2362
2363 /**
2364  * soup_session_abort:
2365  * @session: the session
2366  *
2367  * Cancels all pending requests in @session.
2368  **/
2369 void
2370 soup_session_abort (SoupSession *session)
2371 {
2372         SoupSessionPrivate *priv;
2373         GSList *conns, *c;
2374
2375         g_return_if_fail (SOUP_IS_SESSION (session));
2376         priv = SOUP_SESSION_GET_PRIVATE (session);
2377
2378         SOUP_SESSION_GET_CLASS (session)->flush_queue (session);
2379
2380         /* Close all connections */
2381         g_mutex_lock (&priv->host_lock);
2382         conns = NULL;
2383         g_hash_table_foreach (priv->conns, gather_conns, &conns);
2384
2385         g_mutex_unlock (&priv->host_lock);
2386         for (c = conns; c; c = c->next) {
2387                 soup_connection_disconnect (c->data);
2388                 g_object_unref (c->data);
2389         }
2390
2391         g_slist_free (conns);
2392 }
2393
2394 static void
2395 prefetch_uri(SoupSession *session, SoupURI *uri,
2396              GCancellable *cancellable,
2397              SoupAddressCallback callback, gpointer user_data)
2398 {
2399         SoupSessionPrivate *priv;
2400         SoupSessionHost *host;
2401         SoupAddress *addr;
2402
2403         priv = SOUP_SESSION_GET_PRIVATE (session);
2404
2405         g_mutex_lock (&priv->host_lock);
2406         host = get_host_for_uri (session, uri);
2407         addr = g_object_ref (host->addr);
2408         g_mutex_unlock (&priv->host_lock);
2409
2410         soup_address_resolve_async (addr,
2411                                     soup_session_get_async_context (session),
2412                                     cancellable, callback, user_data);
2413         g_object_unref (addr);
2414 }
2415
2416 /**
2417 * soup_session_prepare_for_uri:
2418 * @session: a #SoupSession
2419 * @uri: a #SoupURI which may be required
2420 *
2421 * Tells @session that @uri may be requested shortly, and so the
2422 * session can try to prepare (resolving the domain name, obtaining
2423 * proxy address, etc.) in order to work more quickly once the URI is
2424 * actually requested.
2425 *
2426 * This method acts asynchronously, in @session's
2427 * #SoupSession:async_context. If you are using #SoupSessionSync and do
2428 * not have a main loop running, then you can't use this method.
2429 *
2430 * Since: 2.30
2431 *
2432 * Deprecated: 2.38: use soup_session_prefetch_dns() instead
2433 **/
2434 void
2435 soup_session_prepare_for_uri (SoupSession *session, SoupURI *uri)
2436 {
2437         g_return_if_fail (SOUP_IS_SESSION (session));
2438         g_return_if_fail (uri != NULL);
2439
2440         if (!uri->host)
2441                 return;
2442
2443         prefetch_uri (session, uri, NULL, NULL, NULL);
2444 }
2445
2446 /**
2447 * soup_session_prefetch_dns:
2448 * @session: a #SoupSession
2449 * @hostname: a hostname to be resolved
2450 * @cancellable: (allow-none): a #GCancellable object, or %NULL
2451 * @callback: (scope async) (allow-none): callback to call with the
2452 *     result, or %NULL
2453 * @user_data: data for @callback
2454 *
2455 * Tells @session that an URI from the given @hostname may be requested
2456 * shortly, and so the session can try to prepare by resolving the
2457 * domain name in advance, in order to work more quickly once the URI
2458 * is actually requested.
2459 *
2460 * If @cancellable is non-%NULL, it can be used to cancel the
2461 * resolution. @callback will still be invoked in this case, with a
2462 * status of %SOUP_STATUS_CANCELLED.
2463 *
2464 * This method acts asynchronously, in @session's
2465 * #SoupSession:async_context. If you are using #SoupSessionSync and do
2466 * not have a main loop running, then you can't use this method.
2467 *
2468 * Since: 2.38
2469 **/
2470 void
2471 soup_session_prefetch_dns (SoupSession *session, const char *hostname,
2472                            GCancellable *cancellable,
2473                            SoupAddressCallback callback, gpointer user_data)
2474 {
2475         SoupURI *uri;
2476
2477         g_return_if_fail (SOUP_IS_SESSION (session));
2478         g_return_if_fail (hostname != NULL);
2479
2480         /* FIXME: Prefetching should work for both HTTP and HTTPS */
2481         uri = soup_uri_new (NULL);
2482         soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP);
2483         soup_uri_set_host (uri, hostname);
2484         soup_uri_set_path (uri, "");
2485
2486         prefetch_uri (session, uri, cancellable, callback, user_data);
2487         soup_uri_free (uri);
2488 }
2489
2490 /**
2491  * soup_session_add_feature:
2492  * @session: a #SoupSession
2493  * @feature: an object that implements #SoupSessionFeature
2494  *
2495  * Adds @feature's functionality to @session. You can also add a
2496  * feature to the session at construct time by using the
2497  * %SOUP_SESSION_ADD_FEATURE property.
2498  *
2499  * Since: 2.24
2500  **/
2501 void
2502 soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature)
2503 {
2504         SoupSessionPrivate *priv;
2505
2506         g_return_if_fail (SOUP_IS_SESSION (session));
2507         g_return_if_fail (SOUP_IS_SESSION_FEATURE (feature));
2508
2509         priv = SOUP_SESSION_GET_PRIVATE (session);
2510         priv->features = g_slist_prepend (priv->features, g_object_ref (feature));
2511         g_hash_table_remove_all (priv->features_cache);
2512         soup_session_feature_attach (feature, session);
2513 }
2514
2515 /**
2516  * soup_session_add_feature_by_type:
2517  * @session: a #SoupSession
2518  * @feature_type: a #GType
2519  *
2520  * If @feature_type is the type of a class that implements
2521  * #SoupSessionFeature, this creates a new feature of that type and
2522  * adds it to @session as with soup_session_add_feature(). You can use
2523  * this when you don't need to customize the new feature in any way.
2524  *
2525  * If @feature_type is not a #SoupSessionFeature type, this gives
2526  * each existing feature on @session the chance to accept @feature_type
2527  * as a "subfeature". This can be used to add new #SoupAuth types,
2528  * for instance.
2529  *
2530  * You can also add a feature to the session at construct time by
2531  * using the %SOUP_SESSION_ADD_FEATURE_BY_TYPE property.
2532  *
2533  * Since: 2.24
2534  **/
2535 void
2536 soup_session_add_feature_by_type (SoupSession *session, GType feature_type)
2537 {
2538         g_return_if_fail (SOUP_IS_SESSION (session));
2539
2540         if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2541                 SoupSessionFeature *feature;
2542
2543                 feature = g_object_new (feature_type, NULL);
2544                 soup_session_add_feature (session, feature);
2545                 g_object_unref (feature);
2546         } else {
2547                 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
2548                 GSList *f;
2549
2550                 for (f = priv->features; f; f = f->next) {
2551                         if (soup_session_feature_add_feature (f->data, feature_type))
2552                                 return;
2553                 }
2554                 g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
2555         }
2556 }
2557
2558 /**
2559  * soup_session_remove_feature:
2560  * @session: a #SoupSession
2561  * @feature: a feature that has previously been added to @session
2562  *
2563  * Removes @feature's functionality from @session.
2564  *
2565  * Since: 2.24
2566  **/
2567 void
2568 soup_session_remove_feature (SoupSession *session, SoupSessionFeature *feature)
2569 {
2570         SoupSessionPrivate *priv;
2571
2572         g_return_if_fail (SOUP_IS_SESSION (session));
2573
2574         priv = SOUP_SESSION_GET_PRIVATE (session);
2575         if (g_slist_find (priv->features, feature)) {
2576                 priv->features = g_slist_remove (priv->features, feature);
2577                 g_hash_table_remove_all (priv->features_cache);
2578                 soup_session_feature_detach (feature, session);
2579                 g_object_unref (feature);
2580         }
2581 }
2582
2583 /**
2584  * soup_session_remove_feature_by_type:
2585  * @session: a #SoupSession
2586  * @feature_type: a #GType
2587  *
2588  * Removes all features of type @feature_type (or any subclass of
2589  * @feature_type) from @session. You can also remove standard features
2590  * from the session at construct time by using the
2591  * %SOUP_SESSION_REMOVE_FEATURE_BY_TYPE property.
2592  *
2593  * Since: 2.24
2594  **/
2595 void
2596 soup_session_remove_feature_by_type (SoupSession *session, GType feature_type)
2597 {
2598         SoupSessionPrivate *priv;
2599         GSList *f;
2600
2601         g_return_if_fail (SOUP_IS_SESSION (session));
2602
2603         priv = SOUP_SESSION_GET_PRIVATE (session);
2604
2605         if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
2606         restart:
2607                 for (f = priv->features; f; f = f->next) {
2608                         if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type)) {
2609                                 soup_session_remove_feature (session, f->data);
2610                                 goto restart;
2611                         }
2612                 }
2613         } else {
2614                 for (f = priv->features; f; f = f->next) {
2615                         if (soup_session_feature_remove_feature (f->data, feature_type))
2616                                 return;
2617                 }
2618                 g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
2619         }
2620 }
2621
2622 /**
2623  * soup_session_get_features:
2624  * @session: a #SoupSession
2625  * @feature_type: the #GType of the class of features to get
2626  *
2627  * Generates a list of @session's features of type @feature_type. (If
2628  * you want to see all features, you can pass %SOUP_TYPE_SESSION_FEATURE
2629  * for @feature_type.)
2630  *
2631  * Return value: (transfer container) (element-type Soup.SessionFeature):
2632  * a list of features. You must free the list, but not its contents
2633  *
2634  * Since: 2.26
2635  **/
2636 GSList *
2637 soup_session_get_features (SoupSession *session, GType feature_type)
2638 {
2639         SoupSessionPrivate *priv;
2640         GSList *f, *ret;
2641
2642         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
2643
2644         priv = SOUP_SESSION_GET_PRIVATE (session);
2645         for (f = priv->features, ret = NULL; f; f = f->next) {
2646                 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
2647                         ret = g_slist_prepend (ret, f->data);
2648         }
2649         return g_slist_reverse (ret);
2650 }
2651
2652 /**
2653  * soup_session_get_feature:
2654  * @session: a #SoupSession
2655  * @feature_type: the #GType of the feature to get
2656  *
2657  * Gets the first feature in @session of type @feature_type. For
2658  * features where there may be more than one feature of a given type,
2659  * use soup_session_get_features().
2660  *
2661  * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
2662  * feature is owned by @session.
2663  *
2664  * Since: 2.26
2665  **/
2666 SoupSessionFeature *
2667 soup_session_get_feature (SoupSession *session, GType feature_type)
2668 {
2669         SoupSessionPrivate *priv;
2670         SoupSessionFeature *feature;
2671         GSList *f;
2672
2673         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
2674
2675         priv = SOUP_SESSION_GET_PRIVATE (session);
2676
2677         feature = g_hash_table_lookup (priv->features_cache,
2678                                        GSIZE_TO_POINTER (feature_type));
2679         if (feature)
2680                 return feature;
2681
2682         for (f = priv->features; f; f = f->next) {
2683                 feature = f->data;
2684                 if (G_TYPE_CHECK_INSTANCE_TYPE (feature, feature_type)) {
2685                         g_hash_table_insert (priv->features_cache,
2686                                              GSIZE_TO_POINTER (feature_type),
2687                                              feature);
2688                         return feature;
2689                 }
2690         }
2691         return NULL;
2692 }
2693
2694 /**
2695  * soup_session_get_feature_for_message:
2696  * @session: a #SoupSession
2697  * @feature_type: the #GType of the feature to get
2698  * @msg: a #SoupMessage
2699  *
2700  * Gets the first feature in @session of type @feature_type, provided
2701  * that it is not disabled for @msg. As with
2702  * soup_session_get_feature(), this should only be used for features
2703  * where @feature_type is only expected to match a single feature. In
2704  * particular, if there are two matching features, and the first is
2705  * disabled on @msg, and the second is not, then this will return
2706  * %NULL, not the second feature.
2707  *
2708  * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
2709  * feature is owned by @session.
2710  *
2711  * Since: 2.28
2712  **/
2713 SoupSessionFeature *
2714 soup_session_get_feature_for_message (SoupSession *session, GType feature_type,
2715                                       SoupMessage *msg)
2716 {
2717         SoupSessionFeature *feature;
2718
2719         feature = soup_session_get_feature (session, feature_type);
2720         if (feature && soup_message_disables_feature (msg, feature))
2721                 return NULL;
2722         return feature;
2723 }