[Spice-commits] gtk/spice-channel.c
Marc-André Lureau
elmarco at kemper.freedesktop.org
Mon Dec 17 08:48:02 PST 2012
gtk/spice-channel.c | 61 +++++++++++++++++++++++++++++++---------------------
1 file changed, 37 insertions(+), 24 deletions(-)
New commits:
commit 951ab42743f24109994fe57dc1fb4b634d4fe435
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Tue Nov 27 20:17:13 2012 +0100
channel: switch to protocol 1 on error during link-time
The Spice server doesn't wait until all the data are received by the
remote before closing the socket (that would need SO_LINGER?). Under
some racy conditions, the client may not have received the link reply
indicating the server protocol version mismatch, which would trigger
reconnection with compatible protocol.
It seems to happen on local networks with Windows sockets (error
WSAECONNRESET). To workaround that issue, spice-gtk can try to
reconnect with protocol 1 when a socket error is encoutered during
link-time.
Fixes:
https://bugzilla.redhat.com/show_bug.cgi?id=874698
diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index 6336485..369a0a5 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -1165,6 +1165,15 @@ static void spice_channel_send_link(SpiceChannel *channel)
free(buffer);
}
+static void spice_channel_switch_protocol(SpiceChannel *channel, gint version)
+{
+ SpiceChannelPrivate *c = channel->priv;
+
+ g_object_set(c->session, "protocol", version, NULL);
+ SPICE_CHANNEL_GET_CLASS(channel)->channel_disconnect(channel);
+ spice_channel_connect(channel);
+}
+
/* coroutine context */
static void spice_channel_recv_link_hdr(SpiceChannel *channel)
{
@@ -1173,39 +1182,40 @@ static void spice_channel_recv_link_hdr(SpiceChannel *channel)
rc = spice_channel_read(channel, &c->peer_hdr, sizeof(c->peer_hdr));
if (rc != sizeof(c->peer_hdr)) {
- g_critical("incomplete link header (%d/%" G_GSIZE_FORMAT ")",
- rc, sizeof(c->peer_hdr));
+ g_warning("incomplete link header (%d/%" G_GSIZE_FORMAT ")",
+ rc, sizeof(c->peer_hdr));
goto error;
}
if (c->peer_hdr.magic != SPICE_MAGIC) {
- g_critical("invalid SPICE_MAGIC!");
+ g_warning("invalid SPICE_MAGIC!");
goto error;
}
CHANNEL_DEBUG(channel, "Peer version: %d:%d", c->peer_hdr.major_version, c->peer_hdr.minor_version);
if (c->peer_hdr.major_version != c->link_hdr.major_version) {
- if (c->peer_hdr.major_version == 1) {
- /* enter spice 0.4 mode */
- g_object_set(c->session, "protocol", 1, NULL);
- CHANNEL_DEBUG(channel, "switching to protocol 1 (spice 0.4)");
- SPICE_CHANNEL_GET_CLASS(channel)->channel_disconnect(channel);
- spice_channel_connect(channel);
- return;
- }
- g_critical("major mismatch (got %d, expected %d)",
- c->peer_hdr.major_version, c->link_hdr.major_version);
+ g_warning("major mismatch (got %d, expected %d)",
+ c->peer_hdr.major_version, c->link_hdr.major_version);
goto error;
}
c->peer_msg = spice_malloc(c->peer_hdr.size);
if (c->peer_msg == NULL) {
- g_critical("invalid peer header size: %u", c->peer_hdr.size);
+ g_warning("invalid peer header size: %u", c->peer_hdr.size);
goto error;
}
return;
error:
+ /* Windows socket seems to give early CONNRESET errors. The server
+ does not linger when closing the socket if the protocol is
+ incompatible. Try with the oldest protocol in this case: */
+ if (c->link_hdr.major_version != 1) {
+ SPICE_DEBUG("%s: error, switching to protocol 1 (spice 0.4)", c->name);
+ spice_channel_switch_protocol(channel, 1);
+ return;
+ }
+
emit_main_context(channel, SPICE_CHANNEL_EVENT, SPICE_CHANNEL_ERROR_LINK);
}
@@ -2060,8 +2070,19 @@ static gboolean spice_channel_iterate(SpiceChannel *channel)
#endif
} while (ret == 0); /* ret == 0 means no IO condition, but woken up */
- if (ret & (G_IO_ERR|G_IO_HUP)) {
- CHANNEL_DEBUG(channel, "got socket error before read(): %d", ret);
+ if (ret & G_IO_IN) {
+ do
+ SPICE_CHANNEL_GET_CLASS(channel)->iterate_read(channel);
+#if HAVE_SASL
+ while (c->sasl_decoded != NULL);
+#else
+ while (FALSE);
+#endif
+ }
+
+ if (c->state > SPICE_CHANNEL_STATE_CONNECTING &&
+ ret & (G_IO_ERR|G_IO_HUP)) {
+ SPICE_DEBUG("got socket error: %d", ret);
emit_main_context(channel, SPICE_CHANNEL_EVENT,
c->state == SPICE_CHANNEL_STATE_READY ?
SPICE_CHANNEL_ERROR_IO : SPICE_CHANNEL_ERROR_LINK);
@@ -2069,14 +2090,6 @@ static gboolean spice_channel_iterate(SpiceChannel *channel)
return FALSE;
}
- do
- SPICE_CHANNEL_GET_CLASS(channel)->iterate_read(channel);
-#if HAVE_SASL
- while (c->sasl_decoded != NULL);
-#else
- while (FALSE);
-#endif
-
return TRUE;
}
More information about the Spice-commits
mailing list