truflux
v2.0.11
Published
Secure socket connection module
Downloads
50
Maintainers
Readme
Truflux The fastest socket module on npm
To install
npm install --save truflux
var tru = require('truflux');
Truflux uses a separate protocol definition module. No internal messaging module is provided.Here are some official definition modules
truflux-msg A stateless messaging protocol.Very easy to use. https://www.npmjs.com/package/truflux-msg
truflux-state-msg A stateful messaging protocol.Recommended for intermediate use. https://www.npmjs.com/package/truflux-state-msg
TL:DR
Truflux is a lightweight high performance socket framework for protocol based messaging for websockets and netsockets supporting read/write streams to allow you to send files across a network, as well as crypto for symmetric and asymmetric secure connections.The browser client is about ~5kb minified and ~2kb gzipped.
For some recipes on common networking actions see the end of the readme. This includes file sending and binding to servers like express .Note that the browser client contains the same functionality as a net client, but has a few small differences which are outlined below
Current Performance stats
The current performance stats are based on the benchmarking test performed by ws, the current "fastest" socket library These number are performed on a lowend 32bit laptop with 1.3GHz CPU with 1GB RAM which is actually inferior to the m3.xlarge EC2 instance that ws uses for testing.
NOTE With a standard 3.2GHz CPU, you can expect a speed increase of about 8x
However as you can see, we soundly beat the current benchmark of 'ws' performance even with the overhead of crypto.
truflux
No crypto
- Running 10000 roundtrips of 64 B binary data: 3.8s 185.96 kB/s
- Running 5000 roundtrips of 16 kB binary data: 3.9s 23.85 MB/s
- Running 1000 roundtrips of 128 kB binary data: 4.5s 36.62 MB/s
- Running 100 roundtrips of 1 MB binary data: 5.2s 21.21 MB/s
- Running 10 roundtrips of 10 MB binary data: 24.4s 4.71 MB/s
Crypto using aes-256-cbc
- Running 10000 roundtrips of 64 B binary data: 4.9s 126.9 kB/s
- Running 5000 roundtrips of 16 kB binary data: 9.1s 8.56 MB/s
- Running 1000 roundtrips of 128 kB binary data: 13.2s 9.49 MB/s
- Running 100 roundtrips of 1 MB binary data: 13.8s 7.27 MB/s
- Running 10 roundtrips of 10 MB binary data: 31s 3.22 MB/s
ws
- Running 10000 roundtrips of 64 B binary data: 22.1s 28.34 kB/s
- Running 5000 roundtrips of 16 kB binary data: 33.9s 2.31 MB/s
- Running 1000 roundtrips of 128 kB binary data: 50s 2.5 MB/s
- Running 100 roundtrips of 1 MB binary data: 35.3s 2.83 MB/s
- Running 10 roundtrips of 10 MB binary data: 35s 2.85 MB/s
We can improve this performance drastically for large files once Node / iojs increase the tcp window size to do more efficient chunking of the buffers NOTE also that truflux does not use unix sockets to circumvent the network interface to artificially inflate its speed.
Basic Echo server example
var tru=require('truflux');
var msg=require('truflux-msg');
var protocol=new msg();
protocol.add('echo',function(data,sock)
{sock.send('display',data);})
.add('display',function(data)
{console.log(data);});
var server=new tru.Server({msg:protocol});
server.open();
var client=new tru.Client({msg:protocol}))
client.open().on('open',function(sock)
{
sock.send('echo','Atreyuuuu!');
});
Basic messenger example
See the following gist on how to create a minimal server/client messenger for truflux in 75 lines with extensible protocol,broadcasting and oldschool prompt interface https://gist.github.com/Auxolust/59d13b5f070b5717fa58
Upcoming improvements
- An upcoming CDN will be provided to allow lightning fast downloads of the browserclient. This will be the recommended action to using truflux in the browser
- UDP protocol for real time streaming (This will most likely be its own package to prevent bloat)
- Message chunk limiter for production servers
- Currently the server does not encrypt blob chunks when it sends data from the server to browserclient. This will be rectified via utf-8 encoding , however there will obviously be a large performance penalty on the client side to perform deciphering which I will address as soon as I can.
- Automatic symmetric key randomisation every x amount of time to prevent cracking the socket security. This will create a pessimistic estimate of the time required by a malicious hacker to bruteforce their way past the symmetric key and automatically randomize the key
Changelog
V2.0.0
Major overhaul. Everything was rewritten with the exception of the security and standard files. Needless to say this mandated a major update. New changes include more concise language, as well as the adoption of traits internally to prevent the performance overhead of function stacks.
The most prevalent of these changes is the abstraction between websockets and netsockets and the new support for http servers
V1.0.0
Modified the ID to use the Pseudo-Hadamard transform as an isomorphic map for the ID when in secure mode. This is the first step to allowing one to plug their own cipher into the endpoints. As a result of this the fixed header size is down to 72 bits in both secure and unsecure mode, reducing header size in secure mode by 57%.This replaces the old aes-256-cbc ciphering of the message ID which was slow and inefficient.The new PHT function results in an average 1.132 microsecond decryption and encryption
Modified file sending to send as binary type, this has reduced file sending size by ~69% on average Also added in asymmetric security and expose the cipher type to allow custom encryption on the channels if needed
The change in the underlying architecture to changing the security has mandated a v1.0.0 increase to avoid breaking changes with old non asymmetric channels
V0.3.0
Added support to pausing and resuming network streams. We now default to sending buffer based encoding when sending files to avoid mangling binary files so you can now send .exes etc through the network. Also modified server broadcast to allow passing in an array of excluded sockets we don't want to receive the broadcasted message
V 0.2.0
Now using fixed header sizes and preferring binary type buffer data
Old Dynamic messages
256 bits Minimal message size in unsecure mode 592 bits Minimal message size in secure mode
New fixed headers
9 Octet header size per message in unsecure mode = 72 bits 21 Octet header size per message in secure mode = 168 bits Average message size is now down 77%
We now support 2147483647 unique message types as well as a theoretical single message maximal size of 31.2 Gb. However we highly suggest streaming for large volume data exchange. Added support to pipe to/from streams when sending files across the network.Encryption has been upgraded from aes-192-cbc to aes-256-cbc.
Performance for pushing out messages has degraded by 0.9% based on sending 1000 messages of varying types, this is in response to buffer packing, as the performance of buffers only truly shines with larger message types.To see the current stats of pushing out messages, see the bottom of the page
We've now changed from recursive descent parsing of the the message parsing to an iterative approach, so you cannot blow the call stack by pushing too many messages too fast
API
There are 4 objects exposed when you require truflux
Server //The object to create a new server with
Client //The object to create a new client with
std //A collection of standards, such as default system messages as well as an enumeration of errors.These will not be changed with the exception of major version updates, so feel free to use
httpBind //A function to bind a truflux server to an http server
httpBind(trufluxServer,httpServer,options)
Used to bind an http server to allow websockets to interact with the truflux server
options:
{
path: String Relative path on the http server for the binding. An error WILL be thrown if you attempt to bind two truflux servers to the same path. DEFAULT='/'
}
truflux.httpBind(trufluxServer,someHttpServer,{path:'/myChatServer'});
someHttpServer.listen(80,function()
{
console.log('Server up');
});
Server
Constructor([options])
options:
{
port: Integer
msg: Truflux-msg Object
ciphers: String or Array of Strings. A list in priority order of the cipher type you wish to you to connect to a client. DEFAULT aes-256-cbc
noDelay: Whether to turn off message buffering according to Nagles algorithm. Default is false to reduce network congestion
}
open(cb)
Opens the server and starts listening. CHAINABLE
cb : callback function
onNewSocket(socket,options)
Manually adds a socket object to the server. This socket must adhere to the standard tcp socket interface. You will probably never have to call this yourself
options:
{
port: Integer
msg: Truflux-msg Object
ciphers: String or Array of Strings. A list in priority order of the cipher type you wish to you to connect to a client. DEFAULT aes-256-cbc
noDelay: Whether to turn off message buffering according to Nagles algorithm. Default is false to reduce network congestion
}
close([cb],[force])
Closes the server .CHAINABLE
cb : callback function
force : Whether to force closing or end gracefully
broadcast(id,[data],[exclude])
Broadcasts a message to currently connected server-clients , see Common client's send function for more info on id and data.CHAINABLE
id: String | Integer
data: Any
exclude: Client| Array of Clients
getNumSockets()
Returns the number of currently connected clients
Events
- client Emitted on new connection from a client
function(client)
- error Emitted on server error. One such possible error would be if the client does not support the required cipher type
function(stdError,clientIfApplicable)
The stdError Object is as follows
{
e:The error code.Error codes are available from sec.std.errors
add:An OPTIONAL value that is passed in from some error codes
}
Common Client
These methods are common to any client, server-client,websocket client or the net client.
Accessible properties
session: An object that can be used to store session variables if needed
Events
These events are common to both server-client and standard client
- error Emitted when an error occurs on a client
function(stdErrorObject,client)
- file Emitted when a file is offered for download
function(NetworkFileWriter,client)
- close emitted the client closing
function(stdErrorObject,client)
send(id,[data])
Sends a message to across the network to the peer.Internally a call to a checkStructure function will be called by the client to check if the message adheres to the schema set out in the message definition .CHAINABLE
id : String/Integer
data : Any. Any data you send will return on the other side with the correct type intact. Objects will be objects, buffers will be buffers, etc
client.send(trufluxMsgObject.id.echo,"Do you want messages? Because that's how you get messages");
// OR
client.send("echo","Um phrasing? Are we still doing that?");
sendFile(Reader,[options])
Returns a NetworkFileReader object and starts sending a file to the peer.See NetworkFileReader for more details
Reader: String for a filepath ||Readable stream that will have its input piped in and sent
options :
{
name :String If string filepath is provided for reader this is not required , defaults to "[Stream]" for streams
type:String, Stream transport type ,hex,base64 etc USELESS IN BROWSER
size:number If string filepath is provided for reader this is not required to be set ,-1 for streams is the default
}
close([force])
Closes the connection and sends a message to the peer to know the connection was closed cleanly, if no force was applied.CHAINABLE
force: Boolean , whether to force close the socket connection or to send a message saying it was closed cleanly
getInfo()
Retrieves the socket info.Returns the following.
address:String, the local address
port:Integer, the local port
remoteAddress:String,
remotePort:Integer
Server-client
There are 3 readOnly properties.The first 2 are used to see the current security status. Truflux allows asymmetric encryption
secure //Boolean Whether we are encrypting our data
peerSecure //Boolean Whether our peer is encrypting their data
ws //Boolean Whether or not the connections is a websocket
setClientSecurity(enabled)
Turns the security of the stream from client->serverClient on/off.Turning security off has performance gains as we don't have to encrypt messages.CHAINABLE
enabled: Boolean
setOwnSecurity(enabled)
Turns the security of the stream from serverClient->client on/off.Turning security off has performance gains as we don't have to encrypt messages.CHAINABLE
enabled: Boolean
Client
Constructor([options])
options Object
{
port: Number USELESS FOR BROWSER CLIENTS
host: String
msg: Truflux-msg Object,
noDelay: Whether to turn off message buffering according to Nagles algorithm USELESS FOR BROWSER CLIENTS
crypto: Crypto module for BROWSER CLIENTS. Allows modular security
}
open()
Connects to the server specified on host : port . CHAINABLE
Events
Events in Browser
NOTE the browser implementation does not utilize eventEmitters. It instead exposes functions as properties
onClose=function()
onOpen=function()
onErr=function(err)
EventEmitter events for non-browser
- open Emitted when the client has connected to the server and communication can occur.The client is now ready to use
function(client)
NetworkFileReader
An object this is provided when calling client.sendFile()
Events
Events in Browser
NOTE the browser implementation does not utilize eventEmitters. It instead exposes functions as properties
onEnd=function()
onAccept=function()
onCancel=function()
onPause=function()
onResume=function()
onDecline=function()
EventEmitter events for non-browser
- accept Emitted when the peer has accepted the offered file for download.
function(NetworkFileReader,client)
- error
function(NetworkFileReader,client)
- decline Emitted when the peer has declined the proffered file
function(NetworkFileReader,client)
- pause Emitted when the peer has paused the proffered file
function(NetworkFileReader,client)
- resume Emitted when the peer has resumed download of the proffered file
function(NetworkFileReader,client)
- finish Emitted when the peer has finished downloading the entire file
function(NetworkFileReader,client)
- cancel Emitted when the peer has cancelled the download of the file
function(NetworkFileReader,client)
NetworkFileWriter
An object that is provided on the file event of the CommonClient
There are a few publicly accessible properties you might find helpful
name //String Name of the sent file
size //Integer Size in bytes of the file
isStream//Boolean whether or not its a stream
paused //whether or not the filewriter is paused. Check for truthyness on the BROWSER CLIENT
accept(acceptor)
Accepts an offered file over the network and starts stream writing it to the specified Writer
acceptor:String for filepath | writable stream that will have the input piped to it
NOTE The browser client does not have an acceptor argument
decline()
Declines an offered network file
cancel()
Cancels the file download if previously accepted
pause()
Pauses the file download
resume()
Resumes the file download
Events
Events in Browser
NOTE the browser implementation does not utilize eventEmitters. It instead exposes functions as properties
onEnd=function(blobDataURI)
onErr=function(err)
EventEmitter events for non-browser
- error
function(stdErrObject,NetworkFileWriter,client);
- end Emitted when the stream has finished writing the object to file
function(NetworkFileWriter,client)
Browser caveats
The browserClient for truflux does not contain the server,httpbind or std objects
Compatibility is always a problem with browsers. By default truflux exports all its properties into the window namespaced as an object called truflux .This has the following properties
window.truflux=
{
Client:Function,//The client constructor to create a new client
fileDown:Boolean,//Whether file downloads are possible
fileUp:Boolean //Whether file uploads are possible
};
When sending files between net sockets, they are automatically encrypted. The current performance penalty in the browser means I have yet to implement it, so be awares that on a non wss:// socket files might be sniffed
Since its wrapped in a function for the import, no errors should be throw if websockets are not supported, however I urge you to use !!window.WebSocket to check if websockets are supported
The browser does not contain crypto within it. Therefore having a server that uses a secure channel will cause a failSecure event to be emitted on the server client In order to enable crypto in the browser, the crypto module must provide the following functionality in compliance with standard node/iojs crypto functionality
crypto.getCiphers()
DiffieHellman=crypto .createDiffieHellman(prime,'base64');
DiffieHellman.generateKeys();
DiffieHellman.getPublicKey('base64;);
DiffieHellman.computeSecret(publicKey,'base64','base64');
decipher=crypto.createDecipher(cipherType,symmetricKey);
data=decipher.update(MessageData,'utf8');
data+=decipher.final('utf8');
cipher=crypto.createCipher(cipherType,symmetricKey);
data=cipher.update(messageData,'utf8');
data+=cipher.final('utf8');
Recipes
Binding to an http server
var truflux=require('truflux');
var server= new truflux.Server(/*...*/);
server.open();
truflux.httpBind(server,someHttpServer,{path:'/myChatServer'});
someHttpServer.listen(80,function()
{
console.log('Server up');
});
Sending a file as a browser client
var sender=browserClient.sendFile(someFileObject);
sender.onAccept=function()
{
console.log('Our file upload offer was accepted');
}
sender.onEnd=function()
{
console.log('The server has our file');
}
Receiving a file as a browser client
browserClient.onFile=function(wstream)
{
wstream.accept();
wstream.onEnd=function(uri)
{
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
a.href = uri;
a.download = wstream.name;
a.click();
//Remove URL if you're not using it anymore
window.URL.revokeObjectURL(uri);
}
}
Upgrading to a symmetric secure connection
//Assuming this is the first time we've called it
serverClient.setOwnSecurity(true)
.setClientSecurity(true)
.once('handshaked',function()
{
console.log(socket.secure);//true
console.log(socket.peerSecure);/*Not necessarily true until truflux gets the client acknowledgement,
however the client will receive the setSecurity message before this so synchronicity is conserved*/
//Once the initial handshake happens, there is no need to listen anymore ie
//No need to listen to the handshake anymore, we just flip the security on and off from here
serverClient.setOwnSecurity(false)
.setOwnSecurity(true)
.setClientSecurity(false)
.setClientSecurity(true)
.send('someMessage',someData);
}).once('failSecure',function()
{
//Client probably does not have the supported ciphers.Lets kick him off
serverClient.close();
};