Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

171
Server-side Push Comes of Age by Brian Sam-Bodden http://www.integrallis.com

description

Server-side browser push technologies have been around for a while in one way or another, ranging from from crude browser polling to Flash enabled frameworks. In this session you’ll get a code-driven walk-through on the evolution and mechanics of server-push technologies, including: Server streaming Polling and long Polling Comet Web Sockets

Transcript of Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Page 1: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Server-side Push

Comes of Age

by Brian Sam-Boddenhttp://www.integrallis.com

Page 2: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

HTTP

Page 3: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

HTTPUnsuspecting Programmers

Page 4: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

HTTPTim Berners-Lee

Unsuspecting Programmers

Page 5: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

... and it was good

Page 6: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

...for documents

Page 7: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

it scales!

Page 8: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Web Applications

Uhmm, yeah...Remember that client-server desktop app?It should be easy to port it to the web, right?

Page 9: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Client ServerRequest

Response

C

C

Well, hello let me make you a page good friend

... don’t forget the images

page-by-page model

C... hold on, I got hit the DB

C... and cook up some HTML

C... and all other assets

Page 10: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Client ServerRequest

Response

C

C

Well, hello let me make you a page good friend

... don’t forget the images

page-by-page model

C... hold on, I got hit the DB

C... and cook up some HTML

C... and all other assets

Page 11: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Client ServerRequest

Response

C

C

Well, hello let me make you a page good friend

... don’t forget the images

page-by-page model

C... hold on, I got hit the DB

C... and cook up some HTML

C... and all other assets

Page 12: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)
Page 13: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

ask for it background

Page 14: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

and change the relevant bits

Page 15: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Client ServerLoad the “application”

CHere’s the initial page load

ajax model

C... just what’s changedUser Action #1

C... and againUser Action #2

Page 16: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

The Web as a Platform

Page 17: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

So things got better, until ...

Page 18: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Push

Uhmm, yeah...Remember that cool web app I underpaid you to build?

It should be easy to notify the user when something important happens, right?

Page 19: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

PUSH

Page 20: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Why?

Page 21: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Collaboration

Page 22: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Chat

Page 23: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Comments

Page 24: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Notifications

Page 25: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Bidding Platforms

Page 26: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Monitoring

Page 27: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Stocks

Page 28: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Scores

Page 29: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Games

Page 30: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)
Page 31: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Bi-directional

Page 32: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Asynchronous

Page 33: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Near Real-Time

Page 34: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Server Initiated*

Page 35: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Communications

Page 36: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

But...

Page 37: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

until fairly recently

Page 38: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Browsers Sucked!

Page 39: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

So we had (have) to Hack it

Page 40: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Java Applets

PollingiFrame

Streaming

Long Polling

Flash Streaming

XHR Streaming

Page 41: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Java Applets

PollingiFrame

Streaming

Long Polling

Flash Streaming

XHR Streaming

Page 42: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Java Applets

Page 43: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Java Applets

Page 44: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)
Page 45: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

PollingJava

AppletsiFrame

Streaming

Long Polling

Flash Streaming

XHR Streaming

Page 46: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Polling

Page 47: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Polling

Page 48: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

are we there yet?

Page 49: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

setInterval(function() { areWeThereYet();}, 1000);

Page 50: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

setInterval(function() { areWeThereYet();}, 1000);

Page 51: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

setInterval(function() { areWeThereYet();}, 1000);

Page 52: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

setInterval(function() { areWeThereYet();}, 1000);

Page 53: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Client ServerRequest

Response

Request

Response

C

C

No Soup for you!

Ok, here you go

Event

polling

Page 54: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

chatty / high traffic

Page 55: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Self-inflicted DDOS Attack!

Page 56: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

iFrame Streaming

Java Applets

Polling

Long Polling

Flash Streaming

XHR Streaming

Page 57: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

iFrame Streaming

Page 58: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

iFrame Streaming

Page 59: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Oh yes, it involves an iFrame

#sadpanda

Page 60: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

iFrameStreaming

Demo

Page 61: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

first you embed an invisible iFrame

Page 62: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

$('<iframe />', { name: 'hidden-iframe', id: 'hidden-iframe', src: '/long-running', css: { 'display': 'none' } }).appendTo('body');

Page 63: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

$('<iframe />', { name: 'hidden-iframe', id: 'hidden-iframe', src: '/long-running', css: { 'display': 'none' } }).appendTo('body');

Page 64: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

$('<iframe />', { name: 'hidden-iframe', id: 'hidden-iframe', src: '/long-running', css: { 'display': 'none' } }).appendTo('body');

Page 65: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

$('<iframe />', { name: 'hidden-iframe', id: 'hidden-iframe', src: '/long-running', css: { 'display': 'none' } }).appendTo('body');

Page 66: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

$('<iframe />', { name: 'hidden-iframe', id: 'hidden-iframe', src: '/long-running', css: { 'display': 'none' } }).appendTo('body');

Page 67: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

$('<iframe />', { name: 'hidden-iframe', id: 'hidden-iframe', src: '/long-running', css: { 'display': 'none' } }).appendTo('body');

Page 68: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

on the server you need streaming capabilities

Page 69: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

get '/long-running' do stream do |out| (10..100).step(10) do |n| word = gimme_funny_word out << update_progress(n, word.keys.first, word.values.first) sleep 1.5 end endend

Page 70: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

get '/long-running' do stream do |out| (10..100).step(10) do |n| word = gimme_funny_word out << update_progress(n, word.keys.first, word.values.first) sleep 1.5 end endend

Page 71: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

get '/long-running' do stream do |out| (10..100).step(10) do |n| word = gimme_funny_word out << update_progress(n, word.keys.first, word.values.first) sleep 1.5 end endend

Page 72: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

get '/long-running' do stream do |out| (10..100).step(10) do |n| word = gimme_funny_word out << update_progress(n, word.keys.first, word.values.first) sleep 1.5 end endend

Page 73: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

get '/long-running' do stream do |out| (10..100).step(10) do |n| word = gimme_funny_word out << update_progress(n, word.keys.first, word.values.first) sleep 1.5 end endend

Page 74: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

get '/long-running' do stream do |out| (10..100).step(10) do |n| word = gimme_funny_word out << update_progress(n, word.keys.first, word.values.first) sleep 1.5 end endend

Page 75: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

def update_progress(percent, word, meaning) %[<script type="text/javascript"> parent.updatePage(#{percent}, '#{word}', '#{meaning}'); </script>]end

Page 76: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

def update_progress(percent, word, meaning) %[<script type="text/javascript"> parent.updatePage(#{percent}, '#{word}', '#{meaning}'); </script>]end

Page 77: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

// called by the streamed server-sent scriptfunction updatePage(percent, word, meaning) { $kickItButton.text(percent + '%'); $theWord.text(word); $theMeaning.text(meaning); if (percent == 100) { $kickItButton.attr('disabled', false); $kickItButton.text('Kick it again!'); }}

Page 78: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

// called by the streamed server-sent scriptfunction updatePage(percent, word, meaning) { $kickItButton.text(percent + '%'); $theWord.text(word); $theMeaning.text(meaning); if (percent == 100) { $kickItButton.attr('disabled', false); $kickItButton.text('Kick it again!'); }}

Page 79: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

// called by the streamed server-sent scriptfunction updatePage(percent, word, meaning) { $kickItButton.text(percent + '%'); $theWord.text(word); $theMeaning.text(meaning); if (percent == 100) { $kickItButton.attr('disabled', false); $kickItButton.text('Kick it again!'); }}

Page 80: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

// called by the streamed server-sent scriptfunction updatePage(percent, word, meaning) { $kickItButton.text(percent + '%'); $theWord.text(word); $theMeaning.text(meaning); if (percent == 100) { $kickItButton.attr('disabled', false); $kickItButton.text('Kick it again!'); }}

Page 81: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

// called by the streamed server-sent scriptfunction updatePage(percent, word, meaning) { $kickItButton.text(percent + '%'); $theWord.text(word); $theMeaning.text(meaning); if (percent == 100) { $kickItButton.attr('disabled', false); $kickItButton.text('Kick it again!'); }}

Page 82: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Drawback:Page is ‘forever’

loading

Page 83: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

iFrame Streaming

Java Applets

Polling

Long Polling

Flash Streaming

XHR Streaming

Page 84: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

XHR Streaming

Page 85: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

XHR Streaming

Page 86: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

XHRStreaming

Demo

Page 87: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

better than iframes

Page 88: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

use AJAX call

Page 89: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

send JSON

Page 90: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

get '/stream' do stream do |out| (10..100).step(2) do |n| out << gimme_funny_word.as_json sleep 1.5 end endend

Page 91: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

get '/stream' do stream do |out| (10..100).step(2) do |n| out << gimme_funny_word.as_json sleep 1.5 end endend

Page 92: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

get '/stream' do stream do |out| (10..100).step(2) do |n| out << gimme_funny_word.as_json sleep 1.5 end endend

Page 93: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

polling the stream

Page 94: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

parse = function() { // parse the xhr.responseText and update the UI }; xhr = new XMLHttpRequest();url = "/stream";xhr.open("GET", url, true);xhr.send(); last_index = 0;interval = setInterval(parse, 500);setTimeout((function() { clearInterval(interval); parse(); xhr.abort(); }), 20000);

Page 95: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

parse = function() { // parse the xhr.responseText and update the UI }; xhr = new XMLHttpRequest();url = "/stream";xhr.open("GET", url, true);xhr.send(); last_index = 0;interval = setInterval(parse, 500);setTimeout((function() { clearInterval(interval); parse(); xhr.abort(); }), 20000);

Page 96: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

parse = function() { // parse the xhr.responseText and update the UI }; xhr = new XMLHttpRequest();url = "/stream";xhr.open("GET", url, true);xhr.send(); last_index = 0;interval = setInterval(parse, 500);setTimeout((function() { clearInterval(interval); parse(); xhr.abort(); }), 20000);

Page 97: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

parse = function() { // parse the xhr.responseText and update the UI }; xhr = new XMLHttpRequest();url = "/stream";xhr.open("GET", url, true);xhr.send(); last_index = 0;interval = setInterval(parse, 500);setTimeout((function() { clearInterval(interval); parse(); xhr.abort(); }), 20000);

Page 98: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

parse = function() { // parse the xhr.responseText and update the UI }; xhr = new XMLHttpRequest();url = "/stream";xhr.open("GET", url, true);xhr.send(); last_index = 0;interval = setInterval(parse, 500);setTimeout((function() { clearInterval(interval); parse(); xhr.abort(); }), 20000);

Page 99: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

frequency of polling the stream >=

server serving rate

Page 100: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

No Throbber Freakout

Page 101: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Long Polling

iFrame Streaming

Java Applets

Polling

Flash Streaming

XHR Streaming

Page 102: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Long Polling

Page 103: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Long Polling

Page 104: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

most commonly used

Page 105: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

response is blocked...

Page 106: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

...until server event occurs

Page 107: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Client ServerRequest

Response

C

C

Nothing here, but hang on...

... and there you go, good day!Event

polling

Page 108: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Long PollingDemo

Page 109: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

get '/read' do content_type :json filename = 'data.txt'

last = params[:timestamp] == 'null' ? 0 : params[:timestamp].to_i current = last_modification(filename) not_changed_or_emtpy = true while (not_changed_or_emtpy) do sleep 0.1 not_changed_or_emtpy = File.zero?(filename) || (current <= last) current = last_modification(filename) end

{ :messages => File.read(filename), :timestamp => current }.to_jsonend

Page 110: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

get '/read' do content_type :json filename = 'data.txt'

last = params[:timestamp] == 'null' ? 0 : params[:timestamp].to_i current = last_modification(filename) not_changed_or_emtpy = true while (not_changed_or_emtpy) do sleep 0.1 not_changed_or_emtpy = File.zero?(filename) || (current <= last) current = last_modification(filename) end

{ :messages => File.read(filename), :timestamp => current }.to_jsonend

Page 111: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

get '/read' do content_type :json filename = 'data.txt'

last = params[:timestamp] == 'null' ? 0 : params[:timestamp].to_i current = last_modification(filename) not_changed_or_emtpy = true while (not_changed_or_emtpy) do sleep 0.1 not_changed_or_emtpy = File.zero?(filename) || (current <= last) current = last_modification(filename) end

{ :messages => File.read(filename), :timestamp => current }.to_jsonend

Page 112: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

get '/read' do content_type :json filename = 'data.txt'

last = params[:timestamp] == 'null' ? 0 : params[:timestamp].to_i current = last_modification(filename) not_changed_or_emtpy = true while (not_changed_or_emtpy) do sleep 0.1 not_changed_or_emtpy = File.zero?(filename) || (current <= last) current = last_modification(filename) end

{ :messages => File.read(filename), :timestamp => current }.to_jsonend

Page 113: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

get '/read' do content_type :json filename = 'data.txt'

last = params[:timestamp] == 'null' ? 0 : params[:timestamp].to_i current = last_modification(filename) not_changed_or_emtpy = true while (not_changed_or_emtpy) do sleep 0.1 not_changed_or_emtpy = File.zero?(filename) || (current <= last) current = last_modification(filename) end

{ :messages => File.read(filename), :timestamp => current }.to_jsonend

Page 114: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

get '/read' do content_type :json filename = 'data.txt'

last = params[:timestamp] == 'null' ? 0 : params[:timestamp].to_i current = last_modification(filename) not_changed_or_emtpy = true while (not_changed_or_emtpy) do sleep 0.1 not_changed_or_emtpy = File.zero?(filename) || (current <= last) current = last_modification(filename) end

{ :messages => File.read(filename), :timestamp => current }.to_jsonend

Page 115: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

get '/read' do content_type :json filename = 'data.txt'

last = params[:timestamp] == 'null' ? 0 : params[:timestamp].to_i current = last_modification(filename) not_changed_or_emtpy = true while (not_changed_or_emtpy) do sleep 0.1 not_changed_or_emtpy = File.zero?(filename) || (current <= last) current = last_modification(filename) end

{ :messages => File.read(filename), :timestamp => current }.to_jsonend

Page 116: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

function longPoll() { $.ajax({ type : 'get', url : '/read?timestamp=' + timestamp, async : true, cache : false, timeout : 10000,

success : function(json) { var messages = json['messages'].split("\n") var last = messages[messages.length-1]; if (last) { $('#msg').append('<div>'+last+'</div>'); timestamp = json['timestamp']; } setTimeout(longPoll, 1000); }, error : function(xhr, textStatus, error) { setTimeout(longPoll, 2000); } });}

Page 117: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

function longPoll() { $.ajax({ type : 'get', url : '/read?timestamp=' + timestamp, async : true, cache : false, timeout : 10000,

success : function(json) { var messages = json['messages'].split("\n") var last = messages[messages.length-1]; if (last) { $('#msg').append('<div>'+last+'</div>'); timestamp = json['timestamp']; } setTimeout(longPoll, 1000); }, error : function(xhr, textStatus, error) { setTimeout(longPoll, 2000); } });}

Page 118: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

function longPoll() { $.ajax({ type : 'get', url : '/read?timestamp=' + timestamp, async : true, cache : false, timeout : 10000,

success : function(json) { var messages = json['messages'].split("\n") var last = messages[messages.length-1]; if (last) { $('#msg').append('<div>'+last+'</div>'); timestamp = json['timestamp']; } setTimeout(longPoll, 1000); }, error : function(xhr, textStatus, error) { setTimeout(longPoll, 2000); } });}

Page 119: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

function longPoll() { $.ajax({ type : 'get', url : '/read?timestamp=' + timestamp, async : true, cache : false, timeout : 10000,

success : function(json) { var messages = json['messages'].split("\n") var last = messages[messages.length-1]; if (last) { $('#msg').append('<div>'+last+'</div>'); timestamp = json['timestamp']; } setTimeout(longPoll, 1000); }, error : function(xhr, textStatus, error) { setTimeout(longPoll, 2000); } });}

Page 120: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Naive Long polling w/ 10sec timeout

{

Requests that returned data

Current polling request

Requests in RED are timed out

long polls

Page 121: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

There is a big issue with the previous example...

Page 122: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

There is a big issue with the previous example...

Besides using a Text File as a database

Page 123: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

The server doesn’t support async responses...

Page 124: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

The busy IO checking loop will block

Page 125: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

aget '/read' do content_type :json filename = 'data.txt'

last = params[:timestamp] == 'null' ? 0 : params[:timestamp].to_i current = last_modification(filename) EM.defer do check_file_changes = proc do if File.zero?(filename) || (current <= last) current = last_modification(filename) EM.next_tick(&check_file_changes) else body({ :messages => File.read(filename), :timestamp => current }.to_json) end end EM.next_tick(&check_file_changes) endend

Page 126: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

aget '/read' do content_type :json filename = 'data.txt'

last = params[:timestamp] == 'null' ? 0 : params[:timestamp].to_i current = last_modification(filename) EM.defer do check_file_changes = proc do if File.zero?(filename) || (current <= last) current = last_modification(filename) EM.next_tick(&check_file_changes) else body({ :messages => File.read(filename), :timestamp => current }.to_json) end end EM.next_tick(&check_file_changes) endend

Page 127: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Difficult to Implement

Page 128: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Flash Streaming

Long Polling

iFrame Streaming

Java Applets

Polling

XHR Streaming

Page 129: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Flash Streaming

Page 130: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Flash Streaming

Page 131: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

XML Socket

Page 132: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Single PixelFlash Movie

Page 133: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Go Away Flash!

Page 134: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Push Frameworks

Page 135: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Comet !=

Just Long Polling

Page 136: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Amalgamation of Techniques

Page 137: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Provide both Client and Server

Components

Page 138: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Many use a Pub-Sub Protocol

Page 139: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Bayeaux

Page 140: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

CometDemowithhttp://faye.jcoglan.com

Page 141: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

var client = new Faye.Client('/faye');

var subscription = client.subscribe('/<%= room %>', function(message) { $messages = $('#messages'); $message = $('<div>' + message['user'] + ' : ' + message['text'] +'</div>') $messages.append($message); }); $("#chat-form").submit(function(e){ e.preventDefault(); var message = $('#message').val(); client.publish('/<%= room %>', {user: '<%= username %>', text: message}); $('#message').val('');});

Page 142: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

var client = new Faye.Client('/faye');

var subscription = client.subscribe('/<%= room %>', function(message) { $messages = $('#messages'); $message = $('<div>' + message['user'] + ' : ' + message['text'] +'</div>') $messages.append($message); }); $("#chat-form").submit(function(e){ e.preventDefault(); var message = $('#message').val(); client.publish('/<%= room %>', {user: '<%= username %>', text: message}); $('#message').val('');});

Page 143: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

var client = new Faye.Client('/faye');

var subscription = client.subscribe('/<%= room %>', function(message) { $messages = $('#messages'); $message = $('<div>' + message['user'] + ' : ' + message['text'] +'</div>') $messages.append($message); }); $("#chat-form").submit(function(e){ e.preventDefault(); var message = $('#message').val(); client.publish('/<%= room %>', {user: '<%= username %>', text: message}); $('#message').val('');});

Page 144: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

var client = new Faye.Client('/faye');

var subscription = client.subscribe('/<%= room %>', function(message) { $messages = $('#messages'); $message = $('<div>' + message['user'] + ' : ' + message['text'] +'</div>') $messages.append($message); }); $("#chat-form").submit(function(e){ e.preventDefault(); var message = $('#message').val(); client.publish('/<%= room %>', {user: '<%= username %>', text: message}); $('#message').val('');});

Page 145: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Now we can create a room ... and have a conversation

Page 146: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)
Page 147: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Web Sockets

Page 148: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Two Way Communications

Page 149: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Over a dedicated socket

Page 150: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

in simple way

Page 151: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

with security, proxies & firewalls

in mind

Page 152: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Web SocketsDemo

Page 153: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

EventMachine.run do EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080) do |ws| ws.onopen do

end

ws.onmessage do |msg|

end

ws.onclose do

end endend

em-websocket provides an easy to use WebSocket class

On the server we’ll implement

some WebSocket event handlers

Page 154: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

EventMachine.run do @channel = EM::Channel.new @users = {} @messages = []... ws.onopen do new_user = @channel.subscribe { |msg| ws.send msg } @users[ws.object_id] = new_user @messages.each do |message| ws.send message end end

subscribe a new user to the channel passing the callback to our push action

we’ll keep a list of users in a Hash keyed by the object_id of the incoming ws connection

push the last batch of messages to the user

Page 155: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

ws.onmessage do |msg| @messages << msg @messages.shift if @messages.length > 10

@channel.push msgend

add the new message to the end of the queue

broadcast the message to all users connected to the channel

we’ll keep the last 10 messages

Page 156: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

ws.onclose do @channel.unsubscribe(@users[ws.object_id]) @users.delete(ws.object_id)end

we unsubscribe them from the channel

remove them from the Hash of users

Page 157: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

EventMachine.run do EventMachine::WebSocket.start(...) do |ws| ... end

class App < Sinatra::Base get '/' do erb :index end end App.run!end

our single page application is contained in /public/views/index.erb

The Sinatra app runs as part of the EV “Reactor Loop”

Page 158: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

<div class="container"> <h1 class="visible-desktop">WebSockets Sinatra Draw</h1> <legend>Draw Something</legend> <div id="whiteboard" class="well well-small"> <canvas id="draw-canvas"></canvas> </div> </div>

We’ll nest the canvas in a div in order to resize it

correctly

$(document).ready(function() { var $canvas = $('#draw-canvas'); var ws = new WebSocket("ws://" + location.hostname + ":8080");

When the document is ready we’ll connect to the EM Websocket server

running on :8080

Page 159: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

var currentX = 0;var currentY = 0;var lastX, lastY, lastReceivedX, lastReceivedY; var drawing = false;var ctx = $('#draw-canvas')[0].getContext('2d');

We’ll grab the 2D canvas context in order to draw on it

$canvas.bind('mousemove',function(ev){ ev = ev || window.event; currentX = ev.pageX - $canvas.offset().left; currentY = ev.pageY - $canvas.offset().top;});

$canvas.bind('touchmove',function(ev){ var touch = ev.originalEvent.touches[0] || ev.originalEvent.changedTouches[0]; currentX = touch.pageX - $canvas.offset().left; currentY = touch.pageY - $canvas.offset().top; });

We’ll update the currentX and currentY coordinates of the mouse over the canvas both for

desktop and mobile browsers

touchmove is provided by jQuery-Mobile-Events

plugin

Page 160: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

$canvas.bind('tapstart',function(ev) { drawing = true}); $canvas.bind('tapend',function(ev) { drawing = false});

tapstart and tapend are also provided by the jQuery-Mobile-Events

Page 161: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

ws.onopen = function(event) { setInterval(function() { if ((currentX !== lastX || currentY !== lastY) && drawing) { lastX = currentX; lastY = currentY; ws.send(JSON.stringify({ x: currentX, y: currentY})); } }, 30);}

Page 162: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

ws.onmessage = function(event) { var msg = $.parseJSON(event.data); ctx.beginPath(); ctx.moveTo(lastReceivedX, lastReceivedY); ctx.lineTo(msg.x, msg.y); ctx.closePath(); ctx.stroke();

lastReceivedX = msg.x; lastReceivedY = msg.y;};

We’ll only draw indirectly when we receive a message (even when we are the ones doing the drawing)

Page 163: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

On Firefox

On Safari Desktop

... and on my almost out of batteries iPhone

Page 164: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

What are we missing?

Page 165: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Server-sent Events

Page 166: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

BOSH

Page 167: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

WebRTC

Page 168: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

What should you do?

Page 169: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

Use a Framework!

Page 170: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

That plays well with your

framework

Page 171: Server-Side Push: Comet, Web Sockets come of age (OSCON 2013)

ThanksAll example code available at:

https://github.com/integrallis/server-side-push

Watch out for an upcoming article at http://integrallis.com

by Brian Sam-Boddenhttp://www.integrallis.com

http://www.slideshare.net/bsbodden/ssp-oscon