|
|
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 |
|