Git init
[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 "soup-address.h"
17 #include "soup-auth.h"
18 #include "soup-auth-basic.h"
19 #include "soup-auth-digest.h"
20 #include "soup-auth-manager-ntlm.h"
21 #include "soup-connection.h"
22 #include "soup-marshal.h"
23 #include "soup-message-private.h"
24 #include "soup-message-queue.h"
25 #include "soup-misc.h"
26 #include "soup-proxy-resolver-static.h"
27 #include "soup-proxy-uri-resolver.h"
28 #include "soup-session.h"
29 #include "soup-session-feature.h"
30 #include "soup-session-private.h"
31 #include "soup-socket.h"
32 #include "soup-ssl.h"
33 #include "soup-uri.h"
34 /*TIZEN patch*/
35 #include "TIZEN.h"
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 typedef struct {
64         SoupURI     *uri;
65         SoupAddress *addr;
66
67         GSList      *connections;      /* CONTAINS: SoupConnection */
68         guint        num_conns;
69
70         guint        num_messages;
71
72         gboolean     ssl_fallback;
73 } SoupSessionHost;
74
75 typedef struct {
76         char *ssl_ca_file;
77         SoupSSLCredentials *ssl_creds;
78         gboolean ssl_strict;
79
80         SoupMessageQueue *queue;
81
82         char *user_agent;
83         char *accept_language;
84         gboolean accept_language_auto;
85
86         GSList *features;
87         GHashTable *features_cache;
88
89         GHashTable *hosts; /* char* -> SoupSessionHost */
90         GHashTable *conns; /* SoupConnection -> SoupSessionHost */
91         guint num_conns;
92         guint max_conns, max_conns_per_host;
93         guint io_timeout, idle_timeout;
94
95         /* Must hold the host_lock before potentially creating a
96          * new SoupSessionHost, or adding/removing a connection.
97          * Must not emit signals or destroy objects while holding it.
98          */
99         GMutex *host_lock;
100
101         GMainContext *async_context;
102
103         GResolver *resolver;
104 } SoupSessionPrivate;
105 #define SOUP_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION, SoupSessionPrivate))
106
107 static void     free_host      (SoupSessionHost *host);
108
109 static void queue_message   (SoupSession *session, SoupMessage *msg,
110                              SoupSessionCallback callback, gpointer user_data);
111 static void requeue_message (SoupSession *session, SoupMessage *msg);
112 static void cancel_message  (SoupSession *session, SoupMessage *msg,
113                              guint status_code);
114 static void auth_required   (SoupSession *session, SoupMessage *msg,
115                              SoupAuth *auth, gboolean retrying);
116 static void flush_queue     (SoupSession *session);
117
118 static void auth_manager_authenticate (SoupAuthManager *manager,
119                                        SoupMessage *msg, SoupAuth *auth,
120                                        gboolean retrying, gpointer user_data);
121
122 #define SOUP_SESSION_MAX_CONNS_DEFAULT 10
123 #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2
124
125 #define SOUP_SESSION_MAX_REDIRECTION_COUNT 20
126
127 #define SOUP_SESSION_USER_AGENT_BASE "libsoup/" PACKAGE_VERSION
128
129 G_DEFINE_ABSTRACT_TYPE (SoupSession, soup_session, G_TYPE_OBJECT)
130
131 enum {
132         REQUEST_QUEUED,
133         REQUEST_STARTED,
134         REQUEST_UNQUEUED,
135         AUTHENTICATE,
136         CONNECTION_CREATED,
137         TUNNELING,
138         LAST_SIGNAL
139 };
140
141 static guint signals[LAST_SIGNAL] = { 0 };
142
143 enum {
144         PROP_0,
145
146         PROP_PROXY_URI,
147         PROP_MAX_CONNS,
148         PROP_MAX_CONNS_PER_HOST,
149         PROP_USE_NTLM,
150         PROP_SSL_CA_FILE,
151         PROP_SSL_STRICT,
152         PROP_ASYNC_CONTEXT,
153         PROP_TIMEOUT,
154         PROP_USER_AGENT,
155         PROP_ACCEPT_LANGUAGE,
156         PROP_ACCEPT_LANGUAGE_AUTO,
157         PROP_IDLE_TIMEOUT,
158         PROP_ADD_FEATURE,
159         PROP_ADD_FEATURE_BY_TYPE,
160         PROP_REMOVE_FEATURE_BY_TYPE,
161
162         LAST_PROP
163 };
164
165 static void set_property (GObject *object, guint prop_id,
166                           const GValue *value, GParamSpec *pspec);
167 static void get_property (GObject *object, guint prop_id,
168                           GValue *value, GParamSpec *pspec);
169
170 static void
171 soup_session_init (SoupSession *session)
172 {
173         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
174         SoupAuthManager *auth_manager;
175
176         priv->queue = soup_message_queue_new (session);
177
178         priv->host_lock = g_mutex_new ();
179         priv->hosts = g_hash_table_new_full (soup_uri_host_hash,
180                                              soup_uri_host_equal,
181                                              NULL, (GDestroyNotify)free_host);
182         priv->conns = g_hash_table_new (NULL, NULL);
183
184         priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
185         priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
186
187         priv->features_cache = g_hash_table_new (NULL, NULL);
188
189         auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER_NTLM, NULL);
190         g_signal_connect (auth_manager, "authenticate",
191                           G_CALLBACK (auth_manager_authenticate), session);
192         soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
193                                           SOUP_TYPE_AUTH_BASIC);
194         soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
195                                           SOUP_TYPE_AUTH_DIGEST);
196         soup_session_add_feature (session, SOUP_SESSION_FEATURE (auth_manager));
197         g_object_unref (auth_manager);
198
199         /* We'll be doing DNS continuously-ish while the session is active,
200          * so hold a ref on the default GResolver.
201          */
202         priv->resolver = g_resolver_get_default ();
203
204         priv->ssl_strict = TRUE;
205 }
206
207 static void
208 dispose (GObject *object)
209 {
210         SoupSession *session = SOUP_SESSION (object);
211         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
212
213         soup_session_abort (session);
214
215         while (priv->features)
216                 soup_session_remove_feature (session, priv->features->data);
217
218         G_OBJECT_CLASS (soup_session_parent_class)->dispose (object);
219 }
220
221 static void
222 finalize (GObject *object)
223 {
224         SoupSession *session = SOUP_SESSION (object);
225         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
226
227         soup_message_queue_destroy (priv->queue);
228
229         g_mutex_free (priv->host_lock);
230         g_hash_table_destroy (priv->hosts);
231         g_hash_table_destroy (priv->conns);
232
233         g_free (priv->user_agent);
234         g_free (priv->accept_language);
235
236         if (priv->ssl_ca_file)
237                 g_free (priv->ssl_ca_file);
238         if (priv->ssl_creds)
239                 soup_ssl_free_client_credentials (priv->ssl_creds);
240
241         if (priv->async_context)
242                 g_main_context_unref (priv->async_context);
243
244         g_hash_table_destroy (priv->features_cache);
245
246         g_object_unref (priv->resolver);
247
248         G_OBJECT_CLASS (soup_session_parent_class)->finalize (object);
249 }
250
251 static void
252 soup_session_class_init (SoupSessionClass *session_class)
253 {
254         GObjectClass *object_class = G_OBJECT_CLASS (session_class);
255
256         g_type_class_add_private (session_class, sizeof (SoupSessionPrivate));
257
258         /* virtual method definition */
259         session_class->queue_message = queue_message;
260         session_class->requeue_message = requeue_message;
261         session_class->cancel_message = cancel_message;
262         session_class->auth_required = auth_required;
263         session_class->flush_queue = flush_queue;
264
265         /* virtual method override */
266         object_class->dispose = dispose;
267         object_class->finalize = finalize;
268         object_class->set_property = set_property;
269         object_class->get_property = get_property;
270
271         /* signals */
272
273         /**
274          * SoupSession::request-queued:
275          * @session: the session
276          * @msg: the request that was queued
277          *
278          * Emitted when a request is queued on @session. (Note that
279          * "queued" doesn't just mean soup_session_queue_message();
280          * soup_session_send_message() implicitly queues the message
281          * as well.)
282          *
283          * When sending a request, first #SoupSession::request_queued
284          * is emitted, indicating that the session has become aware of
285          * the request.
286          *
287          * Once a connection is available to send the request on, the
288          * session emits #SoupSession::request_started. Then, various
289          * #SoupMessage signals are emitted as the message is
290          * processed. If the message is requeued, it will emit
291          * #SoupMessage::restarted, which will then be followed by
292          * another #SoupSession::request_started and another set of
293          * #SoupMessage signals when the message is re-sent.
294          *
295          * Eventually, the message will emit #SoupMessage::finished.
296          * Normally, this signals the completion of message
297          * processing. However, it is possible that the application
298          * will requeue the message from the "finished" handler (or
299          * equivalently, from the soup_session_queue_message()
300          * callback). In that case, the process will loop back to
301          * #SoupSession::request_started.
302          *
303          * Eventually, a message will reach "finished" and not be
304          * requeued. At that point, the session will emit
305          * #SoupSession::request_unqueued to indicate that it is done
306          * with the message.
307          *
308          * To sum up: #SoupSession::request_queued and
309          * #SoupSession::request_unqueued are guaranteed to be emitted
310          * exactly once, but #SoupSession::request_started and
311          * #SoupMessage::finished (and all of the other #SoupMessage
312          * signals) may be invoked multiple times for a given message.
313          *
314          * Since: 2.4.1
315          **/
316         signals[REQUEST_QUEUED] =
317                 g_signal_new ("request-queued",
318                               G_OBJECT_CLASS_TYPE (object_class),
319                               G_SIGNAL_RUN_FIRST,
320                               0, /* FIXME? */
321                               NULL, NULL,
322                               soup_marshal_NONE__OBJECT,
323                               G_TYPE_NONE, 1,
324                               SOUP_TYPE_MESSAGE);
325
326         /**
327          * SoupSession::request-started:
328          * @session: the session
329          * @msg: the request being sent
330          * @socket: the socket the request is being sent on
331          *
332          * Emitted just before a request is sent. See
333          * #SoupSession::request_queued for a detailed description of
334          * the message lifecycle within a session.
335          **/
336         signals[REQUEST_STARTED] =
337                 g_signal_new ("request-started",
338                               G_OBJECT_CLASS_TYPE (object_class),
339                               G_SIGNAL_RUN_FIRST,
340                               G_STRUCT_OFFSET (SoupSessionClass, request_started),
341                               NULL, NULL,
342                               soup_marshal_NONE__OBJECT_OBJECT,
343                               G_TYPE_NONE, 2,
344                               SOUP_TYPE_MESSAGE,
345                               SOUP_TYPE_SOCKET);
346
347         /**
348          * SoupSession::request-unqueued:
349          * @session: the session
350          * @msg: the request that was unqueued
351          *
352          * Emitted when a request is removed from @session's queue,
353          * indicating that @session is done with it. See
354          * #SoupSession::request_queued for a detailed description of the
355          * message lifecycle within a session.
356          *
357          * Since: 2.4.1
358          **/
359         signals[REQUEST_UNQUEUED] =
360                 g_signal_new ("request-unqueued",
361                               G_OBJECT_CLASS_TYPE (object_class),
362                               G_SIGNAL_RUN_FIRST,
363                               0, /* FIXME? */
364                               NULL, NULL,
365                               soup_marshal_NONE__OBJECT,
366                               G_TYPE_NONE, 1,
367                               SOUP_TYPE_MESSAGE);
368
369         /**
370          * SoupSession::authenticate:
371          * @session: the session
372          * @msg: the #SoupMessage being sent
373          * @auth: the #SoupAuth to authenticate
374          * @retrying: %TRUE if this is the second (or later) attempt
375          *
376          * Emitted when the session requires authentication. If
377          * credentials are available call soup_auth_authenticate() on
378          * @auth. If these credentials fail, the signal will be
379          * emitted again, with @retrying set to %TRUE, which will
380          * continue until you return without calling
381          * soup_auth_authenticate() on @auth.
382          *
383          * Note that this may be emitted before @msg's body has been
384          * fully read.
385          *
386          * If you call soup_session_pause_message() on @msg before
387          * returning, then you can authenticate @auth asynchronously
388          * (as long as you g_object_ref() it to make sure it doesn't
389          * get destroyed), and then unpause @msg when you are ready
390          * for it to continue.
391          **/
392         signals[AUTHENTICATE] =
393                 g_signal_new ("authenticate",
394                               G_OBJECT_CLASS_TYPE (object_class),
395                               G_SIGNAL_RUN_FIRST,
396                               G_STRUCT_OFFSET (SoupSessionClass, authenticate),
397                               NULL, NULL,
398                               soup_marshal_NONE__OBJECT_OBJECT_BOOLEAN,
399                               G_TYPE_NONE, 3,
400                               SOUP_TYPE_MESSAGE,
401                               SOUP_TYPE_AUTH,
402                               G_TYPE_BOOLEAN);
403
404         signals[CONNECTION_CREATED] =
405                 g_signal_new ("connection-created",
406                               G_OBJECT_CLASS_TYPE (object_class),
407                               G_SIGNAL_RUN_FIRST,
408                               0,
409                               NULL, NULL,
410                               soup_marshal_NONE__OBJECT,
411                               G_TYPE_NONE, 1,
412                               /* SoupConnection is private, so we can't use
413                                * SOUP_TYPE_CONNECTION here.
414                                */
415                               G_TYPE_OBJECT);
416
417         signals[TUNNELING] =
418                 g_signal_new ("tunneling",
419                               G_OBJECT_CLASS_TYPE (object_class),
420                               G_SIGNAL_RUN_FIRST,
421                               0,
422                               NULL, NULL,
423                               soup_marshal_NONE__OBJECT,
424                               G_TYPE_NONE, 1,
425                               /* SoupConnection is private, so we can't use
426                                * SOUP_TYPE_CONNECTION here.
427                                */
428                               G_TYPE_OBJECT);
429
430
431         /* properties */
432         /**
433          * SOUP_SESSION_PROXY_URI:
434          *
435          * Alias for the #SoupSession:proxy-uri property. (The HTTP
436          * proxy to use for this session.)
437          **/
438         g_object_class_install_property (
439                 object_class, PROP_PROXY_URI,
440                 g_param_spec_boxed (SOUP_SESSION_PROXY_URI,
441                                     "Proxy URI",
442                                     "The HTTP Proxy to use for this session",
443                                     SOUP_TYPE_URI,
444                                     G_PARAM_READWRITE));
445         /**
446          * SOUP_SESSION_MAX_CONNS:
447          *
448          * Alias for the #SoupSession:max-conns property. (The maximum
449          * number of connections that the session can open at once.)
450          **/
451         g_object_class_install_property (
452                 object_class, PROP_MAX_CONNS,
453                 g_param_spec_int (SOUP_SESSION_MAX_CONNS,
454                                   "Max Connection Count",
455                                   "The maximum number of connections that the session can open at once",
456                                   1,
457                                   G_MAXINT,
458                                   SOUP_SESSION_MAX_CONNS_DEFAULT,
459                                   G_PARAM_READWRITE));
460         /**
461          * SOUP_SESSION_MAX_CONNS_PER_HOST:
462          *
463          * Alias for the #SoupSession:max-conns-per-host property.
464          * (The maximum number of connections that the session can
465          * open at once to a given host.)
466          **/
467         g_object_class_install_property (
468                 object_class, PROP_MAX_CONNS_PER_HOST,
469                 g_param_spec_int (SOUP_SESSION_MAX_CONNS_PER_HOST,
470                                   "Max Per-Host Connection Count",
471                                   "The maximum number of connections that the session can open at once to a given host",
472                                   1,
473                                   G_MAXINT,
474                                   SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT,
475                                   G_PARAM_READWRITE));
476         /**
477          * SoupSession:idle-timeout:
478          *
479          * Connection lifetime when idle
480          *
481          * Since: 2.4.1
482          **/
483         /**
484          * SOUP_SESSION_IDLE_TIMEOUT:
485          *
486          * Alias for the #SoupSession:idle-timeout property. (The idle
487          * connection lifetime.)
488          *
489          * Since: 2.4.1
490          **/
491         g_object_class_install_property (
492                 object_class, PROP_IDLE_TIMEOUT,
493                 g_param_spec_uint (SOUP_SESSION_IDLE_TIMEOUT,
494                                    "Idle Timeout",
495                                    "Connection lifetime when idle",
496                                    0, G_MAXUINT, 0,
497                                    G_PARAM_READWRITE));
498         /**
499          * SoupSession:use-ntlm:
500          *
501          * Whether or not to use NTLM authentication.
502          *
503          * Deprecated: use soup_session_add_feature_by_type() with
504          * #SOUP_TYPE_AUTH_NTLM.
505          **/
506         /**
507          * SOUP_SESSION_USE_NTLM:
508          *
509          * Alias for the #SoupSession:use-ntlm property. (Whether or
510          * not to use NTLM authentication.)
511          **/
512         g_object_class_install_property (
513                 object_class, PROP_USE_NTLM,
514                 g_param_spec_boolean (SOUP_SESSION_USE_NTLM,
515                                       "Use NTLM",
516                                       "Whether or not to use NTLM authentication",
517                                       FALSE,
518                                       G_PARAM_READWRITE));
519         /**
520          * SOUP_SESSION_SSL_CA_FILE:
521          *
522          * Alias for the #SoupSession:ssl-ca-file property. (File
523          * containing SSL CA certificates.)
524          **/
525         g_object_class_install_property (
526                 object_class, PROP_SSL_CA_FILE,
527                 g_param_spec_string (SOUP_SESSION_SSL_CA_FILE,
528                                      "SSL CA file",
529                                      "File containing SSL CA certificates",
530                                      NULL,
531                                      G_PARAM_READWRITE));
532         /**
533          * SOUP_SESSION_SSL_STRICT:
534          *
535          * Alias for the #SoupSession:ignore-ssl-cert-errors
536          * property. By default, when validating certificates against
537          * a CA file, Soup will consider invalid certificates as a
538          * connection error. Setting this property to %TRUE makes soup
539          * ignore the errors, and make the connection.
540          *
541          * Since: 2.30
542          **/
543         g_object_class_install_property (
544                 object_class, PROP_SSL_STRICT,
545                 g_param_spec_boolean (SOUP_SESSION_SSL_STRICT,
546                                       "Strictly validate SSL certificates",
547                                       "Whether certificate errors should be considered a connection error",
548                                       TRUE,
549                                       G_PARAM_READWRITE));
550         /**
551          * SOUP_SESSION_ASYNC_CONTEXT:
552          *
553          * Alias for the #SoupSession:async-context property. (The
554          * session's #GMainContext.)
555          **/
556         g_object_class_install_property (
557                 object_class, PROP_ASYNC_CONTEXT,
558                 g_param_spec_pointer (SOUP_SESSION_ASYNC_CONTEXT,
559                                       "Async GMainContext",
560                                       "The GMainContext to dispatch async I/O in",
561                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
562         /**
563          * SOUP_SESSION_TIMEOUT:
564          *
565          * Alias for the #SoupSession:timeout property. (The timeout
566          * in seconds for blocking socket I/O operations.)
567          **/
568         g_object_class_install_property (
569                 object_class, PROP_TIMEOUT,
570                 g_param_spec_uint (SOUP_SESSION_TIMEOUT,
571                                    "Timeout value",
572                                    "Value in seconds to timeout a blocking I/O",
573                                    0, G_MAXUINT, 0,
574                                    G_PARAM_READWRITE));
575
576         /**
577          * SoupSession:user-agent:
578          *
579          * If non-%NULL, the value to use for the "User-Agent" header
580          * on #SoupMessage<!-- -->s sent from this session.
581          *
582          * RFC 2616 says: "The User-Agent request-header field
583          * contains information about the user agent originating the
584          * request. This is for statistical purposes, the tracing of
585          * protocol violations, and automated recognition of user
586          * agents for the sake of tailoring responses to avoid
587          * particular user agent limitations. User agents SHOULD
588          * include this field with requests."
589          *
590          * The User-Agent header contains a list of one or more
591          * product tokens, separated by whitespace, with the most
592          * significant product token coming first. The tokens must be
593          * brief, ASCII, and mostly alphanumeric (although "-", "_",
594          * and "." are also allowed), and may optionally include a "/"
595          * followed by a version string. You may also put comments,
596          * enclosed in parentheses, between or after the tokens.
597          *
598          * If you set a %user_agent property that has trailing
599          * whitespace, #SoupSession will append its own product token
600          * (eg, "<literal>libsoup/2.3.2</literal>") to the end of the
601          * header for you.
602          **/
603         /**
604          * SOUP_SESSION_USER_AGENT:
605          *
606          * Alias for the #SoupSession:user-agent property, qv.
607          **/
608         g_object_class_install_property (
609                 object_class, PROP_USER_AGENT,
610                 g_param_spec_string (SOUP_SESSION_USER_AGENT,
611                                      "User-Agent string",
612                                      "User-Agent string",
613                                      NULL,
614                                      G_PARAM_READWRITE));
615
616         /**
617          * SoupSession:accept-language:
618          *
619          * If non-%NULL, the value to use for the "Accept-Language" header
620          * on #SoupMessage<!-- -->s sent from this session.
621          *
622          * Setting this will disable
623          * #SoupSession:accept-language-auto.
624          *
625          * Since: 2.30
626          **/
627         /**
628          * SOUP_SESSION_ACCEPT_LANGUAGE:
629          *
630          * Alias for the #SoupSession:accept-language property, qv.
631          *
632          * Since: 2.30
633          **/
634         g_object_class_install_property (
635                 object_class, PROP_ACCEPT_LANGUAGE,
636                 g_param_spec_string (SOUP_SESSION_ACCEPT_LANGUAGE,
637                                      "Accept-Language string",
638                                      "Accept-Language string",
639                                      NULL,
640                                      G_PARAM_READWRITE));
641
642         /**
643          * SoupSession:accept-language-auto:
644          *
645          * If %TRUE, #SoupSession will automatically set the string
646          * for the "Accept-Language" header on every #SoupMessage
647          * sent, based on the return value of g_get_language_names().
648          *
649          * Setting this will override any previous value of
650          * #SoupSession:accept-language.
651          *
652          * Since: 2.30
653          **/
654         /**
655          * SOUP_SESSION_ACCEPT_LANGUAGE_AUTO:
656          *
657          * Alias for the #SoupSession:accept-language-auto property, qv.
658          *
659          * Since: 2.30
660          **/
661         g_object_class_install_property (
662                 object_class, PROP_ACCEPT_LANGUAGE_AUTO,
663                 g_param_spec_boolean (SOUP_SESSION_ACCEPT_LANGUAGE_AUTO,
664                                       "Accept-Language automatic mode",
665                                       "Accept-Language automatic mode",
666                                       FALSE,
667                                       G_PARAM_READWRITE));
668
669         /**
670          * SoupSession:add-feature: (skip)
671          *
672          * Add a feature object to the session. (Shortcut for calling
673          * soup_session_add_feature().)
674          *
675          * Since: 2.24
676          **/
677         /**
678          * SOUP_SESSION_ADD_FEATURE: (skip)
679          *
680          * Alias for the #SoupSession:add-feature property. (Shortcut
681          * for calling soup_session_add_feature().
682          *
683          * Since: 2.24
684          **/
685         g_object_class_install_property (
686                 object_class, PROP_ADD_FEATURE,
687                 g_param_spec_object (SOUP_SESSION_ADD_FEATURE,
688                                      "Add Feature",
689                                      "Add a feature object to the session",
690                                      SOUP_TYPE_SESSION_FEATURE,
691                                      G_PARAM_READWRITE));
692         /**
693          * SoupSession:add-feature-by-type: (skip)
694          *
695          * Add a feature object of the given type to the session.
696          * (Shortcut for calling soup_session_add_feature_by_type().)
697          *
698          * Since: 2.24
699          **/
700         /**
701          * SOUP_SESSION_ADD_FEATURE_BY_TYPE: (skip)
702          *
703          * Alias for the #SoupSession:add-feature-by-type property.
704          * (Shortcut for calling soup_session_add_feature_by_type().
705          *
706          * Since: 2.24
707          **/
708         g_object_class_install_property (
709                 object_class, PROP_ADD_FEATURE_BY_TYPE,
710                 g_param_spec_gtype (SOUP_SESSION_ADD_FEATURE_BY_TYPE,
711                                     "Add Feature By Type",
712                                     "Add a feature object of the given type to the session",
713                                     SOUP_TYPE_SESSION_FEATURE,
714                                     G_PARAM_READWRITE));
715         /**
716          * SoupSession:remove-feature-by-type: (skip)
717          *
718          * Remove feature objects from the session. (Shortcut for
719          * calling soup_session_remove_feature_by_type().)
720          *
721          * Since: 2.24
722          **/
723         /**
724          * SOUP_SESSION_REMOVE_FEATURE_BY_TYPE: (skip)
725          *
726          * Alias for the #SoupSession:remove-feature-by-type
727          * property. (Shortcut for calling
728          * soup_session_remove_feature_by_type().
729          *
730          * Since: 2.24
731          **/
732         g_object_class_install_property (
733                 object_class, PROP_REMOVE_FEATURE_BY_TYPE,
734                 g_param_spec_gtype (SOUP_SESSION_REMOVE_FEATURE_BY_TYPE,
735                                     "Remove Feature By Type",
736                                     "Remove features of the given type from the session",
737                                     SOUP_TYPE_SESSION_FEATURE,
738                                     G_PARAM_READWRITE));
739 }
740
741 static gboolean
742 safe_str_equal (const char *a, const char *b)
743 {
744         if (!a && !b)
745                 return TRUE;
746
747         if ((a && !b) || (b && !a))
748                 return FALSE;
749
750         return strcmp (a, b) == 0;
751 }
752
753 /* Converts a language in POSIX format and to be RFC2616 compliant    */
754 /* Based on code from epiphany-webkit (ephy_langs_append_languages()) */
755 static gchar *
756 posix_lang_to_rfc2616 (const gchar *language)
757 {
758         /* Don't include charset variants, etc */
759         if (strchr (language, '.') || strchr (language, '@'))
760                 return NULL;
761
762         /* Ignore "C" locale, which g_get_language_names() always
763          * includes as a fallback.
764          */
765         if (!strcmp (language, "C"))
766                 return NULL;
767
768         return g_strdelimit (g_ascii_strdown (language, -1), "_", '-');
769 }
770
771 /* Converts @quality from 0-100 to 0.0-1.0 and appends to @str */
772 static gchar *
773 add_quality_value (const gchar *str, int quality)
774 {
775         g_return_val_if_fail (str != NULL, NULL);
776
777         if (quality >= 0 && quality < 100) {
778                 /* We don't use %.02g because of "." vs "," locale issues */
779                 if (quality % 10)
780                         return g_strdup_printf ("%s;q=0.%02d", str, quality);
781                 else
782                         return g_strdup_printf ("%s;q=0.%d", str, quality / 10);
783         } else
784                 return g_strdup (str);
785 }
786
787 /* Returns a RFC2616 compliant languages list from system locales */
788 static gchar *
789 accept_languages_from_system (void)
790 {
791         const char * const * lang_names;
792         GPtrArray *langs = NULL;
793         char *lang, **langs_array, *langs_str;
794         int delta;
795         int i;
796
797         lang_names = g_get_language_names ();
798         g_return_val_if_fail (lang_names != NULL, NULL);
799
800         /* Build the array of languages */
801         langs = g_ptr_array_new ();
802         for (i = 0; lang_names[i] != NULL; i++) {
803                 lang = posix_lang_to_rfc2616 (lang_names[i]);
804                 if (lang)
805                         g_ptr_array_add (langs, lang);
806         }
807
808         /* Add quality values */
809         if (langs->len < 10)
810                 delta = 10;
811         else if (langs->len < 20)
812                 delta = 5;
813         else
814                 delta = 1;
815
816         for (i = 0; i < langs->len; i++) {
817                 lang = langs->pdata[i];
818                 langs->pdata[i] = add_quality_value (lang, 100 - i * delta);
819                 g_free (lang);
820         }
821
822         /* Fallback: add "en" if list is empty */
823         if (langs->len == 0)
824                 g_ptr_array_add (langs, g_strdup ("en"));
825
826         g_ptr_array_add (langs, NULL);
827         langs_array = (char **)langs->pdata;
828         langs_str = g_strjoinv (", ", langs_array);
829
830         g_strfreev (langs_array);
831         g_ptr_array_free (langs, FALSE);
832
833         return langs_str;
834 }
835
836 static void
837 set_property (GObject *object, guint prop_id,
838               const GValue *value, GParamSpec *pspec)
839 {
840         SoupSession *session = SOUP_SESSION (object);
841         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
842         SoupURI *uri;
843         gboolean ca_file_changed = FALSE;
844         const char *new_ca_file, *user_agent;
845         SoupSessionFeature *feature;
846
847         switch (prop_id) {
848         case PROP_PROXY_URI:
849                 uri = g_value_get_boxed (value);
850
851                 if (uri) {
852                         soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER);
853                         feature = SOUP_SESSION_FEATURE (soup_proxy_resolver_static_new (uri));
854                         soup_session_add_feature (session, feature);
855                         g_object_unref (feature);
856                 } else
857                         soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
858
859                 soup_session_abort (session);
860                 break;
861         case PROP_MAX_CONNS:
862                 priv->max_conns = g_value_get_int (value);
863                 break;
864         case PROP_MAX_CONNS_PER_HOST:
865                 priv->max_conns_per_host = g_value_get_int (value);
866                 break;
867         case PROP_USE_NTLM:
868                 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
869                 if (feature) {
870                         if (g_value_get_boolean (value))
871                                 soup_session_feature_add_feature (feature, SOUP_TYPE_AUTH_NTLM);
872                         else
873                                 soup_session_feature_remove_feature (feature, SOUP_TYPE_AUTH_NTLM);
874                 } else
875                         g_warning ("Trying to set use-ntlm on session with no auth-manager");
876                 break;
877         case PROP_SSL_CA_FILE:
878                 new_ca_file = g_value_get_string (value);
879
880                 if (!safe_str_equal (priv->ssl_ca_file, new_ca_file))
881                         ca_file_changed = TRUE;
882
883                 g_free (priv->ssl_ca_file);
884                 priv->ssl_ca_file = g_strdup (new_ca_file);
885
886                 if (ca_file_changed && priv->ssl_creds) {
887                         soup_ssl_free_client_credentials (priv->ssl_creds);
888                         priv->ssl_creds = NULL;
889                 }
890
891                 break;
892         case PROP_SSL_STRICT:
893                 priv->ssl_strict = g_value_get_boolean (value);
894                 break;
895         case PROP_ASYNC_CONTEXT:
896                 priv->async_context = g_value_get_pointer (value);
897                 if (priv->async_context)
898                         g_main_context_ref (priv->async_context);
899                 break;
900         case PROP_TIMEOUT:
901                 priv->io_timeout = g_value_get_uint (value);
902                 break;
903         case PROP_USER_AGENT:
904                 g_free (priv->user_agent);
905                 user_agent = g_value_get_string (value);
906                 if (!user_agent)
907                         priv->user_agent = NULL;
908                 else if (!*user_agent) {
909                         priv->user_agent =
910                                 g_strdup (SOUP_SESSION_USER_AGENT_BASE);
911                 } else if (g_str_has_suffix (user_agent, " ")) {
912                         priv->user_agent =
913                                 g_strdup_printf ("%s%s", user_agent,
914                                                  SOUP_SESSION_USER_AGENT_BASE);
915                 } else
916                         priv->user_agent = g_strdup (user_agent);
917                 break;
918         case PROP_ACCEPT_LANGUAGE:
919                 g_free (priv->accept_language);
920                 priv->accept_language = g_strdup (g_value_get_string (value));
921                 priv->accept_language_auto = FALSE;
922                 break;
923         case PROP_ACCEPT_LANGUAGE_AUTO:
924                 priv->accept_language_auto = g_value_get_boolean (value);
925                 if (priv->accept_language) {
926                         g_free (priv->accept_language);
927                         priv->accept_language = NULL;
928                 }
929
930                 /* Get languages from system if needed */
931                 if (priv->accept_language_auto)
932                         priv->accept_language = accept_languages_from_system ();
933                 break;
934         case PROP_IDLE_TIMEOUT:
935                 priv->idle_timeout = g_value_get_uint (value);
936                 break;
937         case PROP_ADD_FEATURE:
938                 soup_session_add_feature (session, g_value_get_object (value));
939                 break;
940         case PROP_ADD_FEATURE_BY_TYPE:
941                 soup_session_add_feature_by_type (session, g_value_get_gtype (value));
942                 break;
943         case PROP_REMOVE_FEATURE_BY_TYPE:
944                 soup_session_remove_feature_by_type (session, g_value_get_gtype (value));
945                 break;
946         default:
947                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
948                 break;
949         }
950 }
951
952 static void
953 get_property (GObject *object, guint prop_id,
954               GValue *value, GParamSpec *pspec)
955 {
956         SoupSession *session = SOUP_SESSION (object);
957         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
958         SoupSessionFeature *feature;
959
960         switch (prop_id) {
961         case PROP_PROXY_URI:
962                 feature = soup_session_get_feature (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
963                 if (feature) {
964                         g_object_get_property (G_OBJECT (feature),
965                                                SOUP_PROXY_RESOLVER_STATIC_PROXY_URI,
966                                                value);
967                 } else
968                         g_value_set_boxed (value, NULL);
969                 break;
970         case PROP_MAX_CONNS:
971                 g_value_set_int (value, priv->max_conns);
972                 break;
973         case PROP_MAX_CONNS_PER_HOST:
974                 g_value_set_int (value, priv->max_conns_per_host);
975                 break;
976         case PROP_USE_NTLM:
977                 feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
978                 if (feature)
979                         g_value_set_boolean (value, soup_session_feature_has_feature (feature, SOUP_TYPE_AUTH_NTLM));
980                 else
981                         g_value_set_boolean (value, FALSE);
982                 break;
983         case PROP_SSL_CA_FILE:
984                 g_value_set_string (value, priv->ssl_ca_file);
985                 break;
986         case PROP_SSL_STRICT:
987                 g_value_set_boolean (value, priv->ssl_strict);
988                 break;
989         case PROP_ASYNC_CONTEXT:
990                 g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
991                 break;
992         case PROP_TIMEOUT:
993                 g_value_set_uint (value, priv->io_timeout);
994                 break;
995         case PROP_USER_AGENT:
996                 g_value_set_string (value, priv->user_agent);
997                 break;
998         case PROP_ACCEPT_LANGUAGE:
999                 g_value_set_string (value, priv->accept_language);
1000                 break;
1001         case PROP_ACCEPT_LANGUAGE_AUTO:
1002                 g_value_set_boolean (value, priv->accept_language_auto);
1003                 break;
1004         case PROP_IDLE_TIMEOUT:
1005                 g_value_set_uint (value, priv->idle_timeout);
1006                 break;
1007         default:
1008                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1009                 break;
1010         }
1011 }
1012
1013
1014 /**
1015  * soup_session_get_async_context:
1016  * @session: a #SoupSession
1017  *
1018  * Gets @session's async_context. This does not add a ref to the
1019  * context, so you will need to ref it yourself if you want it to
1020  * outlive its session.
1021  *
1022  * Return value: (transfer none): @session's #GMainContext, which may
1023  * be %NULL
1024  **/
1025 GMainContext *
1026 soup_session_get_async_context (SoupSession *session)
1027 {
1028         SoupSessionPrivate *priv;
1029
1030         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
1031         priv = SOUP_SESSION_GET_PRIVATE (session);
1032
1033         return priv->async_context;
1034 }
1035
1036 /* Hosts */
1037
1038 static SoupSessionHost *
1039 soup_session_host_new (SoupSession *session, SoupURI *uri)
1040 {
1041         SoupSessionHost *host;
1042
1043         host = g_slice_new0 (SoupSessionHost);
1044         host->uri = soup_uri_copy_host (uri);
1045         host->addr = soup_address_new (host->uri->host, host->uri->port);
1046
1047         return host;
1048 }
1049
1050 /* Requires host_lock to be locked */
1051 static SoupSessionHost *
1052 get_host_for_uri (SoupSession *session, SoupURI *uri)
1053 {
1054         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1055         SoupSessionHost *host;
1056
1057         host = g_hash_table_lookup (priv->hosts, uri);
1058         if (host)
1059                 return host;
1060
1061         host = soup_session_host_new (session, uri);
1062         g_hash_table_insert (priv->hosts, host->uri, host);
1063
1064         return host;
1065 }
1066
1067 /* Note: get_host_for_message doesn't lock the host_lock. The caller
1068  * must do it itself if there's a chance the host doesn't already
1069  * exist.
1070  */
1071 static SoupSessionHost *
1072 get_host_for_message (SoupSession *session, SoupMessage *msg)
1073 {
1074         return get_host_for_uri (session, soup_message_get_uri (msg));
1075 }
1076
1077 static void
1078 free_host (SoupSessionHost *host)
1079 {
1080         while (host->connections) {
1081                 SoupConnection *conn = host->connections->data;
1082
1083                 host->connections = g_slist_remove (host->connections, conn);
1084                 soup_connection_disconnect (conn);
1085         }
1086
1087         soup_uri_free (host->uri);
1088         g_object_unref (host->addr);
1089         g_slice_free (SoupSessionHost, host);
1090 }       
1091
1092 static void
1093 auth_required (SoupSession *session, SoupMessage *msg,
1094                SoupAuth *auth, gboolean retrying)
1095 {
1096         g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
1097 }
1098
1099 static void
1100 auth_manager_authenticate (SoupAuthManager *manager, SoupMessage *msg,
1101                            SoupAuth *auth, gboolean retrying,
1102                            gpointer session)
1103 {
1104         SOUP_SESSION_GET_CLASS (session)->auth_required (
1105                 session, msg, auth, retrying);
1106 }
1107
1108 #define SOUP_METHOD_IS_SAFE(method) (method == SOUP_METHOD_GET || \
1109                                      method == SOUP_METHOD_HEAD || \
1110                                      method == SOUP_METHOD_OPTIONS || \
1111                                      method == SOUP_METHOD_PROPFIND)
1112
1113 static void
1114 redirect_handler (SoupMessage *msg, gpointer user_data)
1115 {
1116         SoupMessageQueueItem *item = user_data;
1117         SoupSession *session = item->session;
1118         const char *new_loc;
1119         SoupURI *new_uri;
1120
1121         new_loc = soup_message_headers_get_one (msg->response_headers,
1122                                                 "Location");
1123         g_return_if_fail (new_loc != NULL);
1124
1125         if (item->redirection_count >= SOUP_SESSION_MAX_REDIRECTION_COUNT) {
1126                 soup_session_cancel_message (session, msg, SOUP_STATUS_TOO_MANY_REDIRECTS);
1127                 return;
1128         }
1129         item->redirection_count++;
1130
1131         if (msg->status_code == SOUP_STATUS_SEE_OTHER ||
1132             (msg->status_code == SOUP_STATUS_FOUND &&
1133              !SOUP_METHOD_IS_SAFE (msg->method)) ||
1134             (msg->status_code == SOUP_STATUS_MOVED_PERMANENTLY &&
1135              msg->method == SOUP_METHOD_POST)) {
1136                 if (msg->method != SOUP_METHOD_HEAD) {
1137                         /* Redirect using a GET */
1138                         g_object_set (msg,
1139                                       SOUP_MESSAGE_METHOD, SOUP_METHOD_GET,
1140                                       NULL);
1141                 }
1142                 soup_message_set_request (msg, NULL,
1143                                           SOUP_MEMORY_STATIC, NULL, 0);
1144                 soup_message_headers_set_encoding (msg->request_headers,
1145                                                    SOUP_ENCODING_NONE);
1146         } else if (msg->status_code == SOUP_STATUS_MOVED_PERMANENTLY ||
1147                    msg->status_code == SOUP_STATUS_TEMPORARY_REDIRECT ||
1148                    msg->status_code == SOUP_STATUS_FOUND) {
1149                 /* Don't redirect non-safe methods */
1150                 if (!SOUP_METHOD_IS_SAFE (msg->method))
1151                         return;
1152         } else {
1153                 /* Three possibilities:
1154                  *
1155                  *   1) This was a non-3xx response that happened to
1156                  *      have a "Location" header
1157                  *   2) It's a non-redirecty 3xx response (300, 304,
1158                  *      305, 306)
1159                  *   3) It's some newly-defined 3xx response (308+)
1160                  *
1161                  * We ignore all of these cases. In the first two,
1162                  * redirecting would be explicitly wrong, and in the
1163                  * last case, we have no clue if the 3xx response is
1164                  * supposed to be redirecty or non-redirecty. Plus,
1165                  * 2616 says unrecognized status codes should be
1166                  * treated as the equivalent to the x00 code, and we
1167                  * don't redirect on 300, so therefore we shouldn't
1168                  * redirect on 308+ either.
1169                  */
1170                 return;
1171         }
1172
1173         /* Location is supposed to be an absolute URI, but some sites
1174          * are lame, so we use soup_uri_new_with_base().
1175          */
1176         new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
1177         if (!new_uri || !new_uri->host) {
1178                 if (new_uri)
1179                         soup_uri_free (new_uri);
1180                 soup_message_set_status_full (msg,
1181                                               SOUP_STATUS_MALFORMED,
1182                                               "Invalid Redirect URL");
1183                 return;
1184         }
1185
1186         soup_message_set_uri (msg, new_uri);
1187         soup_uri_free (new_uri);
1188
1189         soup_session_requeue_message (session, msg);
1190 }
1191
1192 void
1193 soup_session_send_queue_item (SoupSession *session,
1194                               SoupMessageQueueItem *item,
1195                               SoupMessageCompletionFn completion_cb)
1196 {
1197         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1198         const char *conn_header;
1199
1200         if (priv->user_agent) {
1201                 soup_message_headers_replace (item->msg->request_headers,
1202                                               "User-Agent", priv->user_agent);
1203         }
1204
1205         if (priv->accept_language &&
1206             !soup_message_headers_get_list (item->msg->request_headers,
1207                                             "Accept-Language")) {
1208                 soup_message_headers_append (item->msg->request_headers,
1209                                              "Accept-Language",
1210                                              priv->accept_language);
1211         }
1212
1213         /* Force keep alive connections for HTTP 1.0. Performance will
1214          * improve when issuing multiple requests to the same host in
1215          * a short period of time, as we wouldn't need to establish
1216          * new connections. Keep alive is implicit for HTTP 1.1.
1217          */
1218         conn_header = soup_message_headers_get_list (item->msg->request_headers, "Connection");
1219         if (!conn_header ||
1220             (!soup_header_contains (conn_header, "Keep-Alive") &&
1221              !soup_header_contains (conn_header, "close")))
1222                 soup_message_headers_append (item->msg->request_headers,
1223                                              "Connection", "Keep-Alive");
1224
1225         g_signal_emit (session, signals[REQUEST_STARTED], 0,
1226                        item->msg, soup_connection_get_socket (item->conn));
1227         soup_connection_send_request (item->conn, item, completion_cb, item);
1228 }
1229
1230 gboolean
1231 soup_session_cleanup_connections (SoupSession *session,
1232                                   gboolean     prune_idle)
1233 {
1234         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1235         GSList *conns = NULL, *c;
1236         GHashTableIter iter;
1237         gpointer conn, host;
1238         SoupConnectionState state;
1239
1240         g_mutex_lock (priv->host_lock);
1241         g_hash_table_iter_init (&iter, priv->conns);
1242         while (g_hash_table_iter_next (&iter, &conn, &host)) {
1243                 state = soup_connection_get_state (conn);
1244                 if (state == SOUP_CONNECTION_REMOTE_DISCONNECTED ||
1245                     (prune_idle && state == SOUP_CONNECTION_IDLE))
1246                         conns = g_slist_prepend (conns, g_object_ref (conn));
1247         }
1248         g_mutex_unlock (priv->host_lock);
1249
1250         if (!conns)
1251                 return FALSE;
1252
1253         for (c = conns; c; c = c->next) {
1254                 conn = c->data;
1255                 soup_connection_disconnect (conn);
1256                 g_object_unref (conn);
1257         }
1258         g_slist_free (conns);
1259
1260         return TRUE;
1261 }
1262
1263 static void
1264 connection_disconnected (SoupConnection *conn, gpointer user_data)
1265 {
1266         SoupSession *session = user_data;
1267         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1268         SoupSessionHost *host;
1269
1270         g_mutex_lock (priv->host_lock);
1271
1272         host = g_hash_table_lookup (priv->conns, conn);
1273         if (host) {
1274                 g_hash_table_remove (priv->conns, conn);
1275                 host->connections = g_slist_remove (host->connections, conn);
1276                 host->num_conns--;
1277
1278                 if (soup_connection_get_ssl_fallback (conn))
1279                         host->ssl_fallback = TRUE;
1280         }
1281
1282         g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
1283         priv->num_conns--;
1284
1285         g_mutex_unlock (priv->host_lock);
1286         g_object_unref (conn);
1287 }
1288
1289 SoupMessageQueueItem *
1290 soup_session_make_connect_message (SoupSession    *session,
1291                                    SoupConnection *conn)
1292 {
1293         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1294         SoupAddress *server_addr = soup_connection_get_tunnel_addr (conn);
1295         SoupURI *uri;
1296         SoupMessage *msg;
1297         SoupMessageQueueItem *item;
1298
1299         uri = soup_uri_new (NULL);
1300         soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTPS);
1301         soup_uri_set_host (uri, soup_address_get_name (server_addr));
1302         soup_uri_set_port (uri, soup_address_get_port (server_addr));
1303         soup_uri_set_path (uri, "");
1304         msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT, uri);
1305         soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
1306         soup_uri_free (uri);
1307
1308         /* Call the base implementation of soup_session_queue_message
1309          * directly, to add msg to the SoupMessageQueue and cause all
1310          * the right signals to be emitted.
1311          */
1312         queue_message (session, msg, NULL, NULL);
1313         item = soup_message_queue_lookup (priv->queue, msg);
1314         item->conn = g_object_ref (conn);
1315         g_object_unref (msg);
1316
1317         g_signal_emit (session, signals[TUNNELING], 0, conn);
1318         return item;
1319 }
1320
1321 gboolean
1322 soup_session_get_connection (SoupSession *session,
1323                              SoupMessageQueueItem *item,
1324                              gboolean *try_pruning)
1325 {
1326         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1327         SoupConnection *conn;
1328         SoupSessionHost *host;
1329         SoupAddress *remote_addr, *tunnel_addr;
1330         SoupSSLCredentials *ssl_creds;
1331         GSList *conns;
1332         int num_pending = 0;
1333         SoupURI *uri;
1334
1335         if (item->conn) {
1336                 g_return_val_if_fail (soup_connection_get_state (item->conn) != SOUP_CONNECTION_DISCONNECTED, FALSE);
1337                 return TRUE;
1338         }
1339
1340         g_mutex_lock (priv->host_lock);
1341
1342         host = get_host_for_message (session, item->msg);
1343         for (conns = host->connections; conns; conns = conns->next) {
1344                 if (soup_connection_get_state (conns->data) == SOUP_CONNECTION_IDLE) {
1345                         soup_connection_set_state (conns->data, SOUP_CONNECTION_IN_USE);
1346                         g_mutex_unlock (priv->host_lock);
1347                         item->conn = g_object_ref (conns->data);
1348                         return TRUE;
1349                 } else if (soup_connection_get_state (conns->data) == SOUP_CONNECTION_CONNECTING)
1350                         num_pending++;
1351         }
1352
1353         /* Limit the number of pending connections; num_messages / 2
1354          * is somewhat arbitrary...
1355          */
1356         if (num_pending > host->num_messages / 2) {
1357                 g_mutex_unlock (priv->host_lock);
1358                 return FALSE;
1359         }
1360
1361         if (host->num_conns >= priv->max_conns_per_host) {
1362                 g_mutex_unlock (priv->host_lock);
1363                 return FALSE;
1364         }
1365
1366         if (priv->num_conns >= priv->max_conns) {
1367                 *try_pruning = TRUE;
1368                 g_mutex_unlock (priv->host_lock);
1369                 return FALSE;
1370         }
1371
1372         if (item->proxy_addr) {
1373                 remote_addr = item->proxy_addr;
1374                 tunnel_addr = NULL;
1375         } else {
1376                 remote_addr = host->addr;
1377                 tunnel_addr = NULL;
1378         }
1379
1380         uri = soup_message_get_uri (item->msg);
1381         if (uri->scheme == SOUP_URI_SCHEME_HTTPS) {
1382                 if (!priv->ssl_creds)
1383                         priv->ssl_creds = soup_ssl_get_client_credentials (priv->ssl_ca_file);
1384                 ssl_creds = priv->ssl_creds;
1385
1386                 if (item->proxy_addr)
1387                         tunnel_addr = host->addr;
1388         } else
1389                 ssl_creds = NULL;
1390
1391         conn = soup_connection_new (
1392                 SOUP_CONNECTION_REMOTE_ADDRESS, remote_addr,
1393                 SOUP_CONNECTION_TUNNEL_ADDRESS, tunnel_addr,
1394                 SOUP_CONNECTION_PROXY_URI, item->proxy_uri,
1395                 SOUP_CONNECTION_SSL_CREDENTIALS, ssl_creds,
1396                 SOUP_CONNECTION_SSL_STRICT, priv->ssl_strict,
1397                 SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
1398                 SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
1399                 SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
1400                 SOUP_CONNECTION_SSL_FALLBACK, host->ssl_fallback,
1401                 NULL);
1402         g_signal_connect (conn, "disconnected",
1403                           G_CALLBACK (connection_disconnected),
1404                           session);
1405
1406         g_signal_emit (session, signals[CONNECTION_CREATED], 0, conn);
1407
1408         g_hash_table_insert (priv->conns, conn, host);
1409
1410         priv->num_conns++;
1411         host->num_conns++;
1412         host->connections = g_slist_prepend (host->connections, conn);
1413
1414         g_mutex_unlock (priv->host_lock);
1415         item->conn = g_object_ref (conn);
1416         return TRUE;
1417 }
1418
1419 SoupMessageQueue *
1420 soup_session_get_queue (SoupSession *session)
1421 {
1422         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1423
1424         return priv->queue;
1425 }
1426
1427 void
1428 soup_session_unqueue_item (SoupSession          *session,
1429                            SoupMessageQueueItem *item)
1430 {
1431         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1432         SoupSessionHost *host;
1433
1434         if (item->conn) {
1435                 g_object_unref (item->conn);
1436                 item->conn = NULL;
1437         }
1438
1439         if (item->state != SOUP_MESSAGE_FINISHED) {
1440                 g_warning ("finished an item with state %d", item->state);
1441                 return;
1442         }
1443
1444         soup_message_queue_remove (priv->queue, item);
1445
1446         g_mutex_lock (priv->host_lock);
1447         host = get_host_for_message (session, item->msg);
1448         host->num_messages--;
1449         g_mutex_unlock (priv->host_lock);
1450
1451         /* g_signal_handlers_disconnect_by_func doesn't work if you
1452          * have a metamarshal, meaning it doesn't work with
1453          * soup_message_add_header_handler()
1454          */
1455         g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
1456                                               0, 0, NULL, NULL, item);
1457         g_signal_emit (session, signals[REQUEST_UNQUEUED], 0, item->msg);
1458         soup_message_queue_item_unref (item);
1459 }
1460
1461 void
1462 soup_session_set_item_status (SoupSession          *session,
1463                               SoupMessageQueueItem *item,
1464                               guint                 status_code)
1465 {
1466         SoupURI *uri;
1467         char *msg;
1468
1469         switch (status_code) {
1470         case SOUP_STATUS_CANT_RESOLVE:
1471         case SOUP_STATUS_CANT_CONNECT:
1472                 uri = soup_message_get_uri (item->msg);
1473                 msg = g_strdup_printf ("%s (%s)",
1474                                        soup_status_get_phrase (status_code),
1475                                        uri->host);
1476                 soup_message_set_status_full (item->msg, status_code, msg);
1477                 g_free (msg);
1478                 break;
1479
1480         case SOUP_STATUS_CANT_RESOLVE_PROXY:
1481         case SOUP_STATUS_CANT_CONNECT_PROXY:
1482                 if (item->proxy_uri && item->proxy_uri->host) {
1483                         msg = g_strdup_printf ("%s (%s)",
1484                                                soup_status_get_phrase (status_code),
1485                                                item->proxy_uri->host);
1486                         soup_message_set_status_full (item->msg, status_code, msg);
1487                         g_free (msg);
1488                         break;
1489                 }
1490                 soup_message_set_status (item->msg, status_code);
1491                 break;
1492
1493         case SOUP_STATUS_SSL_FAILED:
1494                 if (!g_tls_backend_supports_tls (g_tls_backend_get_default ())) {
1495                         soup_message_set_status_full (item->msg, status_code,
1496                                                       "TLS/SSL support not available; install glib-networking");
1497                 } else
1498                         soup_message_set_status (item->msg, status_code);
1499                 break;
1500
1501         default:
1502                 soup_message_set_status (item->msg, status_code);
1503                 break;
1504         }
1505 }
1506
1507 static void
1508 queue_message (SoupSession *session, SoupMessage *msg,
1509                SoupSessionCallback callback, gpointer user_data)
1510 {
1511         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1512         SoupMessageQueueItem *item;
1513         SoupSessionHost *host;
1514
1515         item = soup_message_queue_append (priv->queue, msg, callback, user_data);
1516
1517         g_mutex_lock (priv->host_lock);
1518         host = get_host_for_message (session, item->msg);
1519         host->num_messages++;
1520         g_mutex_unlock (priv->host_lock);
1521
1522         if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
1523                 soup_message_add_header_handler (
1524                         msg, "got_body", "Location",
1525                         G_CALLBACK (redirect_handler), item);
1526         }
1527
1528         g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
1529 }
1530
1531 /**
1532  * SoupSessionCallback:
1533  * @session: the session
1534  * @msg: the message that has finished
1535  * @user_data: the data passed to soup_session_queue_message
1536  *
1537  * Prototype for the callback passed to soup_session_queue_message(),
1538  * qv.
1539  **/
1540
1541 /**
1542  * soup_session_queue_message:
1543  * @session: a #SoupSession
1544  * @msg: (transfer full): the message to queue
1545  * @callback: (allow-none) (scope async): a #SoupSessionCallback which will
1546  * be called after the message completes or when an unrecoverable error occurs.
1547  * @user_data: (allow-none): a pointer passed to @callback.
1548  * 
1549  * Queues the message @msg for sending. All messages are processed
1550  * while the glib main loop runs. If @msg has been processed before,
1551  * any resources related to the time it was last sent are freed.
1552  *
1553  * Upon message completion, the callback specified in @callback will
1554  * be invoked (in the thread associated with @session's async
1555  * context). If after returning from this callback the message has not
1556  * been requeued, @msg will be unreffed.
1557  */
1558 void
1559 soup_session_queue_message (SoupSession *session, SoupMessage *msg,
1560                             SoupSessionCallback callback, gpointer user_data)
1561 {
1562         g_return_if_fail (SOUP_IS_SESSION (session));
1563         g_return_if_fail (SOUP_IS_MESSAGE (msg));
1564
1565         SOUP_SESSION_GET_CLASS (session)->queue_message (session, msg,
1566                                                          callback, user_data);
1567 }
1568
1569 static void
1570 requeue_message (SoupSession *session, SoupMessage *msg)
1571 {
1572         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1573         SoupMessageQueueItem *item;
1574
1575         item = soup_message_queue_lookup (priv->queue, msg);
1576         g_return_if_fail (item != NULL);
1577         item->state = SOUP_MESSAGE_RESTARTING;
1578         soup_message_queue_item_unref (item);
1579 }
1580
1581 /**
1582  * soup_session_requeue_message:
1583  * @session: a #SoupSession
1584  * @msg: the message to requeue
1585  *
1586  * This causes @msg to be placed back on the queue to be attempted
1587  * again.
1588  **/
1589 void
1590 soup_session_requeue_message (SoupSession *session, SoupMessage *msg)
1591 {
1592         g_return_if_fail (SOUP_IS_SESSION (session));
1593         g_return_if_fail (SOUP_IS_MESSAGE (msg));
1594
1595         SOUP_SESSION_GET_CLASS (session)->requeue_message (session, msg);
1596 }
1597
1598
1599 /**
1600  * soup_session_send_message:
1601  * @session: a #SoupSession
1602  * @msg: the message to send
1603  * 
1604  * Synchronously send @msg. This call will not return until the
1605  * transfer is finished successfully or there is an unrecoverable
1606  * error.
1607  *
1608  * @msg is not freed upon return.
1609  *
1610  * Return value: the HTTP status code of the response
1611  */
1612 guint
1613 soup_session_send_message (SoupSession *session, SoupMessage *msg)
1614 {
1615         g_return_val_if_fail (SOUP_IS_SESSION (session), SOUP_STATUS_MALFORMED);
1616         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_STATUS_MALFORMED);
1617
1618         return SOUP_SESSION_GET_CLASS (session)->send_message (session, msg);
1619 }
1620
1621
1622 /**
1623  * soup_session_pause_message:
1624  * @session: a #SoupSession
1625  * @msg: a #SoupMessage currently running on @session
1626  *
1627  * Pauses HTTP I/O on @msg. Call soup_session_unpause_message() to
1628  * resume I/O.
1629  **/
1630 void
1631 soup_session_pause_message (SoupSession *session,
1632                             SoupMessage *msg)
1633 {
1634         g_return_if_fail (SOUP_IS_SESSION (session));
1635         g_return_if_fail (SOUP_IS_MESSAGE (msg));
1636
1637 #if ENABLE(TIZEN_FIX_PAUSE_MESSAGE)
1638         if(soup_message_io_in_progress (msg))
1639                 soup_message_io_pause (msg);
1640 #else
1641         soup_message_io_pause (msg);
1642 #endif
1643 }
1644
1645 /**
1646  * soup_session_unpause_message:
1647  * @session: a #SoupSession
1648  * @msg: a #SoupMessage currently running on @session
1649  *
1650  * Resumes HTTP I/O on @msg. Use this to resume after calling
1651  * soup_session_pause_message().
1652  *
1653  * If @msg is being sent via blocking I/O, this will resume reading or
1654  * writing immediately. If @msg is using non-blocking I/O, then
1655  * reading or writing won't resume until you return to the main loop.
1656  **/
1657 void
1658 soup_session_unpause_message (SoupSession *session,
1659                               SoupMessage *msg)
1660 {
1661         g_return_if_fail (SOUP_IS_SESSION (session));
1662         g_return_if_fail (SOUP_IS_MESSAGE (msg));
1663
1664 #if ENABLE(TIZEN_FIX_PAUSE_MESSAGE)
1665         if(soup_message_io_in_progress (msg))
1666                 soup_message_io_unpause (msg);
1667 #else
1668         soup_message_io_unpause (msg);
1669 #endif
1670 }
1671
1672
1673 static void
1674 cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
1675 {
1676         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1677         SoupMessageQueueItem *item;
1678
1679         item = soup_message_queue_lookup (priv->queue, msg);
1680         g_return_if_fail (item != NULL);
1681
1682         soup_message_set_status (msg, status_code);
1683         g_cancellable_cancel (item->cancellable);
1684
1685         soup_message_queue_item_unref (item);
1686 }
1687
1688 /**
1689  * soup_session_cancel_message:
1690  * @session: a #SoupSession
1691  * @msg: the message to cancel
1692  * @status_code: status code to set on @msg (generally
1693  * %SOUP_STATUS_CANCELLED)
1694  *
1695  * Causes @session to immediately finish processing @msg (regardless
1696  * of its current state) with a final status_code of @status_code. You
1697  * may call this at any time after handing @msg off to @session; if
1698  * @session has started sending the request but has not yet received
1699  * the complete response, then it will close the request's connection.
1700  * Note that with non-idempotent requests (eg, %POST, %PUT, %DELETE)
1701  * it is possible that you might cancel the request after the server
1702  * acts on it, but before it returns a response, leaving the remote
1703  * resource in an unknown state.
1704  *
1705  * If the message is cancelled while its response body is being read,
1706  * then the response body in @msg will be left partially-filled-in.
1707  * The response headers, on the other hand, will always be either
1708  * empty or complete.
1709  *
1710  * For messages queued with soup_session_queue_message() (and
1711  * cancelled from the same thread), the callback will be invoked
1712  * before soup_session_cancel_message() returns.
1713  **/
1714 void
1715 soup_session_cancel_message (SoupSession *session, SoupMessage *msg,
1716                              guint status_code)
1717 {
1718         SoupSessionPrivate *priv;
1719         SoupMessageQueueItem *item;
1720
1721         g_return_if_fail (SOUP_IS_SESSION (session));
1722         g_return_if_fail (SOUP_IS_MESSAGE (msg));
1723
1724         priv = SOUP_SESSION_GET_PRIVATE (session);
1725         item = soup_message_queue_lookup (priv->queue, msg);
1726         /* If the message is already ending, don't do anything */
1727         if (!item)
1728                 return;
1729         if (item->state == SOUP_MESSAGE_FINISHED) {
1730                 soup_message_queue_item_unref (item);
1731                 return;
1732         }
1733
1734         SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg, status_code);
1735         soup_message_queue_item_unref (item);
1736 }
1737
1738 static void
1739 gather_conns (gpointer key, gpointer host, gpointer data)
1740 {
1741         SoupConnection *conn = key;
1742         GSList **conns = data;
1743
1744         *conns = g_slist_prepend (*conns, g_object_ref (conn));
1745 }
1746
1747 static void
1748 flush_queue (SoupSession *session)
1749 {
1750         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1751         SoupMessageQueueItem *item;
1752
1753         for (item = soup_message_queue_first (priv->queue);
1754              item;
1755              item = soup_message_queue_next (priv->queue, item)) {
1756                 soup_session_cancel_message (session, item->msg,
1757                                              SOUP_STATUS_CANCELLED);
1758         }
1759 }
1760
1761 /**
1762  * soup_session_abort:
1763  * @session: the session
1764  *
1765  * Cancels all pending requests in @session.
1766  **/
1767 void
1768 soup_session_abort (SoupSession *session)
1769 {
1770         SoupSessionPrivate *priv;
1771         GSList *conns, *c;
1772
1773         g_return_if_fail (SOUP_IS_SESSION (session));
1774         priv = SOUP_SESSION_GET_PRIVATE (session);
1775
1776         SOUP_SESSION_GET_CLASS (session)->flush_queue (session);
1777
1778         /* Close all connections */
1779         g_mutex_lock (priv->host_lock);
1780         conns = NULL;
1781         g_hash_table_foreach (priv->conns, gather_conns, &conns);
1782
1783         g_mutex_unlock (priv->host_lock);
1784         for (c = conns; c; c = c->next) {
1785                 soup_connection_disconnect (c->data);
1786                 g_object_unref (c->data);
1787         }
1788
1789         g_slist_free (conns);
1790 }
1791
1792 /**
1793 * soup_session_prepare_for_uri:
1794 * @session: a #SoupSession
1795 * @uri: a #SoupURI which may be required
1796 *
1797 * Tells @session that @uri may be requested shortly, and so the
1798 * session can try to prepare (resolving the domain name, obtaining
1799 * proxy address, etc.) in order to work more quickly once the URI is
1800 * actually requested.
1801 *
1802 * This method acts asynchronously, in @session's %async_context.
1803 * If you are using #SoupSessionSync and do not have a main loop running,
1804 * then you can't use this method.
1805 *
1806 * Since: 2.30
1807 **/
1808 void
1809 soup_session_prepare_for_uri (SoupSession *session, SoupURI *uri)
1810 {
1811         SoupSessionPrivate *priv;
1812         SoupSessionHost *host;
1813         SoupAddress *addr;
1814
1815         g_return_if_fail (SOUP_IS_SESSION (session));
1816         g_return_if_fail (uri != NULL);
1817
1818         if (!uri->host)
1819                 return;
1820
1821         priv = SOUP_SESSION_GET_PRIVATE (session);
1822
1823         g_mutex_lock (priv->host_lock);
1824         host = get_host_for_uri (session, uri);
1825         addr = g_object_ref (host->addr);
1826         g_mutex_unlock (priv->host_lock);
1827
1828         soup_address_resolve_async (addr, priv->async_context,
1829                                     NULL, NULL, NULL);
1830         g_object_unref (addr);
1831 }
1832
1833 /**
1834  * soup_session_add_feature:
1835  * @session: a #SoupSession
1836  * @feature: an object that implements #SoupSessionFeature
1837  *
1838  * Adds @feature's functionality to @session. You can also add a
1839  * feature to the session at construct time by using the
1840  * %SOUP_SESSION_ADD_FEATURE property.
1841  *
1842  * Since: 2.24
1843  **/
1844 void
1845 soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature)
1846 {
1847         SoupSessionPrivate *priv;
1848
1849         g_return_if_fail (SOUP_IS_SESSION (session));
1850         g_return_if_fail (SOUP_IS_SESSION_FEATURE (feature));
1851
1852         priv = SOUP_SESSION_GET_PRIVATE (session);
1853         priv->features = g_slist_prepend (priv->features, g_object_ref (feature));
1854         g_hash_table_remove_all (priv->features_cache);
1855         soup_session_feature_attach (feature, session);
1856 }
1857
1858 /**
1859  * soup_session_add_feature_by_type:
1860  * @session: a #SoupSession
1861  * @feature_type: a #GType
1862  *
1863  * If @feature_type is the type of a class that implements
1864  * #SoupSessionFeature, this creates a new feature of that type and
1865  * adds it to @session as with soup_session_add_feature(). You can use
1866  * this when you don't need to customize the new feature in any way.
1867  *
1868  * If @feature_type is not a #SoupSessionFeature type, this gives
1869  * each existing feature on @session the chance to accept @feature_type
1870  * as a "subfeature". This can be used to add new #SoupAuth types,
1871  * for instance.
1872  *
1873  * You can also add a feature to the session at construct time by
1874  * using the %SOUP_SESSION_ADD_FEATURE_BY_TYPE property.
1875  *
1876  * Since: 2.24
1877  **/
1878 void
1879 soup_session_add_feature_by_type (SoupSession *session, GType feature_type)
1880 {
1881         g_return_if_fail (SOUP_IS_SESSION (session));
1882
1883         if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
1884                 SoupSessionFeature *feature;
1885
1886                 feature = g_object_new (feature_type, NULL);
1887                 soup_session_add_feature (session, feature);
1888                 g_object_unref (feature);
1889         } else {
1890                 SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1891                 GSList *f;
1892
1893                 for (f = priv->features; f; f = f->next) {
1894                         if (soup_session_feature_add_feature (f->data, feature_type))
1895                                 return;
1896                 }
1897                 g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
1898         }
1899 }
1900
1901 /**
1902  * soup_session_remove_feature:
1903  * @session: a #SoupSession
1904  * @feature: a feature that has previously been added to @session
1905  *
1906  * Removes @feature's functionality from @session.
1907  *
1908  * Since: 2.24
1909  **/
1910 void
1911 soup_session_remove_feature (SoupSession *session, SoupSessionFeature *feature)
1912 {
1913         SoupSessionPrivate *priv;
1914
1915         g_return_if_fail (SOUP_IS_SESSION (session));
1916
1917         priv = SOUP_SESSION_GET_PRIVATE (session);
1918         if (g_slist_find (priv->features, feature)) {
1919                 priv->features = g_slist_remove (priv->features, feature);
1920                 g_hash_table_remove_all (priv->features_cache);
1921                 soup_session_feature_detach (feature, session);
1922                 g_object_unref (feature);
1923         }
1924 }
1925
1926 /**
1927  * soup_session_remove_feature_by_type:
1928  * @session: a #SoupSession
1929  * @feature_type: a #GType
1930  *
1931  * Removes all features of type @feature_type (or any subclass of
1932  * @feature_type) from @session. You can also remove standard features
1933  * from the session at construct time by using the
1934  * %SOUP_SESSION_REMOVE_FEATURE_BY_TYPE property.
1935  *
1936  * Since: 2.24
1937  **/
1938 void
1939 soup_session_remove_feature_by_type (SoupSession *session, GType feature_type)
1940 {
1941         SoupSessionPrivate *priv;
1942         GSList *f;
1943
1944         g_return_if_fail (SOUP_IS_SESSION (session));
1945
1946         priv = SOUP_SESSION_GET_PRIVATE (session);
1947
1948         if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
1949         restart:
1950                 for (f = priv->features; f; f = f->next) {
1951                         if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type)) {
1952                                 soup_session_remove_feature (session, f->data);
1953                                 goto restart;
1954                         }
1955                 }
1956         } else {
1957                 for (f = priv->features; f; f = f->next) {
1958                         if (soup_session_feature_remove_feature (f->data, feature_type))
1959                                 return;
1960                 }
1961                 g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
1962         }
1963 }
1964
1965 /**
1966  * soup_session_get_features:
1967  * @session: a #SoupSession
1968  * @feature_type: the #GType of the class of features to get
1969  *
1970  * Generates a list of @session's features of type @feature_type. (If
1971  * you want to see all features, you can pass %G_TYPE_SESSION_FEATURE
1972  * for @feature_type.)
1973  *
1974  * Return value: (transfer container) (element-type Soup.SessionFeature):
1975  * a list of features. You must free the list, but not its contents
1976  *
1977  * Since: 2.26
1978  **/
1979 GSList *
1980 soup_session_get_features (SoupSession *session, GType feature_type)
1981 {
1982         SoupSessionPrivate *priv;
1983         GSList *f, *ret;
1984
1985         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
1986
1987         priv = SOUP_SESSION_GET_PRIVATE (session);
1988         for (f = priv->features, ret = NULL; f; f = f->next) {
1989                 if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
1990                         ret = g_slist_prepend (ret, f->data);
1991         }
1992         return g_slist_reverse (ret);
1993 }
1994
1995 /**
1996  * soup_session_get_feature:
1997  * @session: a #SoupSession
1998  * @feature_type: the #GType of the feature to get
1999  *
2000  * Gets the first feature in @session of type @feature_type. For
2001  * features where there may be more than one feature of a given type,
2002  * use soup_session_get_features().
2003  *
2004  * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
2005  * feature is owned by @session.
2006  *
2007  * Since: 2.26
2008  **/
2009 SoupSessionFeature *
2010 soup_session_get_feature (SoupSession *session, GType feature_type)
2011 {
2012         SoupSessionPrivate *priv;
2013         SoupSessionFeature *feature;
2014         GSList *f;
2015
2016         g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
2017
2018         priv = SOUP_SESSION_GET_PRIVATE (session);
2019
2020         feature = g_hash_table_lookup (priv->features_cache,
2021                                        GSIZE_TO_POINTER (feature_type));
2022         if (feature)
2023                 return feature;
2024
2025         for (f = priv->features; f; f = f->next) {
2026                 feature = f->data;
2027                 if (G_TYPE_CHECK_INSTANCE_TYPE (feature, feature_type)) {
2028                         g_hash_table_insert (priv->features_cache,
2029                                              GSIZE_TO_POINTER (feature_type),
2030                                              feature);
2031                         return feature;
2032                 }
2033         }
2034         return NULL;
2035 }
2036
2037 /**
2038  * soup_session_get_feature_for_message:
2039  * @session: a #SoupSession
2040  * @feature_type: the #GType of the feature to get
2041  * @msg: a #SoupMessage
2042  *
2043  * Gets the first feature in @session of type @feature_type, provided
2044  * that it is not disabled for @msg. As with
2045  * soup_session_get_feature(), this should only be used for features
2046  * where @feature_type is only expected to match a single feature. In
2047  * particular, if there are two matching features, and the first is
2048  * disabled on @msg, and the second is not, then this will return
2049  * %NULL, not the second feature.
2050  *
2051  * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
2052  * feature is owned by @session.
2053  *
2054  * Since: 2.28
2055  **/
2056 SoupSessionFeature *
2057 soup_session_get_feature_for_message (SoupSession *session, GType feature_type,
2058                                       SoupMessage *msg)
2059 {
2060         SoupSessionFeature *feature;
2061
2062         feature = soup_session_get_feature (session, feature_type);
2063         if (feature && soup_message_disables_feature (msg, feature))
2064                 return NULL;
2065         return feature;
2066 }