Transforming WebSockets

Preview:

DESCRIPTION

An introduction to WebSockets and why Primus might be a better fit for your project. This presentation was given at WebRebels

Citation preview

TransformingTransformingTransforming

WebSocketsWebSocketsWebSockets

!

3rdEden

"

3rd-Eden

fb

arnout.kazemier

the good partsthe good partsWebSocketsWebSockets

the good partsWebSockets

Chrome 20

Firefox 12

Opera 12.1

Safari 6

Internet Explorer 10

Chrome 4

Firefox 4

Opera 11

Safari 4.2

Internet Explorer 10

oh, it’s bi-directional &

supports binary

oh, it’s bi-directional &

supports binary

oh, it’s bi-directional &

supports binary

The EndThe EndThe End

that nobody told youthat nobody told youthat nobody told youThe partsThe partsThe parts

HTTP proxy settings in your network settings can cause a full browser crash.

user agent sniff

if (! // Target safari browsers! $.browser.safari!! // Not chrome! && !$.browser.chrome!! // And correct WebKit version! && parseFloat($.browser.version, 0) < 534.54!) {! // Don’t use WebSockets!return;!

}

Writing to a closed WebSocket can cause a full browser crash

write crash

var ws = new WebSocket("wss://localhost:8080/");!!ws.onmessage = function message(event) {! // Wrap sends in a setTimeout out to allow the! // readyState to be correctly set to closed. But! // Only have this delay on mobile devices! if (mobile) return setTimeout(function () {! ws.send("Sup Oslo");! }, 0);!! ws.send("Sup Oslo");!};

Pressing ESC in Firefox closes the WebSocket connection

connection end

$('body').keydown(function (e) {! // make sure that you capture the `esc` key and! // prevent it's default action from happening! if (e.which === 27) e.preventDefault();!});

Firefox can create ghost connections when you connect during ws.onclose.

Don’t use self signed SSL certificates, not for development and not in production

4G, 3G, LTE, mobile providers WTF ARE YOU DOING?? — Reverse proxy

Virus scanners such as AVG block WebSockets.

User, network and server firewalls block WebSockets.

Load balancers don't understand and block WebSockets.

So yeah.. Real-Time is HARD

What if…What if…What if…we could change thiswe could change thiswe could change this

Frameworks!Frameworks!Frameworks!

frameworkws

https://github.com/einaros/ws/

super damned fast

supports binary

cross domain

all the cool kids are doing it

i’m the only somewhat maintainer

no fallbacks or downgrading

broken by firewalls/virus scanners

only server, no client

module*

frameworksocket.io

http://github.com/learnboost/socket.io

multiple transports

cross domain

invested with bugs

poorly / not maintained / left for dead

no message order guarantee

dies behind firewall/virusscanners

frameworkengine.io

http://github.com/learnboost/engine.io

supports multiple transports

cross domain

upgrade instead of downgrade

works behind firewalls & virusscanners

not well battle tested yet

no message order guarantee

frameworkbrowserchannelhttps://github.com/josephg/node-browserchannel

multiple transports

client maintained by google

message order guaranteed

works behind firewalls & virusscanners

not cross domain

no websocket support

coffeescript on the server ._.

frameworksockjs

https://github.com/sockjs/sockjs-node/

multiple transports (tons of them)

cross domain

poor error handling

no query string allowed for connect

argh, more coffeescript

connection delay with firewalls

poorly maintained

in the way of developers

They all have different use cases and API's. !

Changing product specs == rewrite

we could change this toowe could change this toowe could change this tooWhat if…What if…What if…

npm install primus

Primus wraps and improves popular real-time frameworks. So you can focus on building apps.

CommunityCommunityCommunity

community

community

1014

15

stars, but growing steadystar it on http://github.com/primus/primus1014

1 line of code to switch betweenexcluding adding a framework to your package.json

different frameworks build-insocket.io, engine.io, sockjs, websockets, browserchannel, but allows addition third party5

1014

15

community

community

5031

5031

50 different pluginssubstream, emit, primus-emitter, primus-cluster, primus-rooms, primus-multiplex, primus-redis

different authorsdamonoehlman, mmalecki, jcrugzz, daffl, balupton, cayasso & many more31

community#primus on irc.freenode.net

questions are bugs, ask them in our github issue tracker

why was primus inventedwhy was primus inventedwhy was primus inventedHistoryHistoryHistory

historyreal-time layer for the bigpipe app framework

historywhich had an engine.io wrapper

historyno single point of failure

historyextracted and released as !

primus

Use casesUse casesUse cases

use cases

module & framework authors

use cases

startups & web developers

Learning PrimusLearning Primus

Learning Primus

learning primus

cd your-awesome-project!!$ npm install --save primus ws!!echo "??" !echo “profit!”!!vim index.js

learning primus

'use strict';!!var Primus = require("primus")! , server = require("http").createServer(fn)! , primus = new Primus(server, { transformer:"ws" });!!primus.on("connection", function connection(spark) {! console.log("connection received", spark.id);! spark.write("ohai");!! spark.on("data", function data(msg) {! console.log("received", msg);! });!});!!server.listen(8080);

learning primus

<script src="http://localhost:8080/primus/primus.js"></script>!<script>!'use strict';!!var primus = new Primus("http://localhost:8080");!!primus.on("open", function connected() {! console.log("connection opened");! primus.write("ohai");!});!!primus.on("data", function data(msg) {! console.log(“received", msg);!});!</script>

SwitchingSwitchingSwitching

1 line of code

var primus = new Primus(server, { ! transformer: “sockjs" // engine.io, socket.io etc!});

1 line of code

1 line of code

module.exports = require(“primus/transformer").extend({! server: function () {! // This is only exposed and ran on the server.! },!! client: function () {! // This is stringified end send/stored in the client.! // Can be ran on the server, if used through Node.js! },!! // Optional library for the front-end, assumes globals! library: fs.readFileSync(__dirname +"./yourclientlib.js")!});

StabilityStabilityStability

manual patching

manual patching

Stream CompatibleStream CompatibleStream Compatible

stream compatible

primus.on("end", function disconnected() {! console.log("connection ended");!});!!primus.end();!primus.write();!!fs.createReadStream(__dirname + '/index.html').pipe(spark, {! end: false!});

EncodingEncodingEncoding

message encoding

var primus = new Primus(server, { ! parser: "JSON" // JSON by default!});

message encoding

var primus = new Primus(server, { ! parser: "EJSON" // or binary-pack or a third party module!});

message encoding

module.exports = {! encoder: function (data, fn) {! // encode data to a string.! },!! decoder: function (data, fn) {! // decode data to an object! },!! // Optional library for the front-end, assumes globals! library: fs.readFileSync(__dirname +"./yourclientlib.js")!};

message encoding

primus.transform('incoming', function (packet) {! // This would transform all incoming messages to foo;! packet.data = 'foo';!});!!primus.transform('outgoing', function (packet) {! // This would transform all outgoing messages to foo;! packet.data = 'foo';!});

transforming

ReconnectReconnectReconnect

reconnect

var primus = new Primus("http://localhost:8080", {! strategy: "disconnect, online"!});

BroadcastBroadcastBroadcast

broadcast

primus.write("message"); // send message to all users!!primus.forEach(function (spark) {! // Or iterate over all connections, select the once you! // want and only write to those!! spark.write("message");!});

Node clientNode clientNode client

node.js

var primus = new Primus(server)! , Socket = primus.Socket;!!var client = new Socket(“http://localhost:8080”);!!// or if you want to connect to a remote server:!var primus = require(“primus”).createSocket({/* options */});

PluginsPluginsPlugins

plugins

// The long awaited Socket.IO 1.0 release with Primus:!!var server = require("http").createServer(fn)! , primus = new Primus(server, { transformer:"engine.io" });!!primus.use(“emitter","primus-emitter")! .use(“multiplex”, require(“primus-multiplex”))! .use(“primus-emitter”, "primus-rooms");

plugins

module.exports = {! server: function () {! // This is only exposed and ran on the server.! },!! client: function () {! // This is stringified end send/stored in the client.! // Can be ran on the server, if used through Node.js! },!! // Optional library for the front-end, assumes globals! library: fs.readFileSync(__dirname +"./yourclientlib.js")!};

and middlewareand middlewareand middlewarePluginsPluginsPlugins

middleware

var server = require("http").createServer(fn)! , primus = new Primus(server, { transformer:"sockjs" });!!primus.before(“session”, require(“session-parse-module”))! .before(“middleware-name”, "middleware-module-name");

!

3rdEden

"

3rd-Eden

fb

arnout.kazemier

That's it! Thanks & check it out on http://primus.io

http://primus.io