Merge branch 'upstream/1.16' into tizen_gst_1.16.2
[platform/upstream/gst-plugins-good.git] / ext / soup / gstsouphttpsrc.c
index 40abf46..1773eed 100644 (file)
@@ -86,6 +86,8 @@
 GST_DEBUG_CATEGORY_STATIC (souphttpsrc_debug);
 #define GST_CAT_DEFAULT souphttpsrc_debug
 
+#define GST_SOUP_SESSION_CONTEXT "gst.soup.session"
+
 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
@@ -123,7 +125,7 @@ enum
 #define DEFAULT_IRADIO_MODE          TRUE
 #define DEFAULT_SOUP_LOG_LEVEL       SOUP_LOGGER_LOG_HEADERS
 #define DEFAULT_COMPRESS             FALSE
-#define DEFAULT_KEEP_ALIVE           FALSE
+#define DEFAULT_KEEP_ALIVE           TRUE
 #define DEFAULT_SSL_STRICT           TRUE
 #define DEFAULT_SSL_CA_FILE          NULL
 #define DEFAULT_SSL_USE_SYSTEM_CA_FILE TRUE
@@ -139,6 +141,7 @@ enum
 #define REDUCE_BLOCKSIZE_LIMIT 0.20
 #define REDUCE_BLOCKSIZE_COUNT 2
 #define REDUCE_BLOCKSIZE_FACTOR 0.5
+#define GROW_TIME_LIMIT (1 * GST_SECOND)
 
 #ifdef TIZEN_FEATURE_SOUP_MODIFICATION
 #define DLNA_OP_TIMED_SEEK  0x02
@@ -157,6 +160,8 @@ static void gst_soup_http_src_get_property (GObject * object, guint prop_id,
 
 static GstStateChangeReturn gst_soup_http_src_change_state (GstElement *
     element, GstStateChange transition);
+static void gst_soup_http_src_set_context (GstElement * element,
+    GstContext * context);
 static GstFlowReturn gst_soup_http_src_create (GstPushSrc * psrc,
     GstBuffer ** outbuf);
 static gboolean gst_soup_http_src_start (GstBaseSrc * bsrc);
@@ -418,6 +423,8 @@ gst_soup_http_src_class_init (GstSoupHTTPSrcClass * klass)
       "Wouter Cloetens <wouter@mind.be>");
   gstelement_class->change_state =
       GST_DEBUG_FUNCPTR (gst_soup_http_src_change_state);
+  gstelement_class->set_context =
+      GST_DEBUG_FUNCPTR (gst_soup_http_src_set_context);
 
   gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_soup_http_src_start);
   gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_soup_http_src_stop);
@@ -451,6 +458,7 @@ gst_soup_http_src_reset (GstSoupHTTPSrc * src)
 
   src->reduce_blocksize_count = 0;
   src->increase_blocksize_count = 0;
+  src->last_socket_read_time = 0;
 
 #ifdef TIZEN_FEATURE_SOUP_MODIFICATION
   if (src->dash_oldest_segment) {
@@ -465,10 +473,12 @@ gst_soup_http_src_reset (GstSoupHTTPSrc * src)
 #endif
 
   g_cancellable_reset (src->cancellable);
+  g_mutex_lock (&src->mutex);
   if (src->input_stream) {
     g_object_unref (src->input_stream);
     src->input_stream = NULL;
   }
+  g_mutex_unlock (&src->mutex);
 
   gst_caps_replace (&src->src_caps, NULL);
   g_free (src->iradio_name);
@@ -498,9 +508,13 @@ gst_soup_http_src_init (GstSoupHTTPSrc * src)
   src->cookies = NULL;
   src->iradio_mode = DEFAULT_IRADIO_MODE;
   src->session = NULL;
+  src->external_session = NULL;
+  src->forced_external_session = FALSE;
   src->msg = NULL;
   src->timeout = DEFAULT_TIMEOUT;
   src->log_level = DEFAULT_SOUP_LOG_LEVEL;
+  src->compress = DEFAULT_COMPRESS;
+  src->keep_alive = DEFAULT_KEEP_ALIVE;
   src->ssl_strict = DEFAULT_SSL_STRICT;
   src->ssl_use_system_ca_file = DEFAULT_SSL_USE_SYSTEM_CA_FILE;
   src->tls_database = DEFAULT_TLS_DATABASE;
@@ -537,6 +551,11 @@ gst_soup_http_src_dispose (GObject * gobject)
 
   gst_soup_http_src_session_close (src);
 
+  if (src->external_session) {
+    g_object_unref (src->external_session);
+    src->external_session = NULL;
+  }
+
   G_OBJECT_CLASS (parent_class)->dispose (gobject);
 }
 
@@ -870,13 +889,12 @@ gst_soup_http_src_add_range_header (GstSoupHTTPSrc * src, guint64 offset,
           stop_offset);
     } else {
       rc = g_snprintf (buf, sizeof (buf), "bytes=%" G_GUINT64_FORMAT "-",
-                offset);
+          offset);
     }
     if (rc > sizeof (buf) || rc < 0)
       return FALSE;
     soup_message_headers_append (src->msg->request_headers, "Range", buf);
   }
-
   src->read_position = offset;
   return TRUE;
 }
@@ -1000,25 +1018,99 @@ gst_soup_http_src_session_open (GstSoupHTTPSrc * src)
   }
 
   if (!src->session) {
-    GST_DEBUG_OBJECT (src, "Creating session");
-    if (src->proxy == NULL) {
-      src->session =
-          soup_session_new_with_options (SOUP_SESSION_USER_AGENT,
-          src->user_agent, SOUP_SESSION_TIMEOUT, src->timeout,
-          SOUP_SESSION_SSL_STRICT, src->ssl_strict, NULL);
-//          SOUP_SESSION_TLS_INTERACTION, src->tls_interaction, NULL);
+    GstQuery *query;
+    gboolean can_share = (src->timeout == DEFAULT_TIMEOUT)
+        && (src->ssl_strict == DEFAULT_SSL_STRICT)
+        && (src->tls_interaction == NULL) && (src->proxy == NULL)
+        && (src->tls_database == DEFAULT_TLS_DATABASE)
+        && (src->ssl_ca_file == DEFAULT_SSL_CA_FILE)
+        && (src->ssl_use_system_ca_file == DEFAULT_SSL_USE_SYSTEM_CA_FILE);
+
+    query = gst_query_new_context (GST_SOUP_SESSION_CONTEXT);
+    if (gst_pad_peer_query (GST_BASE_SRC_PAD (src), query)) {
+      GstContext *context;
+
+      gst_query_parse_context (query, &context);
+      gst_element_set_context (GST_ELEMENT_CAST (src), context);
     } else {
-      src->session =
-          soup_session_new_with_options (SOUP_SESSION_PROXY_URI, src->proxy,
-          SOUP_SESSION_TIMEOUT, src->timeout,
-          SOUP_SESSION_SSL_STRICT, src->ssl_strict,
-          SOUP_SESSION_USER_AGENT, src->user_agent, NULL);
-//          SOUP_SESSION_TLS_INTERACTION, src->tls_interaction, NULL);
+      GstMessage *message;
+
+      message =
+          gst_message_new_need_context (GST_OBJECT_CAST (src),
+          GST_SOUP_SESSION_CONTEXT);
+      gst_element_post_message (GST_ELEMENT_CAST (src), message);
+    }
+    gst_query_unref (query);
+
+    GST_OBJECT_LOCK (src);
+    if (src->external_session && (can_share || src->forced_external_session)) {
+      GST_DEBUG_OBJECT (src, "Using external session %p",
+          src->external_session);
+      src->session = g_object_ref (src->external_session);
+      src->session_is_shared = TRUE;
+    } else {
+      GST_DEBUG_OBJECT (src, "Creating session (can share %d)", can_share);
+
+      /* We explicitly set User-Agent to NULL here and overwrite it per message
+       * to be able to have the same session with different User-Agents per
+       * source */
+      if (src->proxy == NULL) {
+        src->session =
+            soup_session_new_with_options (SOUP_SESSION_USER_AGENT,
+            NULL, SOUP_SESSION_TIMEOUT, src->timeout,
+            SOUP_SESSION_SSL_STRICT, src->ssl_strict,
+            SOUP_SESSION_TLS_INTERACTION, src->tls_interaction, NULL);
+      } else {
+        src->session =
+            soup_session_new_with_options (SOUP_SESSION_PROXY_URI, src->proxy,
+            SOUP_SESSION_TIMEOUT, src->timeout,
+            SOUP_SESSION_SSL_STRICT, src->ssl_strict,
+            SOUP_SESSION_USER_AGENT, NULL,
+            SOUP_SESSION_TLS_INTERACTION, src->tls_interaction, NULL);
+      }
+
+      if (src->session) {
+        gst_soup_util_log_setup (src->session, src->log_level,
+            GST_ELEMENT (src));
+        soup_session_add_feature_by_type (src->session,
+            SOUP_TYPE_CONTENT_DECODER);
+        soup_session_add_feature_by_type (src->session, SOUP_TYPE_COOKIE_JAR);
+
+        if (can_share) {
+          GstContext *context;
+          GstMessage *message;
+          GstStructure *s;
+
+          GST_DEBUG_OBJECT (src, "Sharing session %p", src->session);
+          src->session_is_shared = TRUE;
+
+          /* Unset the limit the number of maximum allowed connection */
+          g_object_set (src->session, SOUP_SESSION_MAX_CONNS, G_MAXINT,
+              SOUP_SESSION_MAX_CONNS_PER_HOST, G_MAXINT, NULL);
+
+          context = gst_context_new (GST_SOUP_SESSION_CONTEXT, TRUE);
+          s = gst_context_writable_structure (context);
+          gst_structure_set (s, "session", SOUP_TYPE_SESSION, src->session,
+              "force", G_TYPE_BOOLEAN, FALSE, NULL);
+
+          gst_object_ref (src->session);
+          GST_OBJECT_UNLOCK (src);
+          gst_element_set_context (GST_ELEMENT_CAST (src), context);
+          message =
+              gst_message_new_have_context (GST_OBJECT_CAST (src), context);
+          gst_element_post_message (GST_ELEMENT_CAST (src), message);
+          GST_OBJECT_LOCK (src);
+          gst_object_unref (src->session);
+        } else {
+          src->session_is_shared = FALSE;
+        }
+      }
     }
 
     if (!src->session) {
       GST_ELEMENT_ERROR (src, LIBRARY, INIT,
-          (NULL), ("Failed to create async session"));
+          (NULL), ("Failed to create session"));
+      GST_OBJECT_UNLOCK (src);
       return FALSE;
     }
 
@@ -1047,25 +1139,20 @@ gst_soup_http_src_session_open (GstSoupHTTPSrc * src)
     g_signal_connect (src->session, "authenticate",
         G_CALLBACK (gst_soup_http_src_authenticate_cb), src);
 
-    /* Set up logging */
-    gst_soup_util_log_setup (src->session, src->log_level, GST_ELEMENT (src));
-    if (src->tls_database)
-      g_object_set (src->session, "tls-database", src->tls_database, NULL);
-    else if (src->ssl_ca_file)
-      g_object_set (src->session, "ssl-ca-file", src->ssl_ca_file, NULL);
-    else
-      g_object_set (src->session, "ssl-use-system-ca-file",
-          src->ssl_use_system_ca_file, NULL);
+    if (!src->session_is_shared) {
+      if (src->tls_database)
+        g_object_set (src->session, "tls-database", src->tls_database, NULL);
+      else if (src->ssl_ca_file)
+        g_object_set (src->session, "ssl-ca-file", src->ssl_ca_file, NULL);
+      else
+        g_object_set (src->session, "ssl-use-system-ca-file",
+            src->ssl_use_system_ca_file, NULL);
+    }
+    GST_OBJECT_UNLOCK (src);
   } else {
     GST_DEBUG_OBJECT (src, "Re-using session");
   }
 
-  if (src->compress)
-    soup_session_add_feature_by_type (src->session, SOUP_TYPE_CONTENT_DECODER);
-  else
-    soup_session_remove_feature_by_type (src->session,
-        SOUP_TYPE_CONTENT_DECODER);
-
   return TRUE;
 }
 
@@ -1082,18 +1169,26 @@ gst_soup_http_src_session_close (GstSoupHTTPSrc * src)
   }
 
   if (src->session) {
+    if (!src->session_is_shared)
 #ifdef TIZEN_FEATURE_SOUP_MODIFICATION
 /* When Playback is ongoing and Browser is moved to background ( Pressing Menu or Home Key ), The Session gets destroyed.
    But the cookie_jar property remains unfreed. This results in garbage pointer and causes crash.
    Removing the cookie_jar feature during close session of browser to handle the issue. */
-    GST_DEBUG_OBJECT (src, "Removing Cookie Jar instance");
-    soup_session_remove_feature_by_type(src->session, SOUP_TYPE_COOKIE_JAR);
-    src->cookie_jar = NULL;
+    {
+      GST_DEBUG_OBJECT (src, "Removing Cookie Jar instance");
+      soup_session_remove_feature_by_type(src->session, SOUP_TYPE_COOKIE_JAR);
+      src->cookie_jar = NULL;
+         soup_session_abort (src->session);
+    }
+#else
+      soup_session_abort (src->session);
 #endif
-    soup_session_abort (src->session);
+    g_signal_handlers_disconnect_by_func (src->session,
+        G_CALLBACK (gst_soup_http_src_authenticate_cb), src);
     g_object_unref (src->session);
     src->session = NULL;
   }
+
   g_mutex_unlock (&src->mutex);
 }
 
@@ -1101,6 +1196,10 @@ static void
 gst_soup_http_src_authenticate_cb (SoupSession * session, SoupMessage * msg,
     SoupAuth * auth, gboolean retrying, GstSoupHTTPSrc * src)
 {
+  /* Might be from another user of the shared session */
+  if (!GST_IS_SOUP_HTTP_SRC (src) || msg != src->msg)
+    return;
+
   if (!retrying) {
     /* First time authentication only, if we fail and are called again with retry true fall through */
     if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) {
@@ -1157,10 +1256,8 @@ gst_soup_http_src_headers_foreach (const gchar * name, const gchar * val,
 {
   GST_INFO_OBJECT (src, " %s: %s", name, val);
 
-  if (g_ascii_strcasecmp (name, "Set-Cookie") == 0)
-  {
-    if (val)
-    {
+  if (g_ascii_strcasecmp (name, "Set-Cookie") == 0) {
+    if (val) {
       gboolean bret = FALSE;
       GstStructure *s = NULL;
       GstSoupHTTPSrc * tmp = src;
@@ -1178,20 +1275,14 @@ gst_soup_http_src_headers_foreach (const gchar * name, const gchar * val,
 
       GST_INFO_OBJECT (src, "request url [%s], posted cookies [%s] msg and returned = %d", tmp->location, val, bret);
     }
-  }
-  else if (g_ascii_strcasecmp (name, "Dash-Oldest-Segment") == 0)
-  {
-    if (val)
-    {
+  } else if (g_ascii_strcasecmp (name, "Dash-Oldest-Segment") == 0) {
+    if (val) {
       GstSoupHTTPSrc * tmp = src;
       tmp->dash_oldest_segment = g_strdup (val);
       GST_INFO_OBJECT (src, "Dash-Oldest-Segment set as %s ", tmp->dash_oldest_segment);
     }
-  }
-  else if (g_ascii_strcasecmp (name, "Dash-Newest-Segment") == 0)
-  {
-    if (val)
-    {
+  } else if (g_ascii_strcasecmp (name, "Dash-Newest-Segment") == 0) {
+    if (val) {
       GstSoupHTTPSrc * tmp = src;
       tmp->dash_newest_segment = g_strdup (val);
       GST_INFO_OBJECT (src, "Dash-Newest-Segment set as %s ", tmp->dash_newest_segment);
@@ -1226,16 +1317,9 @@ gst_soup_http_src_got_headers (GstSoupHTTPSrc * src, SoupMessage * msg)
     return GST_FLOW_OK;
   }
 
-  if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) {
-    /* force an error */
-    return gst_soup_http_src_parse_status (msg, src);
-  }
-
-  src->got_headers = TRUE;
-  g_cond_broadcast (&src->have_headers_cond);
-
   http_headers = gst_structure_new_empty ("http-headers");
-  gst_structure_set (http_headers, "uri", G_TYPE_STRING, src->location, NULL);
+  gst_structure_set (http_headers, "uri", G_TYPE_STRING, src->location,
+      "http-status-code", G_TYPE_UINT, msg->status_code, NULL);
   if (src->redirection_uri)
     gst_structure_set (http_headers, "redirection-uri", G_TYPE_STRING,
         src->redirection_uri, NULL);
@@ -1252,6 +1336,19 @@ gst_soup_http_src_got_headers (GstSoupHTTPSrc * src, SoupMessage * msg)
       headers, NULL);
   gst_structure_free (headers);
 
+  gst_element_post_message (GST_ELEMENT_CAST (src),
+      gst_message_new_element (GST_OBJECT_CAST (src),
+          gst_structure_copy (http_headers)));
+
+  if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) {
+    /* force an error */
+    gst_structure_free (http_headers);
+    return gst_soup_http_src_parse_status (msg, src);
+  }
+
+  src->got_headers = TRUE;
+  g_cond_broadcast (&src->have_headers_cond);
+
   http_headers_event =
       gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_STICKY, http_headers);
   gst_event_replace (&src->http_headers_event, http_headers_event);
@@ -1632,6 +1729,29 @@ gst_soup_http_src_build_message (GstSoupHTTPSrc * src, const gchar * method)
         ("Error parsing URL."), ("URL: %s", src->location));
     return FALSE;
   }
+
+  /* Duplicating the defaults of libsoup here. We don't want to set a
+   * User-Agent in the session as each source might have its own User-Agent
+   * set */
+  if (!src->user_agent || !*src->user_agent) {
+    gchar *user_agent =
+        g_strdup_printf ("libsoup/%u.%u.%u", soup_get_major_version (),
+        soup_get_minor_version (), soup_get_micro_version ());
+    soup_message_headers_append (src->msg->request_headers, "User-Agent",
+        user_agent);
+    g_free (user_agent);
+  } else if (g_str_has_suffix (src->user_agent, " ")) {
+    gchar *user_agent = g_strdup_printf ("%slibsoup/%u.%u.%u", src->user_agent,
+        soup_get_major_version (),
+        soup_get_minor_version (), soup_get_micro_version ());
+    soup_message_headers_append (src->msg->request_headers, "User-Agent",
+        user_agent);
+    g_free (user_agent);
+  } else {
+    soup_message_headers_append (src->msg->request_headers, "User-Agent",
+        src->user_agent);
+  }
+
   if (!src->keep_alive) {
     soup_message_headers_append (src->msg->request_headers, "Connection",
         "close");
@@ -1679,6 +1799,9 @@ gst_soup_http_src_build_message (GstSoupHTTPSrc * src, const gchar * method)
   }
 #endif
 
+  if (!src->compress)
+    soup_message_disable_feature (src->msg, SOUP_TYPE_CONTENT_DECODER);
+
   soup_message_set_flags (src->msg, SOUP_MESSAGE_OVERWRITE_CHUNKS |
       (src->automatic_redirect ? 0 : SOUP_MESSAGE_NO_REDIRECT));
 
@@ -1700,6 +1823,7 @@ gst_soup_http_src_build_message (GstSoupHTTPSrc * src, const gchar * method)
   return TRUE;
 }
 
+/* Lock taken */
 static GstFlowReturn
 gst_soup_http_src_send_message (GstSoupHTTPSrc * src)
 {
@@ -1707,6 +1831,7 @@ gst_soup_http_src_send_message (GstSoupHTTPSrc * src)
   GError *error = NULL;
 
   g_return_val_if_fail (src->msg != NULL, GST_FLOW_ERROR);
+  g_assert (src->input_stream == NULL);
 
   src->input_stream =
       soup_session_send (src->session, src->msg, src->cancellable, &error);
@@ -1761,7 +1886,14 @@ gst_soup_http_src_do_request (GstSoupHTTPSrc * src, const gchar * method)
   if (src->msg && src->request_position > 0) {
     gst_soup_http_src_add_range_header (src, src->request_position,
         src->stop_position);
-  }
+  } else if (src->msg && src->request_position == 0)
+    soup_message_headers_remove (src->msg->request_headers, "Range");
+
+  /* add_range_header() has the side effect of setting read_position to
+   * the requested position. This *needs* to be set regardless of having
+   * a message or not. Failure to do so would result in calculation being
+   * done with stale/wrong read position */
+  src->read_position = src->request_position;
 
   if (!src->msg) {
     if (!gst_soup_http_src_build_message (src, method)) {
@@ -1810,10 +1942,15 @@ gst_soup_http_src_check_update_blocksize (GstSoupHTTPSrc * src,
 {
   guint blocksize = gst_base_src_get_blocksize (GST_BASE_SRC_CAST (src));
 
-  GST_LOG_OBJECT (src, "Checking to update blocksize. Read:%" G_GINT64_FORMAT
-      " blocksize:%u", bytes_read, blocksize);
+  gint64 time_since_last_read =
+      g_get_monotonic_time () * GST_USECOND - src->last_socket_read_time;
 
-  if (bytes_read >= blocksize * GROW_BLOCKSIZE_LIMIT) {
+  GST_LOG_OBJECT (src, "Checking to update blocksize. Read: %" G_GINT64_FORMAT
+      " bytes, blocksize: %u bytes, time since last read: %" GST_TIME_FORMAT,
+      bytes_read, blocksize, GST_TIME_ARGS (time_since_last_read));
+
+  if (bytes_read >= blocksize * GROW_BLOCKSIZE_LIMIT
+      && time_since_last_read <= GROW_TIME_LIMIT) {
     src->reduce_blocksize_count = 0;
     src->increase_blocksize_count++;
 
@@ -1823,7 +1960,8 @@ gst_soup_http_src_check_update_blocksize (GstSoupHTTPSrc * src,
       gst_base_src_set_blocksize (GST_BASE_SRC_CAST (src), blocksize);
       src->increase_blocksize_count = 0;
     }
-  } else if (bytes_read < blocksize * REDUCE_BLOCKSIZE_LIMIT) {
+  } else if (bytes_read < blocksize * REDUCE_BLOCKSIZE_LIMIT
+      || time_since_last_read > GROW_TIME_LIMIT) {
     src->reduce_blocksize_count++;
     src->increase_blocksize_count = 0;
 
@@ -1915,6 +2053,8 @@ gst_soup_http_src_read_buffer (GstSoupHTTPSrc * src, GstBuffer ** outbuf)
 
     gst_soup_http_src_check_update_blocksize (src, read_bytes);
 
+    src->last_socket_read_time = g_get_monotonic_time () * GST_USECOND;
+
     /* If we're at the end of a range request, read again to let libsoup
      * finalize the request. This allows to reuse the connection again later,
      * otherwise we would have to cancel the message and close the connection
@@ -2018,6 +2158,13 @@ done:
       goto retry;
     }
   }
+
+  if (ret == GST_FLOW_FLUSHING) {
+    g_mutex_lock (&src->mutex);
+    src->retry_count = 0;
+    g_mutex_unlock (&src->mutex);
+  }
+
   return ret;
 }
 
@@ -2048,7 +2195,7 @@ gst_soup_http_src_stop (GstBaseSrc * bsrc)
 
   src = GST_SOUP_HTTP_SRC (bsrc);
   GST_DEBUG_OBJECT (src, "stop()");
-  if (src->keep_alive && !src->msg)
+  if (src->keep_alive && !src->msg && !src->session_is_shared)
     gst_soup_http_src_cancel_message (src);
   else
     gst_soup_http_src_session_close (src);
@@ -2085,6 +2232,33 @@ gst_soup_http_src_change_state (GstElement * element, GstStateChange transition)
   return ret;
 }
 
+static void
+gst_soup_http_src_set_context (GstElement * element, GstContext * context)
+{
+  GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (element);
+
+  if (g_strcmp0 (gst_context_get_context_type (context),
+          GST_SOUP_SESSION_CONTEXT) == 0) {
+    const GstStructure *s = gst_context_get_structure (context);
+
+    GST_OBJECT_LOCK (src);
+    if (src->external_session)
+      g_object_unref (src->external_session);
+    src->external_session = NULL;
+    gst_structure_get (s, "session", SOUP_TYPE_SESSION, &src->external_session,
+        NULL);
+    src->forced_external_session = FALSE;
+    gst_structure_get (s, "force", G_TYPE_BOOLEAN,
+        &src->forced_external_session, NULL);
+
+    GST_DEBUG_OBJECT (src, "Setting external session %p (force: %d)",
+        src->external_session, src->forced_external_session);
+    GST_OBJECT_UNLOCK (src);
+  }
+
+  GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
+}
+
 /* Interrupt a blocking request. */
 static gboolean
 gst_soup_http_src_unlock (GstBaseSrc * bsrc)
@@ -2233,7 +2407,6 @@ gst_soup_http_src_query (GstBaseSrc * bsrc, GstQuery * query)
 
   switch (GST_QUERY_TYPE (query)) {
     case GST_QUERY_SCHEDULING:
-
       gst_query_parse_scheduling (query, &flags, &minsize, &maxsize, &align);
       flags |= GST_SCHEDULING_FLAG_BANDWIDTH_LIMITED;
 
@@ -2244,7 +2417,6 @@ gst_soup_http_src_query (GstBaseSrc * bsrc, GstQuery * query)
       }
 #endif
       gst_query_set_scheduling (query, flags, minsize, maxsize, align);
-
       break;
     default:
       break;
@@ -2297,7 +2469,7 @@ gst_soup_http_src_set_proxy (GstSoupHTTPSrc * src, const gchar * uri)
   if (uri == NULL || *uri == '\0')
     return TRUE;
 
-  if (g_str_has_prefix (uri, "http://")) {
+  if (g_strstr_len (uri, -1, "://")) {
     src->proxy = soup_uri_new (uri);
   } else {
     gchar *new_uri = g_strconcat ("http://", uri, NULL);
@@ -2309,7 +2481,7 @@ gst_soup_http_src_set_proxy (GstSoupHTTPSrc * src, const gchar * uri)
   return (src->proxy != NULL);
 }
 
-static guint
+static GstURIType
 gst_soup_http_src_uri_get_type (GType type)
 {
   return GST_URI_SRC;