Commit 0d2c4410 authored by Romain Courteaud's avatar Romain Courteaud

Clone websocket server Chrome app sample.

Original code can be found at:
https://github.com/GoogleChrome/chrome-app-samples/tree/master/samples/websocket-server
parents
This diff is collapsed.
<a target="_blank" href="https://chrome.google.com/webstore/detail/pkbpddppnkjmlbgliipgmhjeialadokj">![Try it now in CWS](https://raw.github.com/GoogleChrome/chrome-app-samples/master/tryitnowbutton.png "Click here to install this sample from the Chrome Web Store")</a>
# Http WebSocket Server
An example Http and WebSocket server implementation with a sample app which serves itself and allows connected clients to chat with each other. The API is very similar to NodeJS with the intention that this could be an easy drop-in replacement for a NodeJS served application.
## APIs
* [chrome.socket](http://developer.chrome.com/apps/socket.html)
* [Runtime](http://developer.chrome.com/apps/app.runtime.html)
* [Window](http://developer.chrome.com/apps/app.window.html)
## Screenshot
![screenshot](/samples/websocket-server/assets/screenshot_1280_800.png)
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('index.html', {
innerBounds: {
width: 800,
height: 600,
minWidth: 200,
minHeight: 200,
}
});
});
This diff is collapsed.
html,
body {
height: 100%;
margin: 0;
padding: 0;
width: 100%;
}
body {
-webkit-box-orient: vertical;
display: -webkit-box;
}
#log {
-webkit-box-flex: 1;
}
#log,
#input {
display: block;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="index.css">
<script src="sha1.js"></script>
<script src="http.js"></script>
<script src="index.js"></script>
</head>
<body>
<textarea id="log"></textarea>
<input type="text" id="input" placeholder="Type a message to other connected clients here">
</body>
</html>
function $(id) {
return document.getElementById(id);
}
function log(text) {
$('log').value += text + '\n';
}
var port = 9999;
var isServer = false;
if (http.Server && http.WebSocketServer) {
// Listen for HTTP connections.
var server = new http.Server();
var wsServer = new http.WebSocketServer(server);
server.listen(port);
isServer = true;
server.addEventListener('request', function(req) {
var url = req.headers.url;
if (url == '/')
url = '/index.html';
// Serve the pages of this chrome application.
req.serveUrl(url);
return true;
});
// A list of connected websockets.
var connectedSockets = [];
wsServer.addEventListener('request', function(req) {
log('Client connected');
var socket = req.accept();
connectedSockets.push(socket);
// When a message is received on one socket, rebroadcast it on all
// connected sockets.
socket.addEventListener('message', function(e) {
for (var i = 0; i < connectedSockets.length; i++)
connectedSockets[i].send(e.data);
});
// When a socket is closed, remove it from the list of connected sockets.
socket.addEventListener('close', function() {
log('Client disconnected');
for (var i = 0; i < connectedSockets.length; i++) {
if (connectedSockets[i] == socket) {
connectedSockets.splice(i, 1);
break;
}
}
});
return true;
});
}
document.addEventListener('DOMContentLoaded', function() {
log('This is a test of an HTTP and WebSocket server. This application is ' +
'serving its own source code on port ' + port + '. Each client ' +
'connects to the server on a WebSocket and all messages received on ' +
'one WebSocket are echoed to all connected clients - i.e. a chat ' +
'server. Enjoy!');
// FIXME: Wait for 1s so that HTTP Server socket is listening...
setTimeout(function() {
var address = isServer ? 'ws://localhost:' + port + '/' :
window.location.href.replace('http', 'ws');
var ws = new WebSocket(address);
ws.addEventListener('open', function() {
log('Connected');
});
ws.addEventListener('close', function() {
log('Connection lost');
$('input').disabled = true;
});
ws.addEventListener('message', function(e) {
log(e.data);
});
$('input').addEventListener('keydown', function(e) {
if (ws && ws.readyState == 1 && e.keyCode == 13) {
ws.send(this.value);
this.value = '';
}
});
}, 1e3);
});
{
"name": "WebSocket Server Test",
"description": "Runs an Http and WebSocket server with a quick and dirty test chat application.",
"version": "0.3.1",
"manifest_version": 2,
"permissions": [
{"socket": [
"tcp-connect",
"tcp-listen"]}],
"app": {
"background": {
"scripts": ["background.js"]
}
}
}
{
"sample": "websocket-server",
"files_with_snippets": [ ],
"ios": {"works": true}
}
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2005 Google Inc. All Rights Reserved.
/**
* @fileoverview SHA-1 cryptographic hash.
* Variable names follow the notation in FIPS PUB 180-3:
* http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf.
*
* Usage:
* var sha1 = new goog.crypt.sha1();
* sha1.update(bytes);
* var hash = sha1.digest();
*
*/
/**
* SHA-1 cryptographic hash constructor.
*
* The properties declared here are discussed in the above algorithm document.
* @constructor
*/
var Sha1 = function() {
/**
* Holds the previous values of accumulated variables a-e in the compress_
* function.
* @type {Array.<number>}
* @private
*/
this.chain_ = [];
/**
* A buffer holding the partially computed hash result.
* @type {Array.<number>}
* @private
*/
this.buf_ = [];
/**
* An array of 80 bytes, each a part of the message to be hashed. Referred to
* as the message schedule in the docs.
* @type {Array.<number>}
* @private
*/
this.W_ = [];
/**
* Contains data needed to pad messages less than 64 bytes.
* @type {Array.<number>}
* @private
*/
this.pad_ = [];
this.pad_[0] = 128;
for (var i = 1; i < 64; ++i) {
this.pad_[i] = 0;
}
this.reset();
};
/**
* Resets the internal accumulator.
*/
Sha1.prototype.reset = function() {
this.chain_[0] = 0x67452301;
this.chain_[1] = 0xefcdab89;
this.chain_[2] = 0x98badcfe;
this.chain_[3] = 0x10325476;
this.chain_[4] = 0xc3d2e1f0;
this.inbuf_ = 0;
this.total_ = 0;
};
/**
* Internal helper performing 32 bit left rotate.
* @return {number} w rotated left by r bits.
* @private
*/
Sha1.prototype.rotl_ = function(w, r) {
return ((w << r) | (w >>> (32 - r))) & 0xffffffff;
};
/**
* Internal compress helper function.
* @param {Array} buf containing block to compress.
* @private
*/
Sha1.prototype.compress_ = function(buf) {
var W = this.W_;
// get 16 big endian words
for (var i = 0; i < 64; i += 4) {
var w = (buf[i] << 24) |
(buf[i + 1] << 16) |
(buf[i + 2] << 8) |
(buf[i + 3]);
W[i / 4] = w;
}
// expand to 80 words
for (var i = 16; i < 80; i++) {
W[i] = this.rotl_(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
}
var a = this.chain_[0];
var b = this.chain_[1];
var c = this.chain_[2];
var d = this.chain_[3];
var e = this.chain_[4];
var f, k;
for (var i = 0; i < 80; i++) {
if (i < 40) {
if (i < 20) {
f = d ^ (b & (c ^ d));
k = 0x5a827999;
} else {
f = b ^ c ^ d;
k = 0x6ed9eba1;
}
} else {
if (i < 60) {
f = (b & c) | (d & (b | c));
k = 0x8f1bbcdc;
} else {
f = b ^ c ^ d;
k = 0xca62c1d6;
}
}
var t = (this.rotl_(a, 5) + f + e + k + W[i]) & 0xffffffff;
e = d;
d = c;
c = this.rotl_(b, 30);
b = a;
a = t;
}
this.chain_[0] = (this.chain_[0] + a) & 0xffffffff;
this.chain_[1] = (this.chain_[1] + b) & 0xffffffff;
this.chain_[2] = (this.chain_[2] + c) & 0xffffffff;
this.chain_[3] = (this.chain_[3] + d) & 0xffffffff;
this.chain_[4] = (this.chain_[4] + e) & 0xffffffff;
};
/**
* Adds a byte array to internal accumulator.
* @param {Array.<number>} bytes to add to digest.
* @param {number} opt_length is # of bytes to compress.
*/
Sha1.prototype.update = function(bytes, opt_length) {
if (!opt_length) {
opt_length = bytes.length;
}
var n = 0;
// Optimize for 64 byte chunks at 64 byte boundaries.
if (this.inbuf_ == 0) {
while (n + 64 < opt_length) {
this.compress_(bytes.slice(n, n + 64));
n += 64;
this.total_ += 64;
}
}
while (n < opt_length) {
this.buf_[this.inbuf_++] = bytes[n++];
this.total_++;
if (this.inbuf_ == 64) {
this.inbuf_ = 0;
this.compress_(this.buf_);
// Pick up 64 byte chunks.
while (n + 64 < opt_length) {
this.compress_(bytes.slice(n, n + 64));
n += 64;
this.total_ += 64;
}
}
}
};
/**
* @return {Array} byte[20] containing finalized hash.
*/
Sha1.prototype.digest = function() {
var digest = [];
var totalBits = this.total_ * 8;
// Add pad 0x80 0x00*.
if (this.inbuf_ < 56) {
this.update(this.pad_, 56 - this.inbuf_);
} else {
this.update(this.pad_, 64 - (this.inbuf_ - 56));
}
// Add # bits.
for (var i = 63; i >= 56; i--) {
this.buf_[i] = totalBits & 255;
totalBits >>>= 8;
}
this.compress_(this.buf_);
var n = 0;
for (var i = 0; i < 5; i++) {
for (var j = 24; j >= 0; j -= 8) {
digest[n++] = (this.chain_[i] >> j) & 255;
}
}
return digest;
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment