diff options
author | Michael Meeks <michael.meeks@suse.com> | 2011-11-10 10:12:28 +0100 |
---|---|---|
committer | Alexander Larsson <alexl@redhat.com> | 2011-11-10 10:12:28 +0100 |
commit | 14a17873de4451eb981bdb5325559efafeeda5f4 (patch) | |
tree | e2535c8eaf1b49268d3fb2ae7ef5425264cec0bd | |
parent | f711da3d1b7c536ed7d7f89975928f2d8f05fd7a (diff) | |
download | gtk+-14a17873de4451eb981bdb5325559efafeeda5f4.tar.gz |
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.
-rw-r--r-- | gdk/broadway/broadway.c | 189 | ||||
-rw-r--r-- | gdk/broadway/broadway.h | 13 | ||||
-rw-r--r-- | gdk/broadway/broadway.js | 19 | ||||
-rw-r--r-- | 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 9f27746b40..b1047588f7 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 12582654c4..731e9c1cce 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 357620ee7b..90b9d53cff 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 c0431bcbdc..87fc1c87b3 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 (); |