areguera / rpms / cockpit

Forked from rpms/cockpit 5 years ago
Clone

Blame SOURCES/chunked-streaming.patch

b6b72a
From f5d7586a12f5313d6301ba96aadaa06d84f2fc21 Mon Sep 17 00:00:00 2001
b6b72a
From: petervo <petervo@redhat.com>
b6b72a
Date: Fri, 24 Apr 2015 02:26:24 -0700
b6b72a
Subject: [PATCH] bridge: Fix bug with streaming chunked data
b6b72a
b6b72a
Fixes #2170
b6b72a
Closes #2204
b6b72a
Signed-off-by: Stef Walter <stefw@redhat.com>
b6b72a
 * Tweak the comments a bit, change order of checks
b6b72a
---
b6b72a
 src/bridge/cockpithttpstream.c |   6 ++
b6b72a
 src/bridge/mock-transport.c    |  25 ++++++++
b6b72a
 src/bridge/mock-transport.h    |   4 ++
b6b72a
 src/bridge/test-httpstream.c   | 132 +++++++++++++++++++++++++++++++++++++++++
b6b72a
 src/bridge/test-packages.c     |  30 +---------
b6b72a
 5 files changed, 170 insertions(+), 27 deletions(-)
b6b72a
b6b72a
diff --git a/src/bridge/cockpithttpstream.c b/src/bridge/cockpithttpstream.c
b6b72a
index d9ee3df..0e573a9 100644
b6b72a
--- a/src/bridge/cockpithttpstream.c
b6b72a
+++ b/src/bridge/cockpithttpstream.c
b6b72a
@@ -426,6 +426,12 @@ relay_chunked (CockpitHttpStream *self,
b6b72a
     return FALSE; /* want more data */
b6b72a
 
b6b72a
   beg = (pos + 2) - data;
b6b72a
+  if (length < beg)
b6b72a
+    {
b6b72a
+      /* have to have a least the ending chars */
b6b72a
+      return FALSE; /* want more data */
b6b72a
+    }
b6b72a
+
b6b72a
   size = g_ascii_strtoull (data, &end, 16);
b6b72a
   if (pos[1] != '\n' || end != pos)
b6b72a
     {
b6b72a
diff --git a/src/bridge/mock-transport.c b/src/bridge/mock-transport.c
b6b72a
index 4ad2b41..8ff0892 100644
b6b72a
--- a/src/bridge/mock-transport.c
b6b72a
+++ b/src/bridge/mock-transport.c
b6b72a
@@ -194,3 +194,28 @@ mock_transport_count_sent (MockTransport *mock)
b6b72a
 {
b6b72a
   return mock->count;
b6b72a
 }
b6b72a
+
b6b72a
+GBytes *
b6b72a
+mock_transport_combine_output (MockTransport *transport,
b6b72a
+                               const gchar *channel_id,
b6b72a
+                               guint *count)
b6b72a
+{
b6b72a
+  GByteArray *combined;
b6b72a
+  GBytes *block;
b6b72a
+
b6b72a
+  if (count)
b6b72a
+    *count = 0;
b6b72a
+
b6b72a
+  combined = g_byte_array_new ();
b6b72a
+  for (;;)
b6b72a
+    {
b6b72a
+      block = mock_transport_pop_channel (transport, channel_id);
b6b72a
+      if (!block)
b6b72a
+        break;
b6b72a
+
b6b72a
+      g_byte_array_append (combined, g_bytes_get_data (block, NULL), g_bytes_get_size (block));
b6b72a
+      if (count)
b6b72a
+        (*count)++;
b6b72a
+    }
b6b72a
+  return g_byte_array_free_to_bytes (combined);
b6b72a
+}
b6b72a
diff --git a/src/bridge/mock-transport.h b/src/bridge/mock-transport.h
b6b72a
index 6722870..e824051 100644
b6b72a
--- a/src/bridge/mock-transport.h
b6b72a
+++ b/src/bridge/mock-transport.h
b6b72a
@@ -49,4 +49,8 @@ JsonObject *         mock_transport_pop_control   (MockTransport *mock);
b6b72a
 GBytes *             mock_transport_pop_channel   (MockTransport *mock,
b6b72a
                                                    const gchar *channel);
b6b72a
 
b6b72a
+GBytes *             mock_transport_combine_output (MockTransport *transport,
b6b72a
+                                                    const gchar *channel_id,
b6b72a
+                                                    guint *count);
b6b72a
+
b6b72a
 #endif /* MOCK_TRANSPORT_H */
b6b72a
diff --git a/src/bridge/test-httpstream.c b/src/bridge/test-httpstream.c
b6b72a
index 68b27ae..a4ef436 100644
b6b72a
--- a/src/bridge/test-httpstream.c
b6b72a
+++ b/src/bridge/test-httpstream.c
b6b72a
@@ -23,6 +23,8 @@
b6b72a
 #include "cockpithttpstream.h"
b6b72a
 #include "cockpithttpstream.c"
b6b72a
 #include "common/cockpittest.h"
b6b72a
+#include "common/cockpitwebresponse.h"
b6b72a
+#include "common/cockpitwebserver.h"
b6b72a
 
b6b72a
 #include "mock-transport.h"
b6b72a
 #include <json-glib/json-glib.h>
b6b72a
@@ -31,6 +33,135 @@
b6b72a
  * Test
b6b72a
  */
b6b72a
 
b6b72a
+typedef struct {
b6b72a
+  gchar *problem;
b6b72a
+  gboolean done;
b6b72a
+} TestResult;
b6b72a
+
b6b72a
+/*
b6b72a
+ * Yes this is a magic number. It's the lowest number that would
b6b72a
+ * trigger a bug where chunked data would be rejected due to an incomplete read.
b6b72a
+ */
b6b72a
+const gint MAGIC_NUMBER = 3068;
b6b72a
+
b6b72a
+static gboolean
b6b72a
+handle_chunked (CockpitWebServer *server,
b6b72a
+                const gchar *path,
b6b72a
+                GHashTable *headers,
b6b72a
+                CockpitWebResponse *response,
b6b72a
+                gpointer user_data)
b6b72a
+{
b6b72a
+  GBytes *bytes;
b6b72a
+  GHashTable *h = g_hash_table_new (g_str_hash,  g_str_equal);
b6b72a
+
b6b72a
+  cockpit_web_response_headers_full (response, 200,
b6b72a
+                                     "OK", -1, h);
b6b72a
+  bytes = g_bytes_new_take (g_strdup_printf ("%0*d",
b6b72a
+                                             MAGIC_NUMBER, 0),
b6b72a
+                            MAGIC_NUMBER);
b6b72a
+  cockpit_web_response_queue (response, bytes);
b6b72a
+  cockpit_web_response_complete (response);
b6b72a
+
b6b72a
+  g_bytes_unref (bytes);
b6b72a
+  g_hash_table_unref (h);
b6b72a
+  return TRUE;
b6b72a
+}
b6b72a
+
b6b72a
+static void
b6b72a
+on_channel_close (CockpitChannel *channel,
b6b72a
+                  const gchar *problem,
b6b72a
+                  gpointer user_data)
b6b72a
+{
b6b72a
+  TestResult *tr = user_data;
b6b72a
+  g_assert (tr->done == FALSE);
b6b72a
+  tr->done = TRUE;
b6b72a
+  tr->problem = g_strdup (problem);
b6b72a
+}
b6b72a
+
b6b72a
+static void
b6b72a
+on_transport_closed (CockpitTransport *transport,
b6b72a
+                     const gchar *problem,
b6b72a
+                     gpointer user_data)
b6b72a
+{
b6b72a
+  g_assert_not_reached ();
b6b72a
+}
b6b72a
+
b6b72a
+static void
b6b72a
+test_http_chunked (void)
b6b72a
+{
b6b72a
+  MockTransport *transport = NULL;
b6b72a
+  CockpitChannel *channel = NULL;
b6b72a
+  CockpitWebServer *web_server = NULL;
b6b72a
+  JsonObject *options = NULL;
b6b72a
+  JsonObject *headers = NULL;
b6b72a
+  TestResult *tr = g_slice_new (TestResult);
b6b72a
+
b6b72a
+  GBytes *bytes = NULL;
b6b72a
+  GBytes *data = NULL;
b6b72a
+
b6b72a
+  const gchar *control;
b6b72a
+  gchar *expected = g_strdup_printf ("{\"status\":200,\"reason\":\"OK\",\"headers\":{}}%0*d", MAGIC_NUMBER, 0);
b6b72a
+  guint count;
b6b72a
+  guint port;
b6b72a
+
b6b72a
+  web_server = cockpit_web_server_new (0, NULL,
b6b72a
+                                      NULL, NULL, NULL);
b6b72a
+  g_assert (web_server);
b6b72a
+  port = cockpit_web_server_get_port (web_server);
b6b72a
+  g_signal_connect (web_server, "handle-resource::/",
b6b72a
+                    G_CALLBACK (handle_chunked), NULL);
b6b72a
+
b6b72a
+  transport = mock_transport_new ();
b6b72a
+  g_signal_connect (transport, "closed", G_CALLBACK (on_transport_closed), NULL);
b6b72a
+
b6b72a
+  options = json_object_new ();
b6b72a
+  json_object_set_int_member (options, "port", port);
b6b72a
+  json_object_set_string_member (options, "payload", "http-stream1");
b6b72a
+  json_object_set_string_member (options, "method", "GET");
b6b72a
+  json_object_set_string_member (options, "path", "/");
b6b72a
+
b6b72a
+  headers = json_object_new ();
b6b72a
+  json_object_set_string_member (headers, "Pragma", "no-cache");
b6b72a
+  json_object_set_object_member (options, "headers", headers);
b6b72a
+
b6b72a
+  channel = g_object_new (COCKPIT_TYPE_HTTP_STREAM,
b6b72a
+                              "transport", transport,
b6b72a
+                              "id", "444",
b6b72a
+                              "options", options,
b6b72a
+                              NULL);
b6b72a
+
b6b72a
+  json_object_unref (options);
b6b72a
+
b6b72a
+  /* Tell HTTP we have no more data to send */
b6b72a
+  control = "{\"command\": \"done\", \"channel\": \"444\"}";
b6b72a
+  bytes = g_bytes_new_static (control, strlen (control));
b6b72a
+  cockpit_transport_emit_recv (COCKPIT_TRANSPORT (transport), NULL, bytes);
b6b72a
+  g_bytes_unref (bytes);
b6b72a
+
b6b72a
+  tr->done = FALSE;
b6b72a
+  g_signal_connect (channel, "closed", G_CALLBACK (on_channel_close), tr);
b6b72a
+
b6b72a
+  while (tr->done == FALSE)
b6b72a
+    g_main_context_iteration (NULL, TRUE);
b6b72a
+  g_assert_cmpstr (tr->problem, ==, NULL);
b6b72a
+
b6b72a
+  data = mock_transport_combine_output (transport, "444", &count);
b6b72a
+  cockpit_assert_bytes_eq (data, expected, -1);
b6b72a
+  g_assert_cmpuint (count, ==, 2);
b6b72a
+
b6b72a
+  g_bytes_unref (data);
b6b72a
+  g_free (expected);
b6b72a
+
b6b72a
+  g_object_unref (transport);
b6b72a
+  g_object_add_weak_pointer (G_OBJECT (channel), (gpointer *)&channel);
b6b72a
+  g_object_unref (channel);
b6b72a
+  g_assert (channel == NULL);
b6b72a
+  g_clear_object (&web_server);
b6b72a
+
b6b72a
+  g_free (tr->problem);
b6b72a
+  g_slice_free (TestResult, tr);
b6b72a
+}
b6b72a
+
b6b72a
 static void
b6b72a
 test_parse_keep_alive (void)
b6b72a
 {
b6b72a
@@ -82,6 +213,7 @@ main (int argc,
b6b72a
 {
b6b72a
   cockpit_test_init (&argc, &argv);
b6b72a
   g_test_add_func  ("/http-stream/parse_keepalive", test_parse_keep_alive);
b6b72a
+  g_test_add_func  ("/http-stream/http_chunked", test_http_chunked);
b6b72a
 
b6b72a
   return g_test_run ();
b6b72a
 }
b6b72a
diff --git a/src/bridge/test-packages.c b/src/bridge/test-packages.c
b6b72a
index 09596b9..dbfb6d6 100644
b6b72a
--- a/src/bridge/test-packages.c
b6b72a
+++ b/src/bridge/test-packages.c
b6b72a
@@ -146,30 +146,6 @@ teardown (TestCase *tc,
b6b72a
   cockpit_bridge_data_dirs = NULL;
b6b72a
 }
b6b72a
 
b6b72a
-static GBytes *
b6b72a
-combine_output (TestCase *tc,
b6b72a
-                guint *count)
b6b72a
-{
b6b72a
-  GByteArray *combined;
b6b72a
-  GBytes *block;
b6b72a
-
b6b72a
-  if (count)
b6b72a
-    *count = 0;
b6b72a
-
b6b72a
-  combined = g_byte_array_new ();
b6b72a
-  for (;;)
b6b72a
-    {
b6b72a
-      block = mock_transport_pop_channel (tc->transport, "444");
b6b72a
-      if (!block)
b6b72a
-        break;
b6b72a
-
b6b72a
-      g_byte_array_append (combined, g_bytes_get_data (block, NULL), g_bytes_get_size (block));
b6b72a
-      if (count)
b6b72a
-        (*count)++;
b6b72a
-    }
b6b72a
-  return g_byte_array_free_to_bytes (combined);
b6b72a
-}
b6b72a
-
b6b72a
 static const Fixture fixture_simple = {
b6b72a
   .path = "/test/sub/file.ext",
b6b72a
 };
b6b72a
@@ -187,7 +163,7 @@ test_simple (TestCase *tc,
b6b72a
     g_main_context_iteration (NULL, TRUE);
b6b72a
   g_assert_cmpstr (tc->problem, ==, NULL);
b6b72a
 
b6b72a
-  data = combine_output (tc, &count);
b6b72a
+  data = mock_transport_combine_output (tc->transport, "444", &count);
b6b72a
   cockpit_assert_bytes_eq (data, "{\"status\":200,\"reason\":\"OK\",\"headers\":{}}"
b6b72a
                            "These are the contents of file.ext\nOh marmalaaade\n", -1);
b6b72a
   g_assert_cmpuint (count, ==, 2);
b6b72a
@@ -220,7 +196,7 @@ test_large (TestCase *tc,
b6b72a
                        &contents, &length, &error);
b6b72a
   g_assert_no_error (error);
b6b72a
 
b6b72a
-  data = combine_output (tc, &count);
b6b72a
+  data = mock_transport_combine_output (tc->transport, "444", &count);
b6b72a
 
b6b72a
   /* Should not have been sent as one block */
b6b72a
   g_assert_cmpuint (count, ==, 8);
b6b72a
@@ -442,7 +418,7 @@ test_list_bad_name (TestCase *tc,
b6b72a
     g_main_context_iteration (NULL, TRUE);
b6b72a
   g_assert_cmpstr (tc->problem, ==, NULL);
b6b72a
 
b6b72a
-  data = combine_output (tc, &count);
b6b72a
+  data = mock_transport_combine_output (tc->transport, "444", &count);
b6b72a
   cockpit_assert_bytes_eq (data, "{\"status\":200,\"reason\":\"OK\",\"headers\":"
b6b72a
                                      "{\"Content-Type\":\"application/json\"}}"
b6b72a
                                  "{\"ok\":{}}", -1);
b6b72a
-- 
b6b72a
2.3.5
b6b72a