Remove build warning
[platform/upstream/libsoup.git] / libsoup / soup-session.c
old mode 100644 (file)
new mode 100755 (executable)
index 0fb9b03..a11c108
@@ -9,30 +9,60 @@
 #include <config.h>
 #endif
 
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-
 #include <glib/gi18n-lib.h>
 
-#include "soup-auth.h"
-#include "soup-auth-basic.h"
-#include "soup-auth-digest.h"
-#include "soup-auth-manager-ntlm.h"
+#include "soup-session.h"
+#include "soup.h"
+#include "soup-auth-manager.h"
+#include "soup-cache-private.h"
 #include "soup-connection.h"
-#include "soup-marshal.h"
 #include "soup-message-private.h"
+#include "soup-misc-private.h"
 #include "soup-message-queue.h"
-#include "soup-misc.h"
-#include "soup-proxy-resolver-static.h"
-#include "soup-proxy-uri-resolver.h"
-#include "soup-session.h"
-#include "soup-session-feature.h"
+#include "soup-proxy-resolver-wrapper.h"
 #include "soup-session-private.h"
-#include "soup-socket.h"
-#include "soup-uri.h"
+#include "TIZEN.h"
+
+#if ENABLE(TIZEN_PERFORMANCE_TEST_LOG)
+#include <sys/prctl.h>
+#ifndef PR_TASK_PERF_USER_TRACE
+#define PR_TASK_PERF_USER_TRACE 666
+#endif
+
+#define MAX_STRING_LEN 256
+#define HWCLOCK_LOG(s) {const char *str=s; prctl(PR_TASK_PERF_USER_TRACE, str, strlen(str));}
+
+static void prctl_with_url(const char *prestr, const char *url)
+{
+       char s[MAX_STRING_LEN] = "";
+       int len_max = 120;
+       int len_pre = strlen(prestr);
+       int len_url = strlen(url);
+
+       strncpy(s, prestr, len_pre);
+       if(len_pre + len_url < len_max) {
+               strncpy(s+len_pre, url, len_url);
+       }
+       else {
+               int len_part = len_max - len_pre - 10;
+               strncpy(s+len_pre, url, len_part );
+               strncpy(s+len_pre+len_part, "...", MAX_STRING_LEN-len_pre-len_part-1);
+               strncpy(s+len_pre+len_part+3, url+len_url-7, 7);
+       }
+       prctl(PR_TASK_PERF_USER_TRACE, s, strlen(s));
+}
+
+static void prctl_with_url_and_free(const char *prestr, char *url)
+{
+       prctl_with_url(prestr, url);
+       g_free(url);
+}
+#endif
 
 #define HOST_KEEP_ALIVE 5 * 60 * 1000 /* 5 min in msecs */
+#if ENABLE(TIZEN_CERTIFICATE_FILE_SET)
+#define SET_TLS_CERT_FILE_TIMEOUT 7 * 1000 /* msecs */
+#endif
 
 /**
  * SECTION:soup-session
@@ -41,7 +71,8 @@
  * #SoupSession is the object that controls client-side HTTP. A
  * #SoupSession encapsulates all of the state that libsoup is keeping
  * on behalf of your program; cached HTTP connections, authentication
- * information, etc.
+ * information, etc. It also keeps track of various global options
+ * and features that you are using.
  *
  * Most applications will only need a single #SoupSession; the primary
  * reason you might need multiple sessions is if you need to have
  * one session for the first user, and a second session for the other
  * user.)
  *
- * #SoupSession itself is an abstract class, with two subclasses. If
- * you are using the glib main loop, you will generally want to use
- * #SoupSessionAsync, which uses non-blocking I/O and callbacks. On
- * the other hand, if your application is threaded and you want to do
- * synchronous I/O in a separate thread from the UI, use
- * #SoupSessionSync.
+ * In the past, #SoupSession was an abstract class, and users needed
+ * to choose between #SoupSessionAsync (which always uses
+ * #GMainLoop<!-- -->-based I/O), or #SoupSessionSync (which always uses
+ * blocking I/O and can be used from multiple threads simultaneously).
+ * This is no longer necessary; you can (and should) use a plain
+ * #SoupSession, which supports both synchronous and asynchronous use.
+ * (When using a plain #SoupSession, soup_session_queue_message()
+ * behaves like it traditionally did on a #SoupSessionAsync, and
+ * soup_session_send_message() behaves like it traditionally did on a
+ * #SoupSessionSync.)
  **/
 
 static void
@@ -84,12 +119,16 @@ typedef struct {
        SoupSession *session;
 } SoupSessionHost;
 static guint soup_host_uri_hash (gconstpointer key);
-gboolean soup_host_uri_equal (gconstpointer v1, gconstpointer v2);
+static gboolean soup_host_uri_equal (gconstpointer v1, gconstpointer v2);
 
 typedef struct {
+       SoupSession *session;
+       gboolean disposed;
+
        GTlsDatabase *tlsdb;
        char *ssl_ca_file;
        gboolean ssl_strict;
+       gboolean tlsdb_use_default;
 
        SoupMessageQueue *queue;
 
@@ -106,46 +145,66 @@ typedef struct {
        guint max_conns, max_conns_per_host;
        guint io_timeout, idle_timeout;
 
-       /* Must hold the host_lock before potentially creating a
-        * new SoupSessionHost, or adding/removing a connection.
-        * Must not emit signals or destroy objects while holding it.
+       SoupAddress *local_addr;
+
+       /* Must hold the conn_lock before potentially creating a new
+        * SoupSessionHost, adding/removing a connection,
+        * disconnecting a connection, or moving a connection from
+        * IDLE to IN_USE. Must not emit signals or destroy objects
+        * while holding it. conn_cond is signaled when it may be
+        * possible for a previously-blocked message to continue.
         */
-       GMutex host_lock;
+       GMutex conn_lock;
+       GCond conn_cond;
 
        GMainContext *async_context;
        gboolean use_thread_context;
+       GSList *run_queue_sources;
 
        GResolver *resolver;
+       GProxyResolver *proxy_resolver;
+       gboolean proxy_use_default;
+       SoupURI *proxy_uri;
 
        char **http_aliases, **https_aliases;
+#if ENABLE(TIZEN_CERTIFICATE_FILE_SET)
+       GSource *tls_idle_timeout_src;
+       char *certificate_path;
+#endif
+#if ENABLE(TIZEN_TV_CLIENT_CERTIFICATE)
+       gboolean widget_engine;
+#endif
+       GHashTable *request_types;
 } SoupSessionPrivate;
 #define SOUP_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION, SoupSessionPrivate))
 
-static void     free_host      (SoupSessionHost *host);
+#define SOUP_IS_PLAIN_SESSION(o) (G_TYPE_FROM_INSTANCE (o) == SOUP_TYPE_SESSION)
 
-static void queue_message   (SoupSession *session, SoupMessage *msg,
-                            SoupSessionCallback callback, gpointer user_data);
-static void requeue_message (SoupSession *session, SoupMessage *msg);
-static void cancel_message  (SoupSession *session, SoupMessage *msg,
-                            guint status_code);
-static void auth_required   (SoupSession *session, SoupMessage *msg,
-                            SoupAuth *auth, gboolean retrying);
-static void flush_queue     (SoupSession *session);
+static void free_host (SoupSessionHost *host);
+static void connection_state_changed (GObject *object, GParamSpec *param,
+                                     gpointer user_data);
+static void connection_disconnected (SoupConnection *conn, gpointer user_data);
+static void drop_connection (SoupSession *session, SoupSessionHost *host,
+                            SoupConnection *conn);
 
 static void auth_manager_authenticate (SoupAuthManager *manager,
                                       SoupMessage *msg, SoupAuth *auth,
                                       gboolean retrying, gpointer user_data);
 
+static void async_run_queue (SoupSession *session);
+
+static void async_send_request_running (SoupSession *session, SoupMessageQueueItem *item);
+
 #define SOUP_SESSION_MAX_CONNS_DEFAULT 10
 #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2
 
-#define SOUP_SESSION_MAX_REDIRECTION_COUNT 20
+#define SOUP_SESSION_MAX_RESEND_COUNT 20
 
 #define SOUP_SESSION_USER_AGENT_BASE "libsoup/" PACKAGE_VERSION
 
-G_DEFINE_ABSTRACT_TYPE_WITH_CODE (SoupSession, soup_session, G_TYPE_OBJECT,
-                                 soup_init ();
-                                 )
+G_DEFINE_TYPE_WITH_CODE (SoupSession, soup_session, G_TYPE_OBJECT,
+                        soup_init ();
+                        )
 
 enum {
        REQUEST_QUEUED,
@@ -163,6 +222,7 @@ enum {
        PROP_0,
 
        PROP_PROXY_URI,
+       PROP_PROXY_RESOLVER,
        PROP_MAX_CONNS,
        PROP_MAX_CONNS_PER_HOST,
        PROP_USE_NTLM,
@@ -182,14 +242,24 @@ enum {
        PROP_REMOVE_FEATURE_BY_TYPE,
        PROP_HTTP_ALIASES,
        PROP_HTTPS_ALIASES,
+       PROP_LOCAL_ADDRESS,
+#if ENABLE(TIZEN_CERTIFICATE_FILE_SET)
+       PROP_CERTIFICATE_PATH,
+#endif
+#if ENABLE(TIZEN_TV_CLIENT_CERTIFICATE)
+       PROP_WIDGET_ENGINE,
+#endif
 
        LAST_PROP
 };
 
-static void set_property (GObject *object, guint prop_id,
-                         const GValue *value, GParamSpec *pspec);
-static void get_property (GObject *object, guint prop_id,
-                         GValue *value, GParamSpec *pspec);
+#if ENABLE(TIZEN_TV_FORCE_PRELOAD_TLSDB)
+// Preload TLS database. Allow to load the TLS database in background.
+static GTlsDatabase *_gTlsDB = NULL;
+static gchar *_gTlsDB_path = NULL;
+static GMutex _gTlsDB_lock;
+static void soup_preload_tls_database (const gchar *path);
+#endif
 
 static void
 soup_session_init (SoupSession *session)
@@ -197,9 +267,12 @@ soup_session_init (SoupSession *session)
        SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
        SoupAuthManager *auth_manager;
 
+       priv->session = session;
+
        priv->queue = soup_message_queue_new (session);
 
-       g_mutex_init (&priv->host_lock);
+       g_mutex_init (&priv->conn_lock);
+       g_cond_init (&priv->conn_cond);
        priv->http_hosts = g_hash_table_new_full (soup_host_uri_hash,
                                                  soup_host_uri_equal,
                                                  NULL, (GDestroyNotify)free_host);
@@ -213,7 +286,7 @@ soup_session_init (SoupSession *session)
 
        priv->features_cache = g_hash_table_new (NULL, NULL);
 
-       auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER_NTLM, NULL);
+       auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER, NULL);
        g_signal_connect (auth_manager, "authenticate",
                          G_CALLBACK (auth_manager_authenticate), session);
        soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
@@ -233,15 +306,73 @@ soup_session_init (SoupSession *session)
        priv->http_aliases = g_new (char *, 2);
        priv->http_aliases[0] = (char *)g_intern_string ("*");
        priv->http_aliases[1] = NULL;
+#if ENABLE(TIZEN_CERTIFICATE_FILE_SET)
+       priv->tls_idle_timeout_src = NULL;
+       priv->certificate_path = NULL;
+#endif
+
+       priv->request_types = g_hash_table_new (soup_str_case_hash,
+                                               soup_str_case_equal);
+       soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_HTTP);
+       soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_FILE);
+       soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_DATA);
+}
+
+static GObject *
+soup_session_constructor (GType                  type,
+                         guint                  n_construct_properties,
+                         GObjectConstructParam *construct_params)
+{
+       GObject *object;
+
+       object = G_OBJECT_CLASS (soup_session_parent_class)->constructor (type, n_construct_properties, construct_params);
+
+       /* If this is a "plain" SoupSession, fix up the default
+        * properties values, etc.
+        */
+       if (type == SOUP_TYPE_SESSION) {
+               SoupSession *session = SOUP_SESSION (object);
+               SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+               g_clear_pointer (&priv->async_context, g_main_context_unref);
+               priv->async_context = g_main_context_ref_thread_default ();
+               priv->use_thread_context = TRUE;
+
+               priv->io_timeout = priv->idle_timeout = 60;
+
+               priv->http_aliases[0] = NULL;
+
+               /* If the user overrides the proxy or tlsdb during construction,
+                * we don't want to needlessly resolve the extension point. So
+                * we just set flags saying to do it later.
+                */
+               priv->proxy_use_default = TRUE;
+               priv->tlsdb_use_default = TRUE;
+
+               soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_DECODER);
+       }
+
+       return object;
 }
 
 static void
-dispose (GObject *object)
+soup_session_dispose (GObject *object)
 {
        SoupSession *session = SOUP_SESSION (object);
        SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       GSList *iter;
+
+       priv->disposed = TRUE;
+
+       for (iter = priv->run_queue_sources; iter; iter = iter->next) {
+               g_source_destroy (iter->data);
+               g_source_unref (iter->data);
+       }
+       g_clear_pointer (&priv->run_queue_sources, g_slist_free);
 
+       priv->disposed = TRUE;
        soup_session_abort (session);
+       g_warn_if_fail (g_hash_table_size (priv->conns) == 0);
 
        while (priv->features)
                soup_session_remove_feature (session, priv->features->data);
@@ -250,14 +381,15 @@ dispose (GObject *object)
 }
 
 static void
-finalize (GObject *object)
+soup_session_finalize (GObject *object)
 {
        SoupSession *session = SOUP_SESSION (object);
        SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
 
        soup_message_queue_destroy (priv->queue);
 
-       g_mutex_clear (&priv->host_lock);
+       g_mutex_clear (&priv->conn_lock);
+       g_cond_clear (&priv->conn_cond);
        g_hash_table_destroy (priv->http_hosts);
        g_hash_table_destroy (priv->https_hosts);
        g_hash_table_destroy (priv->conns);
@@ -265,2450 +397,4696 @@ finalize (GObject *object)
        g_free (priv->user_agent);
        g_free (priv->accept_language);
 
-       if (priv->tlsdb)
-               g_object_unref (priv->tlsdb);
+       g_clear_object (&priv->tlsdb);
        g_free (priv->ssl_ca_file);
 
-       if (priv->async_context)
-               g_main_context_unref (priv->async_context);
+#if ENABLE(TIZEN_CERTIFICATE_FILE_SET)
+       soup_session_tls_stop_idle_timer (session);
+       g_free (priv->certificate_path);
+       priv->certificate_path = NULL;
+#endif
+       g_clear_pointer (&priv->async_context, g_main_context_unref);
+       g_clear_object (&priv->local_addr);
 
        g_hash_table_destroy (priv->features_cache);
 
        g_object_unref (priv->resolver);
+       g_clear_object (&priv->proxy_resolver);
+       g_clear_pointer (&priv->proxy_uri, soup_uri_free);
 
        g_free (priv->http_aliases);
        g_free (priv->https_aliases);
 
+       g_hash_table_destroy (priv->request_types);
+
        G_OBJECT_CLASS (soup_session_parent_class)->finalize (object);
 }
 
-static void
-soup_session_class_init (SoupSessionClass *session_class)
+/* Converts a language in POSIX format and to be RFC2616 compliant    */
+/* Based on code from epiphany-webkit (ephy_langs_append_languages()) */
+static gchar *
+posix_lang_to_rfc2616 (const gchar *language)
 {
-       GObjectClass *object_class = G_OBJECT_CLASS (session_class);
-
-       g_type_class_add_private (session_class, sizeof (SoupSessionPrivate));
+       /* Don't include charset variants, etc */
+       if (strchr (language, '.') || strchr (language, '@'))
+               return NULL;
 
-       /* virtual method definition */
-       session_class->queue_message = queue_message;
-       session_class->requeue_message = requeue_message;
-       session_class->cancel_message = cancel_message;
-       session_class->auth_required = auth_required;
-       session_class->flush_queue = flush_queue;
+       /* Ignore "C" locale, which g_get_language_names() always
+        * includes as a fallback.
+        */
+       if (!strcmp (language, "C"))
+               return NULL;
 
-       /* virtual method override */
-       object_class->dispose = dispose;
-       object_class->finalize = finalize;
-       object_class->set_property = set_property;
-       object_class->get_property = get_property;
+       return g_strdelimit (g_ascii_strdown (language, -1), "_", '-');
+}
 
-       /* signals */
+/* Converts @quality from 0-100 to 0.0-1.0 and appends to @str */
+static gchar *
+add_quality_value (const gchar *str, int quality)
+{
+       g_return_val_if_fail (str != NULL, NULL);
 
-       /**
-        * SoupSession::request-queued:
-        * @session: the session
-        * @msg: the request that was queued
-        *
-        * Emitted when a request is queued on @session. (Note that
-        * "queued" doesn't just mean soup_session_queue_message();
-        * soup_session_send_message() implicitly queues the message
-        * as well.)
-        *
-        * When sending a request, first #SoupSession::request_queued
-        * is emitted, indicating that the session has become aware of
-        * the request.
-        *
-        * Once a connection is available to send the request on, the
-        * session emits #SoupSession::request_started. Then, various
-        * #SoupMessage signals are emitted as the message is
-        * processed. If the message is requeued, it will emit
-        * #SoupMessage::restarted, which will then be followed by
-        * another #SoupSession::request_started and another set of
-        * #SoupMessage signals when the message is re-sent.
-        *
-        * Eventually, the message will emit #SoupMessage::finished.
-        * Normally, this signals the completion of message
-        * processing. However, it is possible that the application
-        * will requeue the message from the "finished" handler (or
-        * equivalently, from the soup_session_queue_message()
-        * callback). In that case, the process will loop back to
-        * #SoupSession::request_started.
-        *
-        * Eventually, a message will reach "finished" and not be
-        * requeued. At that point, the session will emit
-        * #SoupSession::request_unqueued to indicate that it is done
-        * with the message.
-        *
-        * To sum up: #SoupSession::request_queued and
-        * #SoupSession::request_unqueued are guaranteed to be emitted
-        * exactly once, but #SoupSession::request_started and
-        * #SoupMessage::finished (and all of the other #SoupMessage
-        * signals) may be invoked multiple times for a given message.
-        *
-        * Since: 2.4.1
-        **/
-       signals[REQUEST_QUEUED] =
-               g_signal_new ("request-queued",
-                             G_OBJECT_CLASS_TYPE (object_class),
-                             G_SIGNAL_RUN_FIRST,
-                             0, /* FIXME? */
-                             NULL, NULL,
-                             _soup_marshal_NONE__OBJECT,
-                             G_TYPE_NONE, 1,
-                             SOUP_TYPE_MESSAGE);
+       if (quality >= 0 && quality < 100) {
+               /* We don't use %.02g because of "." vs "," locale issues */
+               if (quality % 10)
+                       return g_strdup_printf ("%s;q=0.%02d", str, quality);
+               else
+                       return g_strdup_printf ("%s;q=0.%d", str, quality / 10);
+       } else
+               return g_strdup (str);
+}
 
-       /**
-        * SoupSession::request-started:
-        * @session: the session
-        * @msg: the request being sent
-        * @socket: the socket the request is being sent on
-        *
-        * Emitted just before a request is sent. See
-        * #SoupSession::request_queued for a detailed description of
-        * the message lifecycle within a session.
-        **/
-       signals[REQUEST_STARTED] =
-               g_signal_new ("request-started",
-                             G_OBJECT_CLASS_TYPE (object_class),
-                             G_SIGNAL_RUN_FIRST,
-                             G_STRUCT_OFFSET (SoupSessionClass, request_started),
-                             NULL, NULL,
-                             _soup_marshal_NONE__OBJECT_OBJECT,
-                             G_TYPE_NONE, 2,
-                             SOUP_TYPE_MESSAGE,
-                             SOUP_TYPE_SOCKET);
+/* Returns a RFC2616 compliant languages list from system locales */
+static gchar *
+accept_languages_from_system (void)
+{
+       const char * const * lang_names;
+       GPtrArray *langs = NULL;
+       char *lang, *langs_str;
+       int delta;
+       int i;
 
-       /**
-        * SoupSession::request-unqueued:
-        * @session: the session
-        * @msg: the request that was unqueued
-        *
-        * Emitted when a request is removed from @session's queue,
-        * indicating that @session is done with it. See
-        * #SoupSession::request_queued for a detailed description of the
-        * message lifecycle within a session.
-        *
-        * Since: 2.4.1
-        **/
-       signals[REQUEST_UNQUEUED] =
-               g_signal_new ("request-unqueued",
-                             G_OBJECT_CLASS_TYPE (object_class),
-                             G_SIGNAL_RUN_FIRST,
-                             0, /* FIXME? */
-                             NULL, NULL,
-                             _soup_marshal_NONE__OBJECT,
-                             G_TYPE_NONE, 1,
-                             SOUP_TYPE_MESSAGE);
+       lang_names = g_get_language_names ();
+       g_return_val_if_fail (lang_names != NULL, NULL);
 
-       /**
-        * SoupSession::authenticate:
-        * @session: the session
-        * @msg: the #SoupMessage being sent
-        * @auth: the #SoupAuth to authenticate
-        * @retrying: %TRUE if this is the second (or later) attempt
-        *
-        * Emitted when the session requires authentication. If
-        * credentials are available call soup_auth_authenticate() on
-        * @auth. If these credentials fail, the signal will be
-        * emitted again, with @retrying set to %TRUE, which will
-        * continue until you return without calling
-        * soup_auth_authenticate() on @auth.
-        *
-        * Note that this may be emitted before @msg's body has been
-        * fully read.
-        *
-        * If you call soup_session_pause_message() on @msg before
-        * returning, then you can authenticate @auth asynchronously
-        * (as long as you g_object_ref() it to make sure it doesn't
-        * get destroyed), and then unpause @msg when you are ready
-        * for it to continue.
-        **/
-       signals[AUTHENTICATE] =
-               g_signal_new ("authenticate",
-                             G_OBJECT_CLASS_TYPE (object_class),
-                             G_SIGNAL_RUN_FIRST,
-                             G_STRUCT_OFFSET (SoupSessionClass, authenticate),
-                             NULL, NULL,
-                             _soup_marshal_NONE__OBJECT_OBJECT_BOOLEAN,
-                             G_TYPE_NONE, 3,
-                             SOUP_TYPE_MESSAGE,
-                             SOUP_TYPE_AUTH,
-                             G_TYPE_BOOLEAN);
+       /* Build the array of languages */
+       langs = g_ptr_array_new_with_free_func (g_free);
+       for (i = 0; lang_names[i] != NULL; i++) {
+               lang = posix_lang_to_rfc2616 (lang_names[i]);
+               if (lang)
+                       g_ptr_array_add (langs, lang);
+       }
 
-       /**
-        * SoupSession::connection-created:
-        * @session: the #SoupSession
-        * @connection: the connection
-        *
-        * Emitted when a new connection is created. This is an
-        * internal signal intended only to be used for debugging
-        * purposes, and may go away in the future.
-        *
-        * Since: 2.30
-        */
-       signals[CONNECTION_CREATED] =
-               g_signal_new ("connection-created",
-                             G_OBJECT_CLASS_TYPE (object_class),
-                             G_SIGNAL_RUN_FIRST,
-                             0,
-                             NULL, NULL,
-                             _soup_marshal_NONE__OBJECT,
-                             G_TYPE_NONE, 1,
-                             /* SoupConnection is private, so we can't use
-                              * SOUP_TYPE_CONNECTION here.
-                              */
-                             G_TYPE_OBJECT);
+       /* Add quality values */
+       if (langs->len < 10)
+               delta = 10;
+       else if (langs->len < 20)
+               delta = 5;
+       else
+               delta = 1;
 
-       /**
-        * SoupSession::tunneling:
-        * @session: the #SoupSession
-        * @connection: the connection
-        *
-        * Emitted when an SSL tunnel is being created on a proxy
-        * connection. This is an internal signal intended only to be
-        * used for debugging purposes, and may go away in the future.
-        *
-        * Since: 2.30
-        */
-       signals[TUNNELING] =
-               g_signal_new ("tunneling",
-                             G_OBJECT_CLASS_TYPE (object_class),
-                             G_SIGNAL_RUN_FIRST,
-                             0,
-                             NULL, NULL,
-                             _soup_marshal_NONE__OBJECT,
-                             G_TYPE_NONE, 1,
-                             /* SoupConnection is private, so we can't use
-                              * SOUP_TYPE_CONNECTION here.
-                              */
-                             G_TYPE_OBJECT);
+       for (i = 0; i < langs->len; i++) {
+               lang = langs->pdata[i];
+               langs->pdata[i] = add_quality_value (lang, 100 - i * delta);
+               g_free (lang);
+       }
+
+       /* Fallback: add "en" if list is empty */
+       if (langs->len == 0)
+               g_ptr_array_add (langs, g_strdup ("en"));
+
+       g_ptr_array_add (langs, NULL);
+       langs_str = g_strjoinv (", ", (char **)langs->pdata);
+       g_ptr_array_free (langs, TRUE);
+
+       return langs_str;
+}
+
+static void
+set_tlsdb (SoupSession *session, GTlsDatabase *tlsdb)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       GTlsDatabase *system_default;
+
+       priv->tlsdb_use_default = FALSE;
+       if (tlsdb == priv->tlsdb)
+               return;
+
+       g_object_freeze_notify (G_OBJECT (session));
+
+       system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
+       if (system_default) {
+               if (priv->tlsdb == system_default || tlsdb == system_default) {
+                       g_object_notify (G_OBJECT (session), "ssl-use-system-ca-file");
+               }
+               g_object_unref (system_default);
+       }
+
+       if (priv->ssl_ca_file) {
+               g_free (priv->ssl_ca_file);
+               priv->ssl_ca_file = NULL;
+               g_object_notify (G_OBJECT (session), "ssl-ca-file");
+       }
+
+       if (priv->tlsdb)
+               g_object_unref (priv->tlsdb);
+       priv->tlsdb = tlsdb;
+       if (priv->tlsdb)
+               g_object_ref (priv->tlsdb);
+
+       g_object_notify (G_OBJECT (session), "tls-database");
+       g_object_thaw_notify (G_OBJECT (session));
+}
+
+static void
+set_use_system_ca_file (SoupSession *session, gboolean use_system_ca_file)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       GTlsDatabase *system_default;
+
+       priv->tlsdb_use_default = FALSE;
+
+       system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
+
+       if (use_system_ca_file)
+               set_tlsdb (session, system_default);
+       else if (priv->tlsdb == system_default)
+               set_tlsdb (session, NULL);
+
+       g_clear_object (&system_default);
+}
+
+static void
+set_ssl_ca_file (SoupSession *session, const char *ssl_ca_file)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       GTlsDatabase *tlsdb;
+       GError *error = NULL;
+
+       priv->tlsdb_use_default = FALSE;
+       if (!g_strcmp0 (priv->ssl_ca_file, ssl_ca_file))
+               return;
+
+       g_object_freeze_notify (G_OBJECT (session));
+
+       if (g_path_is_absolute (ssl_ca_file))
+               tlsdb = g_tls_file_database_new (ssl_ca_file, &error);
+       else {
+               char *path, *cwd;
+
+               cwd = g_get_current_dir ();
+               path = g_build_filename (cwd, ssl_ca_file, NULL);
+               tlsdb = g_tls_file_database_new (path, &error);
+               g_free (path);
+               g_free (cwd);
+       }
+
+       if (error) {
+               if (!g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_UNAVAILABLE)) {
+                       g_warning ("Could not set SSL credentials from '%s': %s",
+                                  ssl_ca_file, error->message);
+
+                       tlsdb = g_tls_file_database_new ("/dev/null", NULL);
+               }
+               g_error_free (error);
+       }
+
+       set_tlsdb (session, tlsdb);
+       if (tlsdb) {
+               g_object_unref (tlsdb);
+
+               priv->ssl_ca_file = g_strdup (ssl_ca_file);
+               g_object_notify (G_OBJECT (session), "ssl-ca-file");
+       } else if (priv->ssl_ca_file) {
+               g_clear_pointer (&priv->ssl_ca_file, g_free);
+               g_object_notify (G_OBJECT (session), "ssl-ca-file");
+       }
+
+       g_object_thaw_notify (G_OBJECT (session));
+}
+
+/* priv->http_aliases and priv->https_aliases are stored as arrays of
+ * *interned* strings, so we can't just use g_strdupv() to set them.
+ */
+static void
+set_aliases (char ***variable, char **value)
+{
+       int len, i;
+
+       if (*variable)
+               g_free (*variable);
+
+       if (!value) {
+               *variable = NULL;
+               return;
+       }
+
+       len = g_strv_length (value);
+       *variable = g_new (char *, len + 1);
+       for (i = 0; i < len; i++)
+               (*variable)[i] = (char *)g_intern_string (value[i]);
+       (*variable)[i] = NULL;
+}
+
+static void
+set_proxy_resolver (SoupSession *session, SoupURI *uri,
+                   SoupProxyURIResolver *soup_resolver,
+                   GProxyResolver *g_resolver)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+       G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+       soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_URI_RESOLVER);
+       G_GNUC_END_IGNORE_DEPRECATIONS;
+       g_clear_object (&priv->proxy_resolver);
+       g_clear_pointer (&priv->proxy_uri, soup_uri_free);
+       priv->proxy_use_default = FALSE;
+
+       if (uri) {
+               char *uri_string;
+
+               priv->proxy_uri = soup_uri_copy (uri);
+               uri_string = soup_uri_to_string_internal (uri, FALSE, TRUE);
+               priv->proxy_resolver = g_simple_proxy_resolver_new (uri_string, NULL);
+               g_free (uri_string);
+       } else if (soup_resolver) {
+               G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+               if (SOUP_IS_PROXY_RESOLVER_DEFAULT (soup_resolver))
+                       priv->proxy_resolver = g_object_ref (g_proxy_resolver_get_default ());
+               else
+                       priv->proxy_resolver = soup_proxy_resolver_wrapper_new (soup_resolver);
+               G_GNUC_END_IGNORE_DEPRECATIONS;
+       } else if (g_resolver)
+               priv->proxy_resolver = g_object_ref (g_resolver);
+}
+
+static void
+soup_session_set_property (GObject *object, guint prop_id,
+                          const GValue *value, GParamSpec *pspec)
+{
+       SoupSession *session = SOUP_SESSION (object);
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       const char *user_agent;
+       SoupSessionFeature *feature;
+       GMainContext *async_context;
+
+       switch (prop_id) {
+       case PROP_LOCAL_ADDRESS:
+               priv->local_addr = g_value_dup_object (value);
+               break;
+       case PROP_PROXY_URI:
+               set_proxy_resolver (session, g_value_get_boxed (value),
+                                   NULL, NULL);
+               soup_session_abort (session);
+               break;
+       case PROP_PROXY_RESOLVER:
+               set_proxy_resolver (session, NULL, NULL,
+                                   g_value_get_object (value));
+               break;
+       case PROP_MAX_CONNS:
+               priv->max_conns = g_value_get_int (value);
+               break;
+       case PROP_MAX_CONNS_PER_HOST:
+               priv->max_conns_per_host = g_value_get_int (value);
+               break;
+       case PROP_USE_NTLM:
+               g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
+               feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
+               if (feature) {
+                       if (g_value_get_boolean (value))
+                               soup_session_feature_add_feature (feature, SOUP_TYPE_AUTH_NTLM);
+                       else
+                               soup_session_feature_remove_feature (feature, SOUP_TYPE_AUTH_NTLM);
+               } else
+                       g_warning ("Trying to set use-ntlm on session with no auth-manager");
+               break;
+       case PROP_SSL_CA_FILE:
+               set_ssl_ca_file (session, g_value_get_string (value));
+               break;
+       case PROP_SSL_USE_SYSTEM_CA_FILE:
+               set_use_system_ca_file (session, g_value_get_boolean (value));
+               break;
+       case PROP_TLS_DATABASE:
+               set_tlsdb (session, g_value_get_object (value));
+               break;
+       case PROP_SSL_STRICT:
+               priv->ssl_strict = g_value_get_boolean (value);
+               break;
+       case PROP_ASYNC_CONTEXT:
+               async_context = g_value_get_pointer (value);
+               if (async_context && async_context != g_main_context_get_thread_default ())
+                       g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
+               priv->async_context = async_context;
+               if (priv->async_context)
+                       g_main_context_ref (priv->async_context);
+               break;
+       case PROP_USE_THREAD_CONTEXT:
+               if (!g_value_get_boolean (value))
+                       g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
+               priv->use_thread_context = g_value_get_boolean (value);
+               if (priv->use_thread_context) {
+                       if (priv->async_context)
+                               g_main_context_unref (priv->async_context);
+                       priv->async_context = g_main_context_get_thread_default ();
+                       if (priv->async_context)
+                               g_main_context_ref (priv->async_context);
+               }
+               break;
+       case PROP_TIMEOUT:
+               priv->io_timeout = g_value_get_uint (value);
+               break;
+       case PROP_USER_AGENT:
+               g_free (priv->user_agent);
+               user_agent = g_value_get_string (value);
+               if (!user_agent)
+                       priv->user_agent = NULL;
+               else if (!*user_agent) {
+                       priv->user_agent =
+                               g_strdup (SOUP_SESSION_USER_AGENT_BASE);
+               } else if (g_str_has_suffix (user_agent, " ")) {
+                       priv->user_agent =
+                               g_strdup_printf ("%s%s", user_agent,
+                                                SOUP_SESSION_USER_AGENT_BASE);
+               } else
+                       priv->user_agent = g_strdup (user_agent);
+               break;
+       case PROP_ACCEPT_LANGUAGE:
+               g_free (priv->accept_language);
+               priv->accept_language = g_strdup (g_value_get_string (value));
+               priv->accept_language_auto = FALSE;
+               break;
+       case PROP_ACCEPT_LANGUAGE_AUTO:
+               priv->accept_language_auto = g_value_get_boolean (value);
+               if (priv->accept_language) {
+                       g_free (priv->accept_language);
+                       priv->accept_language = NULL;
+               }
+
+               /* Get languages from system if needed */
+               if (priv->accept_language_auto)
+                       priv->accept_language = accept_languages_from_system ();
+               break;
+       case PROP_IDLE_TIMEOUT:
+               priv->idle_timeout = g_value_get_uint (value);
+               break;
+       case PROP_ADD_FEATURE:
+               soup_session_add_feature (session, g_value_get_object (value));
+               break;
+       case PROP_ADD_FEATURE_BY_TYPE:
+               soup_session_add_feature_by_type (session, g_value_get_gtype (value));
+               break;
+       case PROP_REMOVE_FEATURE_BY_TYPE:
+               soup_session_remove_feature_by_type (session, g_value_get_gtype (value));
+               break;
+       case PROP_HTTP_ALIASES:
+               set_aliases (&priv->http_aliases, g_value_get_boxed (value));
+               break;
+       case PROP_HTTPS_ALIASES:
+               set_aliases (&priv->https_aliases, g_value_get_boxed (value));
+               break;
+#if ENABLE(TIZEN_CERTIFICATE_FILE_SET)
+        case PROP_CERTIFICATE_PATH:
+               if (priv->certificate_path) {
+                       g_free (priv->certificate_path);
+                       priv->certificate_path = NULL;
+               }
+               if (g_value_get_string (value) && strlen (g_value_get_string (value)))
+                       priv->certificate_path = g_strdup (g_value_get_string (value));
+#if ENABLE(TIZEN_DLOG)
+               TIZEN_LOGI ("set_property() PROP_CERTIFICATE_PATH priv->certificate_path is set");
+               TIZEN_LOGI ("set_property() PROP_CERTIFICATE_PATH priv->certificate_path is set [%s]", priv->certificate_path);
+#endif
+               if (priv->certificate_path)
+#if ENABLE(TIZEN_TV_FORCE_PRELOAD_TLSDB)
+                       //Trigger loading of the TLS database. The load is done in a thread.
+                       soup_preload_tls_database(priv->certificate_path);
+#else
+                       soup_session_tls_start_idle_timer(session, SET_TLS_CERT_FILE_TIMEOUT);
+#endif
+               break;
+#endif
+#if ENABLE(TIZEN_TV_CLIENT_CERTIFICATE)
+       case PROP_WIDGET_ENGINE:
+               priv->widget_engine = g_value_get_boolean (value);
+               break;
+#endif
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static GProxyResolver *
+get_proxy_resolver (SoupSession *session)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+       if (priv->proxy_use_default) {
+               priv->proxy_resolver = g_object_ref (g_proxy_resolver_get_default ());
+               priv->proxy_use_default = FALSE;
+       }
+       return priv->proxy_resolver;
+}
+
+static GTlsDatabase *
+get_tls_database (SoupSession *session)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+       if (priv->tlsdb_use_default) {
+               priv->tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
+               priv->tlsdb_use_default = FALSE;
+       }
+       return priv->tlsdb;
+}
+
+static void
+soup_session_get_property (GObject *object, guint prop_id,
+                          GValue *value, GParamSpec *pspec)
+{
+       SoupSession *session = SOUP_SESSION (object);
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       SoupSessionFeature *feature;
+       GTlsDatabase *tlsdb;
+
+       switch (prop_id) {
+       case PROP_LOCAL_ADDRESS:
+               g_value_set_object (value, priv->local_addr);
+               break;
+       case PROP_PROXY_URI:
+               g_value_set_boxed (value, priv->proxy_uri);
+               break;
+       case PROP_PROXY_RESOLVER:
+               g_value_set_object (value, get_proxy_resolver (session));
+               break;
+       case PROP_MAX_CONNS:
+               g_value_set_int (value, priv->max_conns);
+               break;
+       case PROP_MAX_CONNS_PER_HOST:
+               g_value_set_int (value, priv->max_conns_per_host);
+               break;
+       case PROP_USE_NTLM:
+               feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
+               if (feature)
+                       g_value_set_boolean (value, soup_session_feature_has_feature (feature, SOUP_TYPE_AUTH_NTLM));
+               else
+                       g_value_set_boolean (value, FALSE);
+               break;
+       case PROP_SSL_CA_FILE:
+               g_value_set_string (value, priv->ssl_ca_file);
+               break;
+       case PROP_SSL_USE_SYSTEM_CA_FILE:
+               tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
+               g_value_set_boolean (value, get_tls_database (session) == tlsdb);
+               g_clear_object (&tlsdb);
+               break;
+       case PROP_TLS_DATABASE:
+               g_value_set_object (value, get_tls_database (session));
+               break;
+       case PROP_SSL_STRICT:
+               g_value_set_boolean (value, priv->ssl_strict);
+               break;
+       case PROP_ASYNC_CONTEXT:
+               g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
+               break;
+       case PROP_USE_THREAD_CONTEXT:
+               g_value_set_boolean (value, priv->use_thread_context);
+               break;
+       case PROP_TIMEOUT:
+               g_value_set_uint (value, priv->io_timeout);
+               break;
+       case PROP_USER_AGENT:
+               g_value_set_string (value, priv->user_agent);
+               break;
+       case PROP_ACCEPT_LANGUAGE:
+               g_value_set_string (value, priv->accept_language);
+               break;
+       case PROP_ACCEPT_LANGUAGE_AUTO:
+               g_value_set_boolean (value, priv->accept_language_auto);
+               break;
+       case PROP_IDLE_TIMEOUT:
+               g_value_set_uint (value, priv->idle_timeout);
+               break;
+       case PROP_HTTP_ALIASES:
+               g_value_set_boxed (value, priv->http_aliases);
+               break;
+       case PROP_HTTPS_ALIASES:
+               g_value_set_boxed (value, priv->https_aliases);
+               break;
+#if ENABLE(TIZEN_CERTIFICATE_FILE_SET)
+        case PROP_CERTIFICATE_PATH:
+#if ENABLE(TIZEN_DLOG)
+               TIZEN_LOGI ("get_property() PROP_CERTIFICATE_PATH");
+#endif
+               g_value_set_string (value, priv->certificate_path);
+               break;
+#endif
+#if ENABLE(TIZEN_TV_CLIENT_CERTIFICATE)
+       case PROP_WIDGET_ENGINE:
+               g_value_set_boolean (value, priv->widget_engine);
+               break;
+#endif
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+/**
+ * soup_session_new:
+ *
+ * Creates a #SoupSession with the default options.
+ *
+ * Return value: the new session.
+ *
+ * Since: 2.42
+ */
+SoupSession *
+soup_session_new (void)
+{
+       return g_object_new (SOUP_TYPE_SESSION, NULL);
+}
+
+/**
+ * soup_session_new_with_options:
+ * @optname1: name of first property to set
+ * @...: value of @optname1, followed by additional property/value pairs
+ *
+ * Creates a #SoupSession with the specified options.
+ *
+ * Return value: the new session.
+ *
+ * Since: 2.42
+ */
+SoupSession *
+soup_session_new_with_options (const char *optname1,
+                              ...)
+{
+       SoupSession *session;
+       va_list ap;
+
+       va_start (ap, optname1);
+       session = (SoupSession *)g_object_new_valist (SOUP_TYPE_SESSION,
+                                                     optname1, ap);
+       va_end (ap);
+
+       return session;
+}
+
+/**
+ * soup_session_get_async_context:
+ * @session: a #SoupSession
+ *
+ * Gets @session's #SoupSession:async-context. This does not add a ref
+ * to the context, so you will need to ref it yourself if you want it
+ * to outlive its session.
+ *
+ * For a modern #SoupSession, this will always just return the
+ * thread-default #GMainContext, and so is not especially useful.
+ *
+ * Return value: (transfer none): @session's #GMainContext, which may
+ * be %NULL
+ **/
+GMainContext *
+soup_session_get_async_context (SoupSession *session)
+{
+       SoupSessionPrivate *priv;
+
+       g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
+       priv = SOUP_SESSION_GET_PRIVATE (session);
+
+       if (priv->use_thread_context)
+               return g_main_context_get_thread_default ();
+       else
+               return priv->async_context;
+}
+
+/* Hosts */
+
+/* Note that we can't use soup_uri_host_hash() and soup_uri_host_equal()
+ * because we want to ignore the protocol; http://example.com and
+ * webcal://example.com are the same host.
+ */
+static guint
+soup_host_uri_hash (gconstpointer key)
+{
+       const SoupURI *uri = key;
+
+       g_return_val_if_fail (uri != NULL && uri->host != NULL, 0);
+
+       return uri->port + soup_str_case_hash (uri->host);
+}
+
+static gboolean
+soup_host_uri_equal (gconstpointer v1, gconstpointer v2)
+{
+       const SoupURI *one = v1;
+       const SoupURI *two = v2;
+
+       g_return_val_if_fail (one != NULL && two != NULL, one == two);
+       g_return_val_if_fail (one->host != NULL && two->host != NULL, one->host == two->host);
+
+       if (one->port != two->port)
+               return FALSE;
+
+       return g_ascii_strcasecmp (one->host, two->host) == 0;
+}
+
+
+static SoupSessionHost *
+soup_session_host_new (SoupSession *session, SoupURI *uri)
+{
+       SoupSessionHost *host;
+
+       host = g_slice_new0 (SoupSessionHost);
+       host->uri = soup_uri_copy_host (uri);
+       if (host->uri->scheme != SOUP_URI_SCHEME_HTTP &&
+           host->uri->scheme != SOUP_URI_SCHEME_HTTPS) {
+               SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+               if (soup_uri_is_https (host->uri, priv->https_aliases))
+                       host->uri->scheme = SOUP_URI_SCHEME_HTTPS;
+               else
+                       host->uri->scheme = SOUP_URI_SCHEME_HTTP;
+       }
+
+       host->addr = g_object_new (SOUP_TYPE_ADDRESS,
+                                  SOUP_ADDRESS_NAME, host->uri->host,
+                                  SOUP_ADDRESS_PORT, host->uri->port,
+                                  SOUP_ADDRESS_PROTOCOL, host->uri->scheme,
+                                  NULL);
+       host->keep_alive_src = NULL;
+       host->session = session;
+
+       return host;
+}
+
+/* Requires conn_lock to be locked */
+static SoupSessionHost *
+get_host_for_uri (SoupSession *session, SoupURI *uri)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       SoupSessionHost *host;
+
+       if (soup_uri_is_https (uri, priv->https_aliases))
+               host = g_hash_table_lookup (priv->https_hosts, uri);
+       else
+               host = g_hash_table_lookup (priv->http_hosts, uri);
+       if (host)
+               return host;
+
+       host = soup_session_host_new (session, uri);
+
+       if (soup_uri_is_https (uri, priv->https_aliases))
+               g_hash_table_insert (priv->https_hosts, host->uri, host);
+       else
+               g_hash_table_insert (priv->http_hosts, host->uri, host);
+
+       return host;
+}
+
+/* Requires conn_lock to be locked */
+static SoupSessionHost *
+get_host_for_message (SoupSession *session, SoupMessage *msg)
+{
+       return get_host_for_uri (session, soup_message_get_uri (msg));
+}
+
+static void
+free_host (SoupSessionHost *host)
+{
+       g_warn_if_fail (host->connections == NULL);
+
+       if (host->keep_alive_src) {
+               g_source_destroy (host->keep_alive_src);
+               g_source_unref (host->keep_alive_src);
+       }
+
+       soup_uri_free (host->uri);
+       g_object_unref (host->addr);
+       g_slice_free (SoupSessionHost, host);
+}
+
+static void
+auth_manager_authenticate (SoupAuthManager *manager, SoupMessage *msg,
+                          SoupAuth *auth, gboolean retrying,
+                          gpointer session)
+{
+#if ENABLE(TIZEN_ON_AUTHENTICATION_REQUESTED)
+       soup_message_authenticate(msg, auth, retrying);
+#endif
+       g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
+}
+
+#if ENABLE(TIZEN_HANDLING_307_REDIRECTION)
+#define SOUP_SESSION_WOULD_REDIRECT_AS_GET(session, msg) \
+       ((msg)->status_code == SOUP_STATUS_SEE_OTHER || \
+        ((msg)->status_code == SOUP_STATUS_FOUND && \
+         !SOUP_METHOD_IS_SAFE ((msg)->method)) || \
+       ((msg)->status_code == SOUP_STATUS_TEMPORARY_REDIRECT && \
+        (msg)->method == SOUP_METHOD_GET) || \
+       ((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY && \
+        (msg)->method == SOUP_METHOD_POST))
+#else
+#define SOUP_SESSION_WOULD_REDIRECT_AS_GET(session, msg) \
+       ((msg)->status_code == SOUP_STATUS_SEE_OTHER || \
+        ((msg)->status_code == SOUP_STATUS_FOUND && \
+         !SOUP_METHOD_IS_SAFE ((msg)->method)) || \
+        ((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY && \
+         (msg)->method == SOUP_METHOD_POST))
+#endif
+
+#define SOUP_SESSION_WOULD_REDIRECT_AS_SAFE(session, msg) \
+       (((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY || \
+         (msg)->status_code == SOUP_STATUS_TEMPORARY_REDIRECT || \
+         (msg)->status_code == SOUP_STATUS_FOUND) && \
+        SOUP_METHOD_IS_SAFE ((msg)->method))
+
+#if ENABLE(TIZEN_HANDLING_307_REDIRECTION)
+#define SOUP_SESSION_WOULD_REDIRECT_AS_POST(session, msg) \
+       ((msg)->status_code == SOUP_STATUS_TEMPORARY_REDIRECT && \
+        (msg)->method == SOUP_METHOD_POST)
+#endif
+
+static inline SoupURI *
+redirection_uri (SoupMessage *msg)
+{
+       const char *new_loc;
+       SoupURI *new_uri;
+
+       new_loc = soup_message_headers_get_one (msg->response_headers,
+                                               "Location");
+       if (!new_loc)
+               return NULL;
+       new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
+       if (!new_uri || !new_uri->host) {
+               if (new_uri)
+                       soup_uri_free (new_uri);
+               return NULL;
+       }
+
+       return new_uri;
+}
+
+/**
+ * soup_session_would_redirect:
+ * @session: a #SoupSession
+ * @msg: a #SoupMessage that has response headers
+ *
+ * Checks if @msg contains a response that would cause @session to
+ * redirect it to a new URL (ignoring @msg's %SOUP_MESSAGE_NO_REDIRECT
+ * flag, and the number of times it has already been redirected).
+ *
+ * Return value: whether @msg would be redirected
+ *
+ * Since: 2.38
+ */
+gboolean
+soup_session_would_redirect (SoupSession *session, SoupMessage *msg)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       SoupURI *new_uri;
+
+       /* It must have an appropriate status code and method */
+       if (!SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg) &&
+           !SOUP_SESSION_WOULD_REDIRECT_AS_SAFE (session, msg)
+#if ENABLE(TIZEN_HANDLING_307_REDIRECTION)
+           && !SOUP_SESSION_WOULD_REDIRECT_AS_POST (session, msg)
+#endif
+       )
+               return FALSE;
+
+       /* and a Location header that parses to an http URI */
+       if (!soup_message_headers_get_one (msg->response_headers, "Location"))
+               return FALSE;
+       new_uri = redirection_uri (msg);
+       if (!new_uri)
+               return FALSE;
+       if (!new_uri->host || !*new_uri->host ||
+           (!soup_uri_is_http (new_uri, priv->http_aliases) &&
+            !soup_uri_is_https (new_uri, priv->https_aliases))) {
+               soup_uri_free (new_uri);
+               return FALSE;
+       }
+
+       soup_uri_free (new_uri);
+       return TRUE;
+}
+
+/**
+ * soup_session_redirect_message:
+ * @session: the session
+ * @msg: a #SoupMessage that has received a 3xx response
+ *
+ * Updates @msg's URI according to its status code and "Location"
+ * header, and requeues it on @session. Use this when you have set
+ * %SOUP_MESSAGE_NO_REDIRECT on a message, but have decided to allow a
+ * particular redirection to occur, or if you want to allow a
+ * redirection that #SoupSession will not perform automatically (eg,
+ * redirecting a non-safe method such as DELETE).
+ *
+ * If @msg's status code indicates that it should be retried as a GET
+ * request, then @msg will be modified accordingly.
+ *
+ * If @msg has already been redirected too many times, this will
+ * cause it to fail with %SOUP_STATUS_TOO_MANY_REDIRECTS.
+ *
+ * Return value: %TRUE if a redirection was applied, %FALSE if not
+ * (eg, because there was no Location header, or it could not be
+ * parsed).
+ *
+ * Since: 2.38
+ */
+gboolean
+soup_session_redirect_message (SoupSession *session, SoupMessage *msg)
+{
+       SoupURI *new_uri;
+
+       new_uri = redirection_uri (msg);
+       if (!new_uri)
+               return FALSE;
+
+       if (SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg)) {
+               if (msg->method != SOUP_METHOD_HEAD) {
+                       g_object_set (msg,
+                                     SOUP_MESSAGE_METHOD, SOUP_METHOD_GET,
+                                     NULL);
+               }
+               soup_message_set_request (msg, NULL,
+                                         SOUP_MEMORY_STATIC, NULL, 0);
+               soup_message_headers_set_encoding (msg->request_headers,
+                                                  SOUP_ENCODING_NONE);
+       }
+
+       soup_message_set_uri (msg, new_uri);
+       soup_uri_free (new_uri);
+
+       soup_session_requeue_message (session, msg);
+       return TRUE;
+}
+
+static void
+redirect_handler (SoupMessage *msg, gpointer user_data)
+{
+       SoupMessageQueueItem *item = user_data;
+       SoupSession *session = item->session;
+
+       if (!soup_session_would_redirect (session, msg)) {
+               SoupURI *new_uri = redirection_uri (msg);
+               gboolean invalid = !new_uri || !new_uri->host;
+
+               if (new_uri)
+                       soup_uri_free (new_uri);
+               if (invalid && !item->new_api) {
+                       soup_message_set_status_full (msg,
+                                                     SOUP_STATUS_MALFORMED,
+                                                     "Invalid Redirect URL");
+               }
+               return;
+       }
+
+       soup_session_redirect_message (session, msg);
+}
+
+static void
+re_emit_connection_event (SoupConnection      *conn,
+                         GSocketClientEvent   event,
+                         GIOStream           *connection,
+                         gpointer             user_data)
+{
+       SoupMessageQueueItem *item = user_data;
+
+       soup_message_network_event (item->msg, event, connection);
+}
+
+#if ENABLE(TIZEN_TV_DYNAMIC_CERTIFICATE_LOADING)
+const char*
+re_emit_connection_dynamic_client_certificate (SoupConnection      *conn,
+                                               const char* current_host,
+                                               gpointer user_data)
+{
+        SoupMessageQueueItem *item = user_data;
+
+        return soup_message_dynamic_client_certificate(item->msg, current_host);
+}
+#endif
+
+#if ENABLE(TIZEN_TV_CERTIFICATE_HANDLING)
+gboolean
+re_emit_connection_accept_certificate (SoupConnection      *conn,
+                                       GTlsCertificate* certificate,
+                                       GTlsCertificateFlags errors,
+                                       gpointer             user_data)
+{
+       SoupMessageQueueItem *item = user_data;
+
+       return soup_message_accept_certificate(item->msg, certificate, errors);
+}
+#endif
+
+static void
+soup_session_set_item_connection (SoupSession          *session,
+                                 SoupMessageQueueItem *item,
+                                 SoupConnection       *conn)
+{
+       if (item->conn) {
+               g_signal_handlers_disconnect_by_func (item->conn, re_emit_connection_event, item);
+               g_object_unref (item->conn);
+       }
+
+       item->conn = conn;
+       soup_message_set_connection (item->msg, conn);
+
+       if (item->conn) {
+               g_object_ref (item->conn);
+               g_signal_connect (item->conn, "event",
+                                 G_CALLBACK (re_emit_connection_event), item);
+#if ENABLE(TIZEN_TV_DYNAMIC_CERTIFICATE_LOADING)
+               if (item->msg->method != SOUP_METHOD_CONNECT)
+                       g_signal_connect (item->conn, "dynamic-certificatePath",
+                                         G_CALLBACK (re_emit_connection_dynamic_client_certificate), item);
+#endif
+#if ENABLE(TIZEN_TV_CERTIFICATE_HANDLING)
+               if (item->msg->method != SOUP_METHOD_CONNECT)
+                       g_signal_connect (item->conn, "accept-certificate",
+                                         G_CALLBACK (re_emit_connection_accept_certificate), item);
+#endif
+       }
+}
+
+static void
+message_restarted (SoupMessage *msg, gpointer user_data)
+{
+       SoupMessageQueueItem *item = user_data;
+
+       if (item->conn &&
+           (!soup_message_is_keepalive (msg) ||
+            SOUP_STATUS_IS_REDIRECTION (msg->status_code))) {
+               if (soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE)
+                       soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
+               soup_session_set_item_connection (item->session, item, NULL);
+       }
+
+       soup_message_cleanup_response (msg);
+}
+
+SoupMessageQueueItem *
+soup_session_append_queue_item (SoupSession *session, SoupMessage *msg,
+                               gboolean async, gboolean new_api,
+                               SoupSessionCallback callback, gpointer user_data)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       SoupMessageQueueItem *item;
+       SoupSessionHost *host;
+
+       soup_message_cleanup_response (msg);
+
+       item = soup_message_queue_append (priv->queue, msg, callback, user_data);
+       item->async = async;
+       item->new_api = new_api;
+
+       g_mutex_lock (&priv->conn_lock);
+       host = get_host_for_message (session, item->msg);
+       host->num_messages++;
+       g_mutex_unlock (&priv->conn_lock);
+
+       if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
+               soup_message_add_header_handler (
+                       msg, "got_body", "Location",
+                       G_CALLBACK (redirect_handler), item);
+       }
+       g_signal_connect (msg, "restarted",
+                         G_CALLBACK (message_restarted), item);
+
+       g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
+
+       soup_message_queue_item_ref (item);
+       return item;
+}
+
+static void
+soup_session_send_queue_item (SoupSession *session,
+                             SoupMessageQueueItem *item,
+                             SoupMessageCompletionFn completion_cb)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       const char *conn_header;
+
+       if (priv->user_agent) {
+               soup_message_headers_replace (item->msg->request_headers,
+                                             "User-Agent", priv->user_agent);
+       }
+
+       if (priv->accept_language &&
+           !soup_message_headers_get_list (item->msg->request_headers,
+                                           "Accept-Language")) {
+               soup_message_headers_append (item->msg->request_headers,
+                                            "Accept-Language",
+                                            priv->accept_language);
+       }
+
+       /* Force keep alive connections for HTTP 1.0. Performance will
+        * improve when issuing multiple requests to the same host in
+        * a short period of time, as we wouldn't need to establish
+        * new connections. Keep alive is implicit for HTTP 1.1.
+        */
+       conn_header = soup_message_headers_get_list (item->msg->request_headers, "Connection");
+       if (!conn_header ||
+           (!soup_header_contains (conn_header, "Keep-Alive") &&
+            !soup_header_contains (conn_header, "close")))
+               soup_message_headers_append (item->msg->request_headers,
+                                            "Connection", "Keep-Alive");
 
+       g_signal_emit (session, signals[REQUEST_STARTED], 0,
+                      item->msg, soup_connection_get_socket (item->conn));
+       if (item->state == SOUP_MESSAGE_RUNNING)
+               soup_connection_send_request (item->conn, item, completion_cb, item);
+}
 
-       /* properties */
-       /**
-        * SOUP_SESSION_PROXY_URI:
-        *
-        * Alias for the #SoupSession:proxy-uri property. (The HTTP
-        * proxy to use for this session.)
-        **/
-       g_object_class_install_property (
-               object_class, PROP_PROXY_URI,
-               g_param_spec_boxed (SOUP_SESSION_PROXY_URI,
-                                   "Proxy URI",
-                                   "The HTTP Proxy to use for this session",
-                                   SOUP_TYPE_URI,
-                                   G_PARAM_READWRITE));
-       /**
-        * SOUP_SESSION_MAX_CONNS:
-        *
-        * Alias for the #SoupSession:max-conns property. (The maximum
-        * number of connections that the session can open at once.)
-        **/
-       g_object_class_install_property (
-               object_class, PROP_MAX_CONNS,
-               g_param_spec_int (SOUP_SESSION_MAX_CONNS,
-                                 "Max Connection Count",
-                                 "The maximum number of connections that the session can open at once",
-                                 1,
-                                 G_MAXINT,
-                                 SOUP_SESSION_MAX_CONNS_DEFAULT,
-                                 G_PARAM_READWRITE));
-       /**
-        * SOUP_SESSION_MAX_CONNS_PER_HOST:
-        *
-        * Alias for the #SoupSession:max-conns-per-host property.
-        * (The maximum number of connections that the session can
-        * open at once to a given host.)
-        **/
-       g_object_class_install_property (
-               object_class, PROP_MAX_CONNS_PER_HOST,
-               g_param_spec_int (SOUP_SESSION_MAX_CONNS_PER_HOST,
-                                 "Max Per-Host Connection Count",
-                                 "The maximum number of connections that the session can open at once to a given host",
-                                 1,
-                                 G_MAXINT,
-                                 SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT,
-                                 G_PARAM_READWRITE));
-       /**
-        * SoupSession:idle-timeout:
-        *
-        * Connection lifetime when idle
-        *
-        * Since: 2.4.1
-        **/
-       /**
-        * SOUP_SESSION_IDLE_TIMEOUT:
-        *
-        * Alias for the #SoupSession:idle-timeout property. (The idle
-        * connection lifetime.)
-        *
-        * Since: 2.4.1
-        **/
-       g_object_class_install_property (
-               object_class, PROP_IDLE_TIMEOUT,
-               g_param_spec_uint (SOUP_SESSION_IDLE_TIMEOUT,
-                                  "Idle Timeout",
-                                  "Connection lifetime when idle",
-                                  0, G_MAXUINT, 0,
-                                  G_PARAM_READWRITE));
-       /**
-        * SoupSession:use-ntlm:
-        *
-        * Whether or not to use NTLM authentication.
-        *
-        * Deprecated: use soup_session_add_feature_by_type() with
-        * #SOUP_TYPE_AUTH_NTLM.
-        **/
-       /**
-        * SOUP_SESSION_USE_NTLM:
-        *
-        * Alias for the #SoupSession:use-ntlm property. (Whether or
-        * not to use NTLM authentication.)
-        **/
-       g_object_class_install_property (
-               object_class, PROP_USE_NTLM,
-               g_param_spec_boolean (SOUP_SESSION_USE_NTLM,
-                                     "Use NTLM",
-                                     "Whether or not to use NTLM authentication",
-                                     FALSE,
-                                     G_PARAM_READWRITE));
-       /**
-        * SoupSession:ssl-ca-file:
-        *
-        * File containing SSL CA certificates.
-        *
-        * Deprecated: use #SoupSession:ssl-use-system-ca-file or
-        * #SoupSession:tls-database instead
-        **/
-       /**
-        * SOUP_SESSION_SSL_CA_FILE:
-        *
-        * Alias for the #SoupSession:ssl-ca-file property. (File
-        * containing SSL CA certificates.).
-        *
-        * Deprecated: use #SoupSession:ssl-use-system-ca-file or
-        * #SoupSession:tls-database instead
-        **/
-       g_object_class_install_property (
-               object_class, PROP_SSL_CA_FILE,
-               g_param_spec_string (SOUP_SESSION_SSL_CA_FILE,
-                                    "SSL CA file",
-                                    "File containing SSL CA certificates",
-                                    NULL,
-                                    G_PARAM_READWRITE));
-       /**
-        * SOUP_SESSION_USE_SYSTEM_CA_FILE:
-        *
-        * Alias for the #SoupSession:ssl-use-system-ca-file property,
-        * qv.
-        *
-        * Since: 2.38
-        **/
-       /**
-        * SoupSession:ssl-use-system-ca-file:
-        *
-        * Setting this to %TRUE is equivalent to setting
-        * #SoupSession:tls-database to the default system CA database.
-        * (and likewise, setting #SoupSession:tls-database to the
-        * default database by hand will cause this property to
-        * become %TRUE).
-        *
-        * Setting this to %FALSE (when it was previously %TRUE) will
-        * clear the #SoupSession:tls-database field.
-        *
-        * See #SoupSession:ssl-strict for more information on how
-        * https certificate validation is handled.
-        *
-        * Since: 2.38
-        **/
-       g_object_class_install_property (
-               object_class, PROP_SSL_USE_SYSTEM_CA_FILE,
-               g_param_spec_boolean (SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE,
-                                     "Use system CA file",
-                                     "Use the system certificate database",
-                                     FALSE,
-                                     G_PARAM_READWRITE));
-       /**
-        * SOUP_SESSION_TLS_DATABASE:
-        *
-        * Alias for the #SoupSession:tls-database property, qv.
-        *
-        * Since: 2.38
-        **/
-       /**
-        * SoupSession:tls-database:
-        *
-        * Sets the #GTlsDatabase to use for validating SSL/TLS
-        * certificates.
-        *
-        * Note that setting the #SoupSession:ssl-ca-file or
-        * #SoupSession:ssl-use-system-ca-file property will cause
-        * this property to be set to a #GTlsDatabase corresponding to
-        * the indicated file or system default.
-        *
-        * See #SoupSession:ssl-strict for more information on how
-        * https certificate validation is handled.
-        *
-        * Since: 2.38
-        **/
-       g_object_class_install_property (
-               object_class, PROP_TLS_DATABASE,
-               g_param_spec_object (SOUP_SESSION_TLS_DATABASE,
-                                    "TLS Database",
-                                    "TLS database to use",
-                                    G_TYPE_TLS_DATABASE,
-                                    G_PARAM_READWRITE));
-       /**
-        * SOUP_SESSION_SSL_STRICT:
-        *
-        * Alias for the #SoupSession:ssl-strict property, qv.
-        *
-        * Since: 2.30
-        **/
-       /**
-        * SoupSession:ssl-strict:
-        *
-        * Normally, if #SoupSession:tlsdb is set (including if it was
-        * set via #SoupSession:ssl-use-system-ca-file or
-        * #SoupSession:ssl-ca-file), then libsoup will reject any
-        * certificate that is invalid (ie, expired) or that is not
-        * signed by one of the given CA certificates, and the
-        * #SoupMessage will fail with the status
-        * %SOUP_STATUS_SSL_FAILED.
-        *
-        * If you set #SoupSession:ssl-strict to %FALSE, then all
-        * certificates will be accepted, and you will need to call
-        * soup_message_get_https_status() to distinguish valid from
-        * invalid certificates. (This can be used, eg, if you want to
-        * accept invalid certificates after giving some sort of
-        * warning.)
-        *
-        * If the session has no CA file or TLS database, then all
-        * certificates are always accepted, and this property has no
-        * effect.
-        *
-        * Since: 2.30
-        */
-       g_object_class_install_property (
-               object_class, PROP_SSL_STRICT,
-               g_param_spec_boolean (SOUP_SESSION_SSL_STRICT,
-                                     "Strictly validate SSL certificates",
-                                     "Whether certificate errors should be considered a connection error",
-                                     TRUE,
-                                     G_PARAM_READWRITE));
-       /**
-        * SOUP_SESSION_ASYNC_CONTEXT:
-        *
-        * Alias for the #SoupSession:async-context property. (The
-        * session's #GMainContext.)
+static gboolean
+soup_session_cleanup_connections (SoupSession *session,
+                                 gboolean     cleanup_idle)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       GSList *conns = NULL, *c;
+       GHashTableIter iter;
+       gpointer conn, host;
+       SoupConnectionState state;
+
+       g_mutex_lock (&priv->conn_lock);
+       g_hash_table_iter_init (&iter, priv->conns);
+       while (g_hash_table_iter_next (&iter, &conn, &host)) {
+               state = soup_connection_get_state (conn);
+               if (state == SOUP_CONNECTION_REMOTE_DISCONNECTED ||
+                   (cleanup_idle && state == SOUP_CONNECTION_IDLE)) {
+                       conns = g_slist_prepend (conns, g_object_ref (conn));
+                       g_hash_table_iter_remove (&iter);
+                       drop_connection (session, host, conn);
+               }
+       }
+       g_mutex_unlock (&priv->conn_lock);
+
+       if (!conns)
+               return FALSE;
+
+       for (c = conns; c; c = c->next) {
+               conn = c->data;
+               soup_connection_disconnect (conn);
+               g_object_unref (conn);
+       }
+       g_slist_free (conns);
+
+       return TRUE;
+}
+
+static gboolean
+free_unused_host (gpointer user_data)
+{
+       SoupSessionHost *host = (SoupSessionHost *) user_data;
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (host->session);
+
+       g_mutex_lock (&priv->conn_lock);
+
+       /* In a multithreaded session, a connection might have been
+        * added while we were waiting for conn_lock.
         */
-       g_object_class_install_property (
-               object_class, PROP_ASYNC_CONTEXT,
-               g_param_spec_pointer (SOUP_SESSION_ASYNC_CONTEXT,
-                                     "Async GMainContext",
-                                     "The GMainContext to dispatch async I/O in",
-                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-       /**
-        * SOUP_SESSION_USE_THREAD_CONTEXT:
-        *
-        * Alias for the #SoupSession:use-thread-context property, qv.
-        *
-        * Since: 2.38
+       if (host->connections) {
+               g_mutex_unlock (&priv->conn_lock);
+               return FALSE;
+       }
+
+       /* This will free the host in addition to removing it from the
+        * hash table
         */
-       /**
-        * SoupSession:use-thread-context:
-        *
-        * If set, asynchronous operations in this session will run in
-        * whatever the thread-default #GMainContext is at the time
-        * they are started, rather than always occurring in a context
-        * fixed at the session's construction time. "Bookkeeping"
-        * tasks (like expiring idle connections) will happen in the
-        * context that was thread-default at the time the session was
-        * created.
-        *
-        * Since: 2.38
+       if (host->uri->scheme == SOUP_URI_SCHEME_HTTPS)
+               g_hash_table_remove (priv->https_hosts, host->uri);
+       else
+               g_hash_table_remove (priv->http_hosts, host->uri);
+       g_mutex_unlock (&priv->conn_lock);
+
+       return FALSE;
+}
+
+static void
+drop_connection (SoupSession *session, SoupSessionHost *host, SoupConnection *conn)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+       /* Note: caller must hold conn_lock, and must remove @conn
+        * from priv->conns itself.
         */
-       g_object_class_install_property (
-               object_class, PROP_USE_THREAD_CONTEXT,
-               g_param_spec_boolean (SOUP_SESSION_USE_THREAD_CONTEXT,
-                                     "Use thread-default GMainContext",
-                                     "Whether to use thread-default main contexts",
-                                     FALSE,
-                                     G_PARAM_READWRITE));
-       /**
-        * SOUP_SESSION_TIMEOUT:
-        *
-        * Alias for the #SoupSession:timeout property. (The timeout
-        * in seconds for blocking socket I/O operations.)
-        **/
-       g_object_class_install_property (
-               object_class, PROP_TIMEOUT,
-               g_param_spec_uint (SOUP_SESSION_TIMEOUT,
-                                  "Timeout value",
-                                  "Value in seconds to timeout a blocking I/O",
-                                  0, G_MAXUINT, 0,
-                                  G_PARAM_READWRITE));
 
-       /**
-        * SoupSession:user-agent:
-        *
-        * If non-%NULL, the value to use for the "User-Agent" header
-        * on #SoupMessage<!-- -->s sent from this session.
-        *
-        * RFC 2616 says: "The User-Agent request-header field
-        * contains information about the user agent originating the
-        * request. This is for statistical purposes, the tracing of
-        * protocol violations, and automated recognition of user
-        * agents for the sake of tailoring responses to avoid
-        * particular user agent limitations. User agents SHOULD
-        * include this field with requests."
-        *
-        * The User-Agent header contains a list of one or more
-        * product tokens, separated by whitespace, with the most
-        * significant product token coming first. The tokens must be
-        * brief, ASCII, and mostly alphanumeric (although "-", "_",
-        * and "." are also allowed), and may optionally include a "/"
-        * followed by a version string. You may also put comments,
-        * enclosed in parentheses, between or after the tokens.
-        *
-        * If you set a #SoupSession:user_agent property that has trailing
-        * whitespace, #SoupSession will append its own product token
-        * (eg, "<literal>libsoup/2.3.2</literal>") to the end of the
-        * header for you.
-        **/
-       /**
-        * SOUP_SESSION_USER_AGENT:
-        *
-        * Alias for the #SoupSession:user-agent property, qv.
-        **/
-       g_object_class_install_property (
-               object_class, PROP_USER_AGENT,
-               g_param_spec_string (SOUP_SESSION_USER_AGENT,
-                                    "User-Agent string",
-                                    "User-Agent string",
-                                    NULL,
-                                    G_PARAM_READWRITE));
+       if (host) {
+               host->connections = g_slist_remove (host->connections, conn);
+               host->num_conns--;
+
+               /* Free the SoupHost (and its SoupAddress) if there
+                * has not been any new connection to the host during
+                * the last HOST_KEEP_ALIVE msecs.
+                */
+               if (host->num_conns == 0) {
+                       g_assert (host->keep_alive_src == NULL);
+                       host->keep_alive_src = soup_add_timeout (priv->async_context,
+                                                                HOST_KEEP_ALIVE,
+                                                                free_unused_host,
+                                                                host);
+                       host->keep_alive_src = g_source_ref (host->keep_alive_src);
+               }
+
+               if (soup_connection_get_ssl_fallback (conn))
+                       host->ssl_fallback = TRUE;
+       }
+
+       g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
+       g_signal_handlers_disconnect_by_func (conn, connection_state_changed, session);
+       priv->num_conns--;
+
+       g_object_unref (conn);
+}
+
+static void
+connection_disconnected (SoupConnection *conn, gpointer user_data)
+{
+       SoupSession *session = user_data;
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       SoupSessionHost *host;
+
+       g_mutex_lock (&priv->conn_lock);
+
+       host = g_hash_table_lookup (priv->conns, conn);
+       if (host)
+               g_hash_table_remove (priv->conns, conn);
+       drop_connection (session, host, conn);
+
+       g_mutex_unlock (&priv->conn_lock);
+
+       soup_session_kick_queue (session);
+}
+
+static void
+connection_state_changed (GObject *object, GParamSpec *param, gpointer user_data)
+{
+       SoupSession *session = user_data;
+       SoupConnection *conn = SOUP_CONNECTION (object);
+
+       if (soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE)
+               soup_session_kick_queue (session);
+}
+
+SoupMessageQueue *
+soup_session_get_queue (SoupSession *session)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+       return priv->queue;
+}
+
+static void
+soup_session_unqueue_item (SoupSession          *session,
+                          SoupMessageQueueItem *item)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       SoupSessionHost *host;
+
+       if (item->conn) {
+               if (item->msg->method != SOUP_METHOD_CONNECT ||
+                   !SOUP_STATUS_IS_SUCCESSFUL (item->msg->status_code))
+                       soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
+               soup_session_set_item_connection (session, item, NULL);
+       }
+
+       if (item->state != SOUP_MESSAGE_FINISHED) {
+               g_warning ("finished an item with state %d", item->state);
+               return;
+       }
+
+       soup_message_queue_remove (priv->queue, item);
+
+       g_mutex_lock (&priv->conn_lock);
+       host = get_host_for_message (session, item->msg);
+       host->num_messages--;
+       g_cond_broadcast (&priv->conn_cond);
+       g_mutex_unlock (&priv->conn_lock);
+
+       /* g_signal_handlers_disconnect_by_func doesn't work if you
+        * have a metamarshal, meaning it doesn't work with
+        * soup_message_add_header_handler()
+        */
+       g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
+                                             0, 0, NULL, NULL, item);
+       g_signal_emit (session, signals[REQUEST_UNQUEUED], 0, item->msg);
+       soup_message_queue_item_unref (item);
+}
+
+static void
+soup_session_set_item_status (SoupSession          *session,
+                             SoupMessageQueueItem *item,
+                             guint                 status_code,
+                             GError               *error)
+{
+       SoupURI *uri = NULL;
+
+       switch (status_code) {
+       case SOUP_STATUS_CANT_RESOLVE:
+       case SOUP_STATUS_CANT_CONNECT:
+               uri = soup_message_get_uri (item->msg);
+               break;
+
+       case SOUP_STATUS_CANT_RESOLVE_PROXY:
+       case SOUP_STATUS_CANT_CONNECT_PROXY:
+               if (item->conn)
+                       uri = soup_connection_get_proxy_uri (item->conn);
+               break;
+
+       case SOUP_STATUS_SSL_FAILED:
+               if (!g_tls_backend_supports_tls (g_tls_backend_get_default ())) {
+                       soup_message_set_status_full (item->msg, status_code,
+                                                     "TLS/SSL support not available; install glib-networking");
+                       return;
+               }
+               break;
+
+       default:
+               break;
+       }
+
+       if (error)
+               soup_message_set_status_full (item->msg, status_code, error->message);
+       else if (uri && uri->host) {
+               char *msg = g_strdup_printf ("%s (%s)",
+                                            soup_status_get_phrase (status_code),
+                                            uri->host);
+               soup_message_set_status_full (item->msg, status_code, msg);
+               g_free (msg);
+       } else
+               soup_message_set_status (item->msg, status_code);
+}
+
+
+static void
+message_completed (SoupMessage *msg, gpointer user_data)
+{
+       SoupMessageQueueItem *item = user_data;
+
+       if (item->async)
+               soup_session_kick_queue (item->session);
+
+       if (item->state != SOUP_MESSAGE_RESTARTING) {
+               item->state = SOUP_MESSAGE_FINISHING;
+
+               if (item->new_api && !item->async)
+                       soup_session_process_queue_item (item->session, item, NULL, TRUE);
+       }
+}
+
+static guint
+status_from_connect_error (SoupMessageQueueItem *item, GError *error)
+{
+       guint status;
+
+       if (!error)
+               return SOUP_STATUS_OK;
+
+       if (g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS)) {
+               SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (item->session);
+               SoupSessionHost *host;
+
+               g_mutex_lock (&priv->conn_lock);
+               host = get_host_for_message (item->session, item->msg);
+               if (!host->ssl_fallback) {
+                       host->ssl_fallback = TRUE;
+                       status = SOUP_STATUS_TRY_AGAIN;
+               } else
+                       status = SOUP_STATUS_SSL_FAILED;
+               g_mutex_unlock (&priv->conn_lock);
+       } else if (error->domain == G_TLS_ERROR)
+               status = SOUP_STATUS_SSL_FAILED;
+       else if (error->domain == G_RESOLVER_ERROR)
+               status = SOUP_STATUS_CANT_RESOLVE;
+       else if (error->domain == G_IO_ERROR) {
+               if (error->code == G_IO_ERROR_CANCELLED)
+                       status = SOUP_STATUS_CANCELLED;
+               else if (error->code == G_IO_ERROR_HOST_UNREACHABLE ||
+                        error->code == G_IO_ERROR_NETWORK_UNREACHABLE ||
+                        error->code == G_IO_ERROR_CONNECTION_REFUSED)
+                       status = SOUP_STATUS_CANT_CONNECT;
+               else if (error->code == G_IO_ERROR_PROXY_FAILED ||
+                        error->code == G_IO_ERROR_PROXY_AUTH_FAILED ||
+                        error->code == G_IO_ERROR_PROXY_NEED_AUTH ||
+                        error->code == G_IO_ERROR_PROXY_NOT_ALLOWED)
+                       status = SOUP_STATUS_CANT_CONNECT_PROXY;
+               else
+                       status = SOUP_STATUS_IO_ERROR;
+       } else
+               status = SOUP_STATUS_IO_ERROR;
+
+       if (item->conn && soup_connection_is_via_proxy (item->conn))
+               return soup_status_proxify (status);
+       else
+               return status;
+}
+
+static void
+tunnel_complete (SoupMessageQueueItem *tunnel_item,
+                guint status, GError *error)
+{
+       SoupMessageQueueItem *item = tunnel_item->related;
+       SoupSession *session = tunnel_item->session;
+
+       soup_message_finished (tunnel_item->msg);
+       soup_message_queue_item_unref (tunnel_item);
+
+       if (item->msg->status_code)
+               item->state = SOUP_MESSAGE_FINISHING;
+       soup_message_set_https_status (item->msg, item->conn);
+
+       item->error = error;
+       if (!status)
+               status = status_from_connect_error (item, error);
+       if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+               soup_connection_disconnect (item->conn);
+               soup_session_set_item_connection (session, item, NULL);
+               if (!item->new_api || item->msg->status_code == 0)
+                       soup_session_set_item_status (session, item, status, error);
+       }
+
+       item->state = SOUP_MESSAGE_READY;
+       if (item->async)
+               soup_session_kick_queue (session);
+       soup_message_queue_item_unref (item);
+}
+
+static void
+tunnel_handshake_complete (GObject      *object,
+                          GAsyncResult *result,
+                          gpointer      user_data)
+{
+       SoupConnection *conn = SOUP_CONNECTION (object);
+       SoupMessageQueueItem *tunnel_item = user_data;
+       GError *error = NULL;
+
+       soup_connection_start_ssl_finish (conn, result, &error);
+       tunnel_complete (tunnel_item, 0, error);
+}
+
+static void
+tunnel_message_completed (SoupMessage *msg, gpointer user_data)
+{
+       SoupMessageQueueItem *tunnel_item = user_data;
+       SoupMessageQueueItem *item = tunnel_item->related;
+       SoupSession *session = tunnel_item->session;
+       guint status;
+
+       if (tunnel_item->state == SOUP_MESSAGE_RESTARTING) {
+               soup_message_restarted (msg);
+               if (tunnel_item->conn) {
+                       tunnel_item->state = SOUP_MESSAGE_RUNNING;
+                       soup_session_send_queue_item (session, tunnel_item,
+                                                     tunnel_message_completed);
+                       return;
+               }
+
+               soup_message_set_status (msg, SOUP_STATUS_TRY_AGAIN);
+       }
+
+       tunnel_item->state = SOUP_MESSAGE_FINISHED;
+       soup_session_unqueue_item (session, tunnel_item);
+
+       status = tunnel_item->msg->status_code;
+       if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+               tunnel_complete (tunnel_item, status, NULL);
+               return;
+       }
+
+       if (tunnel_item->async) {
+               soup_connection_start_ssl_async (item->conn, item->cancellable,
+                                                tunnel_handshake_complete,
+                                                tunnel_item);
+       } else {
+               GError *error = NULL;
+
+               soup_connection_start_ssl_sync (item->conn, item->cancellable, &error);
+               tunnel_complete (tunnel_item, 0, error);
+       }
+}
+
+static void
+tunnel_connect (SoupMessageQueueItem *item)
+{
+       SoupSession *session = item->session;
+       SoupMessageQueueItem *tunnel_item;
+       SoupURI *uri;
+       SoupMessage *msg;
+
+       item->state = SOUP_MESSAGE_TUNNELING;
+
+       uri = soup_connection_get_remote_uri (item->conn);
+       msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT, uri);
+       soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
+
+       tunnel_item = soup_session_append_queue_item (session, msg,
+                                                     item->async, FALSE,
+                                                     NULL, NULL);
+       g_object_unref (msg);
+       tunnel_item->related = item;
+       soup_message_queue_item_ref (item);
+       soup_session_set_item_connection (session, tunnel_item, item->conn);
+       tunnel_item->state = SOUP_MESSAGE_RUNNING;
 
-       /**
-        * SoupSession:accept-language:
-        *
-        * If non-%NULL, the value to use for the "Accept-Language" header
-        * on #SoupMessage<!-- -->s sent from this session.
-        *
-        * Setting this will disable
-        * #SoupSession:accept-language-auto.
-        *
-        * Since: 2.30
-        **/
-       /**
-        * SOUP_SESSION_ACCEPT_LANGUAGE:
-        *
-        * Alias for the #SoupSession:accept-language property, qv.
-        *
-        * Since: 2.30
-        **/
-       g_object_class_install_property (
-               object_class, PROP_ACCEPT_LANGUAGE,
-               g_param_spec_string (SOUP_SESSION_ACCEPT_LANGUAGE,
-                                    "Accept-Language string",
-                                    "Accept-Language string",
-                                    NULL,
-                                    G_PARAM_READWRITE));
+       g_signal_emit (session, signals[TUNNELING], 0, tunnel_item->conn);
 
-       /**
-        * SoupSession:accept-language-auto:
-        *
-        * If %TRUE, #SoupSession will automatically set the string
-        * for the "Accept-Language" header on every #SoupMessage
-        * sent, based on the return value of g_get_language_names().
-        *
-        * Setting this will override any previous value of
-        * #SoupSession:accept-language.
-        *
-        * Since: 2.30
-        **/
-       /**
-        * SOUP_SESSION_ACCEPT_LANGUAGE_AUTO:
-        *
-        * Alias for the #SoupSession:accept-language-auto property, qv.
-        *
-        * Since: 2.30
-        **/
-       g_object_class_install_property (
-               object_class, PROP_ACCEPT_LANGUAGE_AUTO,
-               g_param_spec_boolean (SOUP_SESSION_ACCEPT_LANGUAGE_AUTO,
-                                     "Accept-Language automatic mode",
-                                     "Accept-Language automatic mode",
-                                     FALSE,
-                                     G_PARAM_READWRITE));
+       soup_session_send_queue_item (session, tunnel_item,
+                                     tunnel_message_completed);
+}
 
-       /**
-        * SoupSession:add-feature: (skip)
-        *
-        * Add a feature object to the session. (Shortcut for calling
-        * soup_session_add_feature().)
-        *
-        * Since: 2.24
-        **/
-       /**
-        * SOUP_SESSION_ADD_FEATURE: (skip)
-        *
-        * Alias for the #SoupSession:add-feature property. (Shortcut
-        * for calling soup_session_add_feature().
-        *
-        * Since: 2.24
-        **/
-       g_object_class_install_property (
-               object_class, PROP_ADD_FEATURE,
-               g_param_spec_object (SOUP_SESSION_ADD_FEATURE,
-                                    "Add Feature",
-                                    "Add a feature object to the session",
-                                    SOUP_TYPE_SESSION_FEATURE,
-                                    G_PARAM_READWRITE));
-       /**
-        * SoupSession:add-feature-by-type: (skip)
-        *
-        * Add a feature object of the given type to the session.
-        * (Shortcut for calling soup_session_add_feature_by_type().)
-        *
-        * Since: 2.24
-        **/
-       /**
-        * SOUP_SESSION_ADD_FEATURE_BY_TYPE: (skip)
-        *
-        * Alias for the #SoupSession:add-feature-by-type property.
-        * (Shortcut for calling soup_session_add_feature_by_type().
-        *
-        * Since: 2.24
-        **/
-       g_object_class_install_property (
-               object_class, PROP_ADD_FEATURE_BY_TYPE,
-               g_param_spec_gtype (SOUP_SESSION_ADD_FEATURE_BY_TYPE,
-                                   "Add Feature By Type",
-                                   "Add a feature object of the given type to the session",
-                                   SOUP_TYPE_SESSION_FEATURE,
-                                   G_PARAM_READWRITE));
-       /**
-        * SoupSession:remove-feature-by-type: (skip)
-        *
-        * Remove feature objects from the session. (Shortcut for
-        * calling soup_session_remove_feature_by_type().)
-        *
-        * Since: 2.24
-        **/
-       /**
-        * SOUP_SESSION_REMOVE_FEATURE_BY_TYPE: (skip)
-        *
-        * Alias for the #SoupSession:remove-feature-by-type
-        * property. (Shortcut for calling
-        * soup_session_remove_feature_by_type().
-        *
-        * Since: 2.24
-        **/
-       g_object_class_install_property (
-               object_class, PROP_REMOVE_FEATURE_BY_TYPE,
-               g_param_spec_gtype (SOUP_SESSION_REMOVE_FEATURE_BY_TYPE,
-                                   "Remove Feature By Type",
-                                   "Remove features of the given type from the session",
-                                   SOUP_TYPE_SESSION_FEATURE,
-                                   G_PARAM_READWRITE));
-       /**
-        * SoupSession:http-aliases:
-        *
-        * A %NULL-terminated array of URI schemes that should be
-        * considered to be aliases for "http". Eg, if this included
-        * <literal>"dav"</literal>, than a URI of
-        * <literal>dav://example.com/path</literal> would be treated
-        * identically to <literal>http://example.com/path</literal>.
-        * If the value is %NULL, then only "http" is recognized as
-        * meaning "http".
-        *
-        * For backward-compatibility reasons, the default value for
-        * this property is an array containing the single element
-        * <literal>"*"</literal>, a special value which means that
-        * any scheme except "https" is considered to be an alias for
-        * "http".
-        *
-        * See also #SoupSession:https-aliases.
-        *
-        * Since: 2.38
-        */
-       /**
-        * SOUP_SESSION_HTTP_ALIASES:
-        *
-        * Alias for the #SoupSession:http-aliases property. (URI
-        * schemes that will be considered aliases for "http".)
-        *
-        * Since: 2.38
+static void
+connect_complete (SoupMessageQueueItem *item, SoupConnection *conn, GError *error)
+{
+       SoupSession *session = item->session;
+       guint status;
+
+       soup_message_set_https_status (item->msg, item->conn);
+
+       if (!error) {
+               item->state = SOUP_MESSAGE_CONNECTED;
+               return;
+       }
+
+       item->error = error;
+       status = status_from_connect_error (item, error);
+       soup_connection_disconnect (conn);
+       if (item->state == SOUP_MESSAGE_CONNECTING) {
+               if (!item->new_api || item->msg->status_code == 0)
+                       soup_session_set_item_status (session, item, status, error);
+               soup_session_set_item_connection (session, item, NULL);
+               item->state = SOUP_MESSAGE_READY;
+       }
+}
+
+static void
+connect_async_complete (GObject      *object,
+                       GAsyncResult *result,
+                       gpointer      user_data)
+{
+       SoupConnection *conn = SOUP_CONNECTION (object);
+       SoupMessageQueueItem *item = user_data;
+       GError *error = NULL;
+
+       soup_connection_connect_finish (conn, result, &error);
+       connect_complete (item, conn, error);
+
+       if (item->state == SOUP_MESSAGE_CONNECTED ||
+           item->state == SOUP_MESSAGE_READY)
+               async_run_queue (item->session);
+       else
+               soup_session_kick_queue (item->session);
+
+       soup_message_queue_item_unref (item);
+}
+
+/* requires conn_lock */
+static SoupConnection *
+get_connection_for_host (SoupSession *session,
+                        SoupMessageQueueItem *item,
+                        SoupSessionHost *host,
+                        gboolean need_new_connection,
+                        gboolean *try_cleanup)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       SoupConnection *conn;
+       GSList *conns;
+       int num_pending = 0;
+       GProxyResolver *proxy_resolver;
+       GTlsDatabase *tlsdb;
+
+       if (priv->disposed)
+               return FALSE;
+
+       if (item->conn) {
+               g_return_val_if_fail (soup_connection_get_state (item->conn) != SOUP_CONNECTION_DISCONNECTED, FALSE);
+               return item->conn;
+       }
+
+       for (conns = host->connections; conns; conns = conns->next) {
+               conn = conns->data;
+
+               if (!need_new_connection && soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE) {
+#if ENABLE(TIZEN_TV_CREATE_IDLE_TCP_CONNECTION)
+                       soup_connection_set_current_item (conns->data, item);
+#endif
+                       soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
+                       return conn;
+               } else if (soup_connection_get_state (conn) == SOUP_CONNECTION_CONNECTING)
+#if ENABLE(TIZEN_TV_CREATE_IDLE_TCP_CONNECTION)
+               {
+                       if (!soup_connection_has_current_item (conns->data)) {
+                               soup_connection_set_current_item (conns->data, item);
+                               g_mutex_unlock (&priv->conn_lock);
+                               item->conn = g_object_ref (conns->data);
+                               return item->conn;
+                       } else {
+#endif
+                       num_pending++;
+#if ENABLE(TIZEN_TV_CREATE_IDLE_TCP_CONNECTION)
+                       }
+               }
+#endif
+       }
+
+       /* Limit the number of pending connections; num_messages / 2
+        * is somewhat arbitrary...
         */
-       g_object_class_install_property (
-               object_class, PROP_HTTP_ALIASES,
-               g_param_spec_boxed (SOUP_SESSION_HTTP_ALIASES,
-                                   "http aliases",
-                                   "URI schemes that are considered aliases for 'http'",
-                                   G_TYPE_STRV,
-                                   G_PARAM_READWRITE));
-       /**
-        * SoupSession:https-aliases:
-        *
-        * A comma-delimited list of URI schemes that should be
-        * considered to be aliases for "https". See
-        * #SoupSession:http-aliases for more information.
-        *
-        * The default value is %NULL, meaning that no URI schemes
-        * are considered aliases for "https".
-        *
-        * Since: 2.38
+#if ENABLE(TIZEN_UNLIMITED_PENDING_CONNECTIONS)
+       /* FIXME: What should we do here exactly? */
+#else
+       if (num_pending > host->num_messages / 2)
+               return NULL;
+#endif
+
+       if (host->num_conns >= priv->max_conns_per_host) {
+               if (need_new_connection)
+                       *try_cleanup = TRUE;
+               return NULL;
+       }
+
+       if (priv->num_conns >= priv->max_conns) {
+               *try_cleanup = TRUE;
+               return NULL;
+       }
+
+       proxy_resolver = get_proxy_resolver (session);
+       tlsdb = get_tls_database (session);
+
+       conn = g_object_new (
+               SOUP_TYPE_CONNECTION,
+               SOUP_CONNECTION_REMOTE_URI, host->uri,
+               SOUP_CONNECTION_PROXY_RESOLVER, proxy_resolver,
+               SOUP_CONNECTION_SSL, soup_uri_is_https (soup_message_get_uri (item->msg), priv->https_aliases),
+               SOUP_CONNECTION_SSL_CREDENTIALS, tlsdb,
+#if ENABLE(TIZEN_TV_CLIENT_CERTIFICATE)
+               SOUP_CONNECTION_WIDGET_ENGINE, priv->widget_engine,
+#endif
+               SOUP_CONNECTION_SSL_STRICT, priv->ssl_strict && (tlsdb != NULL || SOUP_IS_PLAIN_SESSION (session)),
+               SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
+               SOUP_CONNECTION_USE_THREAD_CONTEXT, priv->use_thread_context,
+               SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
+               SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
+               SOUP_CONNECTION_SSL_FALLBACK, host->ssl_fallback,
+               SOUP_CONNECTION_LOCAL_ADDRESS, priv->local_addr,
+               NULL);
+       g_signal_connect (conn, "disconnected",
+                         G_CALLBACK (connection_disconnected),
+                         session);
+       g_signal_connect (conn, "notify::state",
+                         G_CALLBACK (connection_state_changed),
+                         session);
+
+       /* This is a debugging-related signal, and so can ignore the
+        * usual rule about not emitting signals while holding
+        * conn_lock.
         */
-       /**
-        * SOUP_SESSION_HTTPS_ALIASES:
-        *
-        * Alias for the #SoupSession:https-aliases property. (URI
-        * schemes that will be considered aliases for "https".)
-        *
-        * Since: 2.38
-        **/
-       g_object_class_install_property (
-               object_class, PROP_HTTPS_ALIASES,
-               g_param_spec_boxed (SOUP_SESSION_HTTPS_ALIASES,
-                                   "https aliases",
-                                   "URI schemes that are considered aliases for 'https'",
-                                   G_TYPE_STRV,
-                                   G_PARAM_READWRITE));
+       g_signal_emit (session, signals[CONNECTION_CREATED], 0, conn);
+
+       g_hash_table_insert (priv->conns, conn, host);
+
+       priv->num_conns++;
+       host->num_conns++;
+       host->connections = g_slist_prepend (host->connections, conn);
+
+       if (host->keep_alive_src) {
+               g_source_destroy (host->keep_alive_src);
+               g_source_unref (host->keep_alive_src);
+               host->keep_alive_src = NULL;
+       }
+
+#if ENABLE(TIZEN_TV_CREATE_IDLE_TCP_CONNECTION)
+       soup_connection_set_current_item (conn, item);
+#endif
+       return conn;
+}
+
+static gboolean
+get_connection (SoupMessageQueueItem *item, gboolean *should_cleanup)
+{
+       SoupSession *session = item->session;
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       SoupSessionHost *host;
+       SoupConnection *conn = NULL;
+       gboolean my_should_cleanup = FALSE;
+       gboolean need_new_connection;
+
+       soup_session_cleanup_connections (session, FALSE);
+
+       need_new_connection =
+               (soup_message_get_flags (item->msg) & SOUP_MESSAGE_NEW_CONNECTION) ||
+               (!(soup_message_get_flags (item->msg) & SOUP_MESSAGE_IDEMPOTENT) &&
+                !SOUP_METHOD_IS_IDEMPOTENT (item->msg->method));
+
+       g_mutex_lock (&priv->conn_lock);
+       host = get_host_for_message (session, item->msg);
+       while (TRUE) {
+               conn = get_connection_for_host (session, item, host,
+                                               need_new_connection,
+                                               &my_should_cleanup);
+               if (conn || item->async)
+                       break;
+
+               if (my_should_cleanup) {
+                       g_mutex_unlock (&priv->conn_lock);
+                       soup_session_cleanup_connections (session, TRUE);
+                       g_mutex_lock (&priv->conn_lock);
+
+                       my_should_cleanup = FALSE;
+                       continue;
+               }
+
+               g_cond_wait (&priv->conn_cond, &priv->conn_lock);
+       }
+       g_mutex_unlock (&priv->conn_lock);
+
+       if (!conn) {
+               if (should_cleanup)
+                       *should_cleanup = my_should_cleanup;
+               return FALSE;
+       }
+
+       soup_session_set_item_connection (session, item, conn);
+
+#if ENABLE(TIZEN_TV_CREATE_IDLE_TCP_CONNECTION)
+       if (soup_connection_get_state (item->conn) == SOUP_CONNECTION_CONNECTING) {
+               item->state = SOUP_MESSAGE_CONNECTING;
+               soup_message_queue_item_ref (item);
+               return TRUE;
+       }
+#endif
+
+       if (soup_connection_get_state (item->conn) != SOUP_CONNECTION_NEW) {
+               item->state = SOUP_MESSAGE_READY;
+               soup_message_set_https_status (item->msg, item->conn);
+               return TRUE;
+       }
+
+       item->state = SOUP_MESSAGE_CONNECTING;
+
+       if (item->async) {
+               soup_message_queue_item_ref (item);
+               soup_connection_connect_async (item->conn, item->cancellable,
+                                              connect_async_complete, item);
+               return FALSE;
+       } else {
+               GError *error = NULL;
+
+               soup_connection_connect_sync (item->conn, item->cancellable, &error);
+               connect_complete (item, conn, error);
+
+               return TRUE;
+       }
 }
 
-/* Converts a language in POSIX format and to be RFC2616 compliant    */
-/* Based on code from epiphany-webkit (ephy_langs_append_languages()) */
-static gchar *
-posix_lang_to_rfc2616 (const gchar *language)
+void
+soup_session_process_queue_item (SoupSession          *session,
+                                SoupMessageQueueItem *item,
+                                gboolean             *should_cleanup,
+                                gboolean              loop)
 {
-       /* Don't include charset variants, etc */
-       if (strchr (language, '.') || strchr (language, '@'))
-               return NULL;
+       g_assert (item->session == session);
+
+       do {
+               if (item->paused)
+                       return;
+
+               switch (item->state) {
+               case SOUP_MESSAGE_STARTING:
+#if ENABLE(TIZEN_CERTIFICATE_FILE_SET)
+                       if(soup_uri_get_scheme(soup_message_get_uri(item->msg)) == SOUP_URI_SCHEME_HTTPS){
+                               if (!soup_session_is_tls_db_initialized (session) && !soup_message_is_from_session_restore (item->msg)) {
+                                       soup_session_tls_stop_idle_timer(session);
+                                       soup_session_set_certificate_file(session);
+                               }
+                       }
+#endif
+                       if (!get_connection (item, should_cleanup))
+                               return;
+                       break;
 
-       /* Ignore "C" locale, which g_get_language_names() always
-        * includes as a fallback.
-        */
-       if (!strcmp (language, "C"))
-               return NULL;
+               case SOUP_MESSAGE_CONNECTED:
+                       if (soup_connection_is_tunnelled (item->conn))
+                               tunnel_connect (item);
+                       else
+                               item->state = SOUP_MESSAGE_READY;
+                       break;
 
-       return g_strdelimit (g_ascii_strdown (language, -1), "_", '-');
+               case SOUP_MESSAGE_READY:
+                       if (item->msg->status_code) {
+                               if (item->msg->status_code == SOUP_STATUS_TRY_AGAIN) {
+                                       soup_message_cleanup_response (item->msg);
+                                       item->state = SOUP_MESSAGE_STARTING;
+                               } else
+                                       item->state = SOUP_MESSAGE_FINISHING;
+                               break;
+                       }
+
+                       item->state = SOUP_MESSAGE_RUNNING;
+
+                       soup_session_send_queue_item (session, item, message_completed);
+
+                       if (item->new_api) {
+                               if (item->async)
+                                       async_send_request_running (session, item);
+                               return;
+                       }
+                       break;
+
+               case SOUP_MESSAGE_RUNNING:
+                       if (item->async)
+                               return;
+
+                       g_warn_if_fail (item->new_api);
+                       item->state = SOUP_MESSAGE_FINISHING;
+                       break;
+
+               case SOUP_MESSAGE_CACHED:
+                       /* Will be handled elsewhere */
+                       return;
+
+               case SOUP_MESSAGE_RESTARTING:
+                       item->state = SOUP_MESSAGE_STARTING;
+                       soup_message_restarted (item->msg);
+                       break;
+
+               case SOUP_MESSAGE_FINISHING:
+                       item->state = SOUP_MESSAGE_FINISHED;
+                       soup_message_finished (item->msg);
+                       if (item->state != SOUP_MESSAGE_FINISHED) {
+                               g_return_if_fail (!item->new_api);
+                               break;
+                       }
+
+                       soup_message_queue_item_ref (item);
+                       soup_session_unqueue_item (session, item);
+                       if (item->async && item->callback)
+                               item->callback (session, item->msg, item->callback_data);
+                       soup_message_queue_item_unref (item);
+                       return;
+
+               default:
+                       /* Nothing to do with this message in any
+                        * other state.
+                        */
+                       g_warn_if_fail (item->async);
+                       return;
+               }
+       } while (loop && item->state != SOUP_MESSAGE_FINISHED);
 }
 
-/* Converts @quality from 0-100 to 0.0-1.0 and appends to @str */
-static gchar *
-add_quality_value (const gchar *str, int quality)
+static void
+async_run_queue (SoupSession *session)
 {
-       g_return_val_if_fail (str != NULL, NULL);
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       SoupMessageQueueItem *item;
+       SoupMessage *msg;
+       gboolean try_cleanup = TRUE, should_cleanup = FALSE;
 
-       if (quality >= 0 && quality < 100) {
-               /* We don't use %.02g because of "." vs "," locale issues */
-               if (quality % 10)
-                       return g_strdup_printf ("%s;q=0.%02d", str, quality);
-               else
-                       return g_strdup_printf ("%s;q=0.%d", str, quality / 10);
-       } else
-               return g_strdup (str);
+       g_object_ref (session);
+       soup_session_cleanup_connections (session, FALSE);
+
+ try_again:
+       for (item = soup_message_queue_first (priv->queue);
+            item;
+            item = soup_message_queue_next (priv->queue, item)) {
+               msg = item->msg;
+
+               /* CONNECT messages are handled specially */
+               if (msg->method == SOUP_METHOD_CONNECT)
+                       continue;
+
+               if (item->async_context != soup_session_get_async_context (session))
+                       continue;
+
+               soup_session_process_queue_item (session, item, &should_cleanup, TRUE);
+       }
+
+       if (try_cleanup && should_cleanup) {
+               /* There is at least one message in the queue that
+                * could be sent if we cleanupd an idle connection from
+                * some other server.
+                */
+               if (soup_session_cleanup_connections (session, TRUE)) {
+                       try_cleanup = should_cleanup = FALSE;
+                       goto try_again;
+               }
+       }
+
+       g_object_unref (session);
 }
 
-/* Returns a RFC2616 compliant languages list from system locales */
-static gchar *
-accept_languages_from_system (void)
+static gboolean
+idle_run_queue (gpointer user_data)
 {
-       const char * const * lang_names;
-       GPtrArray *langs = NULL;
-       char *lang, **langs_array, *langs_str;
-       int delta;
-       int i;
+       SoupSessionPrivate *priv = user_data;
+       GSource *source;
 
-       lang_names = g_get_language_names ();
-       g_return_val_if_fail (lang_names != NULL, NULL);
+       if (priv->disposed)
+               return FALSE;
 
-       /* Build the array of languages */
-       langs = g_ptr_array_new ();
-       for (i = 0; lang_names[i] != NULL; i++) {
-               lang = posix_lang_to_rfc2616 (lang_names[i]);
-               if (lang)
-                       g_ptr_array_add (langs, lang);
-       }
+       source = g_main_current_source ();
+       priv->run_queue_sources = g_slist_remove (priv->run_queue_sources, source);
 
-       /* Add quality values */
-       if (langs->len < 10)
-               delta = 10;
-       else if (langs->len < 20)
-               delta = 5;
-       else
-               delta = 1;
+       /* Ensure that the source is destroyed before running the queue */
+       g_source_destroy (source);
+       g_source_unref (source);
 
-       for (i = 0; i < langs->len; i++) {
-               lang = langs->pdata[i];
-               langs->pdata[i] = add_quality_value (lang, 100 - i * delta);
-               g_free (lang);
-       }
+       g_assert (priv->session);
+       async_run_queue (priv->session);
+       return FALSE;
+}
 
-       /* Fallback: add "en" if list is empty */
-       if (langs->len == 0)
-               g_ptr_array_add (langs, g_strdup ("en"));
+/**
+ * SoupSessionCallback:
+ * @session: the session
+ * @msg: the message that has finished
+ * @user_data: the data passed to soup_session_queue_message
+ *
+ * Prototype for the callback passed to soup_session_queue_message(),
+ * qv.
+ **/
 
-       g_ptr_array_add (langs, NULL);
-       langs_array = (char **)langs->pdata;
-       langs_str = g_strjoinv (", ", langs_array);
+static void
+soup_session_real_queue_message (SoupSession *session, SoupMessage *msg,
+                                SoupSessionCallback callback, gpointer user_data)
+{
+       SoupMessageQueueItem *item;
 
-       g_strfreev (langs_array);
-       g_ptr_array_free (langs, FALSE);
+       item = soup_session_append_queue_item (session, msg, TRUE, FALSE,
+                                              callback, user_data);
+       soup_session_kick_queue (session);
+       soup_message_queue_item_unref (item);
+}
 
-       return langs_str;
+/**
+ * soup_session_queue_message:
+ * @session: a #SoupSession
+ * @msg: (transfer full): the message to queue
+ * @callback: (allow-none) (scope async): a #SoupSessionCallback which will
+ * be called after the message completes or when an unrecoverable error occurs.
+ * @user_data: (allow-none): a pointer passed to @callback.
+ * 
+ * Queues the message @msg for asynchronously sending the request and
+ * receiving a response in the current thread-default #GMainContext.
+ * If @msg has been processed before, any resources related to the
+ * time it was last sent are freed.
+ *
+ * Upon message completion, the callback specified in @callback will
+ * be invoked. If after returning from this callback the message has not
+ * been requeued, @msg will be unreffed.
+ *
+ * (The behavior above applies to a plain #SoupSession; if you are
+ * using #SoupSessionAsync or #SoupSessionSync, then the #GMainContext
+ * that is used depends on the settings of #SoupSession:async-context
+ * and #SoupSession:use-thread-context, and for #SoupSessionSync, the
+ * message will actually be sent and processed in another thread, with
+ * only the final callback occurring in the indicated #GMainContext.)
+ *
+ * Contrast this method with soup_session_send_async(), which also
+ * asynchronously sends a message, but returns before reading the
+ * response body, and allows you to read the response via a
+ * #GInputStream.
+ */
+void
+soup_session_queue_message (SoupSession *session, SoupMessage *msg,
+                           SoupSessionCallback callback, gpointer user_data)
+{
+       g_return_if_fail (SOUP_IS_SESSION (session));
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+       SOUP_SESSION_GET_CLASS (session)->queue_message (session, msg,
+                                                        callback, user_data);
+       /* The SoupMessageQueueItem will hold a ref on @msg until it is
+        * finished, so we can drop the ref adopted from the caller now.
+        */
+       g_object_unref (msg);
 }
 
 static void
-set_tlsdb (SoupSession *session, GTlsDatabase *tlsdb)
+soup_session_real_requeue_message (SoupSession *session, SoupMessage *msg)
 {
        SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       GTlsDatabase *system_default;
-
-       if (tlsdb == priv->tlsdb)
-               return;
+       SoupMessageQueueItem *item;
 
-       g_object_freeze_notify (G_OBJECT (session));
+       item = soup_message_queue_lookup (priv->queue, msg);
+       g_return_if_fail (item != NULL);
 
-       system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
-       if (priv->tlsdb == system_default || tlsdb == system_default) {
-               g_object_notify (G_OBJECT (session), "ssl-use-system-ca-file");
+       if (item->resend_count >= SOUP_SESSION_MAX_RESEND_COUNT) {
+               if (SOUP_STATUS_IS_REDIRECTION (msg->status_code))
+                       soup_message_set_status (msg, SOUP_STATUS_TOO_MANY_REDIRECTS);
+               else
+                       g_warning ("SoupMessage %p stuck in infinite loop?", msg);
+       } else {
+               item->resend_count++;
+               item->state = SOUP_MESSAGE_RESTARTING;
        }
-       g_object_unref (system_default);
 
-       if (priv->ssl_ca_file) {
-               g_free (priv->ssl_ca_file);
-               priv->ssl_ca_file = NULL;
-               g_object_notify (G_OBJECT (session), "ssl-ca-file");
-       }
+       soup_message_queue_item_unref (item);
+}
 
-       if (priv->tlsdb)
-               g_object_unref (priv->tlsdb);
-       priv->tlsdb = tlsdb;
-       if (priv->tlsdb)
-               g_object_ref (priv->tlsdb);
+/**
+ * soup_session_requeue_message:
+ * @session: a #SoupSession
+ * @msg: the message to requeue
+ *
+ * This causes @msg to be placed back on the queue to be attempted
+ * again.
+ **/
+void
+soup_session_requeue_message (SoupSession *session, SoupMessage *msg)
+{
+       g_return_if_fail (SOUP_IS_SESSION (session));
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
 
-       g_object_notify (G_OBJECT (session), "tls-database");
-       g_object_thaw_notify (G_OBJECT (session));
+       SOUP_SESSION_GET_CLASS (session)->requeue_message (session, msg);
 }
 
-static void
-set_use_system_ca_file (SoupSession *session, gboolean use_system_ca_file)
+static guint
+soup_session_real_send_message (SoupSession *session, SoupMessage *msg)
+{
+       SoupMessageQueueItem *item;
+       guint status;
+
+       item = soup_session_append_queue_item (session, msg, FALSE, FALSE,
+                                              NULL, NULL);
+       soup_session_process_queue_item (session, item, NULL, TRUE);
+       status = msg->status_code;
+       soup_message_queue_item_unref (item);
+       return status;
+}
+
+/**
+ * soup_session_send_message:
+ * @session: a #SoupSession
+ * @msg: the message to send
+ * 
+ * Synchronously send @msg. This call will not return until the
+ * transfer is finished successfully or there is an unrecoverable
+ * error.
+ *
+ * Unlike with soup_session_queue_message(), @msg is not freed upon
+ * return.
+ *
+ * (Note that if you call this method on a #SoupSessionAsync, it will
+ * still use asynchronous I/O internally, running the glib main loop
+ * to process the message, which may also cause other events to be
+ * processed.)
+ *
+ * Contrast this method with soup_session_send(), which also
+ * synchronously sends a message, but returns before reading the
+ * response body, and allows you to read the response via a
+ * #GInputStream.
+ *
+ * Return value: the HTTP status code of the response
+ */
+guint
+soup_session_send_message (SoupSession *session, SoupMessage *msg)
+{
+       g_return_val_if_fail (SOUP_IS_SESSION (session), SOUP_STATUS_MALFORMED);
+       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_STATUS_MALFORMED);
+
+       return SOUP_SESSION_GET_CLASS (session)->send_message (session, msg);
+}
+
+
+/**
+ * soup_session_pause_message:
+ * @session: a #SoupSession
+ * @msg: a #SoupMessage currently running on @session
+ *
+ * Pauses HTTP I/O on @msg. Call soup_session_unpause_message() to
+ * resume I/O.
+ *
+ * This may only be called for asynchronous messages (those sent on a
+ * #SoupSessionAsync or using soup_session_queue_message()).
+ **/
+void
+soup_session_pause_message (SoupSession *session,
+                           SoupMessage *msg)
 {
-       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       GTlsDatabase *system_default;
+       SoupSessionPrivate *priv;
+       SoupMessageQueueItem *item;
 
-       system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
+       g_return_if_fail (SOUP_IS_SESSION (session));
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
 
-       if (use_system_ca_file)
-               set_tlsdb (session, system_default);
-       else if (priv->tlsdb == system_default)
-               set_tlsdb (session, NULL);
+       priv = SOUP_SESSION_GET_PRIVATE (session);
+       item = soup_message_queue_lookup (priv->queue, msg);
+       g_return_if_fail (item != NULL);
+       g_return_if_fail (item->async);
 
-       g_object_unref (system_default);
+       item->paused = TRUE;
+       if (item->state == SOUP_MESSAGE_RUNNING)
+               soup_message_io_pause (msg);
+       soup_message_queue_item_unref (item);
 }
 
 static void
-set_ssl_ca_file (SoupSession *session, const char *ssl_ca_file)
+soup_session_real_kick_queue (SoupSession *session)
 {
        SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       GTlsDatabase *tlsdb;
-       GError *error = NULL;
+       SoupMessageQueueItem *item;
+       gboolean have_sync_items = FALSE;
 
-       if (!g_strcmp0 (priv->ssl_ca_file, ssl_ca_file))
+       if (priv->disposed)
                return;
 
-       g_object_freeze_notify (G_OBJECT (session));
-
-       if (g_path_is_absolute (ssl_ca_file))
-               tlsdb = g_tls_file_database_new (ssl_ca_file, &error);
-       else {
-               char *path, *cwd;
-
-               cwd = g_get_current_dir ();
-               path = g_build_filename (cwd, ssl_ca_file, NULL);
-               tlsdb = g_tls_file_database_new (path, &error);
-               g_free (path);
+       for (item = soup_message_queue_first (priv->queue);
+            item;
+            item = soup_message_queue_next (priv->queue, item)) {
+               if (item->async) {
+                       GSource *source;
+
+                       /* We use priv rather than session as the
+                        * source data, because other parts of libsoup
+                        * (or the calling app) may have sources using
+                        * the session as the source data.
+                        */
+                       source = g_main_context_find_source_by_user_data (item->async_context, priv);
+                       if (!source) {
+                               source = soup_add_completion_reffed (item->async_context,
+                                                                    idle_run_queue, priv);
+                               priv->run_queue_sources = g_slist_prepend (priv->run_queue_sources,
+                                                                          source);
+                       }
+               } else
+                       have_sync_items = TRUE;
        }
 
-       if (error) {
-               if (!g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_UNAVAILABLE)) {
-                       g_warning ("Could not set SSL credentials from '%s': %s",
-                                  ssl_ca_file, error->message);
-
-                       tlsdb = g_tls_file_database_new ("/dev/null", NULL);
-               }
-               g_error_free (error);
+       if (have_sync_items) {
+               g_mutex_lock (&priv->conn_lock);
+               g_cond_broadcast (&priv->conn_cond);
+               g_mutex_unlock (&priv->conn_lock);
        }
+}
 
-       set_tlsdb (session, tlsdb);
-       g_object_unref (tlsdb);
-
-       priv->ssl_ca_file = g_strdup (ssl_ca_file);
-       g_object_notify (G_OBJECT (session), "ssl-ca-file");
-
-       g_object_thaw_notify (G_OBJECT (session));
+void
+soup_session_kick_queue (SoupSession *session)
+{
+       SOUP_SESSION_GET_CLASS (session)->kick (session);
 }
 
-/* priv->http_aliases and priv->https_aliases are stored as arrays of
- * *interned* strings, so we can't just use g_strdupv() to set them.
- */
-static void
-set_aliases (char ***variable, char **value)
+/**
+ * soup_session_unpause_message:
+ * @session: a #SoupSession
+ * @msg: a #SoupMessage currently running on @session
+ *
+ * Resumes HTTP I/O on @msg. Use this to resume after calling
+ * soup_session_pause_message().
+ *
+ * If @msg is being sent via blocking I/O, this will resume reading or
+ * writing immediately. If @msg is using non-blocking I/O, then
+ * reading or writing won't resume until you return to the main loop.
+ *
+ * This may only be called for asynchronous messages (those sent on a
+ * #SoupSessionAsync or using soup_session_queue_message()).
+ **/
+void
+soup_session_unpause_message (SoupSession *session,
+                             SoupMessage *msg)
 {
-       int len, i;
+       SoupSessionPrivate *priv;
+       SoupMessageQueueItem *item;
 
-       if (*variable)
-               g_free (*variable);
+       g_return_if_fail (SOUP_IS_SESSION (session));
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
 
-       if (!value) {
-               *variable = NULL;
-               return;
-       }
+       priv = SOUP_SESSION_GET_PRIVATE (session);
+       item = soup_message_queue_lookup (priv->queue, msg);
+       g_return_if_fail (item != NULL);
+       g_return_if_fail (item->async);
 
-       len = g_strv_length (value);
-       *variable = g_new (char *, len + 1);
-       for (i = 0; i < len; i++)
-               (*variable)[i] = (char *)g_intern_string (value[i]);
-       (*variable)[i] = NULL;
+       item->paused = FALSE;
+       if (item->state == SOUP_MESSAGE_RUNNING)
+               soup_message_io_unpause (msg);
+       soup_message_queue_item_unref (item);
+
+       soup_session_kick_queue (session);
 }
 
+
 static void
-set_property (GObject *object, guint prop_id,
-             const GValue *value, GParamSpec *pspec)
+soup_session_real_cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
 {
-       SoupSession *session = SOUP_SESSION (object);
        SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       SoupURI *uri;
-       const char *user_agent;
-       SoupSessionFeature *feature;
+       SoupMessageQueueItem *item;
 
-       switch (prop_id) {
-       case PROP_PROXY_URI:
-               uri = g_value_get_boxed (value);
+       item = soup_message_queue_lookup (priv->queue, msg);
+       g_return_if_fail (item != NULL);
 
-               if (uri) {
-                       soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER);
-                       feature = SOUP_SESSION_FEATURE (soup_proxy_resolver_static_new (uri));
-                       soup_session_add_feature (session, feature);
-                       g_object_unref (feature);
-               } else
-                       soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
+       item->paused = FALSE;
+       soup_message_set_status (msg, status_code);
+       g_cancellable_cancel (item->cancellable);
 
-               soup_session_abort (session);
-               break;
-       case PROP_MAX_CONNS:
-               priv->max_conns = g_value_get_int (value);
-               break;
-       case PROP_MAX_CONNS_PER_HOST:
-               priv->max_conns_per_host = g_value_get_int (value);
-               break;
-       case PROP_USE_NTLM:
-               feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
-               if (feature) {
-                       if (g_value_get_boolean (value))
-                               soup_session_feature_add_feature (feature, SOUP_TYPE_AUTH_NTLM);
-                       else
-                               soup_session_feature_remove_feature (feature, SOUP_TYPE_AUTH_NTLM);
-               } else
-                       g_warning ("Trying to set use-ntlm on session with no auth-manager");
-               break;
-       case PROP_SSL_CA_FILE:
-               set_ssl_ca_file (session, g_value_get_string (value));
-               break;
-       case PROP_SSL_USE_SYSTEM_CA_FILE:
-               set_use_system_ca_file (session, g_value_get_boolean (value));
-               break;
-       case PROP_TLS_DATABASE:
-               set_tlsdb (session, g_value_get_object (value));
-               break;
-       case PROP_SSL_STRICT:
-               priv->ssl_strict = g_value_get_boolean (value);
-               break;
-       case PROP_ASYNC_CONTEXT:
-               priv->async_context = g_value_get_pointer (value);
-               if (priv->async_context)
-                       g_main_context_ref (priv->async_context);
-               break;
-       case PROP_USE_THREAD_CONTEXT:
-               priv->use_thread_context = g_value_get_boolean (value);
-               if (priv->use_thread_context) {
-                       if (priv->async_context)
-                               g_main_context_unref (priv->async_context);
-                       priv->async_context = g_main_context_get_thread_default ();
-                       if (priv->async_context)
-                               g_main_context_ref (priv->async_context);
-               }
-               break;
-       case PROP_TIMEOUT:
-               priv->io_timeout = g_value_get_uint (value);
-               break;
-       case PROP_USER_AGENT:
-               g_free (priv->user_agent);
-               user_agent = g_value_get_string (value);
-               if (!user_agent)
-                       priv->user_agent = NULL;
-               else if (!*user_agent) {
-                       priv->user_agent =
-                               g_strdup (SOUP_SESSION_USER_AGENT_BASE);
-               } else if (g_str_has_suffix (user_agent, " ")) {
-                       priv->user_agent =
-                               g_strdup_printf ("%s%s", user_agent,
-                                                SOUP_SESSION_USER_AGENT_BASE);
-               } else
-                       priv->user_agent = g_strdup (user_agent);
-               break;
-       case PROP_ACCEPT_LANGUAGE:
-               g_free (priv->accept_language);
-               priv->accept_language = g_strdup (g_value_get_string (value));
-               priv->accept_language_auto = FALSE;
-               break;
-       case PROP_ACCEPT_LANGUAGE_AUTO:
-               priv->accept_language_auto = g_value_get_boolean (value);
-               if (priv->accept_language) {
-                       g_free (priv->accept_language);
-                       priv->accept_language = NULL;
-               }
+       soup_session_kick_queue (item->session);
+       soup_message_queue_item_unref (item);
+}
 
-               /* Get languages from system if needed */
-               if (priv->accept_language_auto)
-                       priv->accept_language = accept_languages_from_system ();
-               break;
-       case PROP_IDLE_TIMEOUT:
-               priv->idle_timeout = g_value_get_uint (value);
-               break;
-       case PROP_ADD_FEATURE:
-               soup_session_add_feature (session, g_value_get_object (value));
-               break;
-       case PROP_ADD_FEATURE_BY_TYPE:
-               soup_session_add_feature_by_type (session, g_value_get_gtype (value));
-               break;
-       case PROP_REMOVE_FEATURE_BY_TYPE:
-               soup_session_remove_feature_by_type (session, g_value_get_gtype (value));
-               break;
-       case PROP_HTTP_ALIASES:
-               set_aliases (&priv->http_aliases, g_value_get_boxed (value));
-               break;
-       case PROP_HTTPS_ALIASES:
-               set_aliases (&priv->https_aliases, g_value_get_boxed (value));
-               break;
-       default:
-               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-               break;
+/**
+ * soup_session_cancel_message:
+ * @session: a #SoupSession
+ * @msg: the message to cancel
+ * @status_code: status code to set on @msg (generally
+ * %SOUP_STATUS_CANCELLED)
+ *
+ * Causes @session to immediately finish processing @msg (regardless
+ * of its current state) with a final status_code of @status_code. You
+ * may call this at any time after handing @msg off to @session; if
+ * @session has started sending the request but has not yet received
+ * the complete response, then it will close the request's connection.
+ * Note that with requests that have side effects (eg,
+ * <literal>POST</literal>, <literal>PUT</literal>,
+ * <literal>DELETE</literal>) it is possible that you might cancel the
+ * request after the server acts on it, but before it returns a
+ * response, leaving the remote resource in an unknown state.
+ *
+ * If the message is cancelled while its response body is being read,
+ * then the response body in @msg will be left partially-filled-in.
+ * The response headers, on the other hand, will always be either
+ * empty or complete.
+ *
+ * Beware that with the deprecated #SoupSessionAsync, messages queued
+ * with soup_session_queue_message() will have their callbacks invoked
+ * before soup_session_cancel_message() returns. The plain
+ * #SoupSession does not have this behavior; cancelling an
+ * asynchronous message will merely queue its callback to be run after
+ * returning to the main loop.
+ **/
+void
+soup_session_cancel_message (SoupSession *session, SoupMessage *msg,
+                            guint status_code)
+{
+       SoupSessionPrivate *priv;
+       SoupMessageQueueItem *item;
+
+       g_return_if_fail (SOUP_IS_SESSION (session));
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+       priv = SOUP_SESSION_GET_PRIVATE (session);
+       item = soup_message_queue_lookup (priv->queue, msg);
+       /* If the message is already ending, don't do anything */
+       if (!item)
+               return;
+       if (item->state == SOUP_MESSAGE_FINISHED) {
+               soup_message_queue_item_unref (item);
+               return;
        }
+
+       SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg, status_code);
+       soup_message_queue_item_unref (item);
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-             GValue *value, GParamSpec *pspec)
+soup_session_real_flush_queue (SoupSession *session)
 {
-       SoupSession *session = SOUP_SESSION (object);
        SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       SoupSessionFeature *feature;
-       GTlsDatabase *tlsdb;
+       SoupMessageQueueItem *item;
+       GHashTable *current = NULL;
+       gboolean done = FALSE;
+
+       if (SOUP_IS_SESSION_SYNC (session)) {
+               /* Record the current contents of the queue */
+               current = g_hash_table_new (NULL, NULL);
+               for (item = soup_message_queue_first (priv->queue);
+                    item;
+                    item = soup_message_queue_next (priv->queue, item))
+                       g_hash_table_insert (current, item, item);
+       }
 
-       switch (prop_id) {
-       case PROP_PROXY_URI:
-               feature = soup_session_get_feature (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
-               if (feature) {
-                       g_object_get_property (G_OBJECT (feature),
-                                              SOUP_PROXY_RESOLVER_STATIC_PROXY_URI,
-                                              value);
-               } else
-                       g_value_set_boxed (value, NULL);
-               break;
-       case PROP_MAX_CONNS:
-               g_value_set_int (value, priv->max_conns);
-               break;
-       case PROP_MAX_CONNS_PER_HOST:
-               g_value_set_int (value, priv->max_conns_per_host);
-               break;
-       case PROP_USE_NTLM:
-               feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
-               if (feature)
-                       g_value_set_boolean (value, soup_session_feature_has_feature (feature, SOUP_TYPE_AUTH_NTLM));
-               else
-                       g_value_set_boolean (value, FALSE);
-               break;
-       case PROP_SSL_CA_FILE:
-               g_value_set_string (value, priv->ssl_ca_file);
-               break;
-       case PROP_SSL_USE_SYSTEM_CA_FILE:
-               tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
-               g_value_set_boolean (value, priv->tlsdb == tlsdb);
-               g_object_unref (tlsdb);
-               break;
-       case PROP_TLS_DATABASE:
-               g_value_set_object (value, priv->tlsdb);
-               break;
-       case PROP_SSL_STRICT:
-               g_value_set_boolean (value, priv->ssl_strict);
-               break;
-       case PROP_ASYNC_CONTEXT:
-               g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
-               break;
-       case PROP_USE_THREAD_CONTEXT:
-               g_value_set_boolean (value, priv->use_thread_context);
-               break;
-       case PROP_TIMEOUT:
-               g_value_set_uint (value, priv->io_timeout);
-               break;
-       case PROP_USER_AGENT:
-               g_value_set_string (value, priv->user_agent);
-               break;
-       case PROP_ACCEPT_LANGUAGE:
-               g_value_set_string (value, priv->accept_language);
-               break;
-       case PROP_ACCEPT_LANGUAGE_AUTO:
-               g_value_set_boolean (value, priv->accept_language_auto);
-               break;
-       case PROP_IDLE_TIMEOUT:
-               g_value_set_uint (value, priv->idle_timeout);
-               break;
-       case PROP_HTTP_ALIASES:
-               g_value_set_boxed (value, priv->http_aliases);
-               break;
-       case PROP_HTTPS_ALIASES:
-               g_value_set_boxed (value, priv->https_aliases);
-               break;
-       default:
-               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-               break;
+       /* Cancel everything */
+       for (item = soup_message_queue_first (priv->queue);
+            item;
+            item = soup_message_queue_next (priv->queue, item)) {
+               soup_session_cancel_message (session, item->msg,
+                                            SOUP_STATUS_CANCELLED);
+       }
+
+       if (SOUP_IS_SESSION_SYNC (session)) {
+               /* Wait until all of the items in @current have been
+                * removed from the queue. (This is not the same as
+                * "wait for the queue to be empty", because the app
+                * may queue new requests in response to the
+                * cancellation of the old ones. We don't try to
+                * cancel those requests as well, since we'd likely
+                * just end up looping forever.)
+                */
+               g_mutex_lock (&priv->conn_lock);
+               do {
+                       done = TRUE;
+                       for (item = soup_message_queue_first (priv->queue);
+                            item;
+                            item = soup_message_queue_next (priv->queue, item)) {
+                               if (g_hash_table_lookup (current, item))
+                                       done = FALSE;
+                       }
+
+                       if (!done)
+                               g_cond_wait (&priv->conn_cond, &priv->conn_lock);
+               } while (!done);
+               g_mutex_unlock (&priv->conn_lock);
+
+               g_hash_table_destroy (current);
        }
 }
 
-static gboolean
-uri_is_http (SoupSessionPrivate *priv, SoupURI *uri)
+/**
+ * soup_session_abort:
+ * @session: the session
+ *
+ * Cancels all pending requests in @session and closes all idle
+ * persistent connections.
+ *
+ * The message cancellation has the same semantics as with
+ * soup_session_cancel_message(); asynchronous requests on a
+ * #SoupSessionAsync will have their callback called before
+ * soup_session_abort() returns. Requests on a plain #SoupSession will
+ * not.
+ **/
+void
+soup_session_abort (SoupSession *session)
 {
-       int i;
+       SoupSessionPrivate *priv;
+       GSList *conns, *c;
+       GHashTableIter iter;
+       gpointer conn, host;
 
-       if (uri->scheme == SOUP_URI_SCHEME_HTTP)
-               return TRUE;
-       else if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
-               return FALSE;
-       else if (!priv->http_aliases)
-               return FALSE;
+       g_return_if_fail (SOUP_IS_SESSION (session));
+       priv = SOUP_SESSION_GET_PRIVATE (session);
+
+       SOUP_SESSION_GET_CLASS (session)->flush_queue (session);
 
-       for (i = 0; priv->http_aliases[i]; i++) {
-               if (uri->scheme == priv->http_aliases[i])
-                       return TRUE;
+       /* Close all connections */
+       g_mutex_lock (&priv->conn_lock);
+       conns = NULL;
+       g_hash_table_iter_init (&iter, priv->conns);
+       while (g_hash_table_iter_next (&iter, &conn, &host)) {
+               conns = g_slist_prepend (conns, g_object_ref (conn));
+               g_hash_table_iter_remove (&iter);
+               drop_connection (session, host, conn);
        }
+       g_mutex_unlock (&priv->conn_lock);
 
-       if (!priv->http_aliases[1] && !strcmp (priv->http_aliases[0], "*"))
-               return TRUE;
-       else
-               return FALSE;
+       for (c = conns; c; c = c->next) {
+               soup_connection_disconnect (c->data);
+               g_object_unref (c->data);
+       }
+
+       g_slist_free (conns);
 }
 
-static gboolean
-uri_is_https (SoupSessionPrivate *priv, SoupURI *uri)
+static void
+prefetch_uri (SoupSession *session, SoupURI *uri,
+             GCancellable *cancellable,
+             SoupAddressCallback callback, gpointer user_data)
 {
-       int i;
+       SoupSessionPrivate *priv;
+       SoupSessionHost *host;
+       SoupAddress *addr;
 
-       if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
-               return TRUE;
-       else if (uri->scheme == SOUP_URI_SCHEME_HTTP)
-               return FALSE;
-       else if (!priv->https_aliases)
-               return FALSE;
+       priv = SOUP_SESSION_GET_PRIVATE (session);
+
+       g_mutex_lock (&priv->conn_lock);
+       host = get_host_for_uri (session, uri);
+       addr = g_object_ref (host->addr);
+       g_mutex_unlock (&priv->conn_lock);
+
+       soup_address_resolve_async (addr,
+                                   soup_session_get_async_context (session),
+                                   cancellable, callback, user_data);
+       g_object_unref (addr);
+}
+
+/**
+ * soup_session_prepare_for_uri:
+ * @session: a #SoupSession
+ * @uri: a #SoupURI which may be required
+ *
+ * Tells @session that @uri may be requested shortly, and so the
+ * session can try to prepare (resolving the domain name, obtaining
+ * proxy address, etc.) in order to work more quickly once the URI is
+ * actually requested.
+ *
+ * Since: 2.30
+ *
+ * Deprecated: 2.38: use soup_session_prefetch_dns() instead
+ **/
+void
+soup_session_prepare_for_uri (SoupSession *session, SoupURI *uri)
+{
+       g_return_if_fail (SOUP_IS_SESSION (session));
+       g_return_if_fail (uri != NULL);
+
+       if (!uri->host)
+               return;
+
+       prefetch_uri (session, uri, NULL, NULL, NULL);
+}
+
+/**
+* soup_session_prefetch_dns:
+* @session: a #SoupSession
+* @hostname: a hostname to be resolved
+* @cancellable: (allow-none): a #GCancellable object, or %NULL
+* @callback: (scope async) (allow-none): callback to call with the
+*     result, or %NULL
+* @user_data: data for @callback
+*
+* Tells @session that an URI from the given @hostname may be requested
+* shortly, and so the session can try to prepare by resolving the
+* domain name in advance, in order to work more quickly once the URI
+* is actually requested.
+*
+* If @cancellable is non-%NULL, it can be used to cancel the
+* resolution. @callback will still be invoked in this case, with a
+* status of %SOUP_STATUS_CANCELLED.
+*
+* Since: 2.38
+**/
+void
+soup_session_prefetch_dns (SoupSession *session, const char *hostname,
+                          GCancellable *cancellable,
+                          SoupAddressCallback callback, gpointer user_data)
+{
+       SoupURI *uri;
+
+       g_return_if_fail (SOUP_IS_SESSION (session));
+       g_return_if_fail (hostname != NULL);
+
+       /* FIXME: Prefetching should work for both HTTP and HTTPS */
+       uri = soup_uri_new (NULL);
+       soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP);
+       soup_uri_set_host (uri, hostname);
+       soup_uri_set_path (uri, "");
+
+       prefetch_uri (session, uri, cancellable, callback, user_data);
+       soup_uri_free (uri);
+}
+
+/**
+ * soup_session_add_feature:
+ * @session: a #SoupSession
+ * @feature: an object that implements #SoupSessionFeature
+ *
+ * Adds @feature's functionality to @session. You can also add a
+ * feature to the session at construct time by using the
+ * %SOUP_SESSION_ADD_FEATURE property.
+ *
+ * Note that a #SoupContentDecoder is added to the session by default
+ * (unless you are using one of the deprecated session subclasses).
+ *
+ * Since: 2.24
+ **/
+void
+soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature)
+{
+       SoupSessionPrivate *priv;
+
+       g_return_if_fail (SOUP_IS_SESSION (session));
+       g_return_if_fail (SOUP_IS_SESSION_FEATURE (feature));
+
+       priv = SOUP_SESSION_GET_PRIVATE (session);
+
+       G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+       if (SOUP_IS_PROXY_URI_RESOLVER (feature)) {
+               set_proxy_resolver (session, NULL,
+                                   SOUP_PROXY_URI_RESOLVER (feature),
+                                   NULL);
+       }
+       G_GNUC_END_IGNORE_DEPRECATIONS;
+
+       priv->features = g_slist_prepend (priv->features, g_object_ref (feature));
+       g_hash_table_remove_all (priv->features_cache);
+       soup_session_feature_attach (feature, session);
+}
+
+/**
+ * soup_session_add_feature_by_type:
+ * @session: a #SoupSession
+ * @feature_type: a #GType
+ *
+ * If @feature_type is the type of a class that implements
+ * #SoupSessionFeature, this creates a new feature of that type and
+ * adds it to @session as with soup_session_add_feature(). You can use
+ * this when you don't need to customize the new feature in any way.
+ *
+ * If @feature_type is not a #SoupSessionFeature type, this gives each
+ * existing feature on @session the chance to accept @feature_type as
+ * a "subfeature". This can be used to add new #SoupAuth or
+ * #SoupRequest types, for instance.
+ *
+ * You can also add a feature to the session at construct time by
+ * using the %SOUP_SESSION_ADD_FEATURE_BY_TYPE property.
+ *
+ * Note that a #SoupContentDecoder is added to the session by default
+ * (unless you are using one of the deprecated session subclasses).
+ *
+ * Since: 2.24
+ **/
+void
+soup_session_add_feature_by_type (SoupSession *session, GType feature_type)
+{
+       SoupSessionPrivate *priv;
+
+       g_return_if_fail (SOUP_IS_SESSION (session));
+
+       priv = SOUP_SESSION_GET_PRIVATE (session);
+
+       if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
+               SoupSessionFeature *feature;
 
-       for (i = 0; priv->https_aliases[i]; i++) {
-               if (uri->scheme == priv->https_aliases[i])
-                       return TRUE;
-       }
+               feature = g_object_new (feature_type, NULL);
+               soup_session_add_feature (session, feature);
+               g_object_unref (feature);
+       } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
+               SoupRequestClass *request_class;
+               int i;
+
+               request_class = g_type_class_ref (feature_type);
+               for (i = 0; request_class->schemes[i]; i++) {
+                       g_hash_table_insert (priv->request_types,
+                                            (char *)request_class->schemes[i],
+                                            GSIZE_TO_POINTER (feature_type));
+               }
+       } else {
+               GSList *f;
 
-       return FALSE;
+               for (f = priv->features; f; f = f->next) {
+                       if (soup_session_feature_add_feature (f->data, feature_type))
+                               return;
+               }
+               g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
+       }
 }
 
 /**
- * soup_session_get_async_context:
+ * soup_session_remove_feature:
  * @session: a #SoupSession
+ * @feature: a feature that has previously been added to @session
  *
- * Gets @session's async_context. This does not add a ref to the
- * context, so you will need to ref it yourself if you want it to
- * outlive its session.
- *
- * If #SoupSession:use-thread-context is true, this will return the
- * current thread-default main context.
+ * Removes @feature's functionality from @session.
  *
- * Return value: (transfer none): @session's #GMainContext, which may
- * be %NULL
+ * Since: 2.24
  **/
-GMainContext *
-soup_session_get_async_context (SoupSession *session)
+void
+soup_session_remove_feature (SoupSession *session, SoupSessionFeature *feature)
 {
        SoupSessionPrivate *priv;
 
-       g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
-       priv = SOUP_SESSION_GET_PRIVATE (session);
-
-       if (priv->use_thread_context)
-               return g_main_context_get_thread_default ();
-       else
-               return priv->async_context;
-}
-
-/* Hosts */
+       g_return_if_fail (SOUP_IS_SESSION (session));
 
-static guint
-soup_host_uri_hash (gconstpointer key)
-{
-       const SoupURI *uri = key;
+       priv = SOUP_SESSION_GET_PRIVATE (session);
+       if (g_slist_find (priv->features, feature)) {
+               priv->features = g_slist_remove (priv->features, feature);
+               g_hash_table_remove_all (priv->features_cache);
+               soup_session_feature_detach (feature, session);
 
-       g_return_val_if_fail (uri != NULL && uri->host != NULL, 0);
+               G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+               if (SOUP_IS_PROXY_URI_RESOLVER (feature)) {
+                       if (SOUP_IS_PROXY_RESOLVER_WRAPPER (priv->proxy_resolver) &&
+                           SOUP_PROXY_RESOLVER_WRAPPER (priv->proxy_resolver)->soup_resolver == SOUP_PROXY_URI_RESOLVER (feature))
+                               g_clear_object (&priv->proxy_resolver);
+               }
+               G_GNUC_END_IGNORE_DEPRECATIONS;
 
-       return uri->port + soup_str_case_hash (uri->host);
+               g_object_unref (feature);
+       }
 }
 
-gboolean
-soup_host_uri_equal (gconstpointer v1, gconstpointer v2)
+/**
+ * soup_session_remove_feature_by_type:
+ * @session: a #SoupSession
+ * @feature_type: a #GType
+ *
+ * Removes all features of type @feature_type (or any subclass of
+ * @feature_type) from @session. You can also remove standard features
+ * from the session at construct time by using the
+ * %SOUP_SESSION_REMOVE_FEATURE_BY_TYPE property.
+ *
+ * Since: 2.24
+ **/
+void
+soup_session_remove_feature_by_type (SoupSession *session, GType feature_type)
 {
-       const SoupURI *one = v1;
-       const SoupURI *two = v2;
-
-       g_return_val_if_fail (one != NULL && two != NULL, one == two);
-       g_return_val_if_fail (one->host != NULL && two->host != NULL, one->host == two->host);
-
-       if (one->port != two->port)
-               return FALSE;
-
-       return g_ascii_strcasecmp (one->host, two->host) == 0;
-}
-
+       SoupSessionPrivate *priv;
+       GSList *f;
 
-static SoupSessionHost *
-soup_session_host_new (SoupSession *session, SoupURI *uri)
-{
-       SoupSessionHost *host;
+       g_return_if_fail (SOUP_IS_SESSION (session));
 
-       host = g_slice_new0 (SoupSessionHost);
-       host->uri = soup_uri_copy_host (uri);
-       if (host->uri->scheme != SOUP_URI_SCHEME_HTTP &&
-           host->uri->scheme != SOUP_URI_SCHEME_HTTPS) {
-               SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       priv = SOUP_SESSION_GET_PRIVATE (session);
 
-               if (uri_is_https (priv, host->uri))
-                       host->uri->scheme = SOUP_URI_SCHEME_HTTPS;
-               else
-                       host->uri->scheme = SOUP_URI_SCHEME_HTTP;
+       if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
+       restart:
+               for (f = priv->features; f; f = f->next) {
+                       if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type)) {
+                               soup_session_remove_feature (session, f->data);
+                               goto restart;
+                       }
+               }
+               G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+               if (g_type_is_a (feature_type, SOUP_TYPE_PROXY_URI_RESOLVER))
+                       priv->proxy_use_default = FALSE;
+               G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+       } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
+               SoupRequestClass *request_class;
+               int i;
+
+               request_class = g_type_class_peek (feature_type);
+               if (!request_class)
+                       return;
+               for (i = 0; request_class->schemes[i]; i++) {
+                       g_hash_table_remove (priv->request_types,
+                                            request_class->schemes[i]);
+               }
+       } else {
+               for (f = priv->features; f; f = f->next) {
+                       if (soup_session_feature_remove_feature (f->data, feature_type))
+                               return;
+               }
+               g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
        }
-
-       host->addr = g_object_new (SOUP_TYPE_ADDRESS,
-                                  SOUP_ADDRESS_NAME, host->uri->host,
-                                  SOUP_ADDRESS_PORT, host->uri->port,
-                                  SOUP_ADDRESS_PROTOCOL, host->uri->scheme,
-                                  NULL);
-       host->keep_alive_src = NULL;
-       host->session = session;
-
-       return host;
 }
 
-/* Requires host_lock to be locked */
-static SoupSessionHost *
-get_host_for_uri (SoupSession *session, SoupURI *uri)
+/**
+ * soup_session_has_feature:
+ * @session: a #SoupSession
+ * @feature_type: the #GType of the class of features to check for
+ *
+ * Tests if @session has at a feature of type @feature_type (which can
+ * be the type of either a #SoupSessionFeature, or else a subtype of
+ * some class managed by another feature, such as #SoupAuth or
+ * #SoupRequest).
+ *
+ * Return value: %TRUE or %FALSE
+ *
+ * Since: 2.42
+ **/
+gboolean
+soup_session_has_feature (SoupSession *session,
+                         GType        feature_type)
 {
-       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       SoupSessionHost *host;
+       SoupSessionPrivate *priv;
+       GSList *f;
 
-       if (uri_is_https (priv, uri))
-               host = g_hash_table_lookup (priv->https_hosts, uri);
-       else
-               host = g_hash_table_lookup (priv->http_hosts, uri);
-       if (host)
-               return host;
+       g_return_val_if_fail (SOUP_IS_SESSION (session), FALSE);
 
-       host = soup_session_host_new (session, uri);
+       priv = SOUP_SESSION_GET_PRIVATE (session);
 
-       if (uri_is_https (priv, uri))
-               g_hash_table_insert (priv->https_hosts, host->uri, host);
-       else
-               g_hash_table_insert (priv->http_hosts, host->uri, host);
+       if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
+               for (f = priv->features; f; f = f->next) {
+                       if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
+                               return TRUE;
+               }
+       } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
+               return g_hash_table_lookup (priv->request_types,
+                                           GSIZE_TO_POINTER (feature_type)) != NULL;
+       } else {
+               for (f = priv->features; f; f = f->next) {
+                       if (soup_session_feature_has_feature (f->data, feature_type))
+                               return TRUE;
+               }
+       }
 
-       return host;
+       return FALSE;
 }
 
-/* Note: get_host_for_message doesn't lock the host_lock. The caller
- * must do it itself if there's a chance the host doesn't already
- * exist.
- */
-static SoupSessionHost *
-get_host_for_message (SoupSession *session, SoupMessage *msg)
+/**
+ * soup_session_get_features:
+ * @session: a #SoupSession
+ * @feature_type: the #GType of the class of features to get
+ *
+ * Generates a list of @session's features of type @feature_type. (If
+ * you want to see all features, you can pass %SOUP_TYPE_SESSION_FEATURE
+ * for @feature_type.)
+ *
+ * Return value: (transfer container) (element-type Soup.SessionFeature):
+ * a list of features. You must free the list, but not its contents
+ *
+ * Since: 2.26
+ **/
+GSList *
+soup_session_get_features (SoupSession *session, GType feature_type)
 {
-       return get_host_for_uri (session, soup_message_get_uri (msg));
-}
+       SoupSessionPrivate *priv;
+       GSList *f, *ret;
 
-static void
-free_host (SoupSessionHost *host)
-{
-       g_slist_free_full (host->connections,
-                          (GDestroyNotify) soup_connection_disconnect);
+       g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
 
-       if (host->keep_alive_src) {
-               g_source_destroy (host->keep_alive_src);
-               g_source_unref (host->keep_alive_src);
+       priv = SOUP_SESSION_GET_PRIVATE (session);
+       for (f = priv->features, ret = NULL; f; f = f->next) {
+               if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
+                       ret = g_slist_prepend (ret, f->data);
        }
-
-       soup_uri_free (host->uri);
-       g_object_unref (host->addr);
-       g_slice_free (SoupSessionHost, host);
-}
-
-static void
-auth_required (SoupSession *session, SoupMessage *msg,
-              SoupAuth *auth, gboolean retrying)
-{
-       g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
+       return g_slist_reverse (ret);
 }
 
-static void
-auth_manager_authenticate (SoupAuthManager *manager, SoupMessage *msg,
-                          SoupAuth *auth, gboolean retrying,
-                          gpointer session)
+/**
+ * soup_session_get_feature:
+ * @session: a #SoupSession
+ * @feature_type: the #GType of the feature to get
+ *
+ * Gets the first feature in @session of type @feature_type. For
+ * features where there may be more than one feature of a given type,
+ * use soup_session_get_features().
+ *
+ * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
+ * feature is owned by @session.
+ *
+ * Since: 2.26
+ **/
+SoupSessionFeature *
+soup_session_get_feature (SoupSession *session, GType feature_type)
 {
-       SOUP_SESSION_GET_CLASS (session)->auth_required (
-               session, msg, auth, retrying);
-}
-
-/* At some point it might be possible to mark additional methods
- * safe or idempotent...
- */
-#define SOUP_METHOD_IS_SAFE(method) (method == SOUP_METHOD_GET || \
-                                    method == SOUP_METHOD_HEAD || \
-                                    method == SOUP_METHOD_OPTIONS || \
-                                    method == SOUP_METHOD_PROPFIND)
-
-#define SOUP_METHOD_IS_IDEMPOTENT(method) (method == SOUP_METHOD_GET || \
-                                          method == SOUP_METHOD_HEAD || \
-                                          method == SOUP_METHOD_OPTIONS || \
-                                          method == SOUP_METHOD_PROPFIND || \
-                                          method == SOUP_METHOD_PUT || \
-                                          method == SOUP_METHOD_DELETE)
-
-
-#define SOUP_SESSION_WOULD_REDIRECT_AS_GET(session, msg) \
-       ((msg)->status_code == SOUP_STATUS_SEE_OTHER || \
-        ((msg)->status_code == SOUP_STATUS_FOUND && \
-         !SOUP_METHOD_IS_SAFE ((msg)->method)) || \
-        ((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY && \
-         (msg)->method == SOUP_METHOD_POST))
+       SoupSessionPrivate *priv;
+       SoupSessionFeature *feature;
+       GSList *f;
 
-#define SOUP_SESSION_WOULD_REDIRECT_AS_SAFE(session, msg) \
-       (((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY || \
-         (msg)->status_code == SOUP_STATUS_TEMPORARY_REDIRECT || \
-         (msg)->status_code == SOUP_STATUS_FOUND) && \
-        SOUP_METHOD_IS_SAFE ((msg)->method))
+       g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
 
-static inline SoupURI *
-redirection_uri (SoupMessage *msg)
-{
-       const char *new_loc;
-       SoupURI *new_uri;
+       priv = SOUP_SESSION_GET_PRIVATE (session);
 
-       new_loc = soup_message_headers_get_one (msg->response_headers,
-                                               "Location");
-       if (!new_loc)
-               return NULL;
-       new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
-       if (!new_uri || !new_uri->host) {
-               if (new_uri)
-                       soup_uri_free (new_uri);
-               return NULL;
-       }
+       feature = g_hash_table_lookup (priv->features_cache,
+                                      GSIZE_TO_POINTER (feature_type));
+       if (feature)
+               return feature;
 
-       return new_uri;
+       for (f = priv->features; f; f = f->next) {
+               feature = f->data;
+               if (G_TYPE_CHECK_INSTANCE_TYPE (feature, feature_type)) {
+                       g_hash_table_insert (priv->features_cache,
+                                            GSIZE_TO_POINTER (feature_type),
+                                            feature);
+                       return feature;
+               }
+       }
+       return NULL;
 }
 
 /**
- * soup_session_would_redirect:
+ * soup_session_get_feature_for_message:
  * @session: a #SoupSession
- * @msg: a #SoupMessage that has response headers
+ * @feature_type: the #GType of the feature to get
+ * @msg: a #SoupMessage
  *
- * Checks if @msg contains a response that would cause @session to
- * redirect it to a new URL (ignoring @msg's %SOUP_MESSAGE_NO_REDIRECT
- * flag, and the number of times it has already been redirected).
+ * Gets the first feature in @session of type @feature_type, provided
+ * that it is not disabled for @msg. As with
+ * soup_session_get_feature(), this should only be used for features
+ * where @feature_type is only expected to match a single feature. In
+ * particular, if there are two matching features, and the first is
+ * disabled on @msg, and the second is not, then this will return
+ * %NULL, not the second feature.
  *
- * Return value: whether @msg would be redirected
+ * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
+ * feature is owned by @session.
  *
- * Since: 2.38
- */
-gboolean
-soup_session_would_redirect (SoupSession *session, SoupMessage *msg)
+ * Since: 2.28
+ **/
+SoupSessionFeature *
+soup_session_get_feature_for_message (SoupSession *session, GType feature_type,
+                                     SoupMessage *msg)
 {
-       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       SoupURI *new_uri;
+       SoupSessionFeature *feature;
 
-       /* It must have an appropriate status code and method */
-       if (!SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg) &&
-           !SOUP_SESSION_WOULD_REDIRECT_AS_SAFE (session, msg))
-               return FALSE;
+       feature = soup_session_get_feature (session, feature_type);
+       if (feature && soup_message_disables_feature (msg, feature))
+               return NULL;
+       return feature;
+}
 
-       /* and a Location header that parses to an http URI */
-       if (!soup_message_headers_get_one (msg->response_headers, "Location"))
-               return FALSE;
-       new_uri = redirection_uri (msg);
-       if (!new_uri)
-               return FALSE;
-       if (!new_uri->host || !*new_uri->host ||
-           (!uri_is_http (priv, new_uri) && !uri_is_https (priv, new_uri))) {
-               soup_uri_free (new_uri);
-               return FALSE;
-       }
+#if ENABLE(TIZEN_CERTIFICATE_FILE_SET)
 
-       soup_uri_free (new_uri);
-       return TRUE;
+#if ENABLE(TIZEN_TV_FORCE_PRELOAD_TLSDB)
+/**
+ * TLS database preloader thread.
+ **/
+static gpointer
+_preload_tlsdb_thread (gpointer p)
+{
+       GError* error = NULL;
+
+       g_mutex_lock (&_gTlsDB_lock);
+       TIZEN_LOGI("Create new tls database by using thread.");
+       g_assert (_gTlsDB_path);
+       _gTlsDB = g_tls_file_database_new (_gTlsDB_path, &error);
+       g_mutex_unlock (&_gTlsDB_lock);
+
+       return NULL;
 }
 
 /**
- * soup_session_redirect_message:
- * @session: the session
- * @msg: a #SoupMessage that has received a 3xx response
- *
- * Updates @msg's URI according to its status code and "Location"
- * header, and requeues it on @session. Use this when you have set
- * %SOUP_MESSAGE_NO_REDIRECT on a message, but have decided to allow a
- * particular redirection to occur, or if you want to allow a
- * redirection that #SoupSession will not perform automatically (eg,
- * redirecting a non-safe method such as DELETE).
- *
- * If @msg's status code indicates that it should be retried as a GET
- * request, then @msg will be modified accordingly.
- *
- * If @msg has already been redirected too many times, this will
- * cause it to fail with %SOUP_STATUS_TOO_MANY_REDIRECTS.
- *
- * Return value: %TRUE if a redirection was applied, %FALSE if not
- * (eg, because there was no Location header, or it could not be
- * parsed).
- *
- * Since: 2.38
- */
-gboolean
-soup_session_redirect_message (SoupSession *session, SoupMessage *msg)
+ * Launch the TLS database preloader thread for a specified path.
+ * This is a one-time function.
+ **/
+static void
+soup_preload_tls_database (const gchar *path)
 {
-       SoupMessageQueueItem *item;
-       SoupURI *new_uri;
+       g_mutex_lock (&_gTlsDB_lock);
+       //_gTlsDB_path is set only once. It prevent to create thread repeatedly.
+       //So keep the value of _gTlsDB_path.
+       if (!_gTlsDB_path) {
+               _gTlsDB_path = g_strdup (path);
+               if (_gTlsDB_path)
+                       g_thread_new ("TLS Preloader", _preload_tlsdb_thread, NULL);
+       }
+       g_mutex_unlock (&_gTlsDB_lock);
+}
+#endif
 
-       new_uri = redirection_uri (msg);
-       if (!new_uri)
-               return FALSE;
+void soup_session_set_certificate_file(SoupSession *session)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
 
-       item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
-       if (!item) {
-               soup_uri_free (new_uri);
-               return FALSE;
-       }
-       if (item->redirection_count >= SOUP_SESSION_MAX_REDIRECTION_COUNT) {
-               soup_uri_free (new_uri);
-               soup_session_cancel_message (session, msg, SOUP_STATUS_TOO_MANY_REDIRECTS);
-               soup_message_queue_item_unref (item);
-               return FALSE;
+       TIZEN_LOGI ("");
+
+       if (!priv->certificate_path) {
+               TIZEN_LOGI("priv->certificate_path is NULL, return!!");
+               return;
        }
-       item->redirection_count++;
-       soup_message_queue_item_unref (item);
 
-       if (SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg)) {
-               if (msg->method != SOUP_METHOD_HEAD) {
-                       g_object_set (msg,
-                                     SOUP_MESSAGE_METHOD, SOUP_METHOD_GET,
-                                     NULL);
+       if (!priv->tlsdb) {
+
+               GError* error = NULL;
+               GTlsDatabase* tlsdb = NULL;
+
+#if ENABLE(TIZEN_TV_FORCE_PRELOAD_TLSDB)
+               TIZEN_LOGI("Begin to handle TLS database.");
+               g_mutex_lock (&_gTlsDB_lock);
+               //Check whether certificate_path is the same as the path used by pre-load TLS DB.
+               if ((_gTlsDB) && (!g_strcmp0(_gTlsDB_path, priv->certificate_path))) {
+                       TIZEN_LOGI("Use tls database which is created by thread.");
+                       tlsdb = g_object_ref(_gTlsDB); // Take a reference to the global DB.
+                       _gTlsDB = NULL;
+               } else {
+                       tlsdb = g_tls_file_database_new (priv->certificate_path, &error);
                }
-               soup_message_set_request (msg, NULL,
-                                         SOUP_MEMORY_STATIC, NULL, 0);
-               soup_message_headers_set_encoding (msg->request_headers,
-                                                  SOUP_ENCODING_NONE);
-       }
+               g_mutex_unlock (&_gTlsDB_lock);
+#else
+               TIZEN_LOGI ("g_tls_file_database_new() is called. START");
+               tlsdb = g_tls_file_database_new(priv->certificate_path, &error);
+               TIZEN_LOGI ("g_tls_file_database_new() is called. END");
+               TIZEN_LOGI ("g_tls_file_database_new() is called. [%s]", priv->certificate_path);
+#endif
 
-       soup_message_set_uri (msg, new_uri);
-       soup_uri_free (new_uri);
+               if (!error && tlsdb) {
+#if ENABLE(TIZEN_DLOG)
+                       TIZEN_LOGI ("g_tls_file_database_new() is success. no error call set_tlsdb().");
+#endif
+                       set_tlsdb (session, tlsdb);
+               }
 
-       soup_session_requeue_message (session, msg);
-       return TRUE;
+               if (tlsdb)
+                       g_object_unref (tlsdb);
+               if (priv->certificate_path) {
+                       g_free (priv->certificate_path);
+                       priv->certificate_path = NULL;
+               }
+       }
 }
+#endif
 
 static void
-redirect_handler (SoupMessage *msg, gpointer user_data)
+soup_session_class_init (SoupSessionClass *session_class)
 {
-       SoupMessageQueueItem *item = user_data;
-       SoupSession *session = item->session;
+       GObjectClass *object_class = G_OBJECT_CLASS (session_class);
 
-       if (!soup_session_would_redirect (session, msg)) {
-               SoupURI *new_uri = redirection_uri (msg);
-               gboolean invalid = !new_uri || !new_uri->host;
+       g_type_class_add_private (session_class, sizeof (SoupSessionPrivate));
 
-               if (new_uri)
-                       soup_uri_free (new_uri);
-               if (invalid && !item->new_api) {
-                       soup_message_set_status_full (msg,
-                                                     SOUP_STATUS_MALFORMED,
-                                                     "Invalid Redirect URL");
-               }
-               return;
-       }
+       /* virtual method definition */
+       session_class->queue_message = soup_session_real_queue_message;
+       session_class->send_message = soup_session_real_send_message;
+       session_class->requeue_message = soup_session_real_requeue_message;
+       session_class->cancel_message = soup_session_real_cancel_message;
+       session_class->flush_queue = soup_session_real_flush_queue;
+       session_class->kick = soup_session_real_kick_queue;
 
-       soup_session_redirect_message (session, msg);
-}
+       /* virtual method override */
+       object_class->constructor = soup_session_constructor;
+       object_class->dispose = soup_session_dispose;
+       object_class->finalize = soup_session_finalize;
+       object_class->set_property = soup_session_set_property;
+       object_class->get_property = soup_session_get_property;
+
+       /* signals */
+
+       /**
+        * SoupSession::request-queued:
+        * @session: the session
+        * @msg: the request that was queued
+        *
+        * Emitted when a request is queued on @session. (Note that
+        * "queued" doesn't just mean soup_session_queue_message();
+        * soup_session_send_message() implicitly queues the message
+        * as well.)
+        *
+        * When sending a request, first #SoupSession::request_queued
+        * is emitted, indicating that the session has become aware of
+        * the request.
+        *
+        * Once a connection is available to send the request on, the
+        * session emits #SoupSession::request_started. Then, various
+        * #SoupMessage signals are emitted as the message is
+        * processed. If the message is requeued, it will emit
+        * #SoupMessage::restarted, which will then be followed by
+        * another #SoupSession::request_started and another set of
+        * #SoupMessage signals when the message is re-sent.
+        *
+        * Eventually, the message will emit #SoupMessage::finished.
+        * Normally, this signals the completion of message
+        * processing. However, it is possible that the application
+        * will requeue the message from the "finished" handler (or
+        * equivalently, from the soup_session_queue_message()
+        * callback). In that case, the process will loop back to
+        * #SoupSession::request_started.
+        *
+        * Eventually, a message will reach "finished" and not be
+        * requeued. At that point, the session will emit
+        * #SoupSession::request_unqueued to indicate that it is done
+        * with the message.
+        *
+        * To sum up: #SoupSession::request_queued and
+        * #SoupSession::request_unqueued are guaranteed to be emitted
+        * exactly once, but #SoupSession::request_started and
+        * #SoupMessage::finished (and all of the other #SoupMessage
+        * signals) may be invoked multiple times for a given message.
+        *
+        * Since: 2.24
+        **/
+       signals[REQUEST_QUEUED] =
+               g_signal_new ("request-queued",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             0, /* FIXME? */
+                             NULL, NULL,
+                             NULL,
+                             G_TYPE_NONE, 1,
+                             SOUP_TYPE_MESSAGE);
+
+       /**
+        * SoupSession::request-started:
+        * @session: the session
+        * @msg: the request being sent
+        * @socket: the socket the request is being sent on
+        *
+        * Emitted just before a request is sent. See
+        * #SoupSession::request_queued for a detailed description of
+        * the message lifecycle within a session.
+        **/
+       signals[REQUEST_STARTED] =
+               g_signal_new ("request-started",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupSessionClass, request_started),
+                             NULL, NULL,
+                             NULL,
+                             G_TYPE_NONE, 2,
+                             SOUP_TYPE_MESSAGE,
+                             SOUP_TYPE_SOCKET);
+
+       /**
+        * SoupSession::request-unqueued:
+        * @session: the session
+        * @msg: the request that was unqueued
+        *
+        * Emitted when a request is removed from @session's queue,
+        * indicating that @session is done with it. See
+        * #SoupSession::request_queued for a detailed description of the
+        * message lifecycle within a session.
+        *
+        * Since: 2.24
+        **/
+       signals[REQUEST_UNQUEUED] =
+               g_signal_new ("request-unqueued",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             0, /* FIXME? */
+                             NULL, NULL,
+                             NULL,
+                             G_TYPE_NONE, 1,
+                             SOUP_TYPE_MESSAGE);
+
+       /**
+        * SoupSession::authenticate:
+        * @session: the session
+        * @msg: the #SoupMessage being sent
+        * @auth: the #SoupAuth to authenticate
+        * @retrying: %TRUE if this is the second (or later) attempt
+        *
+        * Emitted when the session requires authentication. If
+        * credentials are available call soup_auth_authenticate() on
+        * @auth. If these credentials fail, the signal will be
+        * emitted again, with @retrying set to %TRUE, which will
+        * continue until you return without calling
+        * soup_auth_authenticate() on @auth.
+        *
+        * Note that this may be emitted before @msg's body has been
+        * fully read.
+        *
+        * If you call soup_session_pause_message() on @msg before
+        * returning, then you can authenticate @auth asynchronously
+        * (as long as you g_object_ref() it to make sure it doesn't
+        * get destroyed), and then unpause @msg when you are ready
+        * for it to continue.
+        **/
+       signals[AUTHENTICATE] =
+               g_signal_new ("authenticate",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupSessionClass, authenticate),
+                             NULL, NULL,
+                             NULL,
+                             G_TYPE_NONE, 3,
+                             SOUP_TYPE_MESSAGE,
+                             SOUP_TYPE_AUTH,
+                             G_TYPE_BOOLEAN);
 
-void
-soup_session_send_queue_item (SoupSession *session,
-                             SoupMessageQueueItem *item,
-                             SoupMessageCompletionFn completion_cb)
-{
-       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       const char *conn_header;
+       /**
+        * SoupSession::connection-created:
+        * @session: the #SoupSession
+        * @connection: the connection
+        *
+        * Emitted when a new connection is created. This is an
+        * internal signal intended only to be used for debugging
+        * purposes, and may go away in the future.
+        *
+        * Since: 2.30
+        */
+       signals[CONNECTION_CREATED] =
+               g_signal_new ("connection-created",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             0,
+                             NULL, NULL,
+                             NULL,
+                             G_TYPE_NONE, 1,
+                             /* SoupConnection is private, so we can't use
+                              * SOUP_TYPE_CONNECTION here.
+                              */
+                             G_TYPE_OBJECT);
 
-       if (priv->user_agent) {
-               soup_message_headers_replace (item->msg->request_headers,
-                                             "User-Agent", priv->user_agent);
-       }
+       /**
+        * SoupSession::tunneling:
+        * @session: the #SoupSession
+        * @connection: the connection
+        *
+        * Emitted when an SSL tunnel is being created on a proxy
+        * connection. This is an internal signal intended only to be
+        * used for debugging purposes, and may go away in the future.
+        *
+        * Since: 2.30
+        */
+       signals[TUNNELING] =
+               g_signal_new ("tunneling",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             0,
+                             NULL, NULL,
+                             NULL,
+                             G_TYPE_NONE, 1,
+                             /* SoupConnection is private, so we can't use
+                              * SOUP_TYPE_CONNECTION here.
+                              */
+                             G_TYPE_OBJECT);
 
-       if (priv->accept_language &&
-           !soup_message_headers_get_list (item->msg->request_headers,
-                                           "Accept-Language")) {
-               soup_message_headers_append (item->msg->request_headers,
-                                            "Accept-Language",
-                                            priv->accept_language);
-       }
 
-       /* Force keep alive connections for HTTP 1.0. Performance will
-        * improve when issuing multiple requests to the same host in
-        * a short period of time, as we wouldn't need to establish
-        * new connections. Keep alive is implicit for HTTP 1.1.
+       /* properties */
+       /**
+        * SoupSession:proxy-uri:
+        *
+        * A proxy to use for all http and https requests in this
+        * session. Setting this will clear the
+        * #SoupSession:proxy-resolver property, and remove any
+        * <type>SoupProxyURIResolver</type> features that have been
+        * added to the session. Setting this property will also
+        * cancel all currently pending messages.
+        *
+        * Note that #SoupSession will normally handle looking up the
+        * user's proxy settings for you; you should only use
+        * #SoupSession:proxy-uri if you need to override the user's
+        * normal proxy settings.
+        *
+        * Also note that this proxy will be used for
+        * <emphasis>all</emphasis> requests; even requests to
+        * <literal>localhost</literal>. If you need more control over
+        * proxies, you can create a #GSimpleProxyResolver and set the
+        * #SoupSession:proxy-resolver property.
+        */
+       /**
+        * SOUP_SESSION_PROXY_URI:
+        *
+        * Alias for the #SoupSession:proxy-uri property, qv.
+        **/
+       g_object_class_install_property (
+               object_class, PROP_PROXY_URI,
+               g_param_spec_boxed (SOUP_SESSION_PROXY_URI,
+                                   "Proxy URI",
+                                   "The HTTP Proxy to use for this session",
+                                   SOUP_TYPE_URI,
+                                   G_PARAM_READWRITE));
+       /**
+        * SoupSession:proxy-resolver:
+        *
+        * A #GProxyResolver to use with this session. Setting this
+        * will clear the #SoupSession:proxy-uri property, and remove
+        * any <type>SoupProxyURIResolver</type> features that have
+        * been added to the session.
+        *
+        * By default, in a plain #SoupSession, this is set to the
+        * default #GProxyResolver, but you can set it to %NULL if you
+        * don't want to use proxies, or set it to your own
+        * #GProxyResolver if you want to control what proxies get
+        * used.
+        *
+        * Since: 2.42
+        */
+       /**
+        * SOUP_SESSION_PROXY_RESOLVER:
+        *
+        * Alias for the #SoupSession:proxy-resolver property, qv.
+        **/
+       g_object_class_install_property (
+               object_class, PROP_PROXY_RESOLVER,
+               g_param_spec_object (SOUP_SESSION_PROXY_RESOLVER,
+                                    "Proxy Resolver",
+                                    "The GProxyResolver to use for this session",
+                                    G_TYPE_PROXY_RESOLVER,
+                                    G_PARAM_READWRITE));
+       /**
+        * SOUP_SESSION_MAX_CONNS:
+        *
+        * Alias for the #SoupSession:max-conns property, qv.
+        **/
+       g_object_class_install_property (
+               object_class, PROP_MAX_CONNS,
+               g_param_spec_int (SOUP_SESSION_MAX_CONNS,
+                                 "Max Connection Count",
+                                 "The maximum number of connections that the session can open at once",
+                                 1,
+                                 G_MAXINT,
+                                 SOUP_SESSION_MAX_CONNS_DEFAULT,
+                                 G_PARAM_READWRITE));
+       /**
+        * SOUP_SESSION_MAX_CONNS_PER_HOST:
+        *
+        * Alias for the #SoupSession:max-conns-per-host property, qv.
+        **/
+       g_object_class_install_property (
+               object_class, PROP_MAX_CONNS_PER_HOST,
+               g_param_spec_int (SOUP_SESSION_MAX_CONNS_PER_HOST,
+                                 "Max Per-Host Connection Count",
+                                 "The maximum number of connections that the session can open at once to a given host",
+                                 1,
+                                 G_MAXINT,
+                                 SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT,
+                                 G_PARAM_READWRITE));
+       /**
+        * SoupSession:idle-timeout:
+        *
+        * Connection lifetime (in seconds) when idle. Any connection
+        * left idle longer than this will be closed.
+        *
+        * Although you can change this property at any time, it will
+        * only affect newly-created connections, not currently-open
+        * ones. You can call soup_session_abort() after setting this
+        * if you want to ensure that all future connections will have
+        * this timeout value.
+        *
+        * Note that the default value of 60 seconds only applies to
+        * plain #SoupSessions. If you are using #SoupSessionAsync or
+        * #SoupSessionSync, the default value is 0 (meaning idle
+        * connections will never time out).
+        *
+        * Since: 2.24
+        **/
+       /**
+        * SOUP_SESSION_IDLE_TIMEOUT:
+        *
+        * Alias for the #SoupSession:idle-timeout property, qv.
+        *
+        * Since: 2.24
+        **/
+       g_object_class_install_property (
+               object_class, PROP_IDLE_TIMEOUT,
+               g_param_spec_uint (SOUP_SESSION_IDLE_TIMEOUT,
+                                  "Idle Timeout",
+                                  "Connection lifetime when idle",
+                                  0, G_MAXUINT, 60,
+                                  G_PARAM_READWRITE));
+       /**
+        * SoupSession:use-ntlm:
+        *
+        * Whether or not to use NTLM authentication.
+        *
+        * Deprecated: use soup_session_add_feature_by_type() with
+        * #SOUP_TYPE_AUTH_NTLM.
+        **/
+       /**
+        * SOUP_SESSION_USE_NTLM:
+        *
+        * Alias for the #SoupSession:use-ntlm property, qv.
+        **/
+       g_object_class_install_property (
+               object_class, PROP_USE_NTLM,
+               g_param_spec_boolean (SOUP_SESSION_USE_NTLM,
+                                     "Use NTLM",
+                                     "Whether or not to use NTLM authentication",
+                                     FALSE,
+                                     G_PARAM_READWRITE | G_PARAM_DEPRECATED));
+       /**
+        * SoupSession:ssl-ca-file:
+        *
+        * File containing SSL CA certificates.
+        *
+        * If the specified file does not exist or cannot be read,
+        * then libsoup will print a warning, and then behave as
+        * though it had read in a empty CA file, meaning that all SSL
+        * certificates will be considered invalid.
+        *
+        * Deprecated: use #SoupSession:ssl-use-system-ca-file, or
+        * else #SoupSession:tls-database with a #GTlsFileDatabase
+        * (which allows you to do explicit error handling).
+        **/
+       /**
+        * SOUP_SESSION_SSL_CA_FILE:
+        *
+        * Alias for the #SoupSession:ssl-ca-file property, qv.
+        **/
+       g_object_class_install_property (
+               object_class, PROP_SSL_CA_FILE,
+               g_param_spec_string (SOUP_SESSION_SSL_CA_FILE,
+                                    "SSL CA file",
+                                    "File containing SSL CA certificates",
+                                    NULL,
+                                    G_PARAM_READWRITE | G_PARAM_DEPRECATED));
+       /**
+        * SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE:
+        *
+        * Alias for the #SoupSession:ssl-use-system-ca-file property,
+        * qv.
+        *
+        * Since: 2.38
+        **/
+       /**
+        * SoupSession:ssl-use-system-ca-file:
+        *
+        * Setting this to %TRUE is equivalent to setting
+        * #SoupSession:tls-database to the default system CA database.
+        * (and likewise, setting #SoupSession:tls-database to the
+        * default database by hand will cause this property to
+        * become %TRUE).
+        *
+        * Setting this to %FALSE (when it was previously %TRUE) will
+        * clear the #SoupSession:tls-database field.
+        *
+        * See #SoupSession:ssl-strict for more information on how
+        * https certificate validation is handled.
+        *
+        * Note that the default value of %TRUE only applies to plain
+        * #SoupSessions. If you are using #SoupSessionAsync or
+        * #SoupSessionSync, the default value is %FALSE, for backward
+        * compatibility.
+        *
+        * Since: 2.38
+        **/
+       g_object_class_install_property (
+               object_class, PROP_SSL_USE_SYSTEM_CA_FILE,
+               g_param_spec_boolean (SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE,
+                                     "Use system CA file",
+                                     "Use the system certificate database",
+                                     TRUE,
+                                     G_PARAM_READWRITE));
+       /**
+        * SOUP_SESSION_TLS_DATABASE:
+        *
+        * Alias for the #SoupSession:tls-database property, qv.
+        *
+        * Since: 2.38
+        **/
+       /**
+        * SoupSession:tls-database:
+        *
+        * Sets the #GTlsDatabase to use for validating SSL/TLS
+        * certificates.
+        *
+        * Note that setting the #SoupSession:ssl-ca-file or
+        * #SoupSession:ssl-use-system-ca-file property will cause
+        * this property to be set to a #GTlsDatabase corresponding to
+        * the indicated file or system default.
+        *
+        * See #SoupSession:ssl-strict for more information on how
+        * https certificate validation is handled.
+        *
+        * If you are using a plain #SoupSession then
+        * #SoupSession:ssl-use-system-ca-file will be %TRUE by
+        * default, and so this property will be a copy of the system
+        * CA database. If you are using #SoupSessionAsync or
+        * #SoupSessionSync, this property will be %NULL by default.
+        *
+        * Since: 2.38
+        **/
+       g_object_class_install_property (
+               object_class, PROP_TLS_DATABASE,
+               g_param_spec_object (SOUP_SESSION_TLS_DATABASE,
+                                    "TLS Database",
+                                    "TLS database to use",
+                                    G_TYPE_TLS_DATABASE,
+                                    G_PARAM_READWRITE));
+       /**
+        * SOUP_SESSION_SSL_STRICT:
+        *
+        * Alias for the #SoupSession:ssl-strict property, qv.
+        *
+        * Since: 2.30
+        **/
+       /**
+        * SoupSession:ssl-strict:
+        *
+        * Normally, if #SoupSession:tls-database is set (including if
+        * it was set via #SoupSession:ssl-use-system-ca-file or
+        * #SoupSession:ssl-ca-file), then libsoup will reject any
+        * certificate that is invalid (ie, expired) or that is not
+        * signed by one of the given CA certificates, and the
+        * #SoupMessage will fail with the status
+        * %SOUP_STATUS_SSL_FAILED.
+        *
+        * If you set #SoupSession:ssl-strict to %FALSE, then all
+        * certificates will be accepted, and you will need to call
+        * soup_message_get_https_status() to distinguish valid from
+        * invalid certificates. (This can be used, eg, if you want to
+        * accept invalid certificates after giving some sort of
+        * warning.)
+        *
+        * For a plain #SoupSession, if the session has no CA file or
+        * TLS database, and this property is %TRUE, then all
+        * certificates will be rejected. However, beware that the
+        * deprecated #SoupSession subclasses (#SoupSessionAsync and
+        * #SoupSessionSync) have the opposite behavior: if there is
+        * no CA file or TLS database, then all certificates are always
+        * accepted, and this property has no effect.
+        *
+        * Since: 2.30
         */
-       conn_header = soup_message_headers_get_list (item->msg->request_headers, "Connection");
-       if (!conn_header ||
-           (!soup_header_contains (conn_header, "Keep-Alive") &&
-            !soup_header_contains (conn_header, "close")))
-               soup_message_headers_append (item->msg->request_headers,
-                                            "Connection", "Keep-Alive");
-
-       g_signal_emit (session, signals[REQUEST_STARTED], 0,
-                      item->msg, soup_connection_get_socket (item->conn));
-       if (item->state == SOUP_MESSAGE_RUNNING)
-               soup_connection_send_request (item->conn, item, completion_cb, item);
-}
-
-gboolean
-soup_session_cleanup_connections (SoupSession *session,
-                                 gboolean     prune_idle)
-{
-       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       GSList *conns = NULL, *c;
-       GHashTableIter iter;
-       gpointer conn, host;
-       SoupConnectionState state;
-
-       g_mutex_lock (&priv->host_lock);
-       g_hash_table_iter_init (&iter, priv->conns);
-       while (g_hash_table_iter_next (&iter, &conn, &host)) {
-               state = soup_connection_get_state (conn);
-               if (state == SOUP_CONNECTION_REMOTE_DISCONNECTED ||
-                   (prune_idle && state == SOUP_CONNECTION_IDLE))
-                       conns = g_slist_prepend (conns, g_object_ref (conn));
-       }
-       g_mutex_unlock (&priv->host_lock);
-
-       if (!conns)
-               return FALSE;
-
-       for (c = conns; c; c = c->next) {
-               conn = c->data;
-               soup_connection_disconnect (conn);
-               g_object_unref (conn);
-       }
-       g_slist_free (conns);
-
-       return TRUE;
-}
-
-static gboolean
-free_unused_host (gpointer user_data)
-{
-       SoupSessionHost *host = (SoupSessionHost *) user_data;
-       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (host->session);
-
-       g_mutex_lock (&priv->host_lock);
-       /* This will free the host in addition to removing it from the
-        * hash table
+       g_object_class_install_property (
+               object_class, PROP_SSL_STRICT,
+               g_param_spec_boolean (SOUP_SESSION_SSL_STRICT,
+                                     "Strictly validate SSL certificates",
+                                     "Whether certificate errors should be considered a connection error",
+                                     TRUE,
+                                     G_PARAM_READWRITE));
+       /**
+        * SoupSession:async-context:
+        *
+        * The #GMainContext that miscellaneous session-related
+        * asynchronous callbacks are invoked on. (Eg, setting
+        * #SoupSession:idle-timeout will add a timeout source on this
+        * context.)
+        *
+        * For a plain #SoupSession, this property is always set to
+        * the #GMainContext that is the thread-default at the time
+        * the session was created, and cannot be overridden. For the
+        * deprecated #SoupSession subclasses, the default value is
+        * %NULL, meaning to use the global default #GMainContext.
+        *
+        * If #SoupSession:use-thread-context is %FALSE, this context
+        * will also be used for asynchronous HTTP I/O.
         */
-       if (host->uri->scheme == SOUP_URI_SCHEME_HTTPS)
-               g_hash_table_remove (priv->https_hosts, host->uri);
-       else
-               g_hash_table_remove (priv->http_hosts, host->uri);
-       g_mutex_unlock (&priv->host_lock);
-
-       return FALSE;
-}
-
-static void
-connection_disconnected (SoupConnection *conn, gpointer user_data)
-{
-       SoupSession *session = user_data;
-       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       SoupSessionHost *host;
-
-       g_mutex_lock (&priv->host_lock);
-
-       host = g_hash_table_lookup (priv->conns, conn);
-       if (host) {
-               g_hash_table_remove (priv->conns, conn);
-               host->connections = g_slist_remove (host->connections, conn);
-               host->num_conns--;
-
-               /* Free the SoupHost (and its SoupAddress) if there
-                * has not been any new connection to the host during
-                * the last HOST_KEEP_ALIVE msecs.
-                */
-               if (host->num_conns == 0) {
-                       g_assert (host->keep_alive_src == NULL);
-                       host->keep_alive_src = soup_add_timeout (priv->async_context,
-                                                                HOST_KEEP_ALIVE,
-                                                                free_unused_host,
-                                                                host);
-                       host->keep_alive_src = g_source_ref (host->keep_alive_src);
-               }
-
-               if (soup_connection_get_ssl_fallback (conn))
-                       host->ssl_fallback = TRUE;
-       }
-
-       g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
-       priv->num_conns--;
-
-       g_mutex_unlock (&priv->host_lock);
-       g_object_unref (conn);
-}
-
-SoupMessageQueueItem *
-soup_session_make_connect_message (SoupSession    *session,
-                                  SoupConnection *conn)
-{
-       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       SoupURI *uri;
-       SoupMessage *msg;
-       SoupMessageQueueItem *item;
-
-       uri = soup_connection_get_remote_uri (conn);
-       msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT, uri);
-       soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
-
-       /* Call the base implementation of soup_session_queue_message
-        * directly, to add msg to the SoupMessageQueue and cause all
-        * the right signals to be emitted.
+       /**
+        * SOUP_SESSION_ASYNC_CONTEXT:
+        *
+        * Alias for the #SoupSession:async-context property, qv.
         */
-       queue_message (session, msg, NULL, NULL);
-       item = soup_message_queue_lookup (priv->queue, msg);
-       soup_message_queue_item_set_connection (item, conn);
-       g_object_unref (msg);
-       item->state = SOUP_MESSAGE_RUNNING;
-
-       g_signal_emit (session, signals[TUNNELING], 0, conn);
-       return item;
-}
-
-gboolean
-soup_session_get_connection (SoupSession *session,
-                            SoupMessageQueueItem *item,
-                            gboolean *try_pruning)
-{
-       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       SoupConnection *conn;
-       SoupSessionHost *host;
-       GSList *conns;
-       int num_pending = 0;
-       gboolean need_new_connection;
-
-       if (item->conn) {
-               g_return_val_if_fail (soup_connection_get_state (item->conn) != SOUP_CONNECTION_DISCONNECTED, FALSE);
-               return TRUE;
-       }
-
-       need_new_connection =
-               (soup_message_get_flags (item->msg) & SOUP_MESSAGE_NEW_CONNECTION) ||
-               !SOUP_METHOD_IS_IDEMPOTENT (item->msg->method);
-
-       g_mutex_lock (&priv->host_lock);
-
-       host = get_host_for_message (session, item->msg);
-       for (conns = host->connections; conns; conns = conns->next) {
-               if (!need_new_connection && soup_connection_get_state (conns->data) == SOUP_CONNECTION_IDLE) {
-                       soup_connection_set_state (conns->data, SOUP_CONNECTION_IN_USE);
-                       g_mutex_unlock (&priv->host_lock);
-                       soup_message_queue_item_set_connection (item, conns->data);
-                       soup_message_set_https_status (item->msg, item->conn);
-                       return TRUE;
-               } else if (soup_connection_get_state (conns->data) == SOUP_CONNECTION_CONNECTING)
-                       num_pending++;
-       }
-
-       /* Limit the number of pending connections; num_messages / 2
-        * is somewhat arbitrary...
+       g_object_class_install_property (
+               object_class, PROP_ASYNC_CONTEXT,
+               g_param_spec_pointer (SOUP_SESSION_ASYNC_CONTEXT,
+                                     "Async GMainContext",
+                                     "The GMainContext to dispatch async I/O in",
+                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+       /**
+        * SOUP_SESSION_USE_THREAD_CONTEXT:
+        *
+        * Alias for the #SoupSession:use-thread-context property, qv.
+        *
+        * Since: 2.38
         */
-       if (num_pending > host->num_messages / 2) {
-               g_mutex_unlock (&priv->host_lock);
-               return FALSE;
-       }
-
-       if (host->num_conns >= priv->max_conns_per_host) {
-               if (need_new_connection)
-                       *try_pruning = TRUE;
-               g_mutex_unlock (&priv->host_lock);
-               return FALSE;
-       }
-
-       if (priv->num_conns >= priv->max_conns) {
-               *try_pruning = TRUE;
-               g_mutex_unlock (&priv->host_lock);
-               return FALSE;
-       }
-
-       conn = g_object_new (
-               SOUP_TYPE_CONNECTION,
-               SOUP_CONNECTION_REMOTE_URI, host->uri,
-               SOUP_CONNECTION_PROXY_RESOLVER, soup_session_get_feature (session, SOUP_TYPE_PROXY_URI_RESOLVER),
-               SOUP_CONNECTION_SSL, uri_is_https (priv, soup_message_get_uri (item->msg)),
-               SOUP_CONNECTION_SSL_CREDENTIALS, priv->tlsdb,
-               SOUP_CONNECTION_SSL_STRICT, (priv->tlsdb != NULL) && priv->ssl_strict,
-               SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
-               SOUP_CONNECTION_USE_THREAD_CONTEXT, priv->use_thread_context,
-               SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
-               SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
-               SOUP_CONNECTION_SSL_FALLBACK, host->ssl_fallback,
-               NULL);
-       g_signal_connect (conn, "disconnected",
-                         G_CALLBACK (connection_disconnected),
-                         session);
-
-       g_signal_emit (session, signals[CONNECTION_CREATED], 0, conn);
-
-       g_hash_table_insert (priv->conns, conn, host);
-
-       priv->num_conns++;
-       host->num_conns++;
-       host->connections = g_slist_prepend (host->connections, conn);
-
-       if (host->keep_alive_src) {
-               g_source_destroy (host->keep_alive_src);
-               g_source_unref (host->keep_alive_src);
-               host->keep_alive_src = NULL;
-       }
-
-       g_mutex_unlock (&priv->host_lock);
-       soup_message_queue_item_set_connection (item, conn);
-       return TRUE;
-}
-
-SoupMessageQueue *
-soup_session_get_queue (SoupSession *session)
-{
-       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-
-       return priv->queue;
-}
-
-void
-soup_session_unqueue_item (SoupSession          *session,
-                          SoupMessageQueueItem *item)
-{
-       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       SoupSessionHost *host;
+       /**
+        * SoupSession:use-thread-context:
+        *
+        * If %TRUE (which it always is on a plain #SoupSession),
+        * asynchronous HTTP requests in this session will run in
+        * whatever the thread-default #GMainContext is at the time
+        * they are started, rather than always occurring in
+        * #SoupSession:async-context.
+        *
+        * Since: 2.38
+        */
+       g_object_class_install_property (
+               object_class, PROP_USE_THREAD_CONTEXT,
+               g_param_spec_boolean (SOUP_SESSION_USE_THREAD_CONTEXT,
+                                     "Use thread-default GMainContext",
+                                     "Whether to use thread-default main contexts",
+                                     FALSE,
+                                     G_PARAM_READWRITE));
+       /**
+        * SoupSession:timeout:
+        *
+        * The timeout (in seconds) for socket I/O operations
+        * (including connecting to a server, and waiting for a reply
+        * to an HTTP request).
+        *
+        * Although you can change this property at any time, it will
+        * only affect newly-created connections, not currently-open
+        * ones. You can call soup_session_abort() after setting this
+        * if you want to ensure that all future connections will have
+        * this timeout value.
+        *
+        * Note that the default value of 60 seconds only applies to
+        * plain #SoupSessions. If you are using #SoupSessionAsync or
+        * #SoupSessionSync, the default value is 0 (meaning socket I/O
+        * will not time out).
+        *
+        * Not to be confused with #SoupSession:idle-timeout (which is
+        * the length of time that idle persistent connections will be
+        * kept open).
+        */
+       /**
+        * SOUP_SESSION_TIMEOUT:
+        *
+        * Alias for the #SoupSession:timeout property, qv.
+        **/
+       g_object_class_install_property (
+               object_class, PROP_TIMEOUT,
+               g_param_spec_uint (SOUP_SESSION_TIMEOUT,
+                                  "Timeout value",
+                                  "Value in seconds to timeout a blocking I/O",
+                                  0, G_MAXUINT, 0,
+                                  G_PARAM_READWRITE));
 
-       if (item->conn) {
-               if (soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE)
-                       soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
-               soup_message_queue_item_set_connection (item, NULL);
-       }
+       /**
+        * SoupSession:user-agent:
+        *
+        * If non-%NULL, the value to use for the "User-Agent" header
+        * on #SoupMessage<!-- -->s sent from this session.
+        *
+        * RFC 2616 says: "The User-Agent request-header field
+        * contains information about the user agent originating the
+        * request. This is for statistical purposes, the tracing of
+        * protocol violations, and automated recognition of user
+        * agents for the sake of tailoring responses to avoid
+        * particular user agent limitations. User agents SHOULD
+        * include this field with requests."
+        *
+        * The User-Agent header contains a list of one or more
+        * product tokens, separated by whitespace, with the most
+        * significant product token coming first. The tokens must be
+        * brief, ASCII, and mostly alphanumeric (although "-", "_",
+        * and "." are also allowed), and may optionally include a "/"
+        * followed by a version string. You may also put comments,
+        * enclosed in parentheses, between or after the tokens.
+        *
+        * If you set a #SoupSession:user_agent property that has trailing
+        * whitespace, #SoupSession will append its own product token
+        * (eg, "<literal>libsoup/2.3.2</literal>") to the end of the
+        * header for you.
+        **/
+       /**
+        * SOUP_SESSION_USER_AGENT:
+        *
+        * Alias for the #SoupSession:user-agent property, qv.
+        **/
+       g_object_class_install_property (
+               object_class, PROP_USER_AGENT,
+               g_param_spec_string (SOUP_SESSION_USER_AGENT,
+                                    "User-Agent string",
+                                    "User-Agent string",
+                                    NULL,
+                                    G_PARAM_READWRITE));
 
-       if (item->state != SOUP_MESSAGE_FINISHED) {
-               g_warning ("finished an item with state %d", item->state);
-               return;
-       }
+       /**
+        * SoupSession:accept-language:
+        *
+        * If non-%NULL, the value to use for the "Accept-Language" header
+        * on #SoupMessage<!-- -->s sent from this session.
+        *
+        * Setting this will disable
+        * #SoupSession:accept-language-auto.
+        *
+        * Since: 2.30
+        **/
+       /**
+        * SOUP_SESSION_ACCEPT_LANGUAGE:
+        *
+        * Alias for the #SoupSession:accept-language property, qv.
+        *
+        * Since: 2.30
+        **/
+       g_object_class_install_property (
+               object_class, PROP_ACCEPT_LANGUAGE,
+               g_param_spec_string (SOUP_SESSION_ACCEPT_LANGUAGE,
+                                    "Accept-Language string",
+                                    "Accept-Language string",
+                                    NULL,
+                                    G_PARAM_READWRITE));
 
-       soup_message_queue_remove (priv->queue, item);
+       /**
+        * SoupSession:accept-language-auto:
+        *
+        * If %TRUE, #SoupSession will automatically set the string
+        * for the "Accept-Language" header on every #SoupMessage
+        * sent, based on the return value of g_get_language_names().
+        *
+        * Setting this will override any previous value of
+        * #SoupSession:accept-language.
+        *
+        * Since: 2.30
+        **/
+       /**
+        * SOUP_SESSION_ACCEPT_LANGUAGE_AUTO:
+        *
+        * Alias for the #SoupSession:accept-language-auto property, qv.
+        *
+        * Since: 2.30
+        **/
+       g_object_class_install_property (
+               object_class, PROP_ACCEPT_LANGUAGE_AUTO,
+               g_param_spec_boolean (SOUP_SESSION_ACCEPT_LANGUAGE_AUTO,
+                                     "Accept-Language automatic mode",
+                                     "Accept-Language automatic mode",
+                                     FALSE,
+                                     G_PARAM_READWRITE));
 
-       g_mutex_lock (&priv->host_lock);
-       host = get_host_for_message (session, item->msg);
-       host->num_messages--;
-       g_mutex_unlock (&priv->host_lock);
+       /**
+        * SoupSession:add-feature: (skip)
+        *
+        * Add a feature object to the session. (Shortcut for calling
+        * soup_session_add_feature().)
+        *
+        * Since: 2.24
+        **/
+       /**
+        * SOUP_SESSION_ADD_FEATURE: (skip)
+        *
+        * Alias for the #SoupSession:add-feature property, qv.
+        *
+        * Since: 2.24
+        **/
+       g_object_class_install_property (
+               object_class, PROP_ADD_FEATURE,
+               g_param_spec_object (SOUP_SESSION_ADD_FEATURE,
+                                    "Add Feature",
+                                    "Add a feature object to the session",
+                                    SOUP_TYPE_SESSION_FEATURE,
+                                    G_PARAM_READWRITE));
+       /**
+        * SoupSession:add-feature-by-type: (skip)
+        *
+        * Add a feature object of the given type to the session.
+        * (Shortcut for calling soup_session_add_feature_by_type().)
+        *
+        * Since: 2.24
+        **/
+       /**
+        * SOUP_SESSION_ADD_FEATURE_BY_TYPE: (skip)
+        *
+        * Alias for the #SoupSession:add-feature-by-type property, qv.
+        *
+        * Since: 2.24
+        **/
+       g_object_class_install_property (
+               object_class, PROP_ADD_FEATURE_BY_TYPE,
+               g_param_spec_gtype (SOUP_SESSION_ADD_FEATURE_BY_TYPE,
+                                   "Add Feature By Type",
+                                   "Add a feature object of the given type to the session",
+                                   G_TYPE_OBJECT,
+                                   G_PARAM_READWRITE));
+       /**
+        * SoupSession:remove-feature-by-type: (skip)
+        *
+        * Remove feature objects from the session. (Shortcut for
+        * calling soup_session_remove_feature_by_type().)
+        *
+        * Since: 2.24
+        **/
+       /**
+        * SOUP_SESSION_REMOVE_FEATURE_BY_TYPE: (skip)
+        *
+        * Alias for the #SoupSession:remove-feature-by-type property,
+        * qv.
+        *
+        * Since: 2.24
+        **/
+       g_object_class_install_property (
+               object_class, PROP_REMOVE_FEATURE_BY_TYPE,
+               g_param_spec_gtype (SOUP_SESSION_REMOVE_FEATURE_BY_TYPE,
+                                   "Remove Feature By Type",
+                                   "Remove features of the given type from the session",
+                                   G_TYPE_OBJECT,
+                                   G_PARAM_READWRITE));
+       /**
+        * SoupSession:http-aliases:
+        *
+        * A %NULL-terminated array of URI schemes that should be
+        * considered to be aliases for "http". Eg, if this included
+        * <literal>"dav"</literal>, than a URI of
+        * <literal>dav://example.com/path</literal> would be treated
+        * identically to <literal>http://example.com/path</literal>.
+        *
+        * In a plain #SoupSession, the default value is %NULL,
+        * meaning that only "http" is recognized as meaning "http".
+        * In #SoupSessionAsync and #SoupSessionSync, for backward
+        * compatibility, the default value is an array containing the
+        * single element <literal>"*"</literal>, a special value
+        * which means that any scheme except "https" is considered to
+        * be an alias for "http".
+        *
+        * See also #SoupSession:https-aliases.
+        *
+        * Since: 2.38
+        */
+       /**
+        * SOUP_SESSION_HTTP_ALIASES:
+        *
+        * Alias for the #SoupSession:http-aliases property, qv.
+        *
+        * Since: 2.38
+        */
+       g_object_class_install_property (
+               object_class, PROP_HTTP_ALIASES,
+               g_param_spec_boxed (SOUP_SESSION_HTTP_ALIASES,
+                                   "http aliases",
+                                   "URI schemes that are considered aliases for 'http'",
+                                   G_TYPE_STRV,
+                                   G_PARAM_READWRITE));
+       /**
+        * SoupSession:https-aliases:
+        *
+        * A comma-delimited list of URI schemes that should be
+        * considered to be aliases for "https". See
+        * #SoupSession:http-aliases for more information.
+        *
+        * The default value is %NULL, meaning that no URI schemes
+        * are considered aliases for "https".
+        *
+        * Since: 2.38
+        */
+       /**
+        * SOUP_SESSION_HTTPS_ALIASES:
+        *
+        * Alias for the #SoupSession:https-aliases property, qv.
+        *
+        * Since: 2.38
+        **/
+       g_object_class_install_property (
+               object_class, PROP_HTTPS_ALIASES,
+               g_param_spec_boxed (SOUP_SESSION_HTTPS_ALIASES,
+                                   "https aliases",
+                                   "URI schemes that are considered aliases for 'https'",
+                                   G_TYPE_STRV,
+                                   G_PARAM_READWRITE));
 
-       /* g_signal_handlers_disconnect_by_func doesn't work if you
-        * have a metamarshal, meaning it doesn't work with
-        * soup_message_add_header_handler()
+       /**
+        * SOUP_SESSION_LOCAL_ADDRESS:
+        *
+        * Alias for the #SoupSession:local-address property, qv.
+        *
+        * Since: 2.42
+        **/
+       /**
+        * SoupSession:local-address:
+        *
+        * Sets the #SoupAddress to use for the client side of
+        * the connection.
+        *
+        * Use this property if you want for instance to bind the
+        * local socket to a specific IP address.
+        *
+        * Since: 2.42
+        **/
+       g_object_class_install_property (
+               object_class, PROP_LOCAL_ADDRESS,
+               g_param_spec_object (SOUP_SESSION_LOCAL_ADDRESS,
+                                    "Local address",
+                                    "Address of local end of socket",
+                                    SOUP_TYPE_ADDRESS,
+                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+#if ENABLE(TIZEN_CERTIFICATE_FILE_SET)
+       /**
+        * SOUP_SESSION_CERTIFICATE_PATH
+        * SoupSession:certificate-path:
+        *
+        * Set the certificate path for soup session
+        *
         */
-       g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
-                                             0, 0, NULL, NULL, item);
-       g_signal_emit (session, signals[REQUEST_UNQUEUED], 0, item->msg);
-       soup_message_queue_item_unref (item);
-}
-
-void
-soup_session_set_item_status (SoupSession          *session,
-                             SoupMessageQueueItem *item,
-                             guint                 status_code)
-{
-       SoupURI *uri;
-       char *msg;
+       g_object_class_install_property (
+               object_class, PROP_CERTIFICATE_PATH,
+               g_param_spec_string (SOUP_SESSION_CERTIFICATE_PATH,
+                                    "certificate file path",
+                                    "Set the ca-certificate.crt file path",
+                                    NULL,
+                                    G_PARAM_READWRITE));
+#endif
 
-       switch (status_code) {
-       case SOUP_STATUS_CANT_RESOLVE:
-       case SOUP_STATUS_CANT_CONNECT:
-               uri = soup_message_get_uri (item->msg);
-               msg = g_strdup_printf ("%s (%s)",
-                                      soup_status_get_phrase (status_code),
-                                      uri->host);
-               soup_message_set_status_full (item->msg, status_code, msg);
-               g_free (msg);
-               break;
+#if ENABLE(TIZEN_TV_CLIENT_CERTIFICATE)
+       /**
+       * SOUP_SESSION_USE_WIDGETENGINE:
+       *
+       * Alias for the #SoupSession:widget-engine property.
+       *
+       **/
 
-       case SOUP_STATUS_CANT_RESOLVE_PROXY:
-       case SOUP_STATUS_CANT_CONNECT_PROXY:
-               if (item->proxy_uri && item->proxy_uri->host) {
-                       msg = g_strdup_printf ("%s (%s)",
-                                              soup_status_get_phrase (status_code),
-                                              item->proxy_uri->host);
-                       soup_message_set_status_full (item->msg, status_code, msg);
-                       g_free (msg);
-                       break;
-               }
-               soup_message_set_status (item->msg, status_code);
-               break;
+       g_object_class_install_property (
+               object_class, PROP_WIDGET_ENGINE,
+               g_param_spec_boolean (SOUP_SESSION_WIDGET_ENGINE,
+                               "widget engine",
+                               "Whether or not to be running Widget Engine",
+                               FALSE,
+                               G_PARAM_READWRITE));
 
-       case SOUP_STATUS_SSL_FAILED:
-               if (!g_tls_backend_supports_tls (g_tls_backend_get_default ())) {
-                       soup_message_set_status_full (item->msg, status_code,
-                                                     "TLS/SSL support not available; install glib-networking");
-               } else
-                       soup_message_set_status (item->msg, status_code);
-               break;
 
-       default:
-               soup_message_set_status (item->msg, status_code);
-               break;
-       }
+#endif
 }
 
-static void
-queue_message (SoupSession *session, SoupMessage *msg,
-              SoupSessionCallback callback, gpointer user_data)
-{
-       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       SoupMessageQueueItem *item;
-       SoupSessionHost *host;
-
-       item = soup_message_queue_append (priv->queue, msg, callback, user_data);
-
-       g_mutex_lock (&priv->host_lock);
-       host = get_host_for_message (session, item->msg);
-       host->num_messages++;
-       g_mutex_unlock (&priv->host_lock);
 
-       if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
-               soup_message_add_header_handler (
-                       msg, "got_body", "Location",
-                       G_CALLBACK (redirect_handler), item);
+static gboolean
+expected_to_be_requeued (SoupSession *session, SoupMessage *msg)
+{
+       if (msg->status_code == SOUP_STATUS_UNAUTHORIZED ||
+           msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED) {
+               SoupSessionFeature *feature =
+                       soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
+               return !feature || !soup_message_disables_feature (msg, feature);
        }
 
-       g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
-}
-
-/**
- * SoupSessionCallback:
- * @session: the session
- * @msg: the message that has finished
- * @user_data: the data passed to soup_session_queue_message
- *
- * Prototype for the callback passed to soup_session_queue_message(),
- * qv.
- **/
-
-/**
- * soup_session_queue_message:
- * @session: a #SoupSession
- * @msg: (transfer full): the message to queue
- * @callback: (allow-none) (scope async): a #SoupSessionCallback which will
- * be called after the message completes or when an unrecoverable error occurs.
- * @user_data: (allow-none): a pointer passed to @callback.
- * 
- * Queues the message @msg for sending. All messages are processed
- * while the glib main loop runs. If @msg has been processed before,
- * any resources related to the time it was last sent are freed.
- *
- * Upon message completion, the callback specified in @callback will
- * be invoked (in the thread associated with @session's async
- * context). If after returning from this callback the message has not
- * been requeued, @msg will be unreffed.
- */
-void
-soup_session_queue_message (SoupSession *session, SoupMessage *msg,
-                           SoupSessionCallback callback, gpointer user_data)
-{
-       g_return_if_fail (SOUP_IS_SESSION (session));
-       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+       if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT))
+               return soup_session_would_redirect (session, msg);
 
-       SOUP_SESSION_GET_CLASS (session)->queue_message (session, msg,
-                                                        callback, user_data);
+       return FALSE;
 }
 
+/* send_request_async */
+
 static void
-requeue_message (SoupSession *session, SoupMessage *msg)
+async_send_request_return_result (SoupMessageQueueItem *item,
+                                 gpointer stream, GError *error)
 {
-       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       SoupMessageQueueItem *item;
+       GTask *task;
 
-       item = soup_message_queue_lookup (priv->queue, msg);
-       g_return_if_fail (item != NULL);
-       item->state = SOUP_MESSAGE_RESTARTING;
-       soup_message_queue_item_unref (item);
-}
-
-/**
- * soup_session_requeue_message:
- * @session: a #SoupSession
- * @msg: the message to requeue
- *
- * This causes @msg to be placed back on the queue to be attempted
- * again.
- **/
-void
-soup_session_requeue_message (SoupSession *session, SoupMessage *msg)
-{
-       g_return_if_fail (SOUP_IS_SESSION (session));
-       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+       g_return_if_fail (item->task != NULL);
 
-       SOUP_SESSION_GET_CLASS (session)->requeue_message (session, msg);
-}
+       g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
+                                             0, 0, NULL, NULL, item);
 
+       task = item->task;
+       item->task = NULL;
 
-/**
- * soup_session_send_message:
- * @session: a #SoupSession
- * @msg: the message to send
- * 
- * Synchronously send @msg. This call will not return until the
- * transfer is finished successfully or there is an unrecoverable
- * error.
- *
- * @msg is not freed upon return.
- *
- * Return value: the HTTP status code of the response
- */
-guint
-soup_session_send_message (SoupSession *session, SoupMessage *msg)
-{
-       g_return_val_if_fail (SOUP_IS_SESSION (session), SOUP_STATUS_MALFORMED);
-       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_STATUS_MALFORMED);
+       if (item->io_source) {
+               g_source_destroy (item->io_source);
+               g_clear_pointer (&item->io_source, g_source_unref);
+       }
 
-       return SOUP_SESSION_GET_CLASS (session)->send_message (session, msg);
+       if (error)
+               g_task_return_error (task, error);
+       else if (item->error) {
+               if (stream)
+                       g_object_unref (stream);
+               g_task_return_error (task, g_error_copy (item->error));
+       } else if (SOUP_STATUS_IS_TRANSPORT_ERROR (item->msg->status_code)) {
+               if (stream)
+                       g_object_unref (stream);
+               g_task_return_new_error (task, SOUP_HTTP_ERROR,
+                                        item->msg->status_code,
+                                        "%s",
+                                        item->msg->reason_phrase);
+       } else
+               g_task_return_pointer (task, stream, g_object_unref);
+       g_object_unref (task);
 }
 
-
-/**
- * soup_session_pause_message:
- * @session: a #SoupSession
- * @msg: a #SoupMessage currently running on @session
- *
- * Pauses HTTP I/O on @msg. Call soup_session_unpause_message() to
- * resume I/O.
- **/
-void
-soup_session_pause_message (SoupSession *session,
-                           SoupMessage *msg)
+static void
+async_send_request_restarted (SoupMessage *msg, gpointer user_data)
 {
-       SoupSessionPrivate *priv;
-       SoupMessageQueueItem *item;
-
-       g_return_if_fail (SOUP_IS_SESSION (session));
-       g_return_if_fail (SOUP_IS_MESSAGE (msg));
-
-       priv = SOUP_SESSION_GET_PRIVATE (session);
-       item = soup_message_queue_lookup (priv->queue, msg);
-       g_return_if_fail (item != NULL);
+       SoupMessageQueueItem *item = user_data;
 
-       item->paused = TRUE;
-       if (item->state == SOUP_MESSAGE_RUNNING)
-               soup_message_io_pause (msg);
-       soup_message_queue_item_unref (item);
+       /* We won't be needing this, then. */
+       g_object_set_data (G_OBJECT (item->msg), "SoupSession:ostream", NULL);
+       item->io_started = FALSE;
 }
 
-/**
- * soup_session_unpause_message:
- * @session: a #SoupSession
- * @msg: a #SoupMessage currently running on @session
- *
- * Resumes HTTP I/O on @msg. Use this to resume after calling
- * soup_session_pause_message().
- *
- * If @msg is being sent via blocking I/O, this will resume reading or
- * writing immediately. If @msg is using non-blocking I/O, then
- * reading or writing won't resume until you return to the main loop.
- **/
-void
-soup_session_unpause_message (SoupSession *session,
-                             SoupMessage *msg)
+static void
+async_send_request_finished (SoupMessage *msg, gpointer user_data)
 {
-       SoupSessionPrivate *priv;
-       SoupMessageQueueItem *item;
+       SoupMessageQueueItem *item = user_data;
+       GMemoryOutputStream *mostream;
+       GInputStream *istream = NULL;
+       GError *error = NULL;
 
-       g_return_if_fail (SOUP_IS_SESSION (session));
-       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+       if (!item->task) {
+               /* Something else already took care of it. */
+               return;
+       }
 
-       priv = SOUP_SESSION_GET_PRIVATE (session);
-       item = soup_message_queue_lookup (priv->queue, msg);
-       g_return_if_fail (item != NULL);
+       mostream = g_object_get_data (G_OBJECT (item->task), "SoupSession:ostream");
+       if (mostream) {
+               gpointer data;
+               gssize size;
 
-       item->paused = FALSE;
-       if (item->state == SOUP_MESSAGE_RUNNING)
-               soup_message_io_unpause (msg);
-       soup_message_queue_item_unref (item);
+               /* We thought it would be requeued, but it wasn't, so
+                * return the original body.
+                */
+               size = g_memory_output_stream_get_data_size (mostream);
+               data = size ? g_memory_output_stream_steal_data (mostream) : g_strdup ("");
+               istream = g_memory_input_stream_new_from_data (data, size, g_free);
+       } else if (item->io_started) {
+               /* The message finished before becoming readable. This
+                * will happen, eg, if it's cancelled from got-headers.
+                * Do nothing; the op will complete via read_ready_cb()
+                * after we return;
+                */
+               return;
+       } else {
+               /* The message finished before even being started;
+                * probably a tunnel connect failure.
+                */
+               istream = g_memory_input_stream_new ();
+       }
 
-       SOUP_SESSION_GET_CLASS (session)->kick (session);
+       async_send_request_return_result (item, istream, error);
 }
 
-
 static void
-cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
-{
-       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       SoupMessageQueueItem *item;
-
-       item = soup_message_queue_lookup (priv->queue, msg);
-       g_return_if_fail (item != NULL);
-
-       item->paused = FALSE;
-       soup_message_set_status (msg, status_code);
-       g_cancellable_cancel (item->cancellable);
-
-       soup_message_queue_item_unref (item);
-}
-
-/**
- * soup_session_cancel_message:
- * @session: a #SoupSession
- * @msg: the message to cancel
- * @status_code: status code to set on @msg (generally
- * %SOUP_STATUS_CANCELLED)
- *
- * Causes @session to immediately finish processing @msg (regardless
- * of its current state) with a final status_code of @status_code. You
- * may call this at any time after handing @msg off to @session; if
- * @session has started sending the request but has not yet received
- * the complete response, then it will close the request's connection.
- * Note that with non-idempotent requests (eg,
- * <literal>POST</literal>, <literal>PUT</literal>,
- * <literal>DELETE</literal>) it is possible that you might cancel the
- * request after the server acts on it, but before it returns a
- * response, leaving the remote resource in an unknown state.
- *
- * If the message is cancelled while its response body is being read,
- * then the response body in @msg will be left partially-filled-in.
- * The response headers, on the other hand, will always be either
- * empty or complete.
- *
- * For messages queued with soup_session_queue_message() (and
- * cancelled from the same thread), the callback will be invoked
- * before soup_session_cancel_message() returns.
- **/
-void
-soup_session_cancel_message (SoupSession *session, SoupMessage *msg,
-                            guint status_code)
+send_async_spliced (GObject *source, GAsyncResult *result, gpointer user_data)
 {
-       SoupSessionPrivate *priv;
-       SoupMessageQueueItem *item;
-
-       g_return_if_fail (SOUP_IS_SESSION (session));
-       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+       SoupMessageQueueItem *item = user_data;
+       GInputStream *istream = g_object_get_data (source, "istream");
+       GError *error = NULL;
 
-       priv = SOUP_SESSION_GET_PRIVATE (session);
-       item = soup_message_queue_lookup (priv->queue, msg);
-       /* If the message is already ending, don't do anything */
-       if (!item)
+       /* It should be safe to call the sync close() method here since
+        * the message body has already been written.
+        */
+       g_input_stream_close (istream, NULL, NULL);
+       g_object_unref (istream);
+
+       /* If the message was cancelled, it will be completed via other means */
+       if (g_cancellable_is_cancelled (item->cancellable) ||
+           !item->task) {
+               soup_message_queue_item_unref (item);
                return;
-       if (item->state == SOUP_MESSAGE_FINISHED) {
+       }
+
+       if (g_output_stream_splice_finish (G_OUTPUT_STREAM (source),
+                                          result, &error) == -1) {
+               async_send_request_return_result (item, NULL, error);
                soup_message_queue_item_unref (item);
                return;
        }
 
-       SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg, status_code);
+       /* Otherwise either restarted or finished will eventually be called. */
+       soup_session_kick_queue (item->session);
        soup_message_queue_item_unref (item);
 }
 
 static void
-flush_queue (SoupSession *session)
+send_async_maybe_complete (SoupMessageQueueItem *item,
+                          GInputStream         *stream)
 {
-       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-       SoupMessageQueueItem *item;
+       if (expected_to_be_requeued (item->session, item->msg)) {
+               GOutputStream *ostream;
 
-       for (item = soup_message_queue_first (priv->queue);
-            item;
-            item = soup_message_queue_next (priv->queue, item)) {
-               soup_session_cancel_message (session, item->msg,
-                                            SOUP_STATUS_CANCELLED);
+               /* Gather the current message body... */
+               ostream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
+               g_object_set_data_full (G_OBJECT (item->task), "SoupSession:ostream",
+                                       ostream, g_object_unref);
+
+               g_object_set_data (G_OBJECT (ostream), "istream", stream);
+
+               /* Give the splice op its own ref on item */
+               soup_message_queue_item_ref (item);
+               /* We don't use CLOSE_SOURCE because we need to control when the
+                * side effects of closing the SoupClientInputStream happen.
+                */
+               g_output_stream_splice_async (ostream, stream,
+                                             G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+                                             G_PRIORITY_DEFAULT,
+                                             item->cancellable,
+                                             send_async_spliced, item);
+               return;
        }
+
+       async_send_request_return_result (item, stream, NULL);
 }
 
-/**
- * soup_session_abort:
- * @session: the session
- *
- * Cancels all pending requests in @session.
- **/
-void
-soup_session_abort (SoupSession *session)
+static void try_run_until_read (SoupMessageQueueItem *item);
+
+static gboolean
+read_ready_cb (SoupMessage *msg, gpointer user_data)
 {
-       SoupSessionPrivate *priv;
-       GSList *conns, *c;
-       GHashTableIter iter;
-       gpointer conn, host;
+       SoupMessageQueueItem *item = user_data;
 
-       g_return_if_fail (SOUP_IS_SESSION (session));
-       priv = SOUP_SESSION_GET_PRIVATE (session);
+       g_clear_pointer (&item->io_source, g_source_unref);
+       try_run_until_read (item);
+       return FALSE;
+}
 
-       SOUP_SESSION_GET_CLASS (session)->flush_queue (session);
+static void
+try_run_until_read (SoupMessageQueueItem *item)
+{
+       GError *error = NULL;
+       GInputStream *stream = NULL;
 
-       /* Close all connections */
-       g_mutex_lock (&priv->host_lock);
-       conns = NULL;
-       g_hash_table_iter_init (&iter, priv->conns);
-       while (g_hash_table_iter_next (&iter, &conn, &host))
-               conns = g_slist_prepend (conns, g_object_ref (conn));
-       g_mutex_unlock (&priv->host_lock);
+       if (soup_message_io_run_until_read (item->msg, FALSE, item->cancellable, &error))
+               stream = soup_message_io_get_response_istream (item->msg, &error);
+       if (stream) {
+               send_async_maybe_complete (item, stream);
+               return;
+       }
 
-       for (c = conns; c; c = c->next) {
-               soup_connection_disconnect (c->data);
-               g_object_unref (c->data);
+       if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) {
+               item->state = SOUP_MESSAGE_RESTARTING;
+               soup_message_io_finished (item->msg);
+               g_error_free (error);
+               return;
        }
 
-       g_slist_free (conns);
+       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
+               if (item->state != SOUP_MESSAGE_FINISHED) {
+                       if (soup_message_io_in_progress (item->msg))
+                               soup_message_io_finished (item->msg);
+                       item->state = SOUP_MESSAGE_FINISHING;
+                       soup_session_process_queue_item (item->session, item, NULL, FALSE);
+               }
+               async_send_request_return_result (item, NULL, error);
+               return;
+       }
+
+       g_clear_error (&error);
+       item->io_source = soup_message_io_get_source (item->msg, item->cancellable,
+                                                     read_ready_cb, item);
+       g_source_attach (item->io_source, soup_session_get_async_context (item->session));
 }
 
 static void
-prefetch_uri (SoupSession *session, SoupURI *uri,
-             GCancellable *cancellable,
-             SoupAddressCallback callback, gpointer user_data)
+async_send_request_running (SoupSession *session, SoupMessageQueueItem *item)
 {
-       SoupSessionPrivate *priv;
-       SoupSessionHost *host;
-       SoupAddress *addr;
+       item->io_started = TRUE;
+       try_run_until_read (item);
+}
 
-       priv = SOUP_SESSION_GET_PRIVATE (session);
+static void
+async_return_from_cache (SoupMessageQueueItem *item,
+                        GInputStream         *stream)
+{
+       const char *content_type;
+       GHashTable *params = NULL;
 
-       g_mutex_lock (&priv->host_lock);
-       host = get_host_for_uri (session, uri);
-       addr = g_object_ref (host->addr);
-       g_mutex_unlock (&priv->host_lock);
+       soup_message_got_headers (item->msg);
 
-       soup_address_resolve_async (addr,
-                                   soup_session_get_async_context (session),
-                                   cancellable, callback, user_data);
-       g_object_unref (addr);
+       content_type = soup_message_headers_get_content_type (item->msg->response_headers, &params);
+       if (content_type) {
+               soup_message_content_sniffed (item->msg, content_type, params);
+               g_hash_table_unref (params);
+       }
+
+       item->state = SOUP_MESSAGE_FINISHING;
+       async_send_request_return_result (item, g_object_ref (stream), NULL);
 }
 
-/**
-* soup_session_prepare_for_uri:
-* @session: a #SoupSession
-* @uri: a #SoupURI which may be required
-*
-* Tells @session that @uri may be requested shortly, and so the
-* session can try to prepare (resolving the domain name, obtaining
-* proxy address, etc.) in order to work more quickly once the URI is
-* actually requested.
-*
-* This method acts asynchronously, in @session's
-* #SoupSession:async_context. If you are using #SoupSessionSync and do
-* not have a main loop running, then you can't use this method.
-*
-* Since: 2.30
-*
-* Deprecated: 2.38: use soup_session_prefetch_dns() instead
-**/
-void
-soup_session_prepare_for_uri (SoupSession *session, SoupURI *uri)
+typedef struct {
+       SoupCache *cache;
+       SoupMessage *conditional_msg;
+} AsyncCacheCancelData;
+
+
+static void
+free_async_cache_cancel_data (AsyncCacheCancelData *data)
 {
-       g_return_if_fail (SOUP_IS_SESSION (session));
-       g_return_if_fail (uri != NULL);
+       g_object_unref (data->conditional_msg);
+       g_object_unref (data->cache);
+       g_slice_free (AsyncCacheCancelData, data);
+}
 
-       if (!uri->host)
+static void
+cancel_cache_response (SoupMessageQueueItem *item)
+{
+       item->paused = FALSE;
+       item->state = SOUP_MESSAGE_FINISHING;
+       soup_message_set_status (item->msg, SOUP_STATUS_CANCELLED);
+       soup_session_kick_queue (item->session);
+}
+
+static void
+conditional_request_cancelled_cb (GCancellable *cancellable, AsyncCacheCancelData *data)
+{
+       soup_cache_cancel_conditional_request (data->cache, data->conditional_msg);
+}
+
+static void
+conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+       SoupMessageQueueItem *item = user_data;
+       GInputStream *stream;
+       SoupCache *cache;
+
+       if (g_cancellable_is_cancelled (item->cancellable)) {
+               cancel_cache_response (item);
                return;
+       } else {
+               gulong handler_id = GPOINTER_TO_SIZE (g_object_get_data (G_OBJECT (msg), "SoupSession:handler-id"));
+               g_cancellable_disconnect (item->cancellable, handler_id);
+       }
 
-       prefetch_uri (session, uri, NULL, NULL, NULL);
+       cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
+       soup_cache_update_from_conditional_request (cache, msg);
+
+       if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) {
+               stream = soup_cache_send_response (cache, item->msg);
+               if (stream) {
+                       async_return_from_cache (item, stream);
+                       g_object_unref (stream);
+                       return;
+               }
+       }
+
+       /* The resource was modified or the server returned a 200
+        * OK. Either way we reload it. FIXME.
+        */
+       item->state = SOUP_MESSAGE_STARTING;
+       soup_session_kick_queue (session);
+}
+
+static gboolean
+idle_return_from_cache_cb (gpointer data)
+{
+       GTask *task = data;
+       SoupMessageQueueItem *item = g_task_get_task_data (task);
+       GInputStream *istream;
+
+       if (item->state == SOUP_MESSAGE_FINISHED) {
+               /* The original request was cancelled using
+                * soup_session_cancel_message () so it has been
+                * already handled by the cancellation code path.
+                */
+               return FALSE;
+       } else if (g_cancellable_is_cancelled (item->cancellable)) {
+               /* Cancel original msg after g_cancellable_cancel(). */
+               cancel_cache_response (item);
+               return FALSE;
+       }
+
+       istream = g_object_get_data (G_OBJECT (task), "SoupSession:istream");
+       async_return_from_cache (item, istream);
+
+       return FALSE;
+}
+
+
+static gboolean
+async_respond_from_cache (SoupSession          *session,
+                         SoupMessageQueueItem *item)
+{
+       SoupCache *cache;
+       SoupCacheResponse response;
+
+       cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
+       if (!cache)
+               return FALSE;
+
+       response = soup_cache_has_response (cache, item->msg);
+       if (response == SOUP_CACHE_RESPONSE_FRESH) {
+               GInputStream *stream;
+               GSource *source;
+
+#if ENABLE(TIZEN_PERFORMANCE_TEST_LOG)
+               prctl_with_url_and_free("[EVT] soup_use_cache1 (file cache) : ", soup_uri_to_string(soup_message_get_uri(item->msg), TRUE));
+#endif
+               stream = soup_cache_send_response (cache, item->msg);
+               if (!stream) {
+                       /* Cached file was deleted? */
+                       return FALSE;
+               }
+               g_object_set_data_full (G_OBJECT (item->task), "SoupSession:istream",
+                                       stream, g_object_unref);
+
+               source = g_timeout_source_new (0);
+               g_task_attach_source (item->task, source,
+                                     (GSourceFunc) idle_return_from_cache_cb);
+               g_source_unref (source);
+               return TRUE;
+       } else if (response == SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) {
+               SoupMessage *conditional_msg;
+               AsyncCacheCancelData *data;
+               gulong handler_id;
+
+#if ENABLE(TIZEN_PERFORMANCE_TEST_LOG)
+               prctl_with_url_and_free("[EVT] soup_use_cache2-1 (conditional) : ", soup_uri_to_string(soup_message_get_uri(item->msg), TRUE));
+#endif
+               conditional_msg = soup_cache_generate_conditional_request (cache, item->msg);
+               if (!conditional_msg)
+                       return FALSE;
+
+               /* Detect any quick cancellation before the cache is able to return data. */
+               data = g_slice_new0 (AsyncCacheCancelData);
+               data->cache = g_object_ref (cache);
+               data->conditional_msg = g_object_ref (conditional_msg);
+               handler_id = g_cancellable_connect (item->cancellable, G_CALLBACK (conditional_request_cancelled_cb),
+                                                   data, (GDestroyNotify) free_async_cache_cancel_data);
+
+               g_object_set_data (G_OBJECT (conditional_msg), "SoupSession:handler-id",
+                                  GSIZE_TO_POINTER (handler_id));
+               soup_session_queue_message (session, conditional_msg,
+                                           conditional_get_ready_cb,
+                                           item);
+
+
+               return TRUE;
+       } else {
+#if ENABLE(TIZEN_PERFORMANCE_TEST_LOG)
+               prctl_with_url_and_free("[EVT] soup_cache_stale : ", soup_uri_to_string(soup_message_get_uri(item->msg), TRUE));
+#endif
+               return FALSE;
+       }
 }
 
 /**
-* soup_session_prefetch_dns:
-* @session: a #SoupSession
-* @hostname: a hostname to be resolved
-* @cancellable: (allow-none): a #GCancellable object, or %NULL
-* @callback: (scope async) (allow-none): callback to call with the
-*     result, or %NULL
-* @user_data: data for @callback
-*
-* Tells @session that an URI from the given @hostname may be requested
-* shortly, and so the session can try to prepare by resolving the
-* domain name in advance, in order to work more quickly once the URI
-* is actually requested.
-*
-* If @cancellable is non-%NULL, it can be used to cancel the
-* resolution. @callback will still be invoked in this case, with a
-* status of %SOUP_STATUS_CANCELLED.
-*
-* This method acts asynchronously, in @session's
-* #SoupSession:async_context. If you are using #SoupSessionSync and do
-* not have a main loop running, then you can't use this method.
-*
-* Since: 2.38
-**/
+ * soup_session_send_async:
+ * @session: a #SoupSession
+ * @msg: a #SoupMessage
+ * @cancellable: a #GCancellable
+ * @callback: the callback to invoke
+ * @user_data: data for @callback
+ *
+ * Asynchronously sends @msg and waits for the beginning of a
+ * response. When @callback is called, then either @msg has been sent,
+ * and its response headers received, or else an error has occurred.
+ * Call soup_session_send_finish() to get a #GInputStream for reading
+ * the response body.
+ *
+ * See soup_session_send() for more details on the general semantics.
+ *
+ * Contrast this method with soup_session_queue_message(), which also
+ * asynchronously sends a #SoupMessage, but doesn't invoke its
+ * callback until the response has been completely read.
+ *
+ * (Note that this method cannot be called on the deprecated
+ * #SoupSessionSync subclass, and can only be called on
+ * #SoupSessionAsync if you have set the
+ * #SoupSession:use-thread-context property.)
+ *
+ * Since: 2.42
+ */
 void
-soup_session_prefetch_dns (SoupSession *session, const char *hostname,
-                          GCancellable *cancellable,
-                          SoupAddressCallback callback, gpointer user_data)
+soup_session_send_async (SoupSession         *session,
+                        SoupMessage         *msg,
+                        GCancellable        *cancellable,
+                        GAsyncReadyCallback  callback,
+                        gpointer             user_data)
 {
-       SoupURI *uri;
+       SoupMessageQueueItem *item;
+       gboolean use_thread_context;
+#if ENABLE(TIZEN_TV_IMMEDIATE_REQUESTING)
+       gboolean should_prune = FALSE;
+#endif
 
        g_return_if_fail (SOUP_IS_SESSION (session));
-       g_return_if_fail (hostname != NULL);
+       g_return_if_fail (!SOUP_IS_SESSION_SYNC (session));
+
+       g_object_get (G_OBJECT (session),
+                     SOUP_SESSION_USE_THREAD_CONTEXT, &use_thread_context,
+                     NULL);
+       g_return_if_fail (use_thread_context);
+
+       item = soup_session_append_queue_item (session, msg, TRUE, TRUE,
+                                              NULL, NULL);
+       g_signal_connect (msg, "restarted",
+                         G_CALLBACK (async_send_request_restarted), item);
+       g_signal_connect (msg, "finished",
+                         G_CALLBACK (async_send_request_finished), item);
+
+       if (cancellable) {
+               g_object_unref (item->cancellable);
+               item->cancellable = g_object_ref (cancellable);
+       }
 
-       /* FIXME: Prefetching should work for both HTTP and HTTPS */
-       uri = soup_uri_new (NULL);
-       soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP);
-       soup_uri_set_host (uri, hostname);
-       soup_uri_set_path (uri, "");
+       item->new_api = TRUE;
+       item->task = g_task_new (session, item->cancellable, callback, user_data);
+       g_task_set_task_data (item->task, item, (GDestroyNotify) soup_message_queue_item_unref);
 
-       prefetch_uri (session, uri, cancellable, callback, user_data);
-       soup_uri_free (uri);
+       /* Do not check for cancellations as we do not want to
+        * overwrite custom error messages set during cancellations
+        * (for example SOUP_HTTP_ERROR is set for cancelled messages
+        * in async_send_request_return_result() (status_code==1
+        * means CANCEL and is considered a TRANSPORT_ERROR)).
+        */
+       g_task_set_check_cancellable (item->task, FALSE);
+
+       if (async_respond_from_cache (session, item))
+               item->state = SOUP_MESSAGE_CACHED;
+       else
+#if ENABLE(TIZEN_TV_IMMEDIATE_REQUESTING)
+       {
+               /* CONNECT messages are handled specially */
+               if (item->msg->method != SOUP_METHOD_CONNECT) {
+                       soup_session_process_queue_item (session, item, &should_prune, TRUE);
+               }
+#endif
+               soup_session_kick_queue (session);
+#if ENABLE(TIZEN_TV_IMMEDIATE_REQUESTING)
+       }
+#endif
 }
 
 /**
- * soup_session_add_feature:
+ * soup_session_send_finish:
  * @session: a #SoupSession
- * @feature: an object that implements #SoupSessionFeature
+ * @result: the #GAsyncResult passed to your callback
+ * @error: return location for a #GError, or %NULL
  *
- * Adds @feature's functionality to @session. You can also add a
- * feature to the session at construct time by using the
- * %SOUP_SESSION_ADD_FEATURE property.
+ * Gets the response to a soup_session_send_async() call and (if
+ * successful), returns a #GInputStream that can be used to read the
+ * response body.
  *
- * Since: 2.24
- **/
-void
-soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature)
+ * Return value: (transfer full): a #GInputStream for reading the
+ *   response body, or %NULL on error.
+ *
+ * Since: 2.42
+ */
+GInputStream *
+soup_session_send_finish (SoupSession   *session,
+                         GAsyncResult  *result,
+                         GError       **error)
 {
-       SoupSessionPrivate *priv;
+       GTask *task;
 
-       g_return_if_fail (SOUP_IS_SESSION (session));
-       g_return_if_fail (SOUP_IS_SESSION_FEATURE (feature));
+       g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
+       g_return_val_if_fail (!SOUP_IS_SESSION_SYNC (session), NULL);
+       g_return_val_if_fail (g_task_is_valid (result, session), NULL);
 
-       priv = SOUP_SESSION_GET_PRIVATE (session);
-       priv->features = g_slist_prepend (priv->features, g_object_ref (feature));
-       g_hash_table_remove_all (priv->features_cache);
-       soup_session_feature_attach (feature, session);
+       task = G_TASK (result);
+       if (g_task_had_error (task)) {
+               SoupMessageQueueItem *item = g_task_get_task_data (task);
+
+               if (soup_message_io_in_progress (item->msg))
+                       soup_message_io_finished (item->msg);
+               else if (item->state != SOUP_MESSAGE_FINISHED)
+                       item->state = SOUP_MESSAGE_FINISHING;
+
+               if (item->state != SOUP_MESSAGE_FINISHED)
+                       soup_session_process_queue_item (session, item, NULL, FALSE);
+       }
+
+       return g_task_propagate_pointer (task, error);
 }
 
 /**
- * soup_session_add_feature_by_type:
+ * soup_session_send:
  * @session: a #SoupSession
- * @feature_type: a #GType
+ * @msg: a #SoupMessage
+ * @cancellable: a #GCancellable
+ * @error: return location for a #GError, or %NULL
  *
- * If @feature_type is the type of a class that implements
- * #SoupSessionFeature, this creates a new feature of that type and
- * adds it to @session as with soup_session_add_feature(). You can use
- * this when you don't need to customize the new feature in any way.
+ * Synchronously sends @msg and waits for the beginning of a response.
+ * On success, a #GInputStream will be returned which you can use to
+ * read the response body. ("Success" here means only that an HTTP
+ * response was received and understood; it does not necessarily mean
+ * that a 2xx class status code was received.)
  *
- * If @feature_type is not a #SoupSessionFeature type, this gives
- * each existing feature on @session the chance to accept @feature_type
- * as a "subfeature". This can be used to add new #SoupAuth types,
- * for instance.
+ * If non-%NULL, @cancellable can be used to cancel the request;
+ * soup_session_send() will return a %G_IO_ERROR_CANCELLED error. Note
+ * that with requests that have side effects (eg,
+ * <literal>POST</literal>, <literal>PUT</literal>,
+ * <literal>DELETE</literal>) it is possible that you might cancel the
+ * request after the server acts on it, but before it returns a
+ * response, leaving the remote resource in an unknown state.
  *
- * You can also add a feature to the session at construct time by
- * using the %SOUP_SESSION_ADD_FEATURE_BY_TYPE property.
+ * If @msg is requeued due to a redirect or authentication, the
+ * initial (3xx/401/407) response body will be suppressed, and
+ * soup_session_send() will only return once a final response has been
+ * received.
  *
- * Since: 2.24
- **/
-void
-soup_session_add_feature_by_type (SoupSession *session, GType feature_type)
+ * Contrast this method with soup_session_send_message(), which also
+ * synchronously sends a #SoupMessage, but doesn't return until the
+ * response has been completely read.
+ *
+ * (Note that this method cannot be called on the deprecated
+ * #SoupSessionAsync subclass.)
+ *
+ * Return value: (transfer full): a #GInputStream for reading the
+ *   response body, or %NULL on error.
+ *
+ * Since: 2.42
+ */
+GInputStream *
+soup_session_send (SoupSession   *session,
+                  SoupMessage   *msg,
+                  GCancellable  *cancellable,
+                  GError       **error)
 {
-       g_return_if_fail (SOUP_IS_SESSION (session));
+       SoupMessageQueueItem *item;
+       GInputStream *stream = NULL;
+       GOutputStream *ostream;
+       GMemoryOutputStream *mostream;
+       gssize size;
+       GError *my_error = NULL;
+
+       g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
+       g_return_val_if_fail (!SOUP_IS_SESSION_ASYNC (session), NULL);
+
+       item = soup_session_append_queue_item (session, msg, FALSE, TRUE,
+                                              NULL, NULL);
+
+       item->new_api = TRUE;
+       if (cancellable) {
+               g_object_unref (item->cancellable);
+               item->cancellable = g_object_ref (cancellable);
+       }
+
+       while (!stream) {
+               /* Get a connection, etc */
+               soup_session_process_queue_item (session, item, NULL, TRUE);
+               if (item->state != SOUP_MESSAGE_RUNNING)
+                       break;
+
+               /* Send request, read headers */
+               if (!soup_message_io_run_until_read (msg, TRUE, item->cancellable, &my_error)) {
+                       if (g_error_matches (my_error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) {
+                               item->state = SOUP_MESSAGE_RESTARTING;
+                               soup_message_io_finished (item->msg);
+                               g_clear_error (&my_error);
+                               continue;
+                       } else
+                               break;
+               }
+
+               stream = soup_message_io_get_response_istream (msg, &my_error);
+               if (!stream)
+                       break;
 
-       if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
-               SoupSessionFeature *feature;
+               if (!expected_to_be_requeued (session, msg))
+                       break;
 
-               feature = g_object_new (feature_type, NULL);
-               soup_session_add_feature (session, feature);
-               g_object_unref (feature);
-       } else {
-               SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-               GSList *f;
+               /* Gather the current message body... */
+               ostream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
+               if (g_output_stream_splice (ostream, stream,
+                                           G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
+                                           G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+                                           item->cancellable, &my_error) == -1) {
+                       g_object_unref (stream);
+                       g_object_unref (ostream);
+                       stream = NULL;
+                       break;
+               }
+               g_object_unref (stream);
+               stream = NULL;
 
-               for (f = priv->features; f; f = f->next) {
-                       if (soup_session_feature_add_feature (f->data, feature_type))
-                               return;
+               /* If the message was requeued, loop */
+               if (item->state == SOUP_MESSAGE_RESTARTING) {
+                       g_object_unref (ostream);
+                       continue;
                }
-               g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
+
+               /* Not requeued, so return the original body */
+               mostream = G_MEMORY_OUTPUT_STREAM (ostream);
+               size = g_memory_output_stream_get_data_size (mostream);
+               stream = g_memory_input_stream_new ();
+               if (size) {
+                       g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (stream),
+                                                       g_memory_output_stream_steal_data (mostream),
+                                                       size, g_free);
+               }
+               g_object_unref (ostream);
+       }
+
+       if (my_error)
+               g_propagate_error (error, my_error);
+       else if (item->error) {
+               g_clear_object (&stream);
+               if (error)
+                       *error = g_error_copy (item->error);
+       } else if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
+               g_clear_object (&stream);
+               g_set_error_literal (error, SOUP_HTTP_ERROR, msg->status_code,
+                                    msg->reason_phrase);
+       } else if (!stream)
+               stream = g_memory_input_stream_new ();
+
+       if (!stream) {
+               if (soup_message_io_in_progress (msg))
+                       soup_message_io_finished (msg);
+               else if (item->state != SOUP_MESSAGE_FINISHED)
+                       item->state = SOUP_MESSAGE_FINISHING;
+
+               if (item->state != SOUP_MESSAGE_FINISHED)
+                       soup_session_process_queue_item (session, item, NULL, TRUE);
        }
+
+       soup_message_queue_item_unref (item);
+       return stream;
 }
 
 /**
- * soup_session_remove_feature:
+ * soup_session_request:
  * @session: a #SoupSession
- * @feature: a feature that has previously been added to @session
+ * @uri_string: a URI, in string form
+ * @error: return location for a #GError, or %NULL
  *
- * Removes @feature's functionality from @session.
+ * Creates a #SoupRequest for retrieving @uri_string.
  *
- * Since: 2.24
- **/
-void
-soup_session_remove_feature (SoupSession *session, SoupSessionFeature *feature)
+ * Return value: (transfer full): a new #SoupRequest, or
+ *   %NULL on error.
+ *
+ * Since: 2.42
+ */
+SoupRequest *
+soup_session_request (SoupSession *session, const char *uri_string,
+                     GError **error)
 {
-       SoupSessionPrivate *priv;
-
-       g_return_if_fail (SOUP_IS_SESSION (session));
+       SoupURI *uri;
+       SoupRequest *req;
 
-       priv = SOUP_SESSION_GET_PRIVATE (session);
-       if (g_slist_find (priv->features, feature)) {
-               priv->features = g_slist_remove (priv->features, feature);
-               g_hash_table_remove_all (priv->features_cache);
-               soup_session_feature_detach (feature, session);
-               g_object_unref (feature);
+       uri = soup_uri_new (uri_string);
+       if (!uri) {
+               g_set_error (error, SOUP_REQUEST_ERROR,
+                            SOUP_REQUEST_ERROR_BAD_URI,
+                            _("Could not parse URI '%s'"), uri_string);
+               return NULL;
        }
+
+       req = soup_session_request_uri (session, uri, error);
+       soup_uri_free (uri);
+       return req;
 }
 
 /**
- * soup_session_remove_feature_by_type:
+ * soup_session_request_uri:
  * @session: a #SoupSession
- * @feature_type: a #GType
+ * @uri: a #SoupURI representing the URI to retrieve
+ * @error: return location for a #GError, or %NULL
  *
- * Removes all features of type @feature_type (or any subclass of
- * @feature_type) from @session. You can also remove standard features
- * from the session at construct time by using the
- * %SOUP_SESSION_REMOVE_FEATURE_BY_TYPE property.
+ * Creates a #SoupRequest for retrieving @uri.
  *
- * Since: 2.24
- **/
-void
-soup_session_remove_feature_by_type (SoupSession *session, GType feature_type)
+ * Return value: (transfer full): a new #SoupRequest, or
+ *   %NULL on error.
+ *
+ * Since: 2.42
+ */
+SoupRequest *
+soup_session_request_uri (SoupSession *session, SoupURI *uri,
+                         GError **error)
 {
        SoupSessionPrivate *priv;
-       GSList *f;
+       GType request_type;
 
-       g_return_if_fail (SOUP_IS_SESSION (session));
+       g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
 
        priv = SOUP_SESSION_GET_PRIVATE (session);
 
-       if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
-       restart:
-               for (f = priv->features; f; f = f->next) {
-                       if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type)) {
-                               soup_session_remove_feature (session, f->data);
-                               goto restart;
-                       }
-               }
-       } else {
-               for (f = priv->features; f; f = f->next) {
-                       if (soup_session_feature_remove_feature (f->data, feature_type))
-                               return;
-               }
-               g_warning ("No feature manager for feature of type '%s'", g_type_name (feature_type));
+       request_type = (GType)GPOINTER_TO_SIZE (g_hash_table_lookup (priv->request_types, uri->scheme));
+       if (!request_type) {
+               g_set_error (error, SOUP_REQUEST_ERROR,
+                            SOUP_REQUEST_ERROR_UNSUPPORTED_URI_SCHEME,
+                            _("Unsupported URI scheme '%s'"), uri->scheme);
+               return NULL;
+       }
+
+       return g_initable_new (request_type, NULL, error,
+                              "uri", uri,
+                              "session", session,
+                              NULL);
+}
+
+static SoupRequestHTTP *
+initialize_http_request (SoupRequest  *req,
+                        const char   *method,
+                        GError      **error)
+{
+       SoupRequestHTTP *http;
+       SoupMessage *msg;
+
+       if (!SOUP_IS_REQUEST_HTTP (req)) {
+               g_object_unref (req);
+               g_set_error (error, SOUP_REQUEST_ERROR,
+                            SOUP_REQUEST_ERROR_BAD_URI,
+                            _("Not an HTTP URI"));
+               return NULL;
        }
+
+       http = SOUP_REQUEST_HTTP (req);
+       msg = soup_request_http_get_message (http);
+       g_object_set (G_OBJECT (msg),
+                     SOUP_MESSAGE_METHOD, method,
+                     NULL);
+       g_object_unref (msg);
+
+       return http;
 }
 
 /**
- * soup_session_get_features:
+ * soup_session_request_http:
  * @session: a #SoupSession
- * @feature_type: the #GType of the class of features to get
+ * @method: an HTTP method
+ * @uri_string: a URI, in string form
+ * @error: return location for a #GError, or %NULL
  *
- * Generates a list of @session's features of type @feature_type. (If
- * you want to see all features, you can pass %SOUP_TYPE_SESSION_FEATURE
- * for @feature_type.)
+ * Creates a #SoupRequest for retrieving @uri_string, which must be an
+ * "http" or "https" URI (or another protocol listed in @session's
+ * #SoupSession:http-aliases or #SoupSession:https-aliases).
  *
- * Return value: (transfer container) (element-type Soup.SessionFeature):
- * a list of features. You must free the list, but not its contents
+ * Return value: (transfer full): a new #SoupRequestHTTP, or
+ *   %NULL on error.
  *
- * Since: 2.26
- **/
-GSList *
-soup_session_get_features (SoupSession *session, GType feature_type)
+ * Since: 2.42
+ */
+SoupRequestHTTP *
+soup_session_request_http (SoupSession  *session,
+                          const char   *method,
+                          const char   *uri_string,
+                          GError      **error)
 {
-       SoupSessionPrivate *priv;
-       GSList *f, *ret;
+       SoupRequest *req;
 
-       g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
+       req = soup_session_request (session, uri_string, error);
+       if (!req)
+               return NULL;
 
-       priv = SOUP_SESSION_GET_PRIVATE (session);
-       for (f = priv->features, ret = NULL; f; f = f->next) {
-               if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
-                       ret = g_slist_prepend (ret, f->data);
-       }
-       return g_slist_reverse (ret);
+       return initialize_http_request (req, method, error);
 }
 
 /**
- * soup_session_get_feature:
+ * soup_session_request_http_uri:
  * @session: a #SoupSession
- * @feature_type: the #GType of the feature to get
+ * @method: an HTTP method
+ * @uri: a #SoupURI representing the URI to retrieve
+ * @error: return location for a #GError, or %NULL
  *
- * Gets the first feature in @session of type @feature_type. For
- * features where there may be more than one feature of a given type,
- * use soup_session_get_features().
+ * Creates a #SoupRequest for retrieving @uri, which must be an
+ * "http" or "https" URI (or another protocol listed in @session's
+ * #SoupSession:http-aliases or #SoupSession:https-aliases).
  *
- * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
- * feature is owned by @session.
+ * Return value: (transfer full): a new #SoupRequestHTTP, or
+ *   %NULL on error.
  *
- * Since: 2.26
- **/
-SoupSessionFeature *
-soup_session_get_feature (SoupSession *session, GType feature_type)
+ * Since: 2.42
+ */
+SoupRequestHTTP *
+soup_session_request_http_uri (SoupSession  *session,
+                              const char   *method,
+                              SoupURI      *uri,
+                              GError      **error)
 {
-       SoupSessionPrivate *priv;
-       SoupSessionFeature *feature;
-       GSList *f;
-
-       g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
+       SoupRequest *req;
 
-       priv = SOUP_SESSION_GET_PRIVATE (session);
-
-       feature = g_hash_table_lookup (priv->features_cache,
-                                      GSIZE_TO_POINTER (feature_type));
-       if (feature)
-               return feature;
+       req = soup_session_request_uri (session, uri, error);
+       if (!req)
+               return NULL;
 
-       for (f = priv->features; f; f = f->next) {
-               feature = f->data;
-               if (G_TYPE_CHECK_INSTANCE_TYPE (feature, feature_type)) {
-                       g_hash_table_insert (priv->features_cache,
-                                            GSIZE_TO_POINTER (feature_type),
-                                            feature);
-                       return feature;
-               }
-       }
-       return NULL;
+       return initialize_http_request (req, method, error);
 }
 
 /**
- * soup_session_get_feature_for_message:
- * @session: a #SoupSession
- * @feature_type: the #GType of the feature to get
- * @msg: a #SoupMessage
+ * SOUP_REQUEST_ERROR:
  *
- * Gets the first feature in @session of type @feature_type, provided
- * that it is not disabled for @msg. As with
- * soup_session_get_feature(), this should only be used for features
- * where @feature_type is only expected to match a single feature. In
- * particular, if there are two matching features, and the first is
- * disabled on @msg, and the second is not, then this will return
- * %NULL, not the second feature.
+ * A #GError domain for #SoupRequest<!-- -->-related errors. Used with
+ * #SoupRequestError.
  *
- * Return value: (transfer none): a #SoupSessionFeature, or %NULL. The
- * feature is owned by @session.
+ * Since: 2.42
+ */
+/**
+ * SoupRequestError:
+ * @SOUP_REQUEST_ERROR_BAD_URI: the URI could not be parsed
+ * @SOUP_REQUEST_ERROR_UNSUPPORTED_URI_SCHEME: the URI scheme is not
+ *   supported by this #SoupSession
+ * @SOUP_REQUEST_ERROR_PARSING: the server's response could not
+ *   be parsed
+ * @SOUP_REQUEST_ERROR_ENCODING: the server's response was in an
+ *   unsupported format
  *
- * Since: 2.28
- **/
-SoupSessionFeature *
-soup_session_get_feature_for_message (SoupSession *session, GType feature_type,
-                                     SoupMessage *msg)
+ * A #SoupRequest error.
+ *
+ * Since: 2.42
+ */
+
+GQuark
+soup_request_error_quark (void)
 {
-       SoupSessionFeature *feature;
+       static GQuark error;
+       if (!error)
+               error = g_quark_from_static_string ("soup_request_error_quark");
+       return error;
+}
 
-       feature = soup_session_get_feature (session, feature_type);
-       if (feature && soup_message_disables_feature (msg, feature))
+#if ENABLE(TIZEN_CERTIFICATE_FILE_SET)
+static gboolean
+set_tls_certificate_file (gpointer session)
+{
+       TIZEN_LOGI("");
+       soup_session_set_certificate_file(session);
+
+       return FALSE;
+}
+
+void
+soup_session_tls_start_idle_timer (SoupSession *session, guint idle_timeout)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+       TIZEN_LOGI ("timeout[%d]", idle_timeout);
+       if (priv && idle_timeout > 0 && !priv->tls_idle_timeout_src) {
+               priv->tls_idle_timeout_src =
+                       soup_add_timeout (priv->async_context,
+                                         idle_timeout,
+                                         set_tls_certificate_file, session);
+       }
+}
+
+void
+soup_session_tls_stop_idle_timer (SoupSession *session)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+       TIZEN_LOGI ("");
+       if (priv && priv->tls_idle_timeout_src) {
+               if (!g_source_is_destroyed (priv->tls_idle_timeout_src)) {
+                       TIZEN_LOGI("g_source isn't NULL.");
+                       /* Adding log to cross check if MainContext exists */
+                       TIZEN_LOGE("GMainContext of priv->tls_idle_timeout_src"
+                                       " is %p", priv->async_context);
+                       if(priv->async_context)
+                               g_source_destroy (priv->tls_idle_timeout_src);
+               }
+               priv->tls_idle_timeout_src = NULL;
+       }
+}
+
+gboolean
+soup_session_is_tls_db_initialized (SoupSession *session)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       if (priv && priv->tlsdb)
+               return TRUE;
+       return FALSE;
+}
+#endif
+
+#if ENABLE(TIZEN_TV_CREATE_IDLE_TCP_CONNECTION)
+
+static SoupConnection *
+get_pre_connection_with_uri (SoupSession *session, SoupURI *uri)
+{
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       SoupConnection *conn = NULL;
+       SoupSessionHost *host = NULL;
+
+       if (!priv)
                return NULL;
-       return feature;
+
+       g_mutex_lock (&priv->conn_lock);
+       host = get_host_for_uri (session, uri);
+       if (host->num_conns >= priv->max_conns_per_host) {
+               g_mutex_unlock (&priv->conn_lock);
+               return NULL;
+       }
+
+       if (priv->num_conns >= priv->max_conns) {
+               g_mutex_unlock (&priv->conn_lock);
+               return NULL;
+       }
+
+       conn = g_object_new (
+               SOUP_TYPE_CONNECTION,
+               SOUP_CONNECTION_REMOTE_URI, host->uri,
+               SOUP_CONNECTION_PROXY_RESOLVER, get_proxy_resolver (session),
+               SOUP_CONNECTION_SSL, soup_uri_is_https (uri, priv->https_aliases),
+               SOUP_CONNECTION_SSL_CREDENTIALS, get_tls_database (session),
+               SOUP_CONNECTION_WIDGET_ENGINE, priv->widget_engine,
+               SOUP_CONNECTION_SSL_STRICT, priv->ssl_strict && (priv->tlsdb != NULL || SOUP_IS_PLAIN_SESSION (session)),
+               SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
+               SOUP_CONNECTION_USE_THREAD_CONTEXT, priv->use_thread_context,
+               SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
+               SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
+               SOUP_CONNECTION_SSL_FALLBACK, host->ssl_fallback,
+               SOUP_CONNECTION_LOCAL_ADDRESS, priv->local_addr,
+               NULL);
+
+       g_signal_connect (conn, "disconnected",
+                       G_CALLBACK (connection_disconnected),
+                       session);
+       g_signal_emit (session, signals[CONNECTION_CREATED], 0, conn);
+       priv->num_conns++;
+       host->num_conns++;
+       if (host->keep_alive_src) {
+               g_source_destroy (host->keep_alive_src);
+               g_source_unref (host->keep_alive_src);
+               host->keep_alive_src = NULL;
+       }
+       g_mutex_unlock (&priv->conn_lock);
+       return g_object_ref (conn);
+}
+
+static void
+got_pre_connection (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+       SoupConnection *conn = SOUP_CONNECTION (object);
+       SoupSession *session = user_data;
+       SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+       GError *error = NULL;
+       SoupSocket *sock = NULL;
+       SoupURI *uri = NULL;
+       SoupSessionHost *host = NULL;
+
+       soup_connection_connect_finish (conn, result, &error);
+       sock = soup_connection_get_socket (conn);
+       if (sock && soup_socket_is_ssl (sock)) {
+               GTlsCertificateFlags errors;
+               g_object_get (sock,
+                             SOUP_SOCKET_TLS_ERRORS,
+                             &errors,
+                             NULL);
+       }
+       if (!error && priv) {
+               g_mutex_lock (&priv->conn_lock);
+               soup_connection_set_pre_connect_idle (conn);
+               g_object_get (G_OBJECT (conn),
+                             SOUP_CONNECTION_REMOTE_URI,
+                             &uri,
+                             NULL);
+               host = get_host_for_uri (session, uri);
+               g_hash_table_insert (priv->conns, conn, host);
+               host->connections = g_slist_prepend (host->connections, conn);
+               soup_uri_free(uri);
+               g_mutex_unlock (&priv->conn_lock);
+       } else
+               soup_connection_disconnect (conn);
+
+}
+static gboolean
+pre_connection_accept_certificate (SoupConnection      *conn,
+                         GTlsCertificate* certificate,
+                         GTlsCertificateFlags errors,
+                         gpointer             user_data)
+{
+    SoupSession* session = (SoupSession*)user_data;
+    SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+       if (!priv)
+               return FALSE;
+
+    if (priv->widget_engine)
+        return (errors & (G_TLS_CERTIFICATE_UNKNOWN_CA | G_TLS_CERTIFICATE_BAD_IDENTITY | G_TLS_CERTIFICATE_REVOKED | G_TLS_CERTIFICATE_INSECURE | G_TLS_CERTIFICATE_GENERIC_ERROR)) ? FALSE : TRUE;
+
+       return TRUE;
+}
+
+void soup_session_create_idle_connection(SoupSession *session, SoupURI* pre_uri)
+{
+       SoupConnection * pre_conn = NULL;
+       pre_conn = get_pre_connection_with_uri(session, pre_uri);
+
+       if (pre_conn) {
+               g_signal_connect (pre_conn, "accept-certificate",
+                                 G_CALLBACK (pre_connection_accept_certificate), session);
+               soup_connection_connect_async(pre_conn, NULL, got_pre_connection, session);
+       }
 }
+#endif