zaqar/examples/websocket.html
Eva Balycheva 49c90739de Support binary messages over websocket
For now Zaqar's websocket transport can only send and receive text
messages in JSON format.
By using messages in binary format it's possible to reduce network
traffic between Zaqar server and it's clients. In most cases it's also
possible to increase performance.

This patch implements support for binary messages over websocket
transport in Zaqar server.
The MessagePack format was chosen for encoding/decoding messages as it's
effective enough and available in convenient libraries for most
programming languages.

This patch also modifies "examples/websockets.html" example to be able
to send and receive binary messages.

All "print" function calls are substituted to local logger calls.

DocImpact
APIImpact The patch adds new functionality. Now sending binary request
over websocket doesn't automatically return response with code 400,
because now websocket transport is able to process binary requests
encoded in MessagePack.
blueprint: websocket-binary-support
Change-Id: I07a7c46795e3b510ee397a6d2c4665e770c2c4b2
2016-02-26 01:59:12 +03:00

310 lines
12 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html>
<head>
<title>Zaqar WebSocket example</title>
<meta charset='utf-8' />
<link rel='stylesheet' href='http://yui.yahooapis.com/pure/0.6.0/pure-nr-min.css' />
<script type='text/javascript' src='http://code.jquery.com/jquery-2.1.4.min.js'></script>
<!--[if lte IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.1.10/es5-shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/json3/3.3.2/json3.min.js"></script>
<![endif]-->
<script src="https://rawgithub.com/kawanet/msgpack-lite/master/dist/msgpack.min.js"></script>
<style>
.pure-g > div {
box-sizing: border-box;
}
#title h1 {
text-align: center;
border-bottom: 1px solid #ccc;
margin: 0;
padding: 1em;
}
#login {
background-color: #c7e8f2;
padding-left: 1em;
}
#queues {
padding-left: 1em;
background-color: #bbb;
min-height: 800px;
}
#messages {
padding-left: 1em;
}
#log {
font-size: 80%;
}
#right-col {
padding-left: 1em;
background-color: #f7e699;
min-height: 800px;
}
</style>
<script type='text/javascript'>
// Parameters:
var server_url = 'ws://localhost:9000/';
var project_id = 'cf38008b72d04b89a505b9d66d1d5768';
var client_id = '31209ff3-ba03-4cec-b4ca-655f4899f8f4';
var send_binary = true;
var socket = new WebSocket(server_url);
socket.binaryType = 'arraybuffer';
if (send_binary == true) {
// Use MessagePack(binary) for encoding messages
encode = function(data) {
return msgpack.encode(data);
}
} else {
// Use JSON(text) for encoding messages
encode = function(data) {
return JSON.stringify(data);
}
}
msgpack_decode = function(enc_data) {
return msgpack.decode(new Uint8Array(enc_data));
}
json_decode = function(enc_data) {
return JSON.parse(enc_data);
}
add_connection_info = function(msg) {
msg += ". Using parameters: Server URL: " + server_url;
msg += ". Project ID: " + project_id;
msg += ". Client ID: " + client_id;
msg += ". Binary communication: " + send_binary;
return msg
}
log_info = function(msg) {
var node = document.createElement('div');
var date = new Date().toUTCString();
msg = date + " " + msg;
node.appendChild(document.createTextNode(msg));
$('#log').append(node);
}
socket.onopen = function(evt) {
msg = "Connection opened";
msg = add_connection_info(msg);
log_info(msg);
}
socket.onclose = function(evt) {
msg = "Connection closed";
log_info(msg);
}
socket.onerror = function(evt) {
msg = "Connection error";
msg = add_connection_info(msg);
log_info(msg);
}
socket.onmessage = function(evt) {
if (evt.data instanceof ArrayBuffer) {
// Received payload in MessagePack(binary) format
var data = msgpack_decode(evt.data);
}
if (typeof evt.data === "string") {
// Received payload in JSON(text) format
var data = json_decode(evt.data);
}
if ('request' in data && 'headers' in data) {
// Response received
var action = data["request"]["action"];
msg = "action: " + action;
msg += " status: " + data["headers"]["status"];
msg += " body: " + JSON.stringify(data["body"]);
log_info(msg);
if (action == 'queue_list') {
var queues = data['body']['queues'];
display_queues(queues);
} else if (action == 'message_list') {
var messages = data['body']['messages'];
display_messages(messages);
} else if (action == 'queue_create' || action == 'queue_delete') {
list_queues();
} else if (action == 'authenticate' && data["headers"]["status"] == 200) {
list_queues();
} else if (action == 'message_post' || action == 'message_delete') {
list_messages();
}
} else {
// Can be notification or unexpected data
if(data.hasOwnProperty('body')){
msg = "notification: " + JSON.stringify(data);
log_info(msg)
list_messages();
} else {
msg = "unexpected data: " + JSON.stringify(data);
log_info(msg)
}
}
}
login = function(frm) {
var data = {
'auth': {
'identity': {
'methods': ['password'],
'password': {
'user': {
'name': frm['user'].value,
'domain': {'id': 'default'},
'password': frm['password'].value
}
}
}
}
}
$.ajax({
'type': 'POST',
'url': 'http://localhost:5000/v3/auth/tokens',
'data': JSON.stringify(data),
'contentType': 'application/json',
'dataType': 'json',
'success': function(data, code, response) {
var token = response.getResponseHeader('X-Subject-Token')
if (token == null){
log_info("Connected to Keystone, but no 'X-Subject-Token' "
+ "header was provided. Keystone's CORS filter is probably "
+ "not configured to expose this header.");
} else {
log_info("Got token from Keystone. " +
"Sending authentication request to Zaqar.");
var msg = {'action': 'authenticate',
'headers': {'X-Auth-Token': token,
'Client-ID': client_id,
'X-Project-ID': project_id}}
socket.send(encode(msg));
};
},
'error': function(data, code, errorThrown) {
if (errorThrown) {
log_info("Keystone error: " + errorThrown);
} else {
log_info("Failed to connect to Keystone. Keystone is either" +
" offline, or CORS is not enabled in Keystone.");
}
}
});
return false;
}
send_message = function(action, body) {
var msg = {'action': action,
'headers': {'Client-ID': client_id, 'X-Project-ID':
project_id}};
if (body) {
msg['body'] = body;
};
socket.send(encode(msg));
}
list_queues = function() {
send_message('queue_list');
}
create_queue = function(frm) {
send_message('queue_create', {'queue_name': frm['queue'].value});
return false;
}
get_selected_queue = function() {
var queue_select = $('#queue_list');
return queue_select.val();
}
display_queues = function(queues) {
var queue_select = $('#queue_list');
queue_select.empty();
$.each(queues, function(idx, queue) {
queue_select.append('<option>' + queue.name + '</option>');
});
}
display_messages = function(messages) {
var table_body = $('#message_list tbody');
table_body.empty();
$.each(messages, function(idx, message) {
table_body.append('<tr><td>' + message.age + '</td><td>' + message.body + '</td><td>' + message.ttl + '</td><td><button class=\'pure-button\' onclick=\'delete_message("' + message.id + '")\'>Delete</button></td></tr>');
});
}
delete_queue = function() {
send_message('queue_delete', {'queue_name': get_selected_queue()});
}
list_messages = function() {
send_message('message_list', {'queue_name': get_selected_queue(), 'echo': true});
}
queue_message = function(frm) {
var body = frm['body'].value;
var ttl = parseInt(frm['ttl'].value);
send_message('message_post', {'queue_name': get_selected_queue(), 'messages': [{'body': body, 'ttl': ttl}]});
return false;
}
delete_message = function(message_id) {
send_message('message_delete', {'queue_name': get_selected_queue(), 'message_id': message_id});
}
subscribe_queue = function() {
send_message('subscription_create', {'queue_name': get_selected_queue(), 'ttl': 3600});
}
</script>
</head>
<body>
<div id='title'>
<h1>Zaqar WebSocket example</h1>
</div>
<div id='login'>
<form class='pure-form' onsubmit='return login(this)'>
<fieldset>
<input type='text' name='user' placeholder='User' />
<input type='password' name='password' placeholder='Password' />
<button class='pure-button' type='submit'>Login</button>
</fieldset>
</form>
</div>
<div class='pure-g'>
<div id='queues' class='pure-u-1-3'>
<h4>Queues</h4>
<form class='pure-form' onsubmit='return create_queue(this)'>
<fieldset>
<input type='text' name='queue' placeholder='Queue name' />
<button class='pure-button' type='submit'>Create</button>
</fieldset>
</form>
<form class='pure-form' onsubmit='return false'>
<fieldset>
<select id='queue_list'></select>
<button class='pure-button' onclick='delete_queue()'>Delete</button>
<button class='pure-button' onclick='list_messages()'>List messages</button>
<button class='pure-button' onclick='subscribe_queue()'>Subscribe</button>
<button class='pure-button' onclick='list_queues()'>Refresh</button>
</fieldset>
</form>
</div>
<div id='messages' class='pure-u-1-3'>
<h4>Messages</h4>
<form class='pure-form' onsubmit='return queue_message(this)'>
<fieldset>
<input type='text' name='body' placeholder='Message body' />
<input type='text' name='ttl' size='5' value='3600' placeholder='TTL' />
<button type='submit' class='pure-button'>Post</button>
</fieldset>
</form>
<table class='pure-table pure-table-horizontal' id='message_list'>
<thead>
<tr>
<th>Age</th>
<th>Body</th>
<th>TTL</th>
<th></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div class='pure-u-1-3' id='right-col'>
<h4>Logs</h4>
<div id='log'></div>
</div>
</div>
</body>
</html>