forked from AuroraMiddleware/gtk
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.
This commit is contained in:
parent
f711da3d1b
commit
14a17873de
@ -175,30 +175,84 @@ struct BroadwayOutput {
|
||||
GOutputStream *out;
|
||||
int error;
|
||||
guint32 serial;
|
||||
gboolean proto_v7_plus;
|
||||
};
|
||||
|
||||
static void
|
||||
broadway_output_write_header (BroadwayOutput *output)
|
||||
broadway_output_send_cmd (BroadwayOutput *output,
|
||||
gboolean fin, BroadwayWSOpCode code,
|
||||
const void *buf, gsize count)
|
||||
{
|
||||
g_output_stream_write (output->out, "\0", 1, NULL, NULL);
|
||||
}
|
||||
gboolean mask = FALSE;
|
||||
guchar header[16];
|
||||
size_t p;
|
||||
|
||||
static void
|
||||
broadway_output_write (BroadwayOutput *output,
|
||||
const void *buf, gsize count)
|
||||
{
|
||||
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
|
||||
send_boundary (BroadwayOutput *output)
|
||||
broadway_output_sendmsg (BroadwayOutput *output,
|
||||
const void *buf, gsize count)
|
||||
{
|
||||
broadway_output_write (output, "\xff", 1);
|
||||
broadway_output_write (output, "\0", 1);
|
||||
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
|
||||
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)
|
||||
{
|
||||
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);
|
||||
append_uint32 (url_len, buf, &p);
|
||||
|
||||
assert (p == sizeof (buf));
|
||||
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);
|
||||
|
||||
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);
|
||||
append_uint32 (url_len, buf, &p);
|
||||
g_assert (p == HEADER_LEN + 15);
|
||||
strncpy (buf + p, url, url_len);
|
||||
|
||||
assert (p == sizeof (buf));
|
||||
|
||||
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));
|
||||
g_assert (p == sizeof (buf));
|
||||
|
||||
broadway_output_write (output, buf, sizeof (buf));
|
||||
broadway_output_sendmsg (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));
|
||||
|
||||
broadway_output_write (output, url, len);
|
||||
|
||||
free (url);
|
||||
}
|
||||
#endif
|
||||
|
@ -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);
|
||||
|
@ -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++)
|
||||
|
@ -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
|
||||
@ -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 (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 */
|
||||
|
||||
if (is_mask)
|
||||
{
|
||||
gsize i;
|
||||
for (i = 0; i < payload_len; i++)
|
||||
data[i + 4] ^= data[i%4];
|
||||
data += 4;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
g_byte_array_remove_range (input->buffer, 0, data - buf + payload_len);
|
||||
}
|
||||
}
|
||||
|
||||
while ((ptr = memchr (buf, 0xff, len)) != NULL)
|
||||
else /* old style protocol */
|
||||
{
|
||||
*ptr = 0;
|
||||
ptr++;
|
||||
char *buf, *ptr;
|
||||
gsize len;
|
||||
|
||||
parse_input_message (input, buf + 1);
|
||||
buf = (char *)input->buffer->data;
|
||||
len = input->buffer->len;
|
||||
|
||||
len -= ptr - buf;
|
||||
buf = ptr;
|
||||
|
||||
if (len > 0 && buf[0] != 0)
|
||||
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 ();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user