Index: configure.ac
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-bad/configure.ac,v
retrieving revision 1.804
diff -u -p -r1.804 configure.ac
--- configure.ac	16 Jan 2008 05:40:47 -0000	1.804
+++ configure.ac	22 Jan 2008 04:05:13 -0000
@@ -799,10 +799,20 @@ AG_GST_CHECK_FEATURE(NEON, [neon http cl
 
 dnl *** soup ***
 translit(dnm, m, l) AM_CONDITIONAL(USE_SOUP, true)
-AG_GST_CHECK_FEATURE(SOUP, [soup http client plugins], souphttpsrc, [
-  PKG_CHECK_MODULES(SOUP, libsoup-2.2 >= 2.2.104, HAVE_SOUP="yes", [
-    HAVE_SOUP="no"
-    AC_MSG_RESULT(no)
+AG_GST_CHECK_FEATURE(SOUP, [soup http client plugins (2.4)], souphttpsrc, [
+  PKG_CHECK_MODULES(SOUP, libsoup-2.4, [
+      HAVE_SOUP="yes"
+      SOUP_CFLAGS="$SOUP_CFLAGS -DSOUP_API_VERSION_2_4"
+    ], [
+    AG_GST_CHECK_FEATURE(SOUP, [soup http client plugins (2.2)], souphttpsrc, [
+      PKG_CHECK_MODULES(SOUP, libsoup-2.2 >= 2.2.104, [
+        HAVE_SOUP="yes",
+        SOUP_CFLAGS="$SOUP_CFLAGS -DSOUP_API_VERSION_2_2"
+      ], [
+        HAVE_SOUP="no"
+        AC_MSG_RESULT(no)
+      ])
+    ])
   ])
   AC_SUBST(SOUP_CFLAGS)
   AC_SUBST(SOUP_LIBS)
Index: ext/soup/gstsouphttpsrc.h
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-bad/ext/soup/gstsouphttpsrc.h,v
retrieving revision 1.4
diff -u -p -r1.4 gstsouphttpsrc.h
--- ext/soup/gstsouphttpsrc.h	18 Jan 2008 05:24:39 -0000	1.4
+++ ext/soup/gstsouphttpsrc.h	22 Jan 2008 04:05:13 -0000
@@ -22,6 +22,9 @@
 G_BEGIN_DECLS
 
 #include <libsoup/soup.h>
+#ifndef SOUP_API_VERSION_2_4
+#define SoupURI SoupUri
+#endif
 
 #define GST_TYPE_SOUPHTTP_SRC \
   (gst_souphttp_src_get_type())
@@ -37,14 +40,25 @@ G_BEGIN_DECLS
 typedef struct _GstSouphttpSrc GstSouphttpSrc;
 typedef struct _GstSouphttpSrcClass GstSouphttpSrcClass;
 
+enum GstSouphttpSrcSessionIOStatus {
+  GST_SOUPHTTP_SRC_SESSION_IO_STATUS_IDLE,
+  GST_SOUPHTTP_SRC_SESSION_IO_STATUS_QUEUED,
+  GST_SOUPHTTP_SRC_SESSION_IO_STATUS_RUNNING,
+  GST_SOUPHTTP_SRC_SESSION_IO_STATUS_FINISHED,
+};
+
 struct _GstSouphttpSrc {
   GstPushSrc element;
 
   gchar * location;                     /* Full URI. */
   gchar * user_agent;                   /* User-Agent HTTP header. */
+  gboolean automatic_redirect;          /* Follow redirects. */
+  SoupURI * proxy;                      /* HTTP proxy URI. */
   GMainContext * context;               /* I/O context. */
   GMainLoop * loop;                     /* Event loop. */
   SoupSession * session;                /* Async context. */
+  enum GstSouphttpSrcSessionIOStatus session_io_status;
+                                        /* Async I/O status. */
   SoupMessage * msg;                    /* Request message. */
   GstFlowReturn ret;                    /* Return code from callback. */
   GstBuffer ** outbuf;                  /* Return buffer allocated by callback. */
Index: ext/soup/gstsouphttpsrc.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-bad/ext/soup/gstsouphttpsrc.c,v
retrieving revision 1.8
diff -u -p -r1.8 gstsouphttpsrc.c
--- ext/soup/gstsouphttpsrc.c	20 Jan 2008 05:07:52 -0000	1.8
+++ ext/soup/gstsouphttpsrc.c	22 Jan 2008 04:05:14 -0000
@@ -43,6 +43,8 @@ enum
   PROP_0,
   PROP_LOCATION,
   PROP_USER_AGENT,
+  PROP_AUTOMATIC_REDIRECT,
+  PROP_PROXY,
   PROP_IRADIO_MODE,
   PROP_IRADIO_NAME,
   PROP_IRADIO_GENRE,
@@ -75,18 +77,45 @@ static gboolean gst_souphttp_src_unlock_
 
 static gboolean gst_souphttp_src_set_location (GstSouphttpSrc * src,
     const gchar * uri);
-static gboolean soup_add_range_header (GstSouphttpSrc * src, guint64 offset);
-
-static void soup_got_headers (SoupMessage * msg, GstSouphttpSrc * src);
-static void soup_finished (SoupMessage * msg, GstSouphttpSrc * src);
-static void soup_got_body (SoupMessage * msg, GstSouphttpSrc * src);
-static void soup_got_chunk (SoupMessage * msg, GstSouphttpSrc * src);
-static void soup_response (SoupMessage * msg, gpointer user_data);
-static void soup_parse_status (SoupMessage * msg, GstSouphttpSrc * src);
-static void soup_session_close (GstSouphttpSrc * src);
+static gboolean gst_souphttp_src_set_proxy (GstSouphttpSrc * src,
+    const gchar * uri);
 
 static char *gst_souphttp_src_unicodify (const char *str);
 
+static void gst_souphttp_src_cancel_message (GstSouphttpSrc *src);
+static void gst_souphttp_src_queue_message (GstSouphttpSrc *src);
+static gboolean gst_souphttp_src_add_range_header (GstSouphttpSrc *src,
+    guint64 offset);
+static void gst_souphttp_src_session_unpause_message (GstSouphttpSrc *src);
+static void gst_souphttp_src_session_pause_message (GstSouphttpSrc *src);
+static void gst_souphttp_src_session_close (GstSouphttpSrc *src);
+static void gst_souphttp_src_parse_status (SoupMessage *msg,
+    GstSouphttpSrc *src);
+#ifdef SOUP_API_VERSION_2_4
+static void gst_souphttp_src_got_chunk_cb (SoupMessage *msg, SoupBuffer *chunk,
+    GstSouphttpSrc *src);
+static void gst_souphttp_src_response_cb (SoupSession *session,
+    SoupMessage *msg, GstSouphttpSrc *src);
+#else
+#define soup_message_headers_get soup_message_get_header
+#define soup_message_headers_append soup_message_add_header
+#define soup_message_headers_remove soup_message_remove_header
+static void gst_souphttp_src_got_chunk_cb (SoupMessage *msg,
+    GstSouphttpSrc *src);
+static void gst_souphttp_src_response_cb (SoupMessage *msg,
+    GstSouphttpSrc *src);
+#endif
+static void gst_souphttp_src_got_headers_cb (SoupMessage *msg,
+    GstSouphttpSrc *src);
+static void gst_souphttp_src_got_body_cb (SoupMessage *msg,
+    GstSouphttpSrc *src);
+static void gst_souphttp_src_finished_cb (SoupMessage *msg,
+    GstSouphttpSrc *src);
+
+#ifndef SOUP_API_VERSION_2_4
+#define SoupSessionCallback SoupMessageCallbackFn
+#endif
+
 static void
 _do_init (GType type)
 {
@@ -140,6 +169,15 @@ gst_souphttp_src_class_init (GstSouphttp
       g_param_spec_string ("user-agent", "User-Agent",
           "Value of the User-Agent HTTP request header field",
           DEFAULT_USER_AGENT, G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+      PROP_AUTOMATIC_REDIRECT,
+      g_param_spec_boolean ("automatic-redirect", "automatic-redirect",
+          "Automatically follow HTTP redirects (HTTP Status Code 3xx)",
+          TRUE, G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+      PROP_PROXY,
+      g_param_spec_string ("proxy", "Proxy",
+          "HTTP proxy server URI", "", G_PARAM_READWRITE));
 
   /* icecast stuff */
   g_object_class_install_property (gobject_class,
@@ -187,6 +225,8 @@ static void
 gst_souphttp_src_init (GstSouphttpSrc * src, GstSouphttpSrcClass * g_class)
 {
   src->location = NULL;
+  src->proxy = NULL;
+  src->automatic_redirect = TRUE;
   src->user_agent = g_strdup (DEFAULT_USER_AGENT);
   src->icy_caps = NULL;
   src->iradio_mode = FALSE;
@@ -215,6 +255,10 @@ gst_souphttp_src_dispose (GObject * gobj
   src->location = NULL;
   g_free (src->user_agent);
   src->user_agent = NULL;
+  if (src->proxy != NULL) {
+    soup_uri_free (src->proxy);
+    src->proxy = NULL;
+  }
   g_free (src->iradio_name);
   src->iradio_name = NULL;
   g_free (src->iradio_genre);
@@ -265,6 +309,25 @@ gst_souphttp_src_set_property (GObject *
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
+    case PROP_AUTOMATIC_REDIRECT:
+      src->automatic_redirect = g_value_get_boolean (value);
+      break;
+    case PROP_PROXY:
+    {
+      const gchar *proxy;
+
+      proxy = g_value_get_string (value);
+
+      if (proxy == NULL) {
+        GST_WARNING ("proxy property cannot be NULL");
+        goto done;
+      }
+      if (!gst_souphttp_src_set_proxy (src, proxy)) {
+        GST_WARNING ("badly formatted proxy URI");
+        goto done;
+      }
+      break;
+    }
   }
 done:
   return;
@@ -283,6 +346,18 @@ gst_souphttp_src_get_property (GObject *
     case PROP_USER_AGENT:
       g_value_set_string (value, src->user_agent);
       break;
+    case PROP_AUTOMATIC_REDIRECT:
+      g_value_set_boolean (value, src->automatic_redirect);
+      break;
+    case PROP_PROXY:
+      if (src->proxy == NULL)
+        g_value_set_string (value, "");
+      else {
+        char *proxy = soup_uri_to_string (src->proxy, FALSE);
+        g_value_set_string (value, proxy);
+        free (proxy);
+      }
+      break;
     case PROP_IRADIO_MODE:
       g_value_set_boolean (value, src->iradio_mode);
       break;
@@ -314,247 +389,76 @@ gst_souphttp_src_unicodify (const gchar 
   return gst_tag_freeform_string_to_utf8 (str, -1, env_vars);
 }
 
-static GstFlowReturn
-gst_souphttp_src_create (GstPushSrc * psrc, GstBuffer ** outbuf)
-{
-  GstSouphttpSrc *src;
-
-  src = GST_SOUPHTTP_SRC (psrc);
-
-  if (src->msg && (src->request_position != src->read_position)) {
-    if (src->msg->status == SOUP_MESSAGE_STATUS_IDLE) {
-      soup_add_range_header (src, src->request_position);
-    } else {
-      GST_DEBUG_OBJECT (src, "Seek from position %" G_GUINT64_FORMAT
-          " to %" G_GUINT64_FORMAT ": requeueing connection request",
-          src->read_position, src->request_position);
-      soup_session_cancel_message (src->session, src->msg);
-      src->msg = NULL;
-    }
-  }
-  if (!src->msg) {
-    src->msg = soup_message_new (SOUP_METHOD_GET, src->location);
-    if (!src->msg) {
-      GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
-          (NULL), ("Error parsing URL \"%s\"", src->location));
-      return GST_FLOW_ERROR;
-    }
-    soup_message_add_header (src->msg->request_headers, "Connection", "close");
-    if (src->user_agent) {
-      soup_message_add_header (src->msg->request_headers, "User-Agent",
-          src->user_agent);
-    }
-    if (src->iradio_mode) {
-      soup_message_add_header (src->msg->request_headers, "icy-metadata", "1");
-    }
-
-    g_signal_connect (src->msg, "got_headers",
-        G_CALLBACK (soup_got_headers), src);
-    g_signal_connect (src->msg, "got_body", G_CALLBACK (soup_got_body), src);
-    g_signal_connect (src->msg, "finished", G_CALLBACK (soup_finished), src);
-    g_signal_connect (src->msg, "got_chunk", G_CALLBACK (soup_got_chunk), src);
-    soup_message_set_flags (src->msg, SOUP_MESSAGE_OVERWRITE_CHUNKS);
-    soup_add_range_header (src, src->request_position);
-  }
-
-  src->ret = GST_FLOW_CUSTOM_ERROR;
-  src->outbuf = outbuf;
-  do {
-    if (src->interrupted) {
-      soup_session_cancel_message (src->session, src->msg);
-      src->msg = NULL;
-      break;
-    }
-    if (!src->msg) {
-      GST_DEBUG_OBJECT (src, "EOS reached");
-      break;
-    }
-
-    switch (src->msg->status) {
-      case SOUP_MESSAGE_STATUS_IDLE:
-        GST_DEBUG_OBJECT (src, "Queueing connection request");
-        soup_session_queue_message (src->session, src->msg, soup_response, src);
-        break;
-      case SOUP_MESSAGE_STATUS_FINISHED:
-        GST_DEBUG_OBJECT (src, "Connection closed");
-        soup_session_cancel_message (src->session, src->msg);
-        src->msg = NULL;
-        break;
-      case SOUP_MESSAGE_STATUS_QUEUED:
-        break;
-      case SOUP_MESSAGE_STATUS_CONNECTING:
-      case SOUP_MESSAGE_STATUS_RUNNING:
-      default:
-        soup_message_io_unpause (src->msg);
-        break;
-    }
-
-    if (src->ret == GST_FLOW_CUSTOM_ERROR)
-      g_main_loop_run (src->loop);
-  } while (src->ret == GST_FLOW_CUSTOM_ERROR);
-
-  if (src->ret == GST_FLOW_CUSTOM_ERROR)
-    src->ret = GST_FLOW_UNEXPECTED;
-  return src->ret;
-}
-
-static gboolean
-gst_souphttp_src_start (GstBaseSrc * bsrc)
-{
-  GstSouphttpSrc *src = GST_SOUPHTTP_SRC (bsrc);
-
-  GST_DEBUG_OBJECT (src, "start(\"%s\")", src->location);
-
-  if (!src->location) {
-    GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
-        (NULL), ("Missing location property"));
-    return FALSE;
-  }
-
-  src->context = g_main_context_new ();
-
-  src->loop = g_main_loop_new (src->context, TRUE);
-  if (!src->loop) {
-    GST_ELEMENT_ERROR (src, LIBRARY, INIT,
-        (NULL), ("Failed to start GMainLoop"));
-    g_main_context_unref (src->context);
-    return FALSE;
-  }
-
-  src->session =
-      soup_session_async_new_with_options (SOUP_SESSION_ASYNC_CONTEXT,
-      src->context, NULL);
-  if (!src->session) {
-    GST_ELEMENT_ERROR (src, LIBRARY, INIT,
-        (NULL), ("Failed to create async session"));
-    return FALSE;
-  }
-
-  return TRUE;
-}
-
-/* Close the socket and associated resources
- * used both to recover from errors and go to NULL state. */
-static gboolean
-gst_souphttp_src_stop (GstBaseSrc * bsrc)
-{
-  GstSouphttpSrc *src;
-
-  src = GST_SOUPHTTP_SRC (bsrc);
-  GST_DEBUG_OBJECT (src, "stop()");
-  soup_session_close (src);
-  if (src->loop) {
-    g_main_loop_unref (src->loop);
-    g_main_context_unref (src->context);
-    src->loop = NULL;
-    src->context = NULL;
-  }
-
-  return TRUE;
-}
-
-/* Interrupt a blocking request. */
-static gboolean
-gst_souphttp_src_unlock (GstBaseSrc * bsrc)
-{
-  GstSouphttpSrc *src;
-
-  src = GST_SOUPHTTP_SRC (bsrc);
-  GST_DEBUG_OBJECT (src, "unlock()");
-
-  src->interrupted = TRUE;
-  if (src->loop)
-    g_main_loop_quit (src->loop);
-  return TRUE;
-}
-
-/* Interrupt interrupt. */
-static gboolean
-gst_souphttp_src_unlock_stop (GstBaseSrc * bsrc)
-{
-  GstSouphttpSrc *src;
-
-  src = GST_SOUPHTTP_SRC (bsrc);
-  GST_DEBUG_OBJECT (src, "unlock_stop()");
-
-  src->interrupted = FALSE;
-  return TRUE;
-}
-
-static gboolean
-gst_souphttp_src_get_size (GstBaseSrc * bsrc, guint64 * size)
-{
-  GstSouphttpSrc *src;
-
-  src = GST_SOUPHTTP_SRC (bsrc);
-
-  if (src->have_size) {
-    GST_DEBUG_OBJECT (src, "get_size() = %" G_GUINT64_FORMAT,
-        src->content_size);
-    *size = src->content_size;
-    return TRUE;
-  }
-  GST_DEBUG_OBJECT (src, "get_size() = FALSE");
-  return FALSE;
-}
-
-static gboolean
-gst_souphttp_src_is_seekable (GstBaseSrc * bsrc)
+static void
+gst_souphttp_src_cancel_message (GstSouphttpSrc *src)
 {
-  GstSouphttpSrc *src = GST_SOUPHTTP_SRC (bsrc);
-
-  return src->seekable;
+#ifdef SOUP_API_VERSION_2_4
+  soup_session_cancel_message (src->session, src->msg, SOUP_STATUS_CANCELLED);
+#else
+  soup_session_cancel_message (src->session, src->msg);
+#endif
+  src->session_io_status = GST_SOUPHTTP_SRC_SESSION_IO_STATUS_IDLE;
+  src->msg = NULL;
 }
 
-static gboolean
-gst_souphttp_src_do_seek (GstBaseSrc * bsrc, GstSegment * segment)
+static void
+gst_souphttp_src_queue_message (GstSouphttpSrc *src)
 {
-  GstSouphttpSrc *src = GST_SOUPHTTP_SRC (bsrc);
-
-  GST_DEBUG_OBJECT (src, "do_seek(%" G_GUINT64_FORMAT ")", segment->start);
-
-  if (src->read_position == segment->start)
-    return TRUE;
-
-  if (!src->seekable)
-    return FALSE;
-
-  /* Wait for create() to handle the jump in offset. */
-  src->request_position = segment->start;
-  return TRUE;
+  soup_session_queue_message (src->session, src->msg,
+      (SoupSessionCallback) gst_souphttp_src_response_cb, src);
+  src->session_io_status = GST_SOUPHTTP_SRC_SESSION_IO_STATUS_QUEUED;
 }
 
 static gboolean
-soup_add_range_header (GstSouphttpSrc * src, guint64 offset)
+gst_souphttp_src_add_range_header (GstSouphttpSrc *src, guint64 offset)
 {
   gchar buf[64];
   gint rc;
 
-  soup_message_remove_header (src->msg->request_headers, "Range");
+  soup_message_headers_remove (src->msg->request_headers, "Range");
   if (offset) {
     rc = g_snprintf (buf, sizeof (buf), "bytes=%" G_GUINT64_FORMAT "-", offset);
     if (rc > sizeof (buf) || rc < 0)
       return FALSE;
-    soup_message_add_header (src->msg->request_headers, "Range", buf);
+    soup_message_headers_append (src->msg->request_headers, "Range", buf);
   }
   src->read_position = offset;
   return TRUE;
 }
 
-static gboolean
-gst_souphttp_src_set_location (GstSouphttpSrc * src, const gchar * uri)
+static void
+gst_souphttp_src_session_unpause_message (GstSouphttpSrc *src)
 {
-  if (src->location) {
-    g_free (src->location);
-    src->location = NULL;
-  }
-  src->location = g_strdup (uri);
+#ifdef SOUP_API_VERSION_2_4
+  soup_session_unpause_message (src->session, src->msg);
+#else
+  soup_message_io_unpause (src->msg);
+#endif
+}
 
-  return TRUE;
+static void
+gst_souphttp_src_session_pause_message (GstSouphttpSrc *src)
+{
+#ifdef SOUP_API_VERSION_2_4
+  soup_session_pause_message (src->session, src->msg);
+#else
+  soup_message_io_pause (src->msg);
+#endif
+}
+
+static void
+gst_souphttp_src_session_close (GstSouphttpSrc *src)
+{
+  if (src->session) {
+    soup_session_abort (src->session);  /* This unrefs the message. */
+    g_object_unref (src->session);
+    src->session = NULL;
+    src->msg = NULL;
+  }
 }
 
 static void
-soup_got_headers (SoupMessage * msg, GstSouphttpSrc * src)
+gst_souphttp_src_got_headers_cb (SoupMessage *msg, GstSouphttpSrc *src)
 {
   const char *value;
   GstTagList *tag_list;
@@ -563,14 +467,30 @@ soup_got_headers (SoupMessage * msg, Gst
 
   GST_DEBUG_OBJECT (src, "got headers");
 
+  if (src->automatic_redirect &&
+      SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
+    GST_DEBUG_OBJECT (src, "%u redirect to \"%s\"", msg->status_code,
+        soup_message_headers_get (msg->response_headers, "Location"));
+    return;
+  }
+
+  src->session_io_status = GST_SOUPHTTP_SRC_SESSION_IO_STATUS_RUNNING;
+
   /* Parse Content-Length. */
+#ifdef SOUP_API_VERSION_2_4
+  if (soup_message_headers_get_encoding (msg->response_headers) ==
+      SOUP_ENCODING_CONTENT_LENGTH) {
+    newsize = src->request_position + 
+        soup_message_headers_get_content_length (msg->response_headers);
+#else
   value = soup_message_get_header (msg->response_headers, "Content-Length");
   if (value != NULL) {
     newsize = src->request_position + g_ascii_strtoull (value, NULL, 10);
+#endif
     if (!src->have_size || (src->content_size != newsize)) {
       src->content_size = newsize;
       src->have_size = TRUE;
-      GST_DEBUG_OBJECT (src, "size = %llu", src->content_size);
+      GST_DEBUG_OBJECT (src, "size = %" G_GUINT64_FORMAT, src->content_size);
 
       basesrc = GST_BASE_SRC_CAST (src);
       gst_segment_set_duration (&basesrc->segment, GST_FORMAT_BYTES,
@@ -585,7 +505,7 @@ soup_got_headers (SoupMessage * msg, Gst
   tag_list = gst_tag_list_new ();
 
   if ((value =
-          soup_message_get_header (msg->response_headers,
+          soup_message_headers_get (msg->response_headers,
               "icy-metaint")) != NULL) {
     gint icy_metaint = atoi (value);
 
@@ -596,7 +516,7 @@ soup_got_headers (SoupMessage * msg, Gst
   }
 
   if ((value =
-          soup_message_get_header (msg->response_headers,
+          soup_message_headers_get (msg->response_headers,
               "icy-name")) != NULL) {
     g_free (src->iradio_name);
     src->iradio_name = gst_souphttp_src_unicodify (value);
@@ -607,7 +527,7 @@ soup_got_headers (SoupMessage * msg, Gst
     }
   }
   if ((value =
-          soup_message_get_header (msg->response_headers,
+          soup_message_headers_get (msg->response_headers,
               "icy-genre")) != NULL) {
     g_free (src->iradio_genre);
     src->iradio_genre = gst_souphttp_src_unicodify (value);
@@ -618,7 +538,8 @@ soup_got_headers (SoupMessage * msg, Gst
     }
   }
   if ((value =
-          soup_message_get_header (msg->response_headers, "icy-url")) != NULL) {
+          soup_message_headers_get (msg->response_headers, "icy-url"))
+              != NULL) {
     g_free (src->iradio_url);
     src->iradio_url = gst_souphttp_src_unicodify (value);
     if (src->iradio_url) {
@@ -636,7 +557,7 @@ soup_got_headers (SoupMessage * msg, Gst
   }
 
   /* Handle HTTP errors. */
-  soup_parse_status (msg, src);
+  gst_souphttp_src_parse_status (msg, src);
 
   /* Check if Range header was respected. */
   if (src->ret == GST_FLOW_CUSTOM_ERROR &&
@@ -651,27 +572,37 @@ soup_got_headers (SoupMessage * msg, Gst
 
 /* Have body. Signal EOS. */
 static void
-soup_got_body (SoupMessage * msg, GstSouphttpSrc * src)
+gst_souphttp_src_got_body_cb (SoupMessage *msg, GstSouphttpSrc *src)
 {
-  if (msg != src->msg) {
+  if (G_UNLIKELY (msg != src->msg)) {
     GST_DEBUG_OBJECT (src, "got body, but not for current message");
     return;
   }
+  if (G_UNLIKELY (src->session_io_status !=
+      GST_SOUPHTTP_SRC_SESSION_IO_STATUS_RUNNING)) {
+    /* Probably a redirect. */
+    return;
+  }
   GST_DEBUG_OBJECT (src, "got body");
   src->ret = GST_FLOW_UNEXPECTED;
   if (src->loop)
     g_main_loop_quit (src->loop);
-  soup_message_io_pause (msg);
+  gst_souphttp_src_session_pause_message (src);
 }
 
 /* Finished. Signal EOS. */
 static void
-soup_finished (SoupMessage * msg, GstSouphttpSrc * src)
+gst_souphttp_src_finished_cb (SoupMessage *msg, GstSouphttpSrc *src)
 {
-  if (msg != src->msg) {
+  if (G_UNLIKELY (msg != src->msg)) {
     GST_DEBUG_OBJECT (src, "finished, but not for current message");
     return;
   }
+  if (G_UNLIKELY (src->session_io_status !=
+      GST_SOUPHTTP_SRC_SESSION_IO_STATUS_RUNNING)) {
+    /* Probably a redirect. */
+    return;
+  }
   GST_DEBUG_OBJECT (src, "finished");
   src->ret = GST_FLOW_UNEXPECTED;
   if (src->loop)
@@ -679,53 +610,79 @@ soup_finished (SoupMessage * msg, GstSou
 }
 
 static void
-soup_got_chunk (SoupMessage * msg, GstSouphttpSrc * src)
+#ifdef SOUP_API_VERSION_2_4
+gst_souphttp_src_got_chunk_cb (SoupMessage *msg, SoupBuffer *chunk,
+    GstSouphttpSrc *src)
+#else
+gst_souphttp_src_got_chunk_cb (SoupMessage *msg, GstSouphttpSrc *src)
+#endif
 {
   GstBaseSrc *basesrc;
   guint64 new_position;
+  const char *data;
+  gsize length;
 
   if (G_UNLIKELY (msg != src->msg)) {
     GST_DEBUG_OBJECT (src, "got chunk, but not for current message");
     return;
   }
+  if (G_UNLIKELY (src->session_io_status !=
+      GST_SOUPHTTP_SRC_SESSION_IO_STATUS_RUNNING)) {
+    /* Probably a redirect. */
+    return;
+  }
   basesrc = GST_BASE_SRC_CAST (src);
-  GST_DEBUG_OBJECT (src, "got chunk of %d bytes", msg->response.length);
+#ifdef SOUP_API_VERSION_2_4
+  data = chunk->data;
+  length = chunk->length;
+#else
+  data = msg->response.body;
+  length = msg->response.length;
+#endif
+  GST_DEBUG_OBJECT (src, "got chunk of %d bytes", length);
 
   /* Create the buffer. */
   src->ret = gst_pad_alloc_buffer (GST_BASE_SRC_PAD (basesrc),
-      basesrc->segment.last_stop, msg->response.length,
+      basesrc->segment.last_stop, length,
       GST_PAD_CAPS (GST_BASE_SRC_PAD (basesrc)), src->outbuf);
   if (G_LIKELY (src->ret == GST_FLOW_OK)) {
-    memcpy (GST_BUFFER_DATA (*src->outbuf), msg->response.body,
-        msg->response.length);
-    new_position = src->read_position + msg->response.length;
+    memcpy (GST_BUFFER_DATA (*src->outbuf), data, length);
+    new_position = src->read_position + length;
     if (G_LIKELY (src->request_position == src->read_position))
       src->request_position = new_position;
     src->read_position = new_position;
   }
 
   g_main_loop_quit (src->loop);
-  soup_message_io_pause (msg);
+  gst_souphttp_src_session_pause_message (src);
 }
 
 static void
-soup_response (SoupMessage * msg, gpointer user_data)
+#ifdef SOUP_API_VERSION_2_4
+gst_souphttp_src_response_cb (SoupSession *session, SoupMessage *msg,
+    GstSouphttpSrc *src)
+#else
+gst_souphttp_src_response_cb (SoupMessage *msg, GstSouphttpSrc *src)
+#endif
 {
-  GstSouphttpSrc *src = (GstSouphttpSrc *) user_data;
-
-  if (msg != src->msg) {
+  if (G_UNLIKELY (msg != src->msg)) {
     GST_DEBUG_OBJECT (src, "got response %d: %s, but not for current message",
         msg->status_code, msg->reason_phrase);
     return;
   }
+  if (G_UNLIKELY (src->session_io_status !=
+      GST_SOUPHTTP_SRC_SESSION_IO_STATUS_RUNNING)) {
+    /* Probably a redirect. */
+    return;
+  }
   GST_DEBUG_OBJECT (src, "got response %d: %s", msg->status_code,
       msg->reason_phrase);
-  soup_parse_status (msg, src);
+  gst_souphttp_src_parse_status (msg, src);
   g_main_loop_quit (src->loop);
 }
 
 static void
-soup_parse_status (SoupMessage * msg, GstSouphttpSrc * src)
+gst_souphttp_src_parse_status (SoupMessage *msg, GstSouphttpSrc *src)
 {
   if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
     switch (msg->status_code) {
@@ -761,6 +718,7 @@ soup_parse_status (SoupMessage * msg, Gs
         break;
     }
   } else if (SOUP_STATUS_IS_CLIENT_ERROR (msg->status_code) ||
+      SOUP_STATUS_IS_REDIRECTION (msg->status_code) ||
       SOUP_STATUS_IS_SERVER_ERROR (msg->status_code)) {
     /* Report HTTP error. */
     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
@@ -770,15 +728,246 @@ soup_parse_status (SoupMessage * msg, Gs
   }
 }
 
-static void
-soup_session_close (GstSouphttpSrc * src)
+static GstFlowReturn
+gst_souphttp_src_create (GstPushSrc * psrc, GstBuffer ** outbuf)
 {
-  if (src->session) {
-    soup_session_abort (src->session);  /* This unrefs the message. */
-    g_object_unref (src->session);
-    src->session = NULL;
-    src->msg = NULL;
+  GstSouphttpSrc *src;
+
+  src = GST_SOUPHTTP_SRC (psrc);
+
+  if (src->msg && (src->request_position != src->read_position)) {
+    if (src->session_io_status == GST_SOUPHTTP_SRC_SESSION_IO_STATUS_IDLE) {
+      gst_souphttp_src_add_range_header (src, src->request_position);
+    } else {
+      GST_DEBUG_OBJECT (src, "Seek from position %" G_GUINT64_FORMAT
+          " to %" G_GUINT64_FORMAT ": requeueing connection request",
+          src->read_position, src->request_position);
+      gst_souphttp_src_cancel_message (src);
+    }
+  }
+  if (!src->msg) {
+    src->msg = soup_message_new (SOUP_METHOD_GET, src->location);
+    if (!src->msg) {
+      GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
+          (NULL), ("Error parsing URL \"%s\"", src->location));
+      return GST_FLOW_ERROR;
+    }
+    src->session_io_status = GST_SOUPHTTP_SRC_SESSION_IO_STATUS_IDLE;
+    soup_message_headers_append (src->msg->request_headers, "Connection",
+        "close");
+    if (src->user_agent) {
+      soup_message_headers_append (src->msg->request_headers, "User-Agent",
+          src->user_agent);
+    }
+    if (src->iradio_mode) {
+      soup_message_headers_append (src->msg->request_headers, "icy-metadata",
+          "1");
+    }
+
+    g_signal_connect (src->msg, "got_headers",
+        G_CALLBACK (gst_souphttp_src_got_headers_cb), src);
+    g_signal_connect (src->msg, "got_body",
+        G_CALLBACK (gst_souphttp_src_got_body_cb), src);
+    g_signal_connect (src->msg, "finished",
+        G_CALLBACK (gst_souphttp_src_finished_cb), src);
+    g_signal_connect (src->msg, "got_chunk",
+        G_CALLBACK (gst_souphttp_src_got_chunk_cb), src);
+    soup_message_set_flags (src->msg, SOUP_MESSAGE_OVERWRITE_CHUNKS |
+        src->automatic_redirect ? 0 : SOUP_MESSAGE_NO_REDIRECT);
+    gst_souphttp_src_add_range_header (src, src->request_position);
+  }
+
+  src->ret = GST_FLOW_CUSTOM_ERROR;
+  src->outbuf = outbuf;
+  do {
+    if (src->interrupted) {
+      gst_souphttp_src_cancel_message (src);
+      break;
+    }
+    if (!src->msg) {
+      GST_DEBUG_OBJECT (src, "EOS reached");
+      break;
+    }
+
+    switch (src->session_io_status)
+    {
+      case GST_SOUPHTTP_SRC_SESSION_IO_STATUS_IDLE:
+        GST_DEBUG_OBJECT (src, "Queueing connection request");
+        gst_souphttp_src_queue_message (src);
+        break;
+      case GST_SOUPHTTP_SRC_SESSION_IO_STATUS_FINISHED:
+        GST_DEBUG_OBJECT (src, "Connection closed");
+        gst_souphttp_src_cancel_message (src);
+        break;
+      case GST_SOUPHTTP_SRC_SESSION_IO_STATUS_QUEUED:
+        break;
+      case GST_SOUPHTTP_SRC_SESSION_IO_STATUS_RUNNING:
+        gst_souphttp_src_session_unpause_message (src);
+        break;
+    }
+
+    if (src->ret == GST_FLOW_CUSTOM_ERROR)
+      g_main_loop_run (src->loop);
+  } while (src->ret == GST_FLOW_CUSTOM_ERROR);
+
+  if (src->ret == GST_FLOW_CUSTOM_ERROR)
+    src->ret = GST_FLOW_UNEXPECTED;
+  return src->ret;
+}
+
+static gboolean
+gst_souphttp_src_start (GstBaseSrc * bsrc)
+{
+  GstSouphttpSrc *src = GST_SOUPHTTP_SRC (bsrc);
+
+  GST_DEBUG_OBJECT (src, "start(\"%s\")", src->location);
+
+  if (!src->location) {
+    GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
+        (NULL), ("Missing location property"));
+    return FALSE;
+  }
+
+  src->context = g_main_context_new ();
+
+  src->loop = g_main_loop_new (src->context, TRUE);
+  if (!src->loop) {
+    GST_ELEMENT_ERROR (src, LIBRARY, INIT,
+        (NULL), ("Failed to start GMainLoop"));
+    g_main_context_unref (src->context);
+    return FALSE;
+  }
+
+  if (src->proxy == NULL)
+    src->session =
+        soup_session_async_new_with_options (SOUP_SESSION_ASYNC_CONTEXT,
+        src->context, NULL);
+  else
+    src->session =
+        soup_session_async_new_with_options (SOUP_SESSION_ASYNC_CONTEXT,
+        src->context, SOUP_SESSION_PROXY_URI, src->proxy, NULL);
+  if (!src->session) {
+    GST_ELEMENT_ERROR (src, LIBRARY, INIT,
+        (NULL), ("Failed to create async session"));
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/* Close the socket and associated resources
+ * used both to recover from errors and go to NULL state. */
+static gboolean
+gst_souphttp_src_stop (GstBaseSrc * bsrc)
+{
+  GstSouphttpSrc *src;
+
+  src = GST_SOUPHTTP_SRC (bsrc);
+  GST_DEBUG_OBJECT (src, "stop()");
+  gst_souphttp_src_session_close (src);
+  if (src->loop) {
+    g_main_loop_unref (src->loop);
+    g_main_context_unref (src->context);
+    src->loop = NULL;
+    src->context = NULL;
   }
+
+  return TRUE;
+}
+
+/* Interrupt a blocking request. */
+static gboolean
+gst_souphttp_src_unlock (GstBaseSrc * bsrc)
+{
+  GstSouphttpSrc *src;
+
+  src = GST_SOUPHTTP_SRC (bsrc);
+  GST_DEBUG_OBJECT (src, "unlock()");
+
+  src->interrupted = TRUE;
+  if (src->loop)
+    g_main_loop_quit (src->loop);
+  return TRUE;
+}
+
+/* Interrupt interrupt. */
+static gboolean
+gst_souphttp_src_unlock_stop (GstBaseSrc * bsrc)
+{
+  GstSouphttpSrc *src;
+
+  src = GST_SOUPHTTP_SRC (bsrc);
+  GST_DEBUG_OBJECT (src, "unlock_stop()");
+
+  src->interrupted = FALSE;
+  return TRUE;
+}
+
+static gboolean
+gst_souphttp_src_get_size (GstBaseSrc * bsrc, guint64 * size)
+{
+  GstSouphttpSrc *src;
+
+  src = GST_SOUPHTTP_SRC (bsrc);
+
+  if (src->have_size) {
+    GST_DEBUG_OBJECT (src, "get_size() = %" G_GUINT64_FORMAT,
+        src->content_size);
+    *size = src->content_size;
+    return TRUE;
+  }
+  GST_DEBUG_OBJECT (src, "get_size() = FALSE");
+  return FALSE;
+}
+
+static gboolean
+gst_souphttp_src_is_seekable (GstBaseSrc * bsrc)
+{
+  GstSouphttpSrc *src = GST_SOUPHTTP_SRC (bsrc);
+
+  return src->seekable;
+}
+
+static gboolean
+gst_souphttp_src_do_seek (GstBaseSrc * bsrc, GstSegment * segment)
+{
+  GstSouphttpSrc *src = GST_SOUPHTTP_SRC (bsrc);
+
+  GST_DEBUG_OBJECT (src, "do_seek(%" G_GUINT64_FORMAT ")", segment->start);
+
+  if (src->read_position == segment->start)
+    return TRUE;
+
+  if (!src->seekable)
+    return FALSE;
+
+  /* Wait for create() to handle the jump in offset. */
+  src->request_position = segment->start;
+  return TRUE;
+}
+
+static gboolean
+gst_souphttp_src_set_location (GstSouphttpSrc * src, const gchar * uri)
+{
+  if (src->location) {
+    g_free (src->location);
+    src->location = NULL;
+  }
+  src->location = g_strdup (uri);
+
+  return TRUE;
+}
+
+static gboolean
+gst_souphttp_src_set_proxy (GstSouphttpSrc * src, const gchar * uri)
+{
+  if (src->proxy) {
+    soup_uri_free (src->proxy);
+    src->proxy = NULL;
+  }
+  src->proxy = soup_uri_new (uri);
+
+  return TRUE;
 }
 
 static guint
@@ -824,8 +1013,6 @@ gst_souphttp_src_uri_handler_init (gpoin
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
-  /* note: do not upgrade rank before we depend on a libsoup version where
-   * icecast is supported properly out of the box */
   return gst_element_register (plugin, "souphttpsrc", GST_RANK_NONE,
       GST_TYPE_SOUPHTTP_SRC);
 }
