Skip to content

Instantly share code, notes, and snippets.

@mloughran
Last active September 27, 2015 17:48
Show Gist options
  • Select an option

  • Save mloughran/1308932 to your computer and use it in GitHub Desktop.

Select an option

Save mloughran/1308932 to your computer and use it in GitHub Desktop.

Revisions

  1. mloughran revised this gist Dec 21, 2012. 2 changed files with 3 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -49,3 +49,5 @@ Timeout of 100ms in `onclose`: Sometimes behaves as 0ms, sometimes as 103ms.
    Timeout of 103ms in `onclose`: Inconsistent behaviour - there is definitely a race condition in firefox here. Usually this works perfectly (i.e. existing connection is closed and new one isn't opened), but sometimes a new connection is established, and it's left around as a ghost connection after exiting the page. This is basically the behaviour I saw when we initially reported this issue, but it's even worse: **The TCP connection actually stays around even when the browser tab is closed, and is only closed when Firefox quits!**

    My hunch based on all this experimentation is that if a WebSocket connection is in the process of opening when the page is finally cleaned up by firefox (which seems to be after ~ 105ms), that the connection is orphaned forever. This could potentially leave Firefox amassing large numbers of ghost WebSocket connections.

    **Firefox 17**: Same behaviour as Firefox 13, including **leaving the WebSocket open after the browser tab is closed with some values of timeout (described above)**
    1 change: 1 addition & 0 deletions test.erb
    Original file line number Diff line number Diff line change
    @@ -16,6 +16,7 @@
    ws.onopen = function() { log('onopen'); };
    ws.onclose = function() {
    connect();
    // setTimeout(connect, 103);
    };
    ws.onmessage = function(evt) { log('onmessage: '+evt.data); };
    })();
  2. mloughran revised this gist Jun 18, 2012. 1 changed file with 22 additions and 0 deletions.
    22 changes: 22 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -27,3 +27,25 @@ This is the expected output by echo-ws.rb
    Opened 1
    Closed 1
    Opened 2

    ## Affected browsers

    **Firefox 9**: Tried to workaround this by using a timeout but it doesn't work unless the timeout quite large.

    The transition seems to happen at around 105ms. Using a 103ms timer, connection usually creates a ghost conn, 104ms about half the time, and at 105ms it almost never does (note: only tested on OS X Lion). You can verify by changing the `onclose` function in `test.erb` to

    ws.onclose = function() {
    setTimeout(connect, 103);
    };

    **Firefox 13**: If anything this seems even more broken than 9.

    Timeout 0 case: `onclose` is called (with `wasClean = true`) when you click to navigate away from the page, then a new connection is opened (by the `onclose` handler). This connection then appears to be closed by firefox almost instantly (presumably this is the workaround to this bug, so that the ghost connection is not left lying about).

    Timeout of 90ms in `onclose`: Exactly the same behaviour as case 0ms above.

    Timeout of 100ms in `onclose`: Sometimes behaves as 0ms, sometimes as 103ms.

    Timeout of 103ms in `onclose`: Inconsistent behaviour - there is definitely a race condition in firefox here. Usually this works perfectly (i.e. existing connection is closed and new one isn't opened), but sometimes a new connection is established, and it's left around as a ghost connection after exiting the page. This is basically the behaviour I saw when we initially reported this issue, but it's even worse: **The TCP connection actually stays around even when the browser tab is closed, and is only closed when Firefox quits!**

    My hunch based on all this experimentation is that if a WebSocket connection is in the process of opening when the page is finally cleaned up by firefox (which seems to be after ~ 105ms), that the connection is orphaned forever. This could potentially leave Firefox amassing large numbers of ghost WebSocket connections.
  3. mloughran revised this gist Jun 18, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion echo-ws.rb
    Original file line number Diff line number Diff line change
    @@ -8,5 +8,5 @@
    ws.onopen { puts "Opened #{i}"}
    ws.onmessage { |msg| ws.send "Pong: #{msg}" }
    ws.onclose { puts "Closed #{i}" }
    ws.onerror { |e| puts "Error: #{e.message}" }
    # ws.onerror { |e| puts "Error: #{e.message}" }
    end
  4. mloughran revised this gist Jun 18, 2012. 2 changed files with 8 additions and 4 deletions.
    4 changes: 2 additions & 2 deletions Gemfile.lock
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,8 @@
    GEM
    remote: http://rubygems.org/
    specs:
    addressable (2.2.6)
    em-websocket (0.3.4)
    addressable (2.2.8)
    em-websocket (0.3.6)
    addressable (>= 2.1.1)
    eventmachine (>= 0.12.9)
    eventmachine (0.12.10)
    8 changes: 6 additions & 2 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,12 +1,16 @@
    Install dependencies
    The example code uses ruby and the bundler dependency system which you can install with

    gem install bundler

    Install dependencies using bundler

    bundle install

    Start simple websocket server

    ruby echo-ws.rb

    In another terminal start web
    In another terminal start the web app

    ruby app.rb

  5. mloughran revised this gist Oct 24, 2011. 1 changed file with 8 additions and 4 deletions.
    12 changes: 8 additions & 4 deletions Gemfile.lock
    Original file line number Diff line number Diff line change
    @@ -1,14 +1,18 @@
    GEM
    remote: http://rubygems.org/
    specs:
    addressable (2.2.6)
    em-websocket (0.3.4)
    addressable (>= 2.1.1)
    eventmachine (>= 0.12.9)
    eventmachine (0.12.10)
    rack (1.3.2)
    sinatra (1.2.3)
    rack (~> 1.1)
    tilt (>= 1.2.2, < 2.0)
    rack (1.3.5)
    rack-protection (1.1.4)
    rack
    sinatra (1.3.1)
    rack (~> 1.3, >= 1.3.4)
    rack-protection (~> 1.1, >= 1.1.2)
    tilt (~> 1.3, >= 1.3.3)
    tilt (1.3.3)

    PLATFORMS
  6. mloughran revised this gist Oct 24, 2011. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions Gemfile
    Original file line number Diff line number Diff line change
    @@ -1,2 +1,4 @@
    source :rubygems

    gem 'em-websocket'
    gem 'sinatra'
  7. mloughran revised this gist Oct 24, 2011. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions app.rb
    Original file line number Diff line number Diff line change
    @@ -4,6 +4,7 @@
    set :views, settings.root

    get '/' do
    # THIS IS CRITICAL
    sleep 0.1
    erb :index
    end
  8. mloughran revised this gist Oct 24, 2011. 3 changed files with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions app.rb
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,8 @@
    require 'bundler/setup'
    require 'sinatra'

    set :views, settings.root

    get '/' do
    sleep 0.1
    erb :index
    File renamed without changes.
    File renamed without changes.
  9. mloughran revised this gist Oct 24, 2011. 1 changed file with 25 additions and 0 deletions.
    25 changes: 25 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@
    Install dependencies

    bundle install

    Start simple websocket server

    ruby echo-ws.rb

    In another terminal start web

    ruby app.rb

    To reproduce

    * Open <http://localhost:4567/>
    * Click 'Test page'
    * Verify that connection is opened in echo-ws
    * Click HOME
    * Note that first connection is closed, but a mysterious new connection is opened

    This is the expected output by echo-ws.rb

    Opened 1
    Closed 1
    Opened 2
  10. mloughran revised this gist Oct 24, 2011. 6 changed files with 44 additions and 8 deletions.
    2 changes: 2 additions & 0 deletions Gemfile
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,2 @@
    gem 'em-websocket'
    gem 'sinatra'
    19 changes: 19 additions & 0 deletions Gemfile.lock
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    GEM
    specs:
    addressable (2.2.6)
    em-websocket (0.3.4)
    addressable (>= 2.1.1)
    eventmachine (>= 0.12.9)
    eventmachine (0.12.10)
    rack (1.3.2)
    sinatra (1.2.3)
    rack (~> 1.1)
    tilt (>= 1.2.2, < 2.0)
    tilt (1.3.3)

    PLATFORMS
    ruby

    DEPENDENCIES
    em-websocket
    sinatra
    7 changes: 4 additions & 3 deletions app.rb
    Original file line number Diff line number Diff line change
    @@ -1,10 +1,11 @@
    require 'bundler/setup'
    require 'sinatra'

    get '/index.html' do
    get '/' do
    sleep 0.1
    erb :index
    end

    get '/pusher.html' do
    erb :pusher
    get '/test.html' do
    erb :test
    end
    12 changes: 12 additions & 0 deletions echo-ws.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,12 @@
    require 'bundler/setup'
    require 'em-websocket'

    i = 0

    EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080) do |ws|
    i += 1
    ws.onopen { puts "Opened #{i}"}
    ws.onmessage { |msg| ws.send "Pong: #{msg}" }
    ws.onclose { puts "Closed #{i}" }
    ws.onerror { |e| puts "Error: #{e.message}" }
    end
    5 changes: 3 additions & 2 deletions views/index.erb
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,8 @@
    <!DOCTYPE html>
    <head>
    <title>Pusher Test</title>
    <title>Firefox orphaned connection test</title>
    <body>
    <h1>Home</h1>
    <a href="/pusher.html">Pusher</a>
    <p>This page does not open a websocket connection</p>
    <a href="/test.html">Test page</a>
    </body>
    7 changes: 4 additions & 3 deletions views/pusher.erb → views/test.erb
    Original file line number Diff line number Diff line change
    @@ -12,7 +12,7 @@

    if (Socket) {
    (function connect() {
    var ws = new Socket('ws://ws.pusherapp.com:80/app/9d1c469de450fce4633b?client=js')
    var ws = new Socket('ws://localhost:8080/')
    ws.onopen = function() { log('onopen'); };
    ws.onclose = function() {
    connect();
    @@ -23,8 +23,9 @@
    </script>
    </head>
    <body>
    <h1>Pusher</h1>
    <a href="/index.html">HOME</a>
    <h1>Test page</h1>
    <p>This page opens a websocket connection to ws://localhost:8080/</p>
    <a href="/">HOME</a>
    <div id="log">

    </div>
  11. mloughran revised this gist Oct 24, 2011. 1 changed file with 1 addition and 2 deletions.
    3 changes: 1 addition & 2 deletions views/pusher.erb
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,6 @@
    <!DOCTYPE html>
    <head>
    <title>Pusher Test</title>
    <script src="http://js.pusherapp.com/1.9.3/pusher.min.js" type="text/javascript"></script>
    <title>Firefox orphaned connection test</title>
    <script type="text/javascript">
    var Socket = window['MozWebSocket'] || window['WebSocket']

  12. mloughran revised this gist Oct 24, 2011. 1 changed file with 16 additions and 31 deletions.
    47 changes: 16 additions & 31 deletions views/pusher.erb
    Original file line number Diff line number Diff line change
    @@ -3,39 +3,24 @@
    <title>Pusher Test</title>
    <script src="http://js.pusherapp.com/1.9.3/pusher.min.js" type="text/javascript"></script>
    <script type="text/javascript">
    var Socket;
    if (typeof window['MozWebSocket'] !== 'undefined') {
    Socket = window['MozWebSocket'];
    } else if (typeof window['WebSocket'] !== 'undefined') {
    Socket = window['WebSocket'];
    }
    var Socket = window['MozWebSocket'] || window['WebSocket']

    function log(msg) {
    var node = document.createElement('p');
    node.innerHTML = msg;
    function log(msg) {
    var node = document.createElement('p');
    node.innerHTML = msg;
    document.getElementById('log').appendChild(node);
    }

    document.getElementById('log').appendChild(node);
    }

    if (Socket) {
    var timer;
    (function connect() {
    timer = setTimeout(function(){
    var ws = new Socket('ws://ws.pusherapp.com:80/app/9d1c469de450fce4633b?client=js')
    ws.onopen = function() { log('onopen'); };
    ws.onclose = function() {
    connect();
    };

    ws.onerror = function() {
    log('onerror');
    ws.close();
    connect();
    };
    ws.onmessage = function(evt) { log('onmessage: '+evt.data); };
    }, 0);
    })();
    }
    if (Socket) {
    (function connect() {
    var ws = new Socket('ws://ws.pusherapp.com:80/app/9d1c469de450fce4633b?client=js')
    ws.onopen = function() { log('onopen'); };
    ws.onclose = function() {
    connect();
    };
    ws.onmessage = function(evt) { log('onmessage: '+evt.data); };
    })();
    }
    </script>
    </head>
    <body>
  13. mloughran revised this gist Oct 24, 2011. 1 changed file with 34 additions and 8 deletions.
    42 changes: 34 additions & 8 deletions views/pusher.erb
    Original file line number Diff line number Diff line change
    @@ -3,19 +3,45 @@
    <title>Pusher Test</title>
    <script src="http://js.pusherapp.com/1.9.3/pusher.min.js" type="text/javascript"></script>
    <script type="text/javascript">
    // Enable pusher logging - don't include this in production
    Pusher.log = function(message) {
    if (window.console && window.console.log) window.console.log(message);
    };
    var Socket;
    if (typeof window['MozWebSocket'] !== 'undefined') {
    Socket = window['MozWebSocket'];
    } else if (typeof window['WebSocket'] !== 'undefined') {
    Socket = window['WebSocket'];
    }

    // Flash fallback logging - don't include this in production
    WEB_SOCKET_DEBUG = true;
    function log(msg) {
    var node = document.createElement('p');
    node.innerHTML = msg;

    var pusher = new Pusher('9d1c469de450fce4633b');
    var channel = pusher.subscribe('test_channel');
    document.getElementById('log').appendChild(node);
    }

    if (Socket) {
    var timer;
    (function connect() {
    timer = setTimeout(function(){
    var ws = new Socket('ws://ws.pusherapp.com:80/app/9d1c469de450fce4633b?client=js')
    ws.onopen = function() { log('onopen'); };
    ws.onclose = function() {
    connect();
    };

    ws.onerror = function() {
    log('onerror');
    ws.close();
    connect();
    };
    ws.onmessage = function(evt) { log('onmessage: '+evt.data); };
    }, 0);
    })();
    }
    </script>
    </head>
    <body>
    <h1>Pusher</h1>
    <a href="/index.html">HOME</a>
    <div id="log">

    </div>
    </body>
  14. mloughran created this gist Oct 24, 2011.
    10 changes: 10 additions & 0 deletions app.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,10 @@
    require 'sinatra'

    get '/index.html' do
    sleep 0.1
    erb :index
    end

    get '/pusher.html' do
    erb :pusher
    end
    7 changes: 7 additions & 0 deletions views/index.erb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    <!DOCTYPE html>
    <head>
    <title>Pusher Test</title>
    <body>
    <h1>Home</h1>
    <a href="/pusher.html">Pusher</a>
    </body>
    21 changes: 21 additions & 0 deletions views/pusher.erb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,21 @@
    <!DOCTYPE html>
    <head>
    <title>Pusher Test</title>
    <script src="http://js.pusherapp.com/1.9.3/pusher.min.js" type="text/javascript"></script>
    <script type="text/javascript">
    // Enable pusher logging - don't include this in production
    Pusher.log = function(message) {
    if (window.console && window.console.log) window.console.log(message);
    };

    // Flash fallback logging - don't include this in production
    WEB_SOCKET_DEBUG = true;

    var pusher = new Pusher('9d1c469de450fce4633b');
    var channel = pusher.subscribe('test_channel');
    </script>
    </head>
    <body>
    <h1>Pusher</h1>
    <a href="/index.html">HOME</a>
    </body>