diff --git a/SOURCES/0001-WebSockets-ignore-any-messages-after-close-has-been-.patch b/SOURCES/0001-WebSockets-ignore-any-messages-after-close-has-been-.patch
new file mode 100644
index 0000000..644cb53
--- /dev/null
+++ b/SOURCES/0001-WebSockets-ignore-any-messages-after-close-has-been-.patch
@@ -0,0 +1,95 @@
+From d9c729aa5a7991182fa7bdb8d94442f8f0cf055b Mon Sep 17 00:00:00 2001
+From: Carlos Garcia Campos <cgarcia@igalia.com>
+Date: Fri, 19 Jul 2019 14:56:05 +0200
+Subject: [PATCH] WebSockets: ignore any messages after close has been sent and
+ received
+
+We currently ignore data frames when close has been received, but we
+should also ignore any frame after close has been sent and received.
+Currently, if we receive two close frames we end up with the code and
+reason of the second frame, while the RFC says: "The WebSocket
+Connection Close Code is defined as the status code contained in the
+first Close control frame received by the application implementing
+this protocol."
+---
+ libsoup/soup-websocket-connection.c |  3 ++
+ tests/websocket-test.c              | 48 +++++++++++++++++++++++++++++
+ 2 files changed, 51 insertions(+)
+
+diff --git libsoup/soup-websocket-connection.c libsoup/soup-websocket-connection.c
+--- a/libsoup/soup-websocket-connection.c
++++ b/libsoup/soup-websocket-connection.c
+@@ -690,6 +690,9 @@
+ 	SoupWebsocketConnectionPrivate *pv = self->pv;
+ 	GBytes *message;
+ 
++	if (pv->close_sent && pv->close_received)
++		return;
++
+ 	if (control) {
+ 		/* Control frames must never be fragmented */
+ 		if (!fin) {
+--- a/tests/websocket-test.c
++++ b/tests/websocket-test.c
+@@ -707,6 +707,49 @@
+ }
+ 
+ static gpointer
++close_after_close_server_thread (gpointer user_data)
++{
++	Test *test = user_data;
++	gsize written;
++	const char frames[] =
++		"\x88\x09\x03\xe8""reason1"
++		"\x88\x09\x03\xe8""reason2";
++	GError *error = NULL;
++
++	g_mutex_lock (&test->mutex);
++	g_mutex_unlock (&test->mutex);
++
++	g_output_stream_write_all (g_io_stream_get_output_stream (test->raw_server),
++				   frames, sizeof (frames) -1, &written, NULL, &error);
++	g_assert_no_error (error);
++	g_assert_cmpuint (written, ==, sizeof (frames) - 1);
++	g_io_stream_close (test->raw_server, NULL, &error);
++	g_assert_no_error (error);
++
++	return NULL;
++}
++
++static void
++test_close_after_close (Test *test,
++			gconstpointer data)
++{
++	GThread *thread;
++
++	g_mutex_lock (&test->mutex);
++
++	thread = g_thread_new ("close-after-close-thread", close_after_close_server_thread, test);
++
++	soup_websocket_connection_close (test->client, SOUP_WEBSOCKET_CLOSE_NORMAL, "reason1");
++	g_mutex_unlock (&test->mutex);
++
++	g_thread_join (thread);
++
++	WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
++	g_assert_cmpuint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_NORMAL);
++	g_assert_cmpstr (soup_websocket_connection_get_close_data (test->client), ==, "reason1");
++}
++
++static gpointer
+ timeout_server_thread (gpointer user_data)
+ {
+ 	Test *test = user_data;
+@@ -918,6 +961,11 @@
+ 		    test_message_after_closing,
+ 		    teardown_soup_connection);
+ 
++	g_test_add ("/websocket/direct/close-after-close", Test, NULL,
++		    setup_half_direct_connection,
++		    test_close_after_close,
++		    teardown_direct_connection);
++
+ 
+ 	g_test_add ("/websocket/direct/protocol-negotiate", Test, NULL, NULL,
+ 		    test_protocol_negotiate_direct,
diff --git a/SOURCES/0002-WebSockets-allow-null-characters-in-text-messages-da.patch b/SOURCES/0002-WebSockets-allow-null-characters-in-text-messages-da.patch
new file mode 100644
index 0000000..113e461
--- /dev/null
+++ b/SOURCES/0002-WebSockets-allow-null-characters-in-text-messages-da.patch
@@ -0,0 +1,152 @@
+From 109bb2f692c746bc63a0ade8737b584aecb0b1ad Mon Sep 17 00:00:00 2001
+From: Carlos Garcia Campos <cgarcia@igalia.com>
+Date: Thu, 27 Jun 2019 16:03:21 +0200
+Subject: [PATCH] WebSockets: allow null characters in text messages data
+
+RFC 6455 says that text messages should contains valid UTF-8, and null
+characters valid according to RFC 3629. However, we are using
+g_utf8_validate(), which considers null characters as errors, to
+validate WebSockets text messages. This patch adds an internal
+utf8_validate() function based on g_utf8_validate() but allowing null
+characters and just returning a gboolean since we are always ignoring
+the end parameter in case of errors.
+soup_websocket_connection_send_text() assumes the given text is null
+terminated, so we need a new public function to allow sending text
+messages containing null characters. This patch adds
+soup_websocket_connection_send_message() that receives a
+SoupWebsocketDataType and GBytes, which is consistent with
+SoupWebsocketConnection::message signal.
+
+For RHEL backport, drop the addition of soup_websocket_connection_send_message()
+as we don't need it and don't want to expose new API.
+diff --git libsoup/soup-websocket-connection.c libsoup/soup-websocket-connection.c
+index 66bd6871..67a98731 100644
+--- a/libsoup/soup-websocket-connection.c
++++ b/libsoup/soup-websocket-connection.c
+@@ -155,6 +155,82 @@
+ 
+ static void protocol_error_and_close (SoupWebsocketConnection *self);
+ 
++/* Code below is based on g_utf8_validate() implementation,
++ * but handling NULL characters as valid, as expected by
++ * WebSockets and compliant with RFC 3629.
++ */
++#define VALIDATE_BYTE(mask, expect)                             \
++        G_STMT_START {                                          \
++          if (G_UNLIKELY((*(guchar *)p & (mask)) != (expect)))  \
++                  return FALSE;                                 \
++        } G_STMT_END
++
++/* see IETF RFC 3629 Section 4 */
++static gboolean
++utf8_validate (const char *str,
++               gsize max_len)
++
++{
++        const gchar *p;
++
++        for (p = str; ((p - str) < max_len); p++) {
++                if (*(guchar *)p < 128)
++                        /* done */;
++                else {
++                        if (*(guchar *)p < 0xe0) { /* 110xxxxx */
++                                if (G_UNLIKELY (max_len - (p - str) < 2))
++                                        return FALSE;
++
++                                if (G_UNLIKELY (*(guchar *)p < 0xc2))
++                                        return FALSE;
++                        } else {
++                                if (*(guchar *)p < 0xf0) { /* 1110xxxx */
++                                        if (G_UNLIKELY (max_len - (p - str) < 3))
++                                                return FALSE;
++
++                                        switch (*(guchar *)p++ & 0x0f) {
++                                        case 0:
++                                                VALIDATE_BYTE(0xe0, 0xa0); /* 0xa0 ... 0xbf */
++                                                break;
++                                        case 0x0d:
++                                                VALIDATE_BYTE(0xe0, 0x80); /* 0x80 ... 0x9f */
++                                                break;
++                                        default:
++                                                VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
++                                        }
++                                } else if (*(guchar *)p < 0xf5) { /* 11110xxx excluding out-of-range */
++                                        if (G_UNLIKELY (max_len - (p - str) < 4))
++                                                return FALSE;
++
++                                        switch (*(guchar *)p++ & 0x07) {
++                                        case 0:
++                                                VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
++                                                if (G_UNLIKELY((*(guchar *)p & 0x30) == 0))
++                                                        return FALSE;
++                                                break;
++                                        case 4:
++                                                VALIDATE_BYTE(0xf0, 0x80); /* 0x80 ... 0x8f */
++                                                break;
++                                        default:
++                                                VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
++                                        }
++                                        p++;
++                                        VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
++                                } else {
++                                        return FALSE;
++                                }
++                        }
++
++                        p++;
++                        VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
++                }
++        }
++
++        return TRUE;
++}
++
++#undef VALIDATE_BYTE
++
+ static void
+ frame_free (gpointer data)
+ {
+@@ -629,7 +705,7 @@
+ 		data += 2;
+ 		len -= 2;
+ 		
+-		if (!g_utf8_validate ((char *)data, len, NULL)) {
++		if (!utf8_validate ((const char *)data, len)) {
+ 			g_debug ("received non-UTF8 close data: %d '%.*s' %d", (int)len, (int)len, (char *)data, (int)data[0]);
+ 			protocol_error_and_close (self);
+ 			return;
+@@ -777,9 +853,8 @@
+ 		/* Actually deliver the message? */
+ 		if (fin) {
+ 			if (pv->message_opcode == 0x01 &&
+-			    !g_utf8_validate((char *)pv->message_data->data,
+-			                     pv->message_data->len,
+-			                     NULL)) {
++			    !utf8_validate((const char *)pv->message_data->data,
++					   pv->message_data->len)) {
+ 
+ 				g_debug ("received invalid non-UTF8 text data");
+ 
+@@ -1699,7 +1774,9 @@
+  * @self: the WebSocket
+  * @text: the message contents
+  *
+- * Send a text (UTF-8) message to the peer.
++ * Send a %NULL-terminated text (UTF-8) message to the peer. If you need
++ * to send text messages containing %NULL characters use
++ * soup_websocket_connection_send_message() instead.
+  *
+  * The message is queued to be sent and will be sent when the main loop
+  * is run.
+@@ -1717,7 +1794,7 @@
+ 	g_return_if_fail (text != NULL);
+ 
+ 	length = strlen (text);
+-	g_return_if_fail (g_utf8_validate (text, length, NULL));
++        g_return_if_fail (utf8_validate (text, length));
+ 
+ 	send_message (self, SOUP_WEBSOCKET_QUEUE_NORMAL, 0x01, (const guint8 *) text, length);
+ }
+-- 
+2.26.2
+
diff --git a/SOURCES/0003-WebSockets-only-poll-IO-stream-when-needed.patch b/SOURCES/0003-WebSockets-only-poll-IO-stream-when-needed.patch
new file mode 100644
index 0000000..820d0c4
--- /dev/null
+++ b/SOURCES/0003-WebSockets-only-poll-IO-stream-when-needed.patch
@@ -0,0 +1,299 @@
+From 35f1bac5ff9ec694e64b65e51f0e7a3226aa3aaf Mon Sep 17 00:00:00 2001
+From: Carlos Garcia Campos <cgarcia@igalia.com>
+Date: Wed, 28 Aug 2019 10:51:18 +0200
+Subject: [PATCH] WebSockets: only poll IO stream when needed
+
+Instead of having two pollable sources constantly running, always try to
+read/write without blocking and start polling if the operation returns
+G_IO_ERROR_WOULD_BLOCK. This patch also fixes test
+/websocket/direct/close-after-close that was passing but not actually
+testing what we wanted, because the client close was never sent. When
+the mutex is released, the frame has been queued, but not sent.
+
+diff --git libsoup/soup-websocket-connection.c libsoup/soup-websocket-connection.c
+index 345040fe..6afbbe67 100644
+--- a/libsoup/soup-websocket-connection.c
++++ b/libsoup/soup-websocket-connection.c
+@@ -147,6 +147,7 @@
+ };
+ 
+ #define MAX_INCOMING_PAYLOAD_SIZE_DEFAULT   128 * 1024
++#define READ_BUFFER_SIZE 1024
+ 
+ G_DEFINE_TYPE_WITH_PRIVATE (SoupWebsocketConnection, soup_websocket_connection, G_TYPE_OBJECT)
+ 
+@@ -155,6 +156,11 @@
+ 
+ static void protocol_error_and_close (SoupWebsocketConnection *self);
+ 
++static gboolean on_web_socket_input (GObject *pollable_stream,
++				     gpointer user_data);
++static gboolean on_web_socket_output (GObject *pollable_stream,
++				      gpointer user_data);
++
+ /* Code below is based on g_utf8_validate() implementation,
+  * but handling NULL characters as valid, as expected by
+  * WebSockets and compliant with RFC 3629.
+@@ -283,7 +289,20 @@
+ }
+ 
+ static void
+-stop_input (SoupWebsocketConnection *self)
++soup_websocket_connection_start_input_source (SoupWebsocketConnection *self)
++{
++	SoupWebsocketConnectionPrivate *pv = self->pv;
++
++	if (pv->input_source)
++		return;
++
++	pv->input_source = g_pollable_input_stream_create_source (pv->input, NULL);
++	g_source_set_callback (pv->input_source, (GSourceFunc)on_web_socket_input, self, NULL);
++	g_source_attach (pv->input_source, pv->main_context);
++}
++
++static void
++soup_websocket_connection_stop_input_source (SoupWebsocketConnection *self)
+ {
+ 	SoupWebsocketConnectionPrivate *pv = self->pv;
+ 
+@@ -296,7 +315,20 @@
+ }
+ 
+ static void
+-stop_output (SoupWebsocketConnection *self)
++soup_websocket_connection_start_output_source (SoupWebsocketConnection *self)
++{
++	SoupWebsocketConnectionPrivate *pv = self->pv;
++
++	if (pv->output_source)
++		return;
++
++	pv->output_source = g_pollable_output_stream_create_source (pv->output, NULL);
++	g_source_set_callback (pv->output_source, (GSourceFunc)on_web_socket_output, self, NULL);
++	g_source_attach (pv->output_source, pv->main_context);
++}
++
++static void
++soup_websocket_connection_stop_output_source (SoupWebsocketConnection *self)
+ {
+ 	SoupWebsocketConnectionPrivate *pv = self->pv;
+ 
+@@ -341,8 +373,8 @@
+ 	close_io_stop_timeout (self);
+ 
+ 	if (!pv->io_closing) {
+-		stop_input (self);
+-		stop_output (self);
++		soup_websocket_connection_stop_input_source (self);
++		soup_websocket_connection_stop_output_source (self);
+ 		pv->io_closing = TRUE;
+ 		g_debug ("closing io stream");
+ 		g_io_stream_close_async (pv->io_stream, G_PRIORITY_DEFAULT,
+@@ -359,7 +391,7 @@
+ 	GSocket *socket;
+ 	GError *error = NULL;
+ 
+-	stop_output (self);
++	soup_websocket_connection_stop_output_source (self);
+ 
+ 	if (G_IS_SOCKET_CONNECTION (pv->io_stream)) {
+ 		socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (pv->io_stream));
+@@ -612,9 +644,6 @@
+ 		 self->pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER ? "server" : "client",
+ 	         payload_len, self->pv->max_incoming_payload_size);
+ 	emit_error_and_close (self, error, TRUE);
+-
+-	/* The input is in an invalid state now */
+-	stop_input (self);
+ }
+ 
+ static void
+@@ -981,32 +1010,31 @@
+ 		;
+ }
+ 
+-static gboolean
+-on_web_socket_input (GObject *pollable_stream,
+-		     gpointer user_data)
++static void
++soup_websocket_connection_read (SoupWebsocketConnection *self)
+ {
+-	SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (user_data);
+ 	SoupWebsocketConnectionPrivate *pv = self->pv;
+ 	GError *error = NULL;
+ 	gboolean end = FALSE;
+ 	gssize count;
+ 	gsize len;
+ 
++	soup_websocket_connection_stop_input_source (self);
++
+ 	do {
+ 		len = pv->incoming->len;
+-		g_byte_array_set_size (pv->incoming, len + 1024);
++		g_byte_array_set_size (pv->incoming, len + READ_BUFFER_SIZE);
+ 
+ 		count = g_pollable_input_stream_read_nonblocking (pv->input,
+ 								  pv->incoming->data + len,
+-								  1024, NULL, &error);
+-
++								  READ_BUFFER_SIZE, NULL, &error);
+ 		if (count < 0) {
+ 			if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
+ 				g_error_free (error);
+ 				count = 0;
+ 			} else {
+ 				emit_error_and_close (self, error, TRUE);
+-				return TRUE;
++				return;
+ 			}
+ 		} else if (count == 0) {
+ 			end = TRUE;
+@@ -1026,16 +1054,24 @@
+ 		}
+ 
+ 		close_io_stream (self);
++		return;
+ 	}
+ 
+-	return TRUE;
++	soup_websocket_connection_start_input_source (self);
+ }
+ 
+ static gboolean
+-on_web_socket_output (GObject *pollable_stream,
+-		      gpointer user_data)
++on_web_socket_input (GObject *pollable_stream,
++		     gpointer user_data)
++{
++	soup_websocket_connection_read (SOUP_WEBSOCKET_CONNECTION (user_data));
++
++	return G_SOURCE_REMOVE;
++}
++
++static void
++soup_websocket_connection_write (SoupWebsocketConnection *self)
+ {
+-	SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (user_data);
+ 	SoupWebsocketConnectionPrivate *pv = self->pv;
+ 	const guint8 *data;
+ 	GError *error = NULL;
+@@ -1043,19 +1079,18 @@
+ 	gssize count;
+ 	gsize len;
+ 
++	soup_websocket_connection_stop_output_source (self);
++
+ 	if (soup_websocket_connection_get_state (self) == SOUP_WEBSOCKET_STATE_CLOSED) {
+ 		g_debug ("Ignoring message since the connection is closed");
+-		stop_output (self);
+-		return TRUE;
++		return;
+ 	}
+ 
+ 	frame = g_queue_peek_head (&pv->outgoing);
+ 
+ 	/* No more frames to send */
+-	if (frame == NULL) {
+-		stop_output (self);
+-		return TRUE;
+-	}
++	if (frame == NULL)
++		return;
+ 
+ 	data = g_bytes_get_data (frame->data, &len);
+ 	g_assert (len > 0);
+@@ -1075,7 +1110,7 @@
+ 			frame->pending = TRUE;
+ 		} else {
+ 			emit_error_and_close (self, error, TRUE);
+-			return FALSE;
++			return;
+ 		}
+ 	}
+ 
+@@ -1093,23 +1128,21 @@
+ 			}
+ 		}
+ 		frame_free (frame);
++
++		if (g_queue_is_empty (&pv->outgoing))
++			return;
+ 	}
+ 
+-	return TRUE;
++	soup_websocket_connection_start_output_source (self);
+ }
+ 
+-static void
+-start_output (SoupWebsocketConnection *self)
++static gboolean
++on_web_socket_output (GObject *pollable_stream,
++		      gpointer user_data)
+ {
+-	SoupWebsocketConnectionPrivate *pv = self->pv;
+-
+-	if (pv->output_source)
+-		return;
++	soup_websocket_connection_write (SOUP_WEBSOCKET_CONNECTION (user_data));
+ 
+-	g_debug ("starting output source");
+-	pv->output_source = g_pollable_output_stream_create_source (pv->output, NULL);
+-	g_source_set_callback (pv->output_source, (GSourceFunc)on_web_socket_output, self, NULL);
+-	g_source_attach (pv->output_source, pv->main_context);
++	return G_SOURCE_REMOVE;
+ }
+ 
+ static void
+@@ -1150,7 +1183,7 @@
+ 		g_queue_push_tail (&pv->outgoing, frame);
+ 	}
+ 
+-	start_output (self);
++	soup_websocket_connection_write (self);
+ }
+ 
+ static void
+@@ -1175,9 +1208,7 @@
+ 	pv->output = G_POLLABLE_OUTPUT_STREAM (os);
+ 	g_return_if_fail (g_pollable_output_stream_can_poll (pv->output));
+ 
+-	pv->input_source = g_pollable_input_stream_create_source (pv->input, NULL);
+-	g_source_set_callback (pv->input_source, (GSourceFunc)on_web_socket_input, self, NULL);
+-	g_source_attach (pv->input_source, pv->main_context);
++	soup_websocket_connection_start_input_source (self);
+ }
+ 
+ static void
+diff --git tests/websocket-test.c tests/websocket-test.c
+index 146fdf82..26d064df 100644
+--- a/tests/websocket-test.c
++++ b/tests/websocket-test.c
+@@ -733,6 +733,7 @@
+ 	const char frames[] =
+ 		"\x88\x09\x03\xe8""reason1"
+ 		"\x88\x09\x03\xe8""reason2";
++	GSocket *socket;
+ 	GError *error = NULL;
+ 
+ 	g_mutex_lock (&test->mutex);
+@@ -742,7 +743,8 @@
+ 				   frames, sizeof (frames) -1, &written, NULL, &error);
+ 	g_assert_no_error (error);
+ 	g_assert_cmpuint (written, ==, sizeof (frames) - 1);
+-	g_io_stream_close (test->raw_server, NULL, &error);
++	socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (test->raw_server));
++	g_socket_shutdown (socket, FALSE, TRUE, &error);
+ 	g_assert_no_error (error);
+ 
+ 	return NULL;
+@@ -766,6 +768,7 @@
+ 	WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
+ 	g_assert_cmpuint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_NORMAL);
+ 	g_assert_cmpstr (soup_websocket_connection_get_close_data (test->client), ==, "reason1");
++	g_io_stream_close (test->raw_server, NULL, NULL);
+ }
+ 
+ static gpointer
+-- 
+2.26.2
+
diff --git a/SPECS/libsoup.spec b/SPECS/libsoup.spec
index 3765db9..4a288cd 100644
--- a/SPECS/libsoup.spec
+++ b/SPECS/libsoup.spec
@@ -2,13 +2,17 @@
 
 Name: libsoup
 Version: 2.62.3
-Release: 1%{?dist}
+Release: 2%{?dist}
 Summary: Soup, an HTTP library implementation
 
 License: LGPLv2
 URL: https://wiki.gnome.org/Projects/libsoup
 Source0: https://download.gnome.org/sources/%{name}/2.62/%{name}-%{version}.tar.xz
 
+Patch0001: 0001-WebSockets-ignore-any-messages-after-close-has-been-.patch
+Patch0002: 0002-WebSockets-allow-null-characters-in-text-messages-da.patch
+Patch0003: 0003-WebSockets-only-poll-IO-stream-when-needed.patch
+
 BuildRequires: chrpath
 BuildRequires: glib2-devel >= %{glib2_version}
 BuildRequires: glib-networking
@@ -82,6 +86,9 @@ chrpath --delete $RPM_BUILD_ROOT%{_libdir}/*.so
 %{_datadir}/vala/vapi/libsoup-2.4.vapi
 
 %changelog
+* Thu Aug 27 2020 Martin Pitt <mpitt@redhat.com> - 2.62.3-2
+- Some WebSocket fixes to unbreak cockpit-desktop (rhbz#1872270)
+
 * Fri Aug 10 2018 Kalev Lember <klember@redhat.com> - 2.62.3-1
 - Update to 2.62.3