From 0f5a7f07d156f5bc62ba558dbcfdf9891a2523c3 Mon Sep 17 00:00:00 2001
From: Andrew Esh <andrew.esh@qterics.com>
Date: Tue, 8 Nov 2016 15:29:51 -0600
Subject: [PATCH] Add an internal length header to chunked encoding.

---
 ext/curl/gstcurlbasesink.c | 69 +++++++++++++++++-----------------------------
 ext/curl/gstcurltlssink.c  | 33 +++++++++++-----------
 2 files changed, 43 insertions(+), 59 deletions(-)

diff --git a/ext/curl/gstcurlbasesink.c b/ext/curl/gstcurlbasesink.c
index 5728ab2..14f7e28 100644
--- a/ext/curl/gstcurlbasesink.c
+++ b/ext/curl/gstcurlbasesink.c
@@ -63,6 +63,9 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 
+/* for htonl */
+#include <arpa/inet.h>
+
 #include "gstcurlbasesink.h"
 
 /* Default values */
@@ -747,22 +750,34 @@ static size_t
 transfer_data_buffer (void *curl_ptr, TransferBuffer * buf,
     size_t max_bytes_to_send, guint * last_chunk)
 {
-  guint buf_len = buf->len;
-  size_t bytes_to_send = MIN (max_bytes_to_send, buf->len);
+  /* Qterics specific changes to send the length of the chunk as a fixed set
+   * of 4 bytes at the beginning of every transfer, so that the backend web 
+   * server can use synchronous methods to extract chunks. The header should
+   * be a uint32 sent in network byte order. Note that this algorithm will fail
+   * if max_bytes_to_send is 4 or below.
+   */
+  guint32 header_buf;
+  size_t header_len = sizeof (header_buf);
+  size_t bytes_to_send = MIN (max_bytes_to_send, (buf->len + header_len));
+  size_t buf_bytes_to_send = bytes_to_send - header_len;
+
+  header_buf = htonl ((guint32) buf_bytes_to_send);
+  memcpy ((guint8 *) curl_ptr, (guint8 *) & header_buf, header_len);
+  memcpy (((guint8 *) curl_ptr) + header_len, buf->ptr + buf->offset,
+      buf_bytes_to_send);
+  buf->offset = buf->offset + buf_bytes_to_send;
+  buf->len = buf->len - buf_bytes_to_send;
 
-  memcpy ((guint8 *) curl_ptr, buf->ptr + buf->offset, bytes_to_send);
-  buf->offset = buf->offset + bytes_to_send;
-  buf->len = buf->len - bytes_to_send;
+  GST_LOG ("sent : %" G_GSIZE_FORMAT, bytes_to_send);
 
-  /* the last data chunk */
-  if (bytes_to_send == buf_len) {
+  /* return 0 when there are no more bytes to transfer, and signal the
+   * end of the transfer to the gst interfaces.
+   */
+  if (buf->len == 0) {
     buf->offset = 0;
-    buf->len = 0;
     *last_chunk = 1;
   }
 
-  GST_LOG ("sent : %" G_GSIZE_FORMAT, bytes_to_send);
-
   return bytes_to_send;
 }
 
@@ -901,16 +916,10 @@ handle_transfer (GstCurlBaseSink * sink)
 {
   GstCurlBaseSinkClass *klass = GST_CURL_BASE_SINK_GET_CLASS (sink);
   gint retval;
-  gint activated_fds;
   gint running_handles;
-  gint timeout;
   CURLMcode m_code;
   CURLcode e_code;
 
-  GST_OBJECT_LOCK (sink);
-  timeout = sink->timeout;
-  GST_OBJECT_UNLOCK (sink);
-
   GST_DEBUG_OBJECT (sink, "handling transfers");
 
   /* Receiving CURLM_CALL_MULTI_PERFORM means that libcurl may have more data
@@ -926,35 +935,9 @@ handle_transfer (GstCurlBaseSink * sink)
       klass->transfer_prepare_poll_wait (sink);
     }
 
-    activated_fds = gst_poll_wait (sink->fdset, timeout * GST_SECOND);
-    if (G_UNLIKELY (activated_fds == -1)) {
-      if (errno == EAGAIN || errno == EINTR) {
-        GST_DEBUG_OBJECT (sink, "interrupted by signal");
-      } else if (errno == EBUSY) {
-        GST_DEBUG_OBJECT (sink, "poll stopped");
-        retval = GST_FLOW_EOS;
-
-        GST_OBJECT_LOCK (sink);
-        if (gst_curl_base_sink_has_buffered_data_unlocked (sink))
-          GST_WARNING_OBJECT (sink,
-              "discarding render data due to thread close flag");
-        GST_OBJECT_UNLOCK (sink);
-
-        goto fail;
-      } else {
-        sink->error = g_strdup_printf ("poll failed: %s", g_strerror (errno));
-        retval = GST_FLOW_ERROR;
-        goto fail;
-      }
-    } else if (G_UNLIKELY (activated_fds == 0)) {
-      sink->error = g_strdup_printf ("poll timed out after %" GST_TIME_FORMAT,
-          GST_TIME_ARGS (timeout * GST_SECOND));
-      retval = GST_FLOW_ERROR;
-      goto fail;
-    }
-
     /* readable/writable sockets */
     do {
+      usleep (50000);
       m_code = curl_multi_perform (sink->multi_handle, &running_handles);
     } while (m_code == CURLM_CALL_MULTI_PERFORM);
     GST_DEBUG_OBJECT (sink, "running handles: %d", running_handles);
diff --git a/ext/curl/gstcurltlssink.c b/ext/curl/gstcurltlssink.c
index 35c1c1c..2e4ea99 100644
--- a/ext/curl/gstcurltlssink.c
+++ b/ext/curl/gstcurltlssink.c
@@ -247,22 +247,23 @@ gst_curl_tls_sink_set_options_unlocked (GstCurlBaseSink * bcsink)
   }
 
   /* crypto engine */
-  if ((sink->crypto_engine == NULL) ||
-      (strcmp (sink->crypto_engine, "auto") == 0)) {
-    res = curl_easy_setopt (bcsink->curl, CURLOPT_SSLENGINE_DEFAULT, 1L);
-    if (res != CURLE_OK) {
-      bcsink->error =
-          g_strdup_printf ("failed to set default crypto engine: %s",
-          curl_easy_strerror (res));
-      return FALSE;
-    }
-  } else {
-    res = curl_easy_setopt (bcsink->curl, CURLOPT_SSLENGINE,
-        sink->crypto_engine);
-    if (res != CURLE_OK) {
-      bcsink->error = g_strdup_printf ("failed to set crypto engine: %s",
-          curl_easy_strerror (res));
-      return FALSE;
+  if (sink->crypto_engine != NULL) {
+    if (strcmp (sink->crypto_engine, "auto") == 0) {
+      res = curl_easy_setopt (bcsink->curl, CURLOPT_SSLENGINE_DEFAULT, 1L);
+      if (res != CURLE_OK) {
+        bcsink->error =
+            g_strdup_printf ("failed to set default crypto engine: %s",
+            curl_easy_strerror (res));
+        return FALSE;
+      }
+    } else {
+      res = curl_easy_setopt (bcsink->curl, CURLOPT_SSLENGINE,
+          sink->crypto_engine);
+      if (res != CURLE_OK) {
+        bcsink->error = g_strdup_printf ("failed to set crypto engine: %s",
+            curl_easy_strerror (res));
+        return FALSE;
+      }
     }
   }
 
-- 
2.10.2

