/* * This file is part of Cockpit. * * Copyright (C) 2015 Red Hat, Inc. * * Cockpit is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * Cockpit is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Cockpit; If not, see . */ #include "config.h" #include "mock-auth.h" #include "cockpitws.h" #include "cockpitcreds.h" #include "cockpitchannelresponse.h" #include "common/cockpitpipetransport.h" #include "common/cockpittransport.h" #include "common/cockpitjson.h" #include "common/cockpittest.h" #include "common/mock-io-stream.h" #include "common/cockpitwebserver.h" #include "common/cockpitconf.h" #include "websocket/websocket.h" #include #include #include #include #include #include /* * To recalculate the checksums found in this file, do something like: * $ XDG_DATA_DIRS=$PWD/src/bridge/mock-resource/system/ XDG_DATA_HOME=/nonexistant cockpit-bridge --packages */ #define PASSWORD "this is the password" typedef struct { CockpitWebService *service; GIOStream *io; GMemoryOutputStream *output; CockpitPipe *pipe; GHashTable *headers; } TestResourceCase; typedef struct { const gchar *xdg_data_home; gboolean org_path; } TestResourceFixture; static void on_init_ready (GObject *object, GAsyncResult *result, gpointer data) { gboolean *flag = data; g_assert (*flag == FALSE); cockpit_web_service_get_init_message_finish (COCKPIT_WEB_SERVICE (object), result); *flag = TRUE; } static void setup_resource (TestResourceCase *tc, gconstpointer data) { const TestResourceFixture *fixture = data; CockpitTransport *transport; GInputStream *input; GOutputStream *output; CockpitCreds *creds; gchar **environ; const gchar *user; const gchar *home = NULL; gboolean ready = FALSE; GBytes *password; const gchar *argv[] = { BUILDDIR "/cockpit-bridge", NULL }; environ = g_get_environ (); environ = g_environ_setenv (environ, "XDG_DATA_DIRS", SRCDIR "/src/bridge/mock-resource/system", TRUE); if (fixture) home = fixture->xdg_data_home; if (!home) home = SRCDIR "/src/bridge/mock-resource/home"; environ = g_environ_setenv (environ, "XDG_DATA_HOME", home, TRUE); /* Start up a cockpit-bridge here */ tc->pipe = cockpit_pipe_spawn (argv, (const gchar **)environ, NULL, COCKPIT_PIPE_FLAGS_NONE); g_strfreev (environ); user = g_get_user_name (); password = g_bytes_new_take (g_strdup (PASSWORD), strlen (PASSWORD)); creds = cockpit_creds_new ("cockpit", COCKPIT_CRED_USER, user, COCKPIT_CRED_PASSWORD, password, NULL); g_bytes_unref (password); transport = cockpit_pipe_transport_new (tc->pipe); tc->service = cockpit_web_service_new (creds, transport); /* Manually created services won't be init'd yet, * wait for that before sending data */ cockpit_web_service_get_init_message_aysnc (tc->service, on_init_ready, &ready); while (!ready) g_main_context_iteration (NULL, TRUE); g_object_unref (transport); cockpit_creds_unref (creds); input = g_memory_input_stream_new_from_data ("", 0, NULL); output = g_memory_output_stream_new (NULL, 0, g_realloc, g_free); tc->io = mock_io_stream_new (input, output); tc->output = G_MEMORY_OUTPUT_STREAM (output); g_object_unref (input); tc->headers = cockpit_web_server_new_table (); g_hash_table_insert (tc->headers, g_strdup ("Accept-Encoding"), g_strdup ("gzip, identity")); } static void teardown_resource (TestResourceCase *tc, gconstpointer data) { cockpit_assert_expected (); g_hash_table_unref (tc->headers); g_object_add_weak_pointer (G_OBJECT (tc->service), (gpointer *)&tc->service); g_object_unref (tc->service); g_assert (tc->service == NULL); g_object_unref (tc->io); g_object_unref (tc->output); g_object_unref (tc->pipe); } static void test_resource_simple (TestResourceCase *tc, gconstpointer data) { CockpitWebResponse *response; GError *error = NULL; GBytes *bytes; const gchar *url = "/@localhost/another/test.html"; response = cockpit_web_response_new (tc->io, url, url, NULL, NULL); cockpit_channel_response_serve (tc->service, tc->headers, response, "@localhost", "/another/test.html"); while (cockpit_web_response_get_state (response) != COCKPIT_WEB_RESPONSE_SENT) g_main_context_iteration (NULL, TRUE); g_output_stream_close (G_OUTPUT_STREAM (tc->output), NULL, &error); g_assert_no_error (error); bytes = g_memory_output_stream_steal_as_bytes (tc->output); cockpit_assert_bytes_eq (bytes, "HTTP/1.1 200 OK\r\n" "Content-Security-Policy: default-src 'self' http://localhost; connect-src 'self' http://localhost ws: wss:\r\n" "Content-Type: text/html\r\n" "Cache-Control: no-cache, no-store\r\n" "Access-Control-Allow-Origin: http://localhost\r\n" "Transfer-Encoding: chunked\r\n" "Vary: Cookie\r\n" "\r\n" "52\r\n" "\n" "\n" "In home dir\n" "\n" "In home dir\n" "\n" "\r\n" "0\r\n\r\n", -1); g_bytes_unref (bytes); g_object_unref (response); } static void test_resource_simple_host (TestResourceCase *tc, gconstpointer data) { CockpitWebResponse *response; GError *error = NULL; GBytes *bytes; const gchar *url = "/@localhost/another/test.html"; g_hash_table_insert (tc->headers, g_strdup ("Host"), g_strdup ("my.host")); response = cockpit_web_response_new (tc->io, url, url, NULL, NULL); cockpit_channel_response_serve (tc->service, tc->headers, response, "@localhost", "/another/test.html"); while (cockpit_web_response_get_state (response) != COCKPIT_WEB_RESPONSE_SENT) g_main_context_iteration (NULL, TRUE); g_output_stream_close (G_OUTPUT_STREAM (tc->output), NULL, &error); g_assert_no_error (error); bytes = g_memory_output_stream_steal_as_bytes (tc->output); cockpit_assert_bytes_eq (bytes, "HTTP/1.1 200 OK\r\n" "Content-Security-Policy: default-src 'self' http://my.host; connect-src 'self' http://my.host ws: wss:\r\n" "Content-Type: text/html\r\n" "Cache-Control: no-cache, no-store\r\n" "Access-Control-Allow-Origin: http://my.host\r\n" "Transfer-Encoding: chunked\r\n" "Vary: Cookie\r\n" "\r\n" "52\r\n" "\n" "\n" "In home dir\n" "\n" "In home dir\n" "\n" "\r\n" "0\r\n\r\n", -1); g_bytes_unref (bytes); g_object_unref (response); } static void test_resource_language (TestResourceCase *tc, gconstpointer data) { CockpitWebResponse *response; GError *error = NULL; GBytes *bytes; gchar *url = "/@localhost/another/test.html"; response = cockpit_web_response_new (tc->io, url, url, NULL, NULL); g_hash_table_insert (tc->headers, g_strdup ("Accept-Language"), g_strdup ("pig, blah")); cockpit_channel_response_serve (tc->service, tc->headers, response, "@localhost", "/another/test.html"); while (cockpit_web_response_get_state (response) != COCKPIT_WEB_RESPONSE_SENT) g_main_context_iteration (NULL, TRUE); g_output_stream_close (G_OUTPUT_STREAM (tc->output), NULL, &error); g_assert_no_error (error); bytes = g_memory_output_stream_steal_as_bytes (tc->output); cockpit_assert_bytes_eq (bytes, "HTTP/1.1 200 OK\r\n" "Content-Security-Policy: default-src 'self' http://localhost; connect-src 'self' http://localhost ws: wss:\r\n" "Content-Type: text/html\r\n" "Cache-Control: no-cache, no-store\r\n" "Access-Control-Allow-Origin: http://localhost\r\n" "Transfer-Encoding: chunked\r\n" "Vary: Cookie\r\n" "\r\n" "60\r\n" "\n" "\n" "Inlay omehay irday\n" "\n" "Inlay omehay irday\n" "\n" "\r\n" "0\r\n\r\n", -1); g_bytes_unref (bytes); g_object_unref (response); } static void test_resource_cookie (TestResourceCase *tc, gconstpointer data) { CockpitWebResponse *response; GError *error = NULL; GBytes *bytes; const gchar *url = "/@localhost/another/test.html"; response = cockpit_web_response_new (tc->io, url, url, NULL, NULL); g_hash_table_insert (tc->headers, g_strdup ("Cookie"), g_strdup ("CockpitLang=pig")); cockpit_channel_response_serve (tc->service, tc->headers, response, "@localhost", "/another/test.html"); while (cockpit_web_response_get_state (response) != COCKPIT_WEB_RESPONSE_SENT) g_main_context_iteration (NULL, TRUE); g_output_stream_close (G_OUTPUT_STREAM (tc->output), NULL, &error); g_assert_no_error (error); bytes = g_memory_output_stream_steal_as_bytes (tc->output); cockpit_assert_bytes_eq (bytes, "HTTP/1.1 200 OK\r\n" "Content-Security-Policy: default-src 'self' http://localhost; connect-src 'self' http://localhost ws: wss:\r\n" "Content-Type: text/html\r\n" "Cache-Control: no-cache, no-store\r\n" "Access-Control-Allow-Origin: http://localhost\r\n" "Transfer-Encoding: chunked\r\n" "Vary: Cookie\r\n" "\r\n" "60\r\n" "\n" "\n" "Inlay omehay irday\n" "\n" "Inlay omehay irday\n" "\n" "\r\n" "0\r\n\r\n", -1); g_bytes_unref (bytes); g_object_unref (response); } static void test_resource_not_found (TestResourceCase *tc, gconstpointer data) { CockpitWebResponse *response; GError *error = NULL; GBytes *bytes; const gchar *url = "/cockpit/another@localhost/not-exist"; response = cockpit_web_response_new (tc->io, url, url, NULL, NULL); cockpit_channel_response_serve (tc->service, tc->headers, response, "another@localhost", "/not-exist"); while (cockpit_web_response_get_state (response) != COCKPIT_WEB_RESPONSE_SENT) g_main_context_iteration (NULL, TRUE); g_output_stream_close (G_OUTPUT_STREAM (tc->output), NULL, &error); g_assert_no_error (error); bytes = g_memory_output_stream_steal_as_bytes (tc->output); cockpit_assert_bytes_eq (bytes, "HTTP/1.1 404 Not Found\r\n" "Content-Type: text/html; charset=utf8\r\n" "Transfer-Encoding: chunked\r\n" "\r\n13\r\n" "\r\n9\r\n" "Not Found\r\n15\r\n" "\r\n9\r\n" "Not Found\r\nf\r\n" "\n\r\n0\r\n\r\n", -1); g_bytes_unref (bytes); g_object_unref (response); } static void test_resource_no_path (TestResourceCase *tc, gconstpointer data) { CockpitWebResponse *response; GError *error = NULL; GBytes *bytes; const gchar *url = "/cockpit/another@localhost"; /* Missing path after package */ response = cockpit_web_response_new (tc->io, url, url, NULL, NULL); cockpit_channel_response_serve (tc->service, tc->headers, response, "another@localhost", ""); while (cockpit_web_response_get_state (response) != COCKPIT_WEB_RESPONSE_SENT) g_main_context_iteration (NULL, TRUE); g_output_stream_close (G_OUTPUT_STREAM (tc->output), NULL, &error); g_assert_no_error (error); bytes = g_memory_output_stream_steal_as_bytes (tc->output); cockpit_assert_bytes_eq (bytes, "HTTP/1.1 404 Not Found\r\n" "Content-Type: text/html; charset=utf8\r\n" "Transfer-Encoding: chunked\r\n" "\r\n13\r\n" "\r\n9\r\n" "Not Found\r\n15\r\n" "\r\n9\r\n" "Not Found\r\nf\r\n" "\n\r\n0\r\n\r\n", -1); g_bytes_unref (bytes); g_object_unref (response); } static void test_resource_failure (TestResourceCase *tc, gconstpointer data) { CockpitWebResponse *response; GError *error = NULL; GBytes *bytes; GPid pid; cockpit_expect_message ("*: external channel failed: terminated"); /* Now kill the bridge */ g_assert (cockpit_pipe_get_pid (tc->pipe, &pid)); g_assert_cmpint (pid, >, 0); g_assert_cmpint (kill (pid, SIGTERM), ==, 0); /* Wait until it's gone; we can't use waitpid(), it interferes with GChildWatch */ while (kill (pid, 0) >= 0) g_usleep (1000); response = cockpit_web_response_new (tc->io, "/unused", "/unused", NULL, NULL); cockpit_channel_response_serve (tc->service, tc->headers, response, "@localhost", "/another/test.html"); while (cockpit_web_response_get_state (response) != COCKPIT_WEB_RESPONSE_SENT) g_main_context_iteration (NULL, TRUE); g_output_stream_close (G_OUTPUT_STREAM (tc->output), NULL, &error); g_assert_no_error (error); bytes = g_memory_output_stream_steal_as_bytes (tc->output); cockpit_assert_bytes_eq (bytes, "HTTP/1.1 500 Internal Server Error\r\n" "Content-Type: text/html; charset=utf8\r\n" "Transfer-Encoding: chunked\r\n" "\r\n13\r\n" "\r\n15\r\n" "Internal Server Error\r\n15\r\n" "\r\n15\r\n" "Internal Server Error\r\nf\r\n" "\n\r\n0\r\n\r\n", -1); g_bytes_unref (bytes); g_object_unref (response); } static const TestResourceFixture checksum_fixture = { .xdg_data_home = "/nonexistant" }; static void request_checksum (TestResourceCase *tc) { CockpitWebResponse *response; GInputStream *input; GOutputStream *output; GIOStream *io; input = g_memory_input_stream_new_from_data ("", 0, NULL); output = g_memory_output_stream_new (NULL, 0, g_realloc, g_free); io = mock_io_stream_new (input, output); g_object_unref (input); /* Start the connection up, and poke it a bit */ response = cockpit_web_response_new (io, "/unused", "/unused", NULL, NULL); cockpit_channel_response_serve (tc->service, tc->headers, response, "@localhost", "/checksum"); while (cockpit_web_response_get_state (response) != COCKPIT_WEB_RESPONSE_SENT) g_main_context_iteration (NULL, TRUE); g_object_unref (io); /* Use this when the checksum changes, due to mock resource changes */ #if 0 bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (output)); g_printerr ("%.*s\n", (gint)g_bytes_get_size (bytes), (gchar *)g_bytes_get_data (bytes, NULL)); g_bytes_unref (bytes); #endif g_object_unref (output); g_object_unref (response); } static void test_resource_checksum (TestResourceCase *tc, gconstpointer data) { CockpitWebResponse *response; GError *error = NULL; GBytes *bytes; /* We require that no user packages are loaded, so we have a checksum */ g_assert (data == &checksum_fixture); request_checksum (tc); response = cockpit_web_response_new (tc->io, "/unused", "/unused", NULL, NULL); cockpit_channel_response_serve (tc->service, tc->headers, response, "$060119c2a544d8e5becd0f74f9dcde146b8d99e3", "/test/sub/file.ext"); while (cockpit_web_response_get_state (response) != COCKPIT_WEB_RESPONSE_SENT) g_main_context_iteration (NULL, TRUE); g_output_stream_close (G_OUTPUT_STREAM (tc->output), NULL, &error); g_assert_no_error (error); bytes = g_memory_output_stream_steal_as_bytes (tc->output); cockpit_assert_bytes_eq (bytes, "HTTP/1.1 200 OK\r\n" "ETag: \"$060119c2a544d8e5becd0f74f9dcde146b8d99e3-c\"\r\n" "Access-Control-Allow-Origin: http://localhost\r\n" "Transfer-Encoding: chunked\r\n" "Cache-Control: max-age=31556926, public\r\n" "\r\n" "32\r\n" "These are the contents of file.ext\nOh marmalaaade\n" "\r\n" "0\r\n\r\n", -1); g_bytes_unref (bytes); g_object_unref (response); } static void test_resource_not_modified (TestResourceCase *tc, gconstpointer data) { CockpitWebResponse *response; GError *error = NULL; GBytes *bytes; request_checksum (tc); g_hash_table_insert (tc->headers, g_strdup ("If-None-Match"), g_strdup ("\"$060119c2a544d8e5becd0f74f9dcde146b8d99e3-c\"")); response = cockpit_web_response_new (tc->io, "/unused", "/unused", NULL, tc->headers); cockpit_channel_response_serve (tc->service, tc->headers, response, "$060119c2a544d8e5becd0f74f9dcde146b8d99e3", "/test/sub/file.ext"); while (cockpit_web_response_get_state (response) != COCKPIT_WEB_RESPONSE_SENT) g_main_context_iteration (NULL, TRUE); g_output_stream_close (G_OUTPUT_STREAM (tc->output), NULL, &error); g_assert_no_error (error); bytes = g_memory_output_stream_steal_as_bytes (tc->output); cockpit_assert_bytes_eq (bytes, "HTTP/1.1 304 Not Modified\r\n" "ETag: \"$060119c2a544d8e5becd0f74f9dcde146b8d99e3-c\"\r\n" "\r\n", -1); g_bytes_unref (bytes); g_object_unref (response); } static void test_resource_not_modified_new_language (TestResourceCase *tc, gconstpointer data) { CockpitWebResponse *response; GError *error = NULL; GBytes *bytes; request_checksum (tc); g_hash_table_insert (tc->headers, g_strdup ("If-None-Match"), g_strdup ("\"$060119c2a544d8e5becd0f74f9dcde146b8d99e3-c\"")); g_hash_table_insert (tc->headers, g_strdup ("Accept-Language"), g_strdup ("de")); response = cockpit_web_response_new (tc->io, "/unused", "/unused", NULL, tc->headers); cockpit_channel_response_serve (tc->service, tc->headers, response, "$060119c2a544d8e5becd0f74f9dcde146b8d99e3", "/test/sub/file.ext"); while (cockpit_web_response_get_state (response) != COCKPIT_WEB_RESPONSE_SENT) g_main_context_iteration (NULL, TRUE); g_output_stream_close (G_OUTPUT_STREAM (tc->output), NULL, &error); g_assert_no_error (error); bytes = g_memory_output_stream_steal_as_bytes (tc->output); cockpit_assert_bytes_eq (bytes, "HTTP/1.1 200 OK\r\n" "ETag: \"$060119c2a544d8e5becd0f74f9dcde146b8d99e3-de\"\r\n" "Access-Control-Allow-Origin: http://localhost\r\n" "Transfer-Encoding: chunked\r\n" "Cache-Control: max-age=31556926, public\r\n" "\r\n" "32\r\n" "These are the contents of file.ext\nOh marmalaaade\n" "\r\n" "0\r\n\r\n", -1); g_bytes_unref (bytes); g_object_unref (response); } static void test_resource_not_modified_cookie_language (TestResourceCase *tc, gconstpointer data) { CockpitWebResponse *response; GError *error = NULL; GBytes *bytes; gchar *cookie; request_checksum (tc); g_hash_table_insert (tc->headers, g_strdup ("If-None-Match"), g_strdup ("\"$060119c2a544d8e5becd0f74f9dcde146b8d99e3-c\"")); cookie = g_strdup_printf ("%s; CockpitLang=fr", (gchar *)g_hash_table_lookup (tc->headers, "Cookie")); g_hash_table_insert (tc->headers, g_strdup ("Cookie"), cookie); response = cockpit_web_response_new (tc->io, "/unused", "/unused", NULL, tc->headers); cockpit_channel_response_serve (tc->service, tc->headers, response, "$060119c2a544d8e5becd0f74f9dcde146b8d99e3", "/test/sub/file.ext"); while (cockpit_web_response_get_state (response) != COCKPIT_WEB_RESPONSE_SENT) g_main_context_iteration (NULL, TRUE); g_output_stream_close (G_OUTPUT_STREAM (tc->output), NULL, &error); g_assert_no_error (error); bytes = g_memory_output_stream_steal_as_bytes (tc->output); cockpit_assert_bytes_eq (bytes, "HTTP/1.1 200 OK\r\n" "ETag: \"$060119c2a544d8e5becd0f74f9dcde146b8d99e3-fr\"\r\n" "Access-Control-Allow-Origin: http://localhost\r\n" "Transfer-Encoding: chunked\r\n" "Cache-Control: max-age=31556926, public\r\n" "\r\n" "32\r\n" "These are the contents of file.ext\nOh marmalaaade\n" "\r\n" "0\r\n\r\n", -1); g_bytes_unref (bytes); g_object_unref (response); } static void test_resource_no_checksum (TestResourceCase *tc, gconstpointer data) { CockpitWebResponse *response; GError *error = NULL; GBytes *bytes; /* Missing checksum */ response = cockpit_web_response_new (tc->io, "/unused", "/unused", NULL, NULL); cockpit_channel_response_serve (tc->service, tc->headers, response, "xxx", "/test"); while (cockpit_web_response_get_state (response) != COCKPIT_WEB_RESPONSE_SENT) g_main_context_iteration (NULL, TRUE); g_output_stream_close (G_OUTPUT_STREAM (tc->output), NULL, &error); g_assert_no_error (error); bytes = g_memory_output_stream_steal_as_bytes (tc->output); cockpit_assert_bytes_eq (bytes, "HTTP/1.1 404 Not Found\r\n" "Content-Type: text/html; charset=utf8\r\n" "Transfer-Encoding: chunked\r\n" "\r\n13\r\n" "\r\n9\r\n" "Not Found\r\n15\r\n" "\r\n9\r\n" "Not Found\r\nf\r\n" "\n\r\n0\r\n\r\n", -1); g_bytes_unref (bytes); g_object_unref (response); } static void test_resource_bad_checksum (TestResourceCase *tc, gconstpointer data) { CockpitWebResponse *response; GError *error = NULL; GBytes *bytes; /* Missing checksum */ response = cockpit_web_response_new (tc->io, "/unused", "/unused", NULL, NULL); cockpit_channel_response_serve (tc->service, tc->headers, response, "09323094823029348", "/path"); while (cockpit_web_response_get_state (response) != COCKPIT_WEB_RESPONSE_SENT) g_main_context_iteration (NULL, TRUE); g_output_stream_close (G_OUTPUT_STREAM (tc->output), NULL, &error); g_assert_no_error (error); bytes = g_memory_output_stream_steal_as_bytes (tc->output); cockpit_assert_bytes_eq (bytes, "HTTP/1.1 404 Not Found\r\n" "Content-Type: text/html; charset=utf8\r\n" "Transfer-Encoding: chunked\r\n" "\r\n13\r\n" "\r\n9\r\n" "Not Found\r\n15\r\n" "\r\n9\r\n" "Not Found\r\nf\r\n" "\n\r\n0\r\n\r\n", -1); g_bytes_unref (bytes); g_object_unref (response); } static void test_resource_language_suffix (TestResourceCase *tc, gconstpointer data) { CockpitWebResponse *response; GError *error = NULL; GBytes *bytes; response = cockpit_web_response_new (tc->io, "/unused", "/unused", NULL, NULL); cockpit_channel_response_serve (tc->service, tc->headers, response, "@localhost", "/another/test.de.html"); while (cockpit_web_response_get_state (response) != COCKPIT_WEB_RESPONSE_SENT) g_main_context_iteration (NULL, TRUE); g_output_stream_close (G_OUTPUT_STREAM (tc->output), NULL, &error); g_assert_no_error (error); bytes = g_memory_output_stream_steal_as_bytes (tc->output); cockpit_assert_bytes_eq (bytes, "HTTP/1.1 200 OK\r\n" "Content-Security-Policy: default-src 'self' http://localhost; connect-src 'self' http://localhost ws: wss:\r\n" "Content-Type: text/html\r\n" "Cache-Control: no-cache, no-store\r\n" "Access-Control-Allow-Origin: http://localhost\r\n" "Transfer-Encoding: chunked\r\n" "Vary: Cookie\r\n" "\r\n" "62\r\n" "\n" "\n" "Im Home-Verzeichnis\n" "\n" "Im Home-Verzeichnis\n" "\n" "\r\n" "0\r\n\r\n", -1); g_bytes_unref (bytes); g_object_unref (response); } static void test_resource_language_fallback (TestResourceCase *tc, gconstpointer data) { CockpitWebResponse *response; GError *error = NULL; GBytes *bytes; response = cockpit_web_response_new (tc->io, "/unused", "/unused", NULL, NULL); /* Language cookie overrides */ cockpit_channel_response_serve (tc->service, tc->headers, response, "@localhost", "/another/test.fi.html"); while (cockpit_web_response_get_state (response) != COCKPIT_WEB_RESPONSE_SENT) g_main_context_iteration (NULL, TRUE); g_output_stream_close (G_OUTPUT_STREAM (tc->output), NULL, &error); g_assert_no_error (error); bytes = g_memory_output_stream_steal_as_bytes (tc->output); cockpit_assert_bytes_eq (bytes, "HTTP/1.1 200 OK\r\n" "Content-Security-Policy: default-src 'self' http://localhost; connect-src 'self' http://localhost ws: wss:\r\n" "Content-Type: text/html\r\n" "Cache-Control: no-cache, no-store\r\n" "Access-Control-Allow-Origin: http://localhost\r\n" "Transfer-Encoding: chunked\r\n" "Vary: Cookie\r\n" "\r\n" "52\r\n" "\n" "\n" "In home dir\n" "\n" "In home dir\n" "\n" "\r\n" "0\r\n\r\n", -1); g_bytes_unref (bytes); g_object_unref (response); } static void test_resource_gzip_encoding (TestResourceCase *tc, gconstpointer data) { CockpitWebResponse *response; GError *error = NULL; GBytes *bytes; response = cockpit_web_response_new (tc->io, "/unused", "/unused", NULL, NULL); cockpit_channel_response_serve (tc->service, tc->headers, response, "@localhost", "/another/test-file.txt"); while (cockpit_web_response_get_state (response) != COCKPIT_WEB_RESPONSE_SENT) g_main_context_iteration (NULL, TRUE); g_output_stream_close (G_OUTPUT_STREAM (tc->output), NULL, &error); g_assert_no_error (error); bytes = g_memory_output_stream_steal_as_bytes (tc->output); cockpit_assert_bytes_eq (bytes, "HTTP/1.1 200 OK\r\n" "Content-Encoding: gzip\r\n" "Cache-Control: no-cache, no-store\r\n" "Access-Control-Allow-Origin: http://localhost\r\n" "Content-Type: text/plain\r\n" "Transfer-Encoding: chunked\r\n" "Vary: Cookie\r\n" "\r\n" "34\r\n" "\x1F\x8B\x08\x08N1\x03U\x00\x03test-file.txt\x00sT(\xCEM\xCC\xC9Q(I-" ".QH\xCB\xCCI\xE5\x02\x00>PjG\x12\x00\x00\x00\x0D\x0A" "0\x0D\x0A\x0D\x0A", 256); g_bytes_unref (bytes); g_object_unref (response); } static void test_resource_head (TestResourceCase *tc, gconstpointer data) { CockpitWebResponse *response; GError *error = NULL; GBytes *bytes; const gchar *url = "/@localhost/another/test.html"; response = cockpit_web_response_new (tc->io, url, url, NULL, NULL); cockpit_web_response_set_method (response, "HEAD"); cockpit_channel_response_serve (tc->service, tc->headers, response, "@localhost", "/another/test.html"); while (cockpit_web_response_get_state (response) != COCKPIT_WEB_RESPONSE_SENT) g_main_context_iteration (NULL, TRUE); g_output_stream_close (G_OUTPUT_STREAM (tc->output), NULL, &error); g_assert_no_error (error); bytes = g_memory_output_stream_steal_as_bytes (tc->output); cockpit_assert_bytes_eq (bytes, "HTTP/1.1 200 OK\r\n" "Content-Security-Policy: default-src 'self' http://localhost; connect-src 'self' http://localhost ws: wss:\r\n" "Content-Type: text/html\r\n" "Cache-Control: no-cache, no-store\r\n" "Access-Control-Allow-Origin: http://localhost\r\n" "Transfer-Encoding: chunked\r\n" "Vary: Cookie\r\n" "\r\n" "0\r\n\r\n", -1); g_bytes_unref (bytes); g_object_unref (response); } static gboolean on_hack_raise_sigchld (gpointer user_data) { raise (SIGCHLD); return TRUE; } int main (int argc, char *argv[]) { cockpit_test_init (&argc, &argv); /* * HACK: Work around races in glib SIGCHLD handling. * * https://bugzilla.gnome.org/show_bug.cgi?id=731771 * https://bugzilla.gnome.org/show_bug.cgi?id=711090 */ g_timeout_add_seconds (1, on_hack_raise_sigchld, NULL); /* Try to debug crashing during tests */ signal (SIGSEGV, cockpit_test_signal_backtrace); /* We don't want to test the ping functionality in these tests */ cockpit_ws_ping_interval = G_MAXUINT; g_test_add ("/web-channel/resource/simple", TestResourceCase, NULL, setup_resource, test_resource_simple, teardown_resource); g_test_add ("/web-channel/resource/simple_host", TestResourceCase, NULL, setup_resource, test_resource_simple_host, teardown_resource); g_test_add ("/web-channel/resource/language", TestResourceCase, NULL, setup_resource, test_resource_language, teardown_resource); g_test_add ("/web-channel/resource/cookie", TestResourceCase, NULL, setup_resource, test_resource_cookie, teardown_resource); g_test_add ("/web-channel/resource/not-found", TestResourceCase, NULL, setup_resource, test_resource_not_found, teardown_resource); g_test_add ("/web-channel/resource/no-path", TestResourceCase, NULL, setup_resource, test_resource_no_path, teardown_resource); g_test_add ("/web-channel/resource/failure", TestResourceCase, NULL, setup_resource, test_resource_failure, teardown_resource); g_test_add ("/web-channel/resource/checksum", TestResourceCase, &checksum_fixture, setup_resource, test_resource_checksum, teardown_resource); g_test_add ("/web-channel/resource/not-modified", TestResourceCase, &checksum_fixture, setup_resource, test_resource_not_modified, teardown_resource); g_test_add ("/web-channel/resource/not-modified-new-language", TestResourceCase, &checksum_fixture, setup_resource, test_resource_not_modified_new_language, teardown_resource); g_test_add ("/web-channel/resource/not-modified-cookie-language", TestResourceCase, &checksum_fixture, setup_resource, test_resource_not_modified_cookie_language, teardown_resource); g_test_add ("/web-channel/resource/no-checksum", TestResourceCase, NULL, setup_resource, test_resource_no_checksum, teardown_resource); g_test_add ("/web-channel/resource/bad-checksum", TestResourceCase, NULL, setup_resource, test_resource_bad_checksum, teardown_resource); g_test_add ("/web-channel/resource/language-suffix", TestResourceCase, NULL, setup_resource, test_resource_language_suffix, teardown_resource); g_test_add ("/web-channel/resource/language-fallback", TestResourceCase, NULL, setup_resource, test_resource_language_fallback, teardown_resource); g_test_add ("/web-channel/resource/gzip-encoding", TestResourceCase, NULL, setup_resource, test_resource_gzip_encoding, teardown_resource); g_test_add ("/web-channel/resource/head", TestResourceCase, NULL, setup_resource, test_resource_head, teardown_resource); return g_test_run (); }