Commit f2e30144 authored by Jasper St. Pierre's avatar Jasper St. Pierre

broadway: Remove support for old browsers

Require binary array buffers and modern WebSocket protocol support.
parent d61bcf13
......@@ -7,56 +7,6 @@
#include "broadway-output.h"
/************************************************************************
* Base64 functions *
************************************************************************/
static const char base64_alphabet[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
#if 0 /* Unused for now */
static void
base64_uint8 (guint8 v, char *c)
{
c[0] = base64_alphabet[(v >> 0) & 0x3f];
c[1] = base64_alphabet[(v >> 6) & 0x3];
}
#endif
static void
base64_uint16 (guint32 v, char *c)
{
c[0] = base64_alphabet[(v >> 0) & 0x3f];
c[1] = base64_alphabet[(v >> 6) & 0x3f];
c[2] = base64_alphabet[(v >> 12) & 0xf];
}
#if 0
static void
base64_uint24 (guint32 v, char *c)
{
c[0] = base64_alphabet[(v >> 0) & 0x3f];
c[1] = base64_alphabet[(v >> 6) & 0x3f];
c[2] = base64_alphabet[(v >> 12) & 0x3f];
c[3] = base64_alphabet[(v >> 18) & 0x3f];
}
#endif
static void
base64_uint32 (guint32 v, char *c)
{
c[0] = base64_alphabet[(v >> 0) & 0x3f];
c[1] = base64_alphabet[(v >> 6) & 0x3f];
c[2] = base64_alphabet[(v >> 12) & 0x3f];
c[3] = base64_alphabet[(v >> 18) & 0x3f];
c[4] = base64_alphabet[(v >> 24) & 0x3f];
c[5] = base64_alphabet[(v >> 30) & 0x2];
}
/***********************************************************
* conversion of raw image data to png data: uris *
***********************************************************/
static cairo_status_t
write_png_data (void *closure,
const unsigned char *data,
......@@ -93,118 +43,6 @@ to_png_rgba (GString *buf, int w, int h, int byte_stride, guint32 *data)
cairo_surface_destroy (surface);
}
struct PngTarget {
GString *buf;
int state;
int save;
};
static cairo_status_t
write_png_url (void *closure,
const unsigned char *data,
unsigned int data_len)
{
struct PngTarget *target = closure;
gsize res, old_len;
old_len = target->buf->len;
g_string_set_size (target->buf,
old_len + (data_len / 3 + 1) * 4 + 4);
res = g_base64_encode_step (data, data_len, FALSE,
target->buf->str + old_len,
&target->state, &target->save);
g_string_set_size (target->buf, old_len + res);
return CAIRO_STATUS_SUCCESS;
}
static void
to_png_url_rgb (GString *buf, int w, int h, int byte_stride, guint32 *data)
{
cairo_surface_t *surface;
struct PngTarget target;
gsize res, old_len;
target.buf = buf;
target.state = 0;
target.save = 0;
g_string_append (buf, "data:image/png;base64,");
surface = cairo_image_surface_create_for_data ((guchar *)data,
CAIRO_FORMAT_RGB24, w, h, byte_stride);
cairo_surface_write_to_png_stream (surface, write_png_url, &target);
cairo_surface_destroy (surface);
old_len = buf->len;
g_string_set_size (buf, old_len + 4);
res = g_base64_encode_close (FALSE,
buf->str + old_len,
&target.state, &target.save);
g_string_set_size (buf, old_len + res);
}
static void
to_png_url_rgba (GString *buf, int w, int h, int byte_stride, guint32 *data)
{
cairo_surface_t *surface;
struct PngTarget target;
gsize res, old_len;
target.buf = buf;
target.state = 0;
target.save = 0;
g_string_append (buf, "data:image/png;base64,");
surface = cairo_image_surface_create_for_data ((guchar *)data,
CAIRO_FORMAT_ARGB32, w, h, byte_stride);
cairo_surface_write_to_png_stream (surface, write_png_url, &target);
cairo_surface_destroy (surface);
old_len = buf->len;
g_string_set_size (buf, old_len + 4);
res = g_base64_encode_close (FALSE,
buf->str + old_len,
&target.state, &target.save);
g_string_set_size (buf, old_len + res);
}
#if 0
static char *
to_png_a (int w, int h, int byte_stride, guint8 *data)
{
cairo_surface_t *surface;
struct PngTarget target;
gsize res, old_len;
target.url = g_string_new ("data:image/png;base64,");
target.state = 0;
target.save = 0;
surface = cairo_image_surface_create_for_data (data,
CAIRO_FORMAT_A8, w, h, byte_stride);
cairo_surface_write_to_png_stream (surface, write_png_url, &target);
old_len = target.url->len;
g_string_set_size (target.url, old_len + 4);
res = g_base64_encode_close (FALSE,
target.url->str + old_len,
&target.state, &target.save);
g_string_set_size (target.url, old_len + res);
return g_string_free (target.url, FALSE);
}
#endif
/************************************************************************
* Basic I/O primitives *
************************************************************************/
......@@ -214,8 +52,6 @@ struct BroadwayOutput {
GString *buf;
int error;
guint32 serial;
gboolean proto_v7_plus;
gboolean binary;
};
static void
......@@ -251,19 +87,9 @@ broadway_output_send_cmd (BroadwayOutput *output,
g_output_stream_write_all (output->out, buf, count, NULL, NULL, NULL);
}
static void
broadway_output_send_cmd_pre_v7 (BroadwayOutput *output,
const void *buf, gsize count)
{
g_output_stream_write_all (output->out, "\0", 1, NULL, NULL, NULL);
g_output_stream_write_all (output->out, buf, count, NULL, NULL, NULL);
g_output_stream_write_all (output->out, "\xff", 1, NULL, NULL, NULL);
}
void broadway_output_pong (BroadwayOutput *output)
{
if (output->proto_v7_plus)
broadway_output_send_cmd (output, TRUE, BROADWAY_WS_CNX_PONG, NULL, 0);
broadway_output_send_cmd (output, TRUE, BROADWAY_WS_CNX_PONG, NULL, 0);
}
int
......@@ -272,14 +98,8 @@ broadway_output_flush (BroadwayOutput *output)
if (output->buf->len == 0)
return TRUE;
if (!output->proto_v7_plus)
broadway_output_send_cmd_pre_v7 (output, output->buf->str, output->buf->len);
else if (output->binary)
broadway_output_send_cmd (output, TRUE, BROADWAY_WS_BINARY,
output->buf->str, output->buf->len);
else
broadway_output_send_cmd (output, TRUE, BROADWAY_WS_TEXT,
output->buf->str, output->buf->len);
broadway_output_send_cmd (output, TRUE, BROADWAY_WS_BINARY,
output->buf->str, output->buf->len);
g_string_set_size (output->buf, 0);
......@@ -288,8 +108,7 @@ broadway_output_flush (BroadwayOutput *output)
}
BroadwayOutput *
broadway_output_new (GOutputStream *out, guint32 serial,
gboolean proto_v7_plus, gboolean binary)
broadway_output_new (GOutputStream *out, guint32 serial)
{
BroadwayOutput *output;
......@@ -298,8 +117,6 @@ broadway_output_new (GOutputStream *out, guint32 serial,
output->out = g_object_ref (out);
output->buf = g_string_new ("");
output->serial = serial;
output->proto_v7_plus = proto_v7_plus;
output->binary = binary;
return output;
}
......@@ -338,40 +155,25 @@ append_char (BroadwayOutput *output, char c)
static void
append_bool (BroadwayOutput *output, gboolean val)
{
if (output->binary)
g_string_append_c (output->buf, val ? 1: 0);
else
g_string_append_c (output->buf, val ? '1': '0');
g_string_append_c (output->buf, val ? 1: 0);
}
static void
append_flags (BroadwayOutput *output, guint32 val)
{
if (output->binary)
g_string_append_c (output->buf, val);
else
g_string_append_c (output->buf, val + '0');
g_string_append_c (output->buf, val);
}
static void
append_uint16 (BroadwayOutput *output, guint32 v)
{
gsize old_len = output->buf->len;
guint8 *buf;
if (output->binary)
{
g_string_set_size (output->buf, old_len + 2);
buf = (guint8 *)output->buf->str + old_len;
buf[0] = (v >> 0) & 0xff;
buf[1] = (v >> 8) & 0xff;
}
else
{
g_string_set_size (output->buf, old_len + 3);
base64_uint16 (v, output->buf->str + old_len);
}
g_string_set_size (output->buf, old_len + 2);
buf = (guint8 *)output->buf->str + old_len;
buf[0] = (v >> 0) & 0xff;
buf[1] = (v >> 8) & 0xff;
}
static void
......@@ -380,38 +182,23 @@ append_uint32 (BroadwayOutput *output, guint32 v)
gsize old_len = output->buf->len;
guint8 *buf;
if (output->binary)
{
g_string_set_size (output->buf, old_len + 4);
buf = (guint8 *)output->buf->str + old_len;
buf[0] = (v >> 0) & 0xff;
buf[1] = (v >> 8) & 0xff;
buf[2] = (v >> 16) & 0xff;
buf[3] = (v >> 24) & 0xff;
}
else
{
g_string_set_size (output->buf, old_len + 6);
base64_uint32 (v, output->buf->str + old_len);
}
g_string_set_size (output->buf, old_len + 4);
buf = (guint8 *)output->buf->str + old_len;
buf[0] = (v >> 0) & 0xff;
buf[1] = (v >> 8) & 0xff;
buf[2] = (v >> 16) & 0xff;
buf[3] = (v >> 24) & 0xff;
}
static void
overwrite_uint32 (BroadwayOutput *output, gsize pos, guint32 v)
{
if (output->binary)
{
guint8 *buf = (guint8 *)output->buf->str + pos;
guint8 *buf = (guint8 *)output->buf->str + pos;
buf[0] = (v >> 0) & 0xff;
buf[1] = (v >> 8) & 0xff;
buf[2] = (v >> 16) & 0xff;
buf[3] = (v >> 24) & 0xff;
}
else
{
base64_uint32 (v, output->buf->str + pos);
}
buf[0] = (v >> 0) & 0xff;
buf[1] = (v >> 8) & 0xff;
buf[2] = (v >> 16) & 0xff;
buf[3] = (v >> 24) & 0xff;
}
......@@ -576,10 +363,7 @@ broadway_output_put_rgb (BroadwayOutput *output, int id, int x, int y,
append_uint32 (output, 0);
image_start = output->buf->len;
if (output->binary)
to_png_rgb (output->buf, w, h, byte_stride, (guint32*)data);
else
to_png_url_rgb (output->buf, w, h, byte_stride, (guint32*)data);
to_png_rgb (output->buf, w, h, byte_stride, (guint32*)data);
len = output->buf->len - image_start;
......@@ -834,14 +618,9 @@ broadway_output_put_rgba (BroadwayOutput *output, int id, int x, int y,
image_start = output->buf->len;
subdata = (guint8 *)data + rects[i].x1 * 4 + rects[i].y1 * byte_stride;
if (output->binary)
to_png_rgba (output->buf, rects[i].x2 - rects[i].x1,
rects[i].y2 - rects[i].y1,
byte_stride, (guint32*)subdata);
else
to_png_url_rgba (output->buf, rects[i].x2 - rects[i].x1,
rects[i].y2 - rects[i].y1,
byte_stride, (guint32*)subdata);
to_png_rgba (output->buf, rects[i].x2 - rects[i].x1,
rects[i].y2 - rects[i].y1,
byte_stride, (guint32*)subdata);
len = output->buf->len - image_start;
......
......@@ -17,9 +17,7 @@ typedef enum {
} BroadwayWSOpCode;
BroadwayOutput *broadway_output_new (GOutputStream *out,
guint32 serial,
gboolean proto_v7_plus,
gboolean binary);
guint32 serial);
void broadway_output_free (BroadwayOutput *output);
int broadway_output_flush (BroadwayOutput *output);
int broadway_output_has_error (BroadwayOutput *output);
......
......@@ -96,8 +96,6 @@ struct BroadwayInput {
GSource *source;
gboolean seen_time;
gint64 time_base;
gboolean proto_v7_plus;
gboolean binary;
gboolean active;
};
......@@ -557,138 +555,97 @@ hex_dump (guchar *data, gsize len)
static void
parse_input (BroadwayInput *input)
{
BroadwayServer *server = input->server;
if (!input->buffer->len)
return;
if (input->proto_v7_plus)
{
hex_dump (input->buffer->data, input->buffer->len);
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, *mask;
while (input->buffer->len > 2)
{
gsize len, payload_len;
BroadwayWSOpCode code;
gboolean is_mask, fin;
guchar *buf, *data, *mask;
buf = input->buffer->data;
len = input->buffer->len;
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]);
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;
}
mask = NULL;
if (is_mask)
{
if (data - buf + 4 > len)
return;
mask = data;
data += 4;
}
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] ^= mask[i%4];
}
switch (code) {
case BROADWAY_WS_CNX_CLOSE:
break; /* hang around anyway */
case BROADWAY_WS_TEXT:
if (!fin)
{
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;
}
mask = NULL;
if (is_mask)
{
if (data - buf + 4 > len)
return;
mask = data;
data += 4;
}
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] ^= mask[i%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");
g_warning ("can't yet accept fragmented input");
#endif
}
else
{
char *terminated = g_strndup((char *)data, payload_len);
parse_input_message (input, terminated);
g_free (terminated);
}
break;
case BROADWAY_WS_CNX_PING:
broadway_output_pong (input->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);
}
}
else /* old style protocol */
{
char *buf, *ptr;
gsize len;
buf = (char *)input->buffer->data;
len = input->buffer->len;
if (buf[0] != 0)
{
if (server->input == input)
server->input = NULL;
broadway_input_free (input);
return;
}
while ((ptr = memchr (buf, 0xff, len)) != NULL)
{
*ptr = 0;
ptr++;
parse_input_message (input, buf + 1);
len -= ptr - buf;
buf = ptr;
}
else
{
char *terminated = g_strndup((char *)data, payload_len);
parse_input_message (input, terminated);
g_free (terminated);
}
break;
case BROADWAY_WS_CNX_PING:
broadway_output_pong (input->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)
{
if (server->input == input)
server->input = NULL;
broadway_input_free (input);
break;
}
}
g_byte_array_remove_range (input->buffer, 0, buf - (char *)input->buffer->data);
g_byte_array_remove_range (input->buffer, 0, data - buf + payload_len);
}
}
......@@ -1002,25 +959,18 @@ generate_handshake_response_wsietf_v7 (const gchar *key)
}
static void
start_input (HttpRequest *request, gboolean binary)
start_input (HttpRequest *request)
{
char **lines;
char *p;
int num_key1, num_key2;
guint64 key1, key2;
int num_space;
int i;
guint8 challenge[16];
char *res;
gsize len;
GChecksum *checksum;
char *origin, *host;
BroadwayInput *input;
const void *data_buffer;
gsize data_buffer_size;
GInputStream *in;
char *key_v7;
gboolean proto_v7_plus;
char *key;
GSocket *socket;
int flag = 1;
......@@ -1029,61 +979,19 @@ start_input (HttpRequest *request, gboolean binary)
#endif
lines = g_strsplit (request->request->str, "\n", 0);
num_key1 = 0;
num_key2 = 0;
key1 = 0;
key2 = 0;
key_v7 = NULL;
key = NULL;
origin = NULL;
host = NULL;
for (i = 0; lines[i] != NULL; i++)
{
if ((p = parse_line (lines[i], "Sec-WebSocket-Key1")))
{
num_space = 0;
while (*p != 0)
{
if (g_ascii_isdigit (*p))
key1 = key1 * 10 + g_ascii_digit_value (*p);
else if (*p == ' ')
num_space++;
p++;
}
key1 /= num_space;
num_key1++;
}
else if ((p = parse_line (lines[i], "Sec-WebSocket-Key2")))
{
num_space = 0;
while (*p != 0)
{
if (g_ascii_isdigit (*p))
key2 = key2 * 10 + g_ascii_digit_value (*p);
else if (*p == ' ')
num_space++;
p++;
}
key2 /= num_space;
num_key2++;
}
else if ((p = parse_line (lines[i], "Sec-WebSocket-Key")))
{
key_v7 = p;
}
if ((p = parse_line (lines[i], "Sec-WebSocket-Key")))
key = p;
else if ((p = parse_line (lines[i], "Origin")))
{
origin = p;
}
origin = p;
else if ((p = parse_line (lines[i], "Host")))
{
host = p;
}
host = p;
else if ((p = parse_line (lines[i], "Sec-WebSocket-Origin")))
{
origin = p;
}
origin = p;
}
if (host == NULL)
......@@ -1093,9 +1001,9 @@ start_input (HttpRequest *request, gboolean binary)
return;
}
if (key_v7 != NULL)
if (key != NULL)
{
char* accept = generate_handshake_response_wsietf_v7 (key_v7);
char* accept = generate_handshake_response_wsietf_v7 (key);
res = g_strdup_printf ("HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
......@@ -1115,58 +1023,12 @@ start_input (HttpRequest *request, gboolean binary)
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);