*: initial component

This commit is contained in:
Guillermo Rauch
2012-12-09 20:25:37 -03:00
commit 38cb105b62
7 changed files with 375 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
node_modules
build
components

9
Makefile Normal file
View File

@@ -0,0 +1,9 @@
REPORTER = dot
test:
@./node_modules/.bin/mocha \
--reporter $(REPORTER) \
--bail
.PHONY: test

98
Readme.md Normal file
View File

@@ -0,0 +1,98 @@
# socket.io-protocol
This repository contains the protocol specification and JavaScript
parser for the Socket.IO protocol.
## Protocol version
**Current protocol revision:** `1`.
## Parser API
### Parser#encode(Object:packet):String
Encodes a `Packet` object as a string.
### Parser#decode(String:packet):Packet
Returns a `Packet` object for the given string. If a parsing error
occurs the returned packet is an error object.
### Parser#types
Array of packet type keys.
### Packet
Each packet is represented as a vanilla `Object` with a `type` key that
can be one of the following:
- `Packet#CONNECT` (`0`)
- `Packet#DISCONNECT` (`1`)
- `Packet#EVENT` (`2`)
- `Packet#ACK` (`3`)
- `Packet#ERROR` (`4`)
Each packet also contains a `nsp` key that indicates what namespace it
belongs to (see "Multiplexing" below for an explanation).
#### EVENT
- `data` (`Array`) a list of arguments, the first of which is the event
name. Arguments can contain any type of field that can result of
`JSON` decoding, including objects and arrays of arbitrary size.
- `id` (`Number`) if the `id` identifier is present, it indicates that the
server wishes to be acknowledged of the reception of this event.
#### ACK
- `data` (`Array`) see `EVENT` `data`.
- `id` (`Number`) see `EVENT` `id`.
#### ERROR
- `data` (`Mixed`) error data
## Transport
The socket.io protocol can be delivered over a variety of transports.
[socket.io-client](http://github.com/learnboost/socket.io-client)
is the implementation of the protocol for the browser and Node.JS over
[engine.io-client](http://github.com/learnboost/engine.io-client).
[socket.io](http://github.com/learnboost/socket.io) is the server
implementation of the protocol over
[engine.io](http://github.com/learnboost/engine.io).
## Multiplexing
Socket.IO has built-in multiplexing support, which means that each packet
always belongs to a given `namespace`, identified by a path string (like
`/this`). The corresponding key in the `Packet` object is `nsp`.
When the socket.io transport connection is established, a connection
attempt to the `/` namespace is assumed (ie: the server behaves as if
the client had sent a `CONNECT` packet to the `/` namespace).
In order to support multiplexing of multiple sockets under
the same transport, additional `CONNECT` packets can be sent by the
client to arbitrary namespace URIs (eg: `/another`).
When the server responds with a `CONNECT` packet to the corresponding
namespace, the multiplexed socket is considered connected.
Alternatively, the server can respond with an `ERROR` packet to indicate
a multiplexed socket connection error, such as authentication errors.
The associated error payload varies according to each error, and can
be user-defined.
After a `CONNECT` packet is received by the server for a given `nsp`,
the client can then send and receive `EVENT` packets. If any of the
parties receives an `EVENT` packet with an `id` field, an `ACK` packet is
expected to confirm the reception of said packet.
## License
MIT

11
component.json Normal file
View File

@@ -0,0 +1,11 @@
{
"name": "socket.io-parser",
"version": "0.0.1",
"description": "socket.io protocol parser",
"dependencies": {
"component/json": "0.0.1"
},
"scripts": [
"index.js"
]
}

177
index.js Normal file
View File

@@ -0,0 +1,177 @@
/**
* Module dependencies.
*/
var json;
try {
json = require('json');
} catch(e){
json = JSON;
}
/**
* Protocol version.
*
* @api public
*/
exports.protocol = 1;
/**
* Packet types.
*
* @api public
*/
exports.types = [
'CONNECT',
'DISCONNECT',
'EVENT',
'ACK',
'ERROR'
];
/**
* Packet type `connect`.
*
* @api public
*/
exports.CONNECT = 0;
/**
* Packet type `disconnect`.
*
* @api public
*/
exports.DISCONNECT = 1;
/**
* Packet type `event`.
*
* @api public
*/
exports.EVENT = 2;
/**
* Packet type `ack`.
*
* @api public
*/
exports.ACK = 3;
/**
* Packet type `error`.
*
* @api public
*/
exports.ERROR = 4;
/**
* Encode.
*
* @param {Object} packet
* @return {String} encoded
* @api public
*/
exports.encode = function(obj){
var str = '';
var nsp = false;
// first is type
str += obj.type;
// if we have a namespace other than `/`
// we append it followed by a comma `,`
if (obj.nsp && '/' != obj.nsp) {
nsp = true;
str += obj.nsp;
}
// immediately followed by the id
if (obj.id) {
if (nsp) {
str += ',';
nsp = false;
}
str += obj.id;
}
// json data
if (obj.data) {
if (nsp) str += ',';
str += json.stringify(obj.data);
}
return str;
};
/**
* Decode.
*
* @param {String} str
* @return {Object} packet
* @api public
*/
exports.decode = function(str){
var p = {};
var i = 0;
// look up type
p.type = Number(str.charAt(0));
if (null == exports.types[p.type]) return error();
// look up namespace (if any)
if ('/' == str.charAt(i + 1)) {
p.nsp = '';
while (++i) {
var c = str.charAt(i);
if (',' == c) break;
p.nsp += c;
if (i + 1 == str.length) break;
}
} else {
p.nsp = '/';
}
// look up id
var next = str.charAt(i + 1);
if ('' != next && Number(next) == next) {
p.id = '';
while (++i) {
var c = str.charAt(i);
if (null == c || Number(c) != c) {
--i;
break;
}
p.id += str.charAt(i);
if (i + 1 == str.length) break;
}
}
// look up json data
if (str.charAt(++i)) {
try {
p.data = json.parse(str.substr(i));
} catch(e){
return error();
}
}
return p;
};
function error(data){
return {
type: exports.ERROR,
data: 'parser error'
};
}

17
package.json Normal file
View File

@@ -0,0 +1,17 @@
{
"name": "socket.io-parser",
"version": "0.0.1",
"description": "socket.io protocol parser",
"devDependencies": {
"mocha": "*",
"expect.js": "*"
},
"component": {
"dependencies": {
"component/json": "0.0.1"
},
"scripts": [
"index.js"
]
}
}

60
test/test.js Normal file
View File

@@ -0,0 +1,60 @@
var parser = require('..');
var expect = require('expect.js');
var encode = parser.encode;
var decode = parser.decode;
// tests encoding and decoding a packet
function test(obj){
expect(decode(encode(obj))).to.eql(obj);
}
describe('parser', function(){
it('exposes types', function(){
expect(parser.CONNECT).to.be.a('number');
expect(parser.DISCONNECT).to.be.a('number');
expect(parser.EVENT).to.be.a('number');
expect(parser.ACK).to.be.a('number');
expect(parser.ERROR).to.be.a('number');
});
it('encodes connection', function(){
test({
type: parser.CONNECT,
nsp: '/woot'
});
});
it('encodes disconnection', function(){
test({
type: parser.DISCONNECT,
nsp: '/woot'
});
});
it('encodes an event', function(){
test({
type: parser.EVENT,
data: ['a', 1, {}],
nsp: '/'
});
test({
type: parser.EVENT,
data: ['a', 1, {}],
id: 1,
nsp: '/test'
});
});
it('encodes an ack', function(){
test({
type: parser.ACK,
data: ['a', 1, {}],
id: 123,
nsp: '/'
});
});
});