[gtk+] broadway: Initial support fro V7+ websockets



commit 14a17873de4451eb981bdb5325559efafeeda5f4
Author: Michael Meeks <michael meeks suse com>
Date:   Thu Nov 10 10:12:28 2011 +0100

    broadway: Initial support fro V7+ websockets
    
    Allows more modern browsers eg. firefox 5+ to use gtk/broadway
    Auto-detects protocol version, and can switch between them at
    as you connect a different browser.
    
    This works to some extent, but seems to hang sometimes, for
    instance the "button box" test in testgtk never shows up.

 gdk/broadway/broadway.c            |  189 +++++++++++++---------
 gdk/broadway/broadway.h            |   13 ++-
 gdk/broadway/broadway.js           |   19 ++-
 gdk/broadway/gdkdisplay-broadway.c |  313 +++++++++++++++++++++++++++++-------
 4 files changed, 386 insertions(+), 148 deletions(-)
---
diff --git a/gdk/broadway/broadway.c b/gdk/broadway/broadway.c
index 9f27746..b104758 100644
--- a/gdk/broadway/broadway.c
+++ b/gdk/broadway/broadway.c
@@ -175,30 +175,84 @@ struct BroadwayOutput {
   GOutputStream *out;
   int error;
   guint32 serial;
+  gboolean proto_v7_plus;
 };
 
 static void
-broadway_output_write_header (BroadwayOutput *output)
-{
-  g_output_stream_write (output->out, "\0", 1, NULL, NULL);
+broadway_output_send_cmd (BroadwayOutput *output,
+			  gboolean fin, BroadwayWSOpCode code,
+			  const void *buf, gsize count)
+{
+  gboolean mask = FALSE;
+  guchar header[16];
+  size_t p;
+
+  gboolean mid_header = count > 125 && count <= 65535;
+  gboolean long_header = count > 65535;
+
+  /* NB. big-endian spec => bit 0 == MSB */
+  header[0] = ( (fin ? 0x80 : 0) | (code & 0x0f) );
+  header[1] = ( (mask ? 0x80 : 0) |
+                (mid_header ? 126 : long_header ? 127 : count) );
+  p = 2;
+  if (mid_header)
+    {
+      *(guint16 *)(header + p) = GUINT16_TO_BE( (guint16)count );
+      p += 2;
+    }
+  else if (long_header)
+    {
+      *(guint64 *)(header + p) = GUINT64_TO_BE( count );
+      p += 8;
+    }
+  // FIXME: if we are paranoid we should 'mask' the data
+  // FIXME: we should really emit these as a single write
+  g_output_stream_write_all (output->out, header, p, NULL, NULL, NULL);
+  g_output_stream_write_all (output->out, buf, count, NULL, NULL, NULL);
 }
 
 static void
-broadway_output_write (BroadwayOutput *output,
-		       const void *buf, gsize count)
+broadway_output_sendmsg (BroadwayOutput *output,
+			 const void *buf, gsize count)
 {
-  g_output_stream_write_all (output->out, buf, count, NULL, NULL, NULL);
+  if (!output->proto_v7_plus)
+    g_output_stream_write_all (output->out, buf, count, NULL, NULL, NULL);
+  else
+    broadway_output_send_cmd (output, TRUE, BROADWAY_WS_TEXT, buf, count);
+}
+
+void broadway_output_pong (BroadwayOutput *output)
+{
+  if (output->proto_v7_plus)
+    broadway_output_send_cmd (output, TRUE, BROADWAY_WS_CNX_PONG, NULL, 0);
 }
 
 static void
-send_boundary (BroadwayOutput *output)
+broadway_output_sendmsg_initiate (BroadwayOutput *output)
+{
+  if (!output->proto_v7_plus)
+    g_output_stream_write (output->out, "\0", 1, NULL, NULL);
+  else
+    {
+    }
+}
+
+int
+broadway_output_flush (BroadwayOutput *output)
 {
-  broadway_output_write (output, "\xff", 1);
-  broadway_output_write (output, "\0", 1);
+  if (!output->proto_v7_plus)
+    {
+      broadway_output_sendmsg (output, "\xff", 1);
+      broadway_output_sendmsg (output, "\0", 1);
+      return !output->error;
+    }
+  else /* no need to flush */
+    return !output->error;
 }
 
 BroadwayOutput *
-broadway_output_new(GOutputStream *out, guint32 serial)
+broadway_output_new(GOutputStream *out, guint32 serial,
+		    gboolean proto_v7_plus)
 {
   BroadwayOutput *output;
 
@@ -206,8 +260,9 @@ broadway_output_new(GOutputStream *out, guint32 serial)
 
   output->out = g_object_ref (out);
   output->serial = serial;
+  output->proto_v7_plus = proto_v7_plus;
 
-  broadway_output_write_header (output);
+  broadway_output_sendmsg_initiate (output);
 
   return output;
 }
@@ -225,13 +280,6 @@ broadway_output_get_next_serial (BroadwayOutput *output)
   return output->serial;
 }
 
-int
-broadway_output_flush (BroadwayOutput *output)
-{
-  send_boundary (output);
-  return !output->error;
-}
-
 
 /************************************************************************
  *                     Core rendering operations                        *
@@ -291,7 +339,7 @@ broadway_output_copy_rectangles (BroadwayOutput *output,  int id,
 
   assert (p == len);
 
-  broadway_output_write (output, buf, len);
+  broadway_output_sendmsg (output, buf, len);
   free (buf);
 }
 
@@ -309,7 +357,7 @@ broadway_output_grab_pointer (BroadwayOutput *output,
 
   assert (p == sizeof (buf));
 
-  broadway_output_write (output, buf, sizeof (buf));
+  broadway_output_sendmsg (output, buf, sizeof (buf));
 }
 
 guint32
@@ -324,7 +372,7 @@ broadway_output_ungrab_pointer (BroadwayOutput *output)
 
   assert (p == sizeof (buf));
 
-  broadway_output_write (output, buf, sizeof (buf));
+  broadway_output_sendmsg (output, buf, sizeof (buf));
 
   return serial;
 }
@@ -347,7 +395,7 @@ broadway_output_new_surface(BroadwayOutput *output,
 
   assert (p == sizeof (buf));
 
-  broadway_output_write (output, buf, sizeof (buf));
+  broadway_output_sendmsg (output, buf, sizeof (buf));
 }
 
 void
@@ -361,7 +409,7 @@ broadway_output_show_surface(BroadwayOutput *output,  int id)
 
   assert (p == sizeof (buf));
 
-  broadway_output_write (output, buf, sizeof (buf));
+  broadway_output_sendmsg (output, buf, sizeof (buf));
 }
 
 void
@@ -375,7 +423,7 @@ broadway_output_hide_surface(BroadwayOutput *output,  int id)
 
   assert (p == sizeof (buf));
 
-  broadway_output_write (output, buf, sizeof (buf));
+  broadway_output_sendmsg (output, buf, sizeof (buf));
 }
 
 void
@@ -389,7 +437,7 @@ broadway_output_destroy_surface(BroadwayOutput *output,  int id)
 
   assert (p == sizeof (buf));
 
-  broadway_output_write (output, buf, sizeof (buf));
+  broadway_output_sendmsg (output, buf, sizeof (buf));
 }
 
 
@@ -427,7 +475,7 @@ broadway_output_move_resize_surface (BroadwayOutput *output,
     }
   assert (p <= sizeof (buf));
 
-  broadway_output_write (output, buf, p);
+  broadway_output_sendmsg (output, buf, p);
 }
 
 void
@@ -445,7 +493,7 @@ broadway_output_set_transient_for (BroadwayOutput *output,
 
   assert (p == sizeof (buf));
 
-  broadway_output_write (output, buf, sizeof (buf));
+  broadway_output_sendmsg (output, buf, sizeof (buf));
 }
 
 
@@ -453,27 +501,31 @@ void
 broadway_output_put_rgb (BroadwayOutput *output,  int id, int x, int y,
 			 int w, int h, int byte_stride, void *data)
 {
-  char buf[HEADER_LEN + 15];
-  gsize len;
-  char *url;
+  gsize buf_size;
+  gsize url_len;
+  char *url, *buf;
   int p;
 
+  url = to_png_rgb (w, h, byte_stride, (guint32*)data);
+  url_len = strlen (url);
+
+  buf_size = HEADER_LEN + 15 + url_len;
+  buf = g_malloc (buf_size);
+
   p = write_header (output, buf, 'i');
 
   append_uint16 (id, buf, &p);
   append_uint16 (x, buf, &p);
   append_uint16 (y, buf, &p);
 
-  url = to_png_rgb (w, h, byte_stride, (guint32*)data);
-  len = strlen (url);
-  append_uint32 (len, buf, &p);
-
-  assert (p == sizeof (buf));
+  append_uint32 (url_len, buf, &p);
 
-  broadway_output_write (output, buf, sizeof (buf));
+  g_assert (p == HEADER_LEN + 15);
+  strncpy (buf + p, url, url_len);
 
-  broadway_output_write (output, url, len);
+  broadway_output_sendmsg (output, buf, buf_size);
 
+  g_free (buf);
   free (url);
 }
 
@@ -704,37 +756,40 @@ void
 broadway_output_put_rgba (BroadwayOutput *output,  int id, int x, int y,
 			  int w, int h, int byte_stride, void *data)
 {
-  char buf[HEADER_LEN + 15];
-  gsize len;
-  char *url;
   BroadwayBox *rects;
   int p, i, n_rects;
-  guint8 *subdata;
 
   rects = rgba_find_rects (data, w, h, byte_stride, &n_rects);
 
   for (i = 0; i < n_rects; i++)
     {
+      gsize url_len, buf_size;
+      char *buf, *url;
+      guint8 *subdata;
+
       subdata = (guint8 *)data + rects[i].x1 * 4 + rects[i].y1 * byte_stride;
+      url = to_png_rgba (rects[i].x2 - rects[i].x1,
+			 rects[i].y2 - rects[i].y1,
+			 byte_stride, (guint32*)subdata);
+
+      url_len = strlen (url);
+      buf_size = HEADER_LEN + 15 + url_len;
+      buf = g_malloc (buf_size);
+
 
       p = write_header (output, buf, 'i');
       append_uint16 (id, buf, &p);
       append_uint16 (x + rects[i].x1, buf, &p);
       append_uint16 (y + rects[i].y1, buf, &p);
 
-      url = to_png_rgba (rects[i].x2 - rects[i].x1,
-			 rects[i].y2 - rects[i].y1,
-			 byte_stride, (guint32*)subdata);
-      len = strlen (url);
-      append_uint32 (len, buf, &p);
-
-      assert (p == sizeof (buf));
+      append_uint32 (url_len, buf, &p);
+      g_assert (p == HEADER_LEN + 15);
+      strncpy (buf + p, url, url_len);
 
-      broadway_output_write (output, buf, sizeof (buf));
-
-      broadway_output_write (output, url, len);
+      broadway_output_sendmsg (output, buf, buf_size);
 
       free (url);
+      g_free (buf);
     }
 
   free (rects);
@@ -750,35 +805,7 @@ broadway_output_surface_flush (BroadwayOutput *output,
   p = write_header (output, buf, 'f');
   append_uint16 (id, buf, &p);
 
-  assert (p == sizeof (buf));
-
-  broadway_output_write (output, buf, sizeof (buf));
-}
-
-#if 0
-static void
-send_image_a (BroadwayOutput *output,  int id, int x, int y,
-	      int w, int h, int byte_stride, guint8 *data)
-{
-  char buf[HEADER_LEN + 15];
-  gsize len;
-  char *url;
-
-  p = write_header (output, buf, 'i');
-  append_uint16 (id, buf, &p);
-  append_uint16 (x, buf, &p);
-  append_uint16 (y, buf, &p);
-
-  url = to_png_a (w, h, byte_stride, data);
-  len = strlen (url);
-  append_uint32 (len, buf, &p);
-
-  assert (p == sizeof (buf));
-
-  broadway_output_write (output, buf, sizeof (buf));
+  g_assert (p == sizeof (buf));
 
-  broadway_output_write (output, url, len);
-
-  free (url);
+  broadway_output_sendmsg (output, buf, sizeof (buf));
 }
-#endif
diff --git a/gdk/broadway/broadway.h b/gdk/broadway/broadway.h
index 1258265..731e9c1 100644
--- a/gdk/broadway/broadway.h
+++ b/gdk/broadway/broadway.h
@@ -8,8 +8,18 @@ typedef struct  {
     int width, height;
 } BroadwayRect;
 
+typedef enum {
+  BROADWAY_WS_CONTINUATION = 0,
+  BROADWAY_WS_TEXT = 1,
+  BROADWAY_WS_BINARY = 2,
+  BROADWAY_WS_CNX_CLOSE = 8,
+  BROADWAY_WS_CNX_PING = 9,
+  BROADWAY_WS_CNX_PONG = 0xa
+} BroadwayWSOpCode;
+
 BroadwayOutput *broadway_output_new             (GOutputStream  *out,
-						 guint32         serial);
+						 guint32         serial,
+						 gboolean        proto_v7_plus);
 void            broadway_output_free            (BroadwayOutput *output);
 int             broadway_output_flush           (BroadwayOutput *output);
 int             broadway_output_has_error       (BroadwayOutput *output);
@@ -66,3 +76,4 @@ void            broadway_output_grab_pointer    (BroadwayOutput *output,
 						 int id,
 						 gboolean owner_event);
 guint32         broadway_output_ungrab_pointer  (BroadwayOutput *output);
+void            broadway_output_pong            (BroadwayOutput *output);
diff --git a/gdk/broadway/broadway.js b/gdk/broadway/broadway.js
index 357620e..90b9d53 100644
--- a/gdk/broadway/broadway.js
+++ b/gdk/broadway/broadway.js
@@ -2769,10 +2769,19 @@ function connect()
 	    useToplevelWindows = true;
     }
 
+    var loc = window.location.toString().replace("http:", "ws:");
+    loc = loc.substr(0, loc.lastIndexOf('/')) + "/socket";
+    var ws = null;
+
     if ("WebSocket" in window) {
-	var loc = window.location.toString().replace("http:", "ws:");
-	loc = loc.substr(0, loc.lastIndexOf('/')) + "/socket";
-	var ws = new WebSocket(loc, "broadway");
+	ws = new WebSocket(loc, "broadway");
+    } else if ("MozWebSocket" in window) { // Firefox 6
+	ws = new MozWebSocket(loc);
+    } else {
+	alert("WebSocket not supported, input will not work!");
+	return;
+    }
+
 	ws.onopen = function() {
 	    inputSocket = ws;
 	    var w, h;
@@ -2797,9 +2806,7 @@ function connect()
 	ws.onmessage = function(event) {
 	    handleMessage(event.data);
 	};
-    } else {
-	alert("WebSocket not supported, input will not work!");
-    }
+
     setupDocument(document);
     window.onunload = function (ev) {
 	for (var i = 0; i < toplevelWindows.length; i++)
diff --git a/gdk/broadway/gdkdisplay-broadway.c b/gdk/broadway/gdkdisplay-broadway.c
index c0431bc..87fc1c8 100644
--- a/gdk/broadway/gdkdisplay-broadway.c
+++ b/gdk/broadway/gdkdisplay-broadway.c
@@ -48,6 +48,10 @@
 static void   gdk_broadway_display_dispose            (GObject            *object);
 static void   gdk_broadway_display_finalize           (GObject            *object);
 
+#if 0
+#define DEBUG_WEBSOCKETS 1
+#endif
+
 G_DEFINE_TYPE (GdkBroadwayDisplay, gdk_broadway_display, GDK_TYPE_DISPLAY)
 
 static void
@@ -128,7 +132,7 @@ typedef struct HttpRequest {
   GString *request;
 }  HttpRequest;
 
-static void start_output (HttpRequest *request);
+static void start_output (HttpRequest *request, gboolean proto_v7_plus);
 
 static void
 http_request_free (HttpRequest *request)
@@ -146,6 +150,7 @@ struct BroadwayInput {
   GSource *source;
   gboolean seen_time;
   gint64 time_base;
+  gboolean proto_v7_plus;
 };
 
 static void
@@ -229,7 +234,7 @@ parse_input_message (BroadwayInput *input, const char *message)
 	 5 seconds after last_seen_time, to avoid issues that could appear when
 	 a long hiatus due to a reconnect seems to be instant */
       input->time_base = time_ - (broadway_display->last_seen_time + 5000);
-    } 
+    }
     time_ = time_ - input->time_base;
   }
 
@@ -309,47 +314,158 @@ parse_input_message (BroadwayInput *input, const char *message)
 
 }
 
+static inline void
+hex_dump (guchar *data, gsize len)
+{
+#ifdef DEBUG_WEBSOCKETS
+  gsize i, j;
+  for (j = 0; j < len + 15; j += 16)
+    {
+      fprintf (stderr, "0x%.4x  ", j);
+      for (i = 0; i < 16; i++)
+	{
+	    if ((j + i) < len)
+	      fprintf (stderr, "%.2x ", data[j+i]);
+	    else
+	      fprintf (stderr, "  ");
+	    if (i == 8)
+	      fprintf (stderr, " ");
+	}
+      fprintf (stderr, " | ");
+
+      for (i = 0; i < 16; i++)
+	if ((j + i) < len && g_ascii_isalnum(data[j+i]))
+	  fprintf (stderr, "%c", data[j+i]);
+	else
+	  fprintf (stderr, ".");
+      fprintf (stderr, "\n");
+    }
+#endif
+}
+
 static void
 parse_input (BroadwayInput *input)
 {
   GdkBroadwayDisplay *broadway_display;
-  char *buf, *ptr;
-  gsize len;
 
   broadway_display = GDK_BROADWAY_DISPLAY (input->display);
 
-  buf = (char *)input->buffer->data;
-  len = input->buffer->len;
-
-  if (len == 0)
+  if (!input->buffer->len)
     return;
 
-  if (buf[0] != 0)
+  if (input->proto_v7_plus)
     {
-      broadway_display->input = NULL;
-      broadway_input_free (input);
-      return;
-    }
+      hex_dump (input->buffer->data, input->buffer->len);
 
-  while ((ptr = memchr (buf, 0xff, len)) != NULL)
-    {
-      *ptr = 0;
-      ptr++;
+      while (input->buffer->len > 2)
+	{
+	  gsize len, payload_len;
+	  BroadwayWSOpCode code;
+	  gboolean is_mask, fin;
+	  guchar *buf, *data;
+
+	  buf = input->buffer->data;
+	  len = input->buffer->len;
+
+#ifdef DEBUG_WEBSOCKETS
+	  g_print ("Parse input first byte 0x%2x 0x%2x\n", buf[0], buf[1]);
+#endif
+
+	  fin = buf[0] & 0x80;
+	  code = buf[0] & 0x0f;
+	  payload_len = buf[1] & 0x7f;
+	  is_mask = buf[1] & 0x80;
+	  data = buf + 2;
+
+	  if (payload_len > 125)
+	    {
+	      if (len < 4)
+		return;
+	      payload_len = GUINT16_FROM_BE( *(guint16 *) data );
+	      data += 2;
+	    }
+	  else if (payload_len > 126)
+	    {
+	      if (len < 10)
+		return;
+	      payload_len = GUINT64_FROM_BE( *(guint64 *) data );
+	      data += 8;
+	    }
+	  if (data - buf + payload_len > len)
+	    return; /* wait to accumulate more */
 
-      parse_input_message (input, buf + 1);
+	  if (is_mask)
+	    {
+	      gsize i;
+	      for (i = 0; i < payload_len; i++)
+		data[i + 4] ^= data[i%4];
+	      data += 4;
+	    }
 
-      len -= ptr - buf;
-      buf = ptr;
+	  switch (code) {
+	  case BROADWAY_WS_CNX_CLOSE:
+	    break; /* hang around anyway */
+	  case BROADWAY_WS_TEXT:
+	    if (!fin)
+	      {
+#ifdef DEBUG_WEBSOCKETS
+		g_warning ("can't yet accept fragmented input");
+#endif
+	      }
+	    else
+	      parse_input_message (input, (char *)data);
+	    break;
+	  case BROADWAY_WS_CNX_PING:
+	    broadway_output_pong (broadway_display->output);
+	    break;
+	  case BROADWAY_WS_CNX_PONG:
+	    break; /* we never send pings, but tolerate pongs */
+	  case BROADWAY_WS_BINARY:
+	  case BROADWAY_WS_CONTINUATION:
+	  default:
+	    {
+	      g_warning ("fragmented or unknown input code 0x%2x with fin set", code);
+	      break;
+	    }
+	  }
 
-      if (len > 0 && buf[0] != 0)
+	  g_byte_array_remove_range (input->buffer, 0, data - buf + payload_len);
+	}
+    }
+  else /* old style protocol */
+    {
+      char *buf, *ptr;
+      gsize len;
+
+      buf = (char *)input->buffer->data;
+      len = input->buffer->len;
+
+      if (buf[0] != 0)
 	{
 	  broadway_display->input = NULL;
 	  broadway_input_free (input);
-	  break;
+	  return;
 	}
-    }
 
-  g_byte_array_remove_range (input->buffer, 0, buf - (char *)input->buffer->data);
+      while ((ptr = memchr (buf, 0xff, len)) != NULL)
+	{
+	  *ptr = 0;
+	  ptr++;
+
+	  parse_input_message (input, buf + 1);
+
+	  len -= ptr - buf;
+	  buf = ptr;
+
+	  if (len > 0 && buf[0] != 0)
+	    {
+	      broadway_display->input = NULL;
+	      broadway_input_free (input);
+	      break;
+	    }
+	}
+      g_byte_array_remove_range (input->buffer, 0, buf - (char *)input->buffer->data);
+    }
 }
 
 
@@ -404,7 +520,7 @@ _gdk_broadway_display_read_all_input_nonblocking (GdkDisplay *display)
       broadway_input_free (input);
       if (res < 0)
 	{
-	  g_print ("input error %s", error->message);
+	  g_print ("input error %s\n", error->message);
 	  g_error_free (error);
 	}
       return;
@@ -537,6 +653,32 @@ send_error (HttpRequest *request,
   http_request_free (request);
 }
 
+/* magic from: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 */
+#define SEC_WEB_SOCKET_KEY_MAGIC "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+
+/* 'x3JJHMbDL1EzLkh9GBhXDw==' generates 'HSmrc0sMlYUkAGmm5OPpG2HaGWk=' */
+static gchar *
+generate_handshake_response_wsietf_v7 (const gchar *key)
+{
+  guchar digest[20];
+  gsize digest_len;
+  GChecksum *checksum;
+
+  checksum = g_checksum_new (G_CHECKSUM_SHA1);
+  if (!checksum)
+    return NULL;
+
+  g_checksum_update (checksum, (guchar *)key, -1);
+  g_checksum_update (checksum, (guchar *)SEC_WEB_SOCKET_KEY_MAGIC, -1);
+
+  g_checksum_get_digest (checksum, digest, &digest_len);
+  g_checksum_free (checksum);
+
+  g_assert (digest_len == 20);
+
+  return g_base64_encode (digest, digest_len);
+}
+
 static void
 start_input (HttpRequest *request)
 {
@@ -556,6 +698,8 @@ start_input (HttpRequest *request)
   const void *data_buffer;
   gsize data_buffer_size;
   GInputStream *in;
+  char *key_v7;
+  gboolean proto_v7_plus;
 
   broadway_display = GDK_BROADWAY_DISPLAY (request->display);
 
@@ -565,12 +709,16 @@ start_input (HttpRequest *request)
       return;
     }
 
+#ifdef DEBUG_WEBSOCKETS
+  g_print ("incoming request:\n%s\n", request->request->str);
+#endif
   lines = g_strsplit (request->request->str, "\n", 0);
 
   num_key1 = 0;
   num_key2 = 0;
   key1 = 0;
   key2 = 0;
+  key_v7 = NULL;
   origin = NULL;
   host = NULL;
   for (i = 0; lines[i] != NULL; i++)
@@ -605,6 +753,10 @@ start_input (HttpRequest *request)
 	  key2 /= num_space;
 	  num_key2++;
 	}
+      else if ((p = parse_line (lines[i], "Sec-WebSocket-Key")))
+	{
+	  key_v7 = p;
+	}
       else if ((p = parse_line (lines[i], "Origin")))
 	{
 	  origin = p;
@@ -613,56 +765,97 @@ start_input (HttpRequest *request)
 	{
 	  host = p;
 	}
+      else if ((p = parse_line (lines[i], "Sec-WebSocket-Origin")))
+	{
+	  origin = p;
+	}
     }
 
-  if (num_key1 != 1 || num_key2 != 1 || origin == NULL || host == NULL)
+  if (origin == NULL || host == NULL)
     {
       g_strfreev (lines);
       send_error (request, 400, "Bad websocket request");
       return;
     }
 
-  challenge[0] = (key1 >> 24) & 0xff;
-  challenge[1] = (key1 >> 16) & 0xff;
-  challenge[2] = (key1 >>  8) & 0xff;
-  challenge[3] = (key1 >>  0) & 0xff;
-  challenge[4] = (key2 >> 24) & 0xff;
-  challenge[5] = (key2 >> 16) & 0xff;
-  challenge[6] = (key2 >>  8) & 0xff;
-  challenge[7] = (key2 >>  0) & 0xff;
-
-  if (!g_input_stream_read_all (G_INPUT_STREAM (request->data), challenge+8, 8, NULL, NULL, NULL))
+  if (key_v7 != NULL)
     {
-      g_strfreev (lines);
-      send_error (request, 400, "Bad websocket request");
-      return;
+      char* accept = generate_handshake_response_wsietf_v7 (key_v7);
+      res = g_strdup_printf ("HTTP/1.1 101 Switching Protocols\r\n"
+			     "Upgrade: websocket\r\n"
+			     "Connection: Upgrade\r\n"
+			     "Sec-WebSocket-Accept: %s\r\n"
+			     "Sec-WebSocket-Origin: %s\r\n"
+			     "Sec-WebSocket-Location: ws://%s/socket\r\n"
+			     "Sec-WebSocket-Protocol: broadway\r\n"
+			     "\r\n", accept, origin, host);
+      g_free (accept);
+
+#ifdef DEBUG_WEBSOCKETS
+      g_print ("v7 proto response:\n%s", res);
+#endif
+
+      g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
+				 res, strlen (res), NULL, NULL, NULL);
+      g_free (res);
+      proto_v7_plus = TRUE;
     }
+  else
+    {
+      if (num_key1 != 1 || num_key2 != 1)
+	{
+	  g_strfreev (lines);
+	  send_error (request, 400, "Bad websocket request");
+	  return;
+	}
 
-  checksum = g_checksum_new (G_CHECKSUM_MD5);
-  g_checksum_update (checksum, challenge, 16);
-  len = 16;
-  g_checksum_get_digest (checksum, challenge, &len);
-  g_checksum_free (checksum);
+      challenge[0] = (key1 >> 24) & 0xff;
+      challenge[1] = (key1 >> 16) & 0xff;
+      challenge[2] = (key1 >>  8) & 0xff;
+      challenge[3] = (key1 >>  0) & 0xff;
+      challenge[4] = (key2 >> 24) & 0xff;
+      challenge[5] = (key2 >> 16) & 0xff;
+      challenge[6] = (key2 >>  8) & 0xff;
+      challenge[7] = (key2 >>  0) & 0xff;
 
-  res = g_strdup_printf ("HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
-			 "Upgrade: WebSocket\r\n"
-			 "Connection: Upgrade\r\n"
-			 "Sec-WebSocket-Origin: %s\r\n"
-			 "Sec-WebSocket-Location: ws://%s/socket\r\n"
-			 "Sec-WebSocket-Protocol: broadway\r\n"
-			 "\r\n",
-			 origin, host);
+      if (!g_input_stream_read_all (G_INPUT_STREAM (request->data), challenge+8, 8, NULL, NULL, NULL))
+	{
+	  g_strfreev (lines);
+	  send_error (request, 400, "Bad websocket request");
+	  return;
+	}
 
-  g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
-			     res, strlen (res), NULL, NULL, NULL);
-  g_free (res);
-  g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
-			     challenge, 16, NULL, NULL, NULL);
+      checksum = g_checksum_new (G_CHECKSUM_MD5);
+      g_checksum_update (checksum, challenge, 16);
+      len = 16;
+      g_checksum_get_digest (checksum, challenge, &len);
+      g_checksum_free (checksum);
+
+      res = g_strdup_printf ("HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+			     "Upgrade: WebSocket\r\n"
+			     "Connection: Upgrade\r\n"
+			     "Sec-WebSocket-Origin: %s\r\n"
+			     "Sec-WebSocket-Location: ws://%s/socket\r\n"
+			     "Sec-WebSocket-Protocol: broadway\r\n"
+			     "\r\n",
+			     origin, host);
+
+#ifdef DEBUG_WEBSOCKETS
+      g_print ("legacy response:\n%s", res);
+#endif
+      g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
+				 res, strlen (res), NULL, NULL, NULL);
+      g_free (res);
+      g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
+				 challenge, 16, NULL, NULL, NULL);
+      proto_v7_plus = FALSE;
+    }
 
   input = g_new0 (BroadwayInput, 1);
 
   input->display = request->display;
   input->connection = g_object_ref (request->connection);
+  input->proto_v7_plus = proto_v7_plus;
 
   data_buffer = g_buffered_input_stream_peek_buffer (G_BUFFERED_INPUT_STREAM (request->data), &data_buffer_size);
   input->buffer = g_byte_array_sized_new (data_buffer_size);
@@ -670,7 +863,7 @@ start_input (HttpRequest *request)
 
   broadway_display->input = input;
 
-  start_output (request);
+  start_output (request, proto_v7_plus);
 
   /* This will free and close the data input stream, but we got all the buffered content already */
   http_request_free (request);
@@ -688,7 +881,7 @@ start_input (HttpRequest *request)
 }
 
 static void
-start_output (HttpRequest *request)
+start_output (HttpRequest *request, gboolean proto_v7_plus)
 {
   GSocket *socket;
   GdkBroadwayDisplay *broadway_display;
@@ -708,7 +901,7 @@ start_output (HttpRequest *request)
 
   broadway_display->output =
     broadway_output_new (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
-			 broadway_display->saved_serial);
+			 broadway_display->saved_serial, proto_v7_plus);
 
   _gdk_broadway_resync_windows ();
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]