Initial sketch of websockets support

This commit is contained in:
Alexander Larsson 2010-11-19 14:13:13 +01:00
parent f11a7d2fde
commit 852b317b4f
2 changed files with 172 additions and 3 deletions

View File

@ -278,6 +278,21 @@ function handleLoad(event)
if (outstanding_commands.length == 1) { if (outstanding_commands.length == 1) {
handleOutstanding() handleOutstanding()
} }
if ("WebSocket" in window) {
var loc = window.location.toString().replace("http:", "ws:");
loc = loc.substr(0, loc.lastIndexOf('/')) + "/input";
var ws = new WebSocket(loc, "broadway");
ws.onopen = function() {
ws.send("Message to send1");
ws.send("Message to send2");
};
ws.onmessage = function (evt) { var msg = evt.data;
};
ws.onclose = function() { };
} else {
alert("WebSocket not supported, input will not work!");
}
} }
function connect() function connect()

View File

@ -153,6 +153,160 @@ set_fd_blocking (int fd)
fcntl (fd, F_SETFL, arg); fcntl (fd, F_SETFL, arg);
} }
static char *
parse_line (char *line, char *key)
{
char *p;
if (!g_str_has_prefix (line, key))
return NULL;
p = line + strlen (key);
if (*p != ':')
return NULL;
p++;
/* Skip optional initial space */
if (*p == ' ')
p++;
return p;
}
static void
got_input (GInputStream *stream,
GAsyncResult *result,
HttpRequest *request)
{
char *message;
gsize len;
GError *error = NULL;
message = g_data_input_stream_read_upto_finish (G_DATA_INPUT_STREAM (stream), result, &len, &error);
if (message == NULL)
{
g_print (error->message);
g_error_free (error);
exit (1);
}
g_assert (message[0] == 0);
g_print ("message: %s\n", message+1);
/* Skip past ending 0xff */
g_data_input_stream_read_byte (request->data, NULL, NULL);
g_data_input_stream_read_upto_async (request->data, "\xff", 1, 0, NULL,
(GAsyncReadyCallback)got_input, request);
}
static void
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;
lines = g_strsplit (request->request->str, "\n", 0);
num_key1 = 0;
num_key2 = 0;
key1 = 0;
key2 = 0;
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], "Origin")))
{
origin = p;
}
else if ((p = parse_line (lines[i], "Host")))
{
host = p;
}
}
if (num_key1 != 1 || num_key2 != 1 || origin == NULL || host == NULL)
{
g_print ("error");
exit (1);
}
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))
{
g_print ("error");
exit (1);
}
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/input\r\n"
"Sec-WebSocket-Protocol: broadway\r\n"
"\r\n",
origin, host);
/* TODO: This should really be async */
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);
g_data_input_stream_read_upto_async (request->data, "\xff", 1, 0, NULL,
(GAsyncReadyCallback)got_input, request);
}
static void static void
start_output (HttpRequest *request) start_output (HttpRequest *request)
{ {
@ -160,8 +314,6 @@ start_output (HttpRequest *request)
GdkDisplayBroadway *display_broadway; GdkDisplayBroadway *display_broadway;
int fd; int fd;
g_print ("start_output\n");
socket = g_socket_connection_get_socket (request->connection); socket = g_socket_connection_get_socket (request->connection);
display_broadway = GDK_DISPLAY_BROADWAY (request->display); display_broadway = GDK_DISPLAY_BROADWAY (request->display);
@ -256,12 +408,14 @@ got_request (HttpRequest *request)
send_data (request, "text/javascript", broadway_js, G_N_ELEMENTS(broadway_js) - 1); send_data (request, "text/javascript", broadway_js, G_N_ELEMENTS(broadway_js) - 1);
else if (strcmp (escaped, "/output") == 0) else if (strcmp (escaped, "/output") == 0)
start_output (request); start_output (request);
else if (strcmp (escaped, "/input") == 0)
start_input (request);
else else
send_error (request, 404, "File not found"); send_error (request, 404, "File not found");
} }
static void static void
got_http_request_line (GOutputStream *stream, got_http_request_line (GInputStream *stream,
GAsyncResult *result, GAsyncResult *result,
HttpRequest *request) HttpRequest *request)
{ {