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:
Michael Meeks 2011-11-10 10:12:28 +01:00 committed by Alexander Larsson
parent f711da3d1b
commit 14a17873de
4 changed files with 387 additions and 149 deletions

View File

@ -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

View File

@ -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);

View File

@ -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++)

View File

@ -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 (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 ();