lib/spike/index.js
function (req, res, next) {

    // Check request's accepts header for event-stream. Move on if it doesn't
    // support it.
    if (!req.headers.accept || req.headers.accept.indexOf('text/event-stream') === -1) {
      return next();
    }

    // Restore or create a session for the bin
    var session = utils.sessionForBin(req.bin, true),
        key = utils.keyForBin(req.bin),
        userSession = req.session,
        checksum = req.param('checksum'),
        // cache the bin because req.bin is just a reference and gets changed when
        // the revision gets bumped.
        bin = req.bin,
        keepalive = null,
        close, closeTimer;

    if (isOwnerWithWrite(userSession, bin, checksum) && pendingToKillStreaming[key]) {
      clearTimeout(pendingToKillStreaming[key] || null);
    }

    openSpikes += 1;
    metrics.gauge('spike.open', openSpikes);

    // console.log('STREAM ACCEPTED', req.originalUrl);

    res.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache'
    });
    res.write('id: 0\n\n');

    session.res.push(res);

    if (req.keepLatest) {
      if (!sessionsFromUser[req.user.name]) {
        sessionsFromUser[req.user.name] = [];
      }
      sessionsFromUser[req.user.name].push({ key: key, res: res });
    }

    keepalive = setInterval(function () {
      if (!res.connection.writable) {
        return close();
      }
      res.write('id: 0\n\n');
    }, 15 * 1000);

    // FIXME this will break when using /rem/last/ as req.bin won't be the same bin anymore.
    var closed = false;

    close = function () {
      if (closed) {
        return;
      }
      closed = true;

      if (isOwnerWithWrite(userSession, bin, checksum)) {
        // this means the owner has disconnected from jsbin, and it's possible
        // that we can remove their write access to the bin (and thus not add
        // streaming to full previews). So we set a timer, and if this eventsource
        // isn't reopened then we assume it's closed. If this eventsource *does*
        // reopen, then we clear the timer, and they can continue to stream.
        pendingToKillStreaming[key] = setTimeout(function () {

          binModel.updateBinData(req.bin, {streaming_key: ''}, function (error) {
            if (error) {
              console.error(error);
            }
          });
        }, 24 * 60 * 1000); // leave it open for 24 hours
      }
      clearInterval(keepalive);
      clearTimeout(closeTimer);
      utils.removeConnection(utils.keyForBin(req.bin), res);
      // ping stats
      spike.ping(req.bin, {}, true);
    };

    // every 5 minutes, let's let them reconnect
    closeTimer = setTimeout(function () {
      res.end();
      req.emit('close');
    }, 15 * 60 * 1000);

    res.socket.on('close', close);
    req.on('close', close);
    res.req = req;

    // ping stats
    spike.ping(req.bin, {}, true);
  }
function (req, res, next) {

    // Check request's accepts header for event-stream. Move on if it doesn't
    // support it.
    if (!req.headers.accept || req.headers.accept.indexOf('text/event-stream') === -1) {
      return next();
    }

    // Restore or create a session for the bin
    var session = utils.sessionForBin(req.bin, true),
        key = utils.keyForBin(req.bin),
        userSession = req.session,
        checksum = req.param('checksum'),
        // cache the bin because req.bin is just a reference and gets changed when
        // the revision gets bumped.
        bin = req.bin,
        keepalive = null,
        close, closeTimer;

    if (isOwnerWithWrite(userSession, bin, checksum) && pendingToKillStreaming[key]) {
      clearTimeout(pendingToKillStreaming[key] || null);
    }

    openSpikes += 1;
    metrics.gauge('spike.open', openSpikes);

    // console.log('STREAM ACCEPTED', req.originalUrl);

    res.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache'
    });
    res.write('id: 0\n\n');

    session.res.push(res);

    if (req.keepLatest) {
      if (!sessionsFromUser[req.user.name]) {
        sessionsFromUser[req.user.name] = [];
      }
      sessionsFromUser[req.user.name].push({ key: key, res: res });
    }

    keepalive = setInterval(function () {
      if (!res.connection.writable) {
        return close();
      }
      res.write('id: 0\n\n');
    }, 15 * 1000);

    // FIXME this will break when using /rem/last/ as req.bin won't be the same bin anymore.
    var closed = false;

    close = function () {
      if (closed) {
        return;
      }
      closed = true;

      if (isOwnerWithWrite(userSession, bin, checksum)) {
        // this means the owner has disconnected from jsbin, and it's possible
        // that we can remove their write access to the bin (and thus not add
        // streaming to full previews). So we set a timer, and if this eventsource
        // isn't reopened then we assume it's closed. If this eventsource *does*
        // reopen, then we clear the timer, and they can continue to stream.
        pendingToKillStreaming[key] = setTimeout(function () {

          binModel.updateBinData(req.bin, {streaming_key: ''}, function (error) {
            if (error) {
              console.error(error);
            }
          });
        }, 24 * 60 * 1000); // leave it open for 24 hours
      }
      clearInterval(keepalive);
      clearTimeout(closeTimer);
      utils.removeConnection(utils.keyForBin(req.bin), res);
      // ping stats
      spike.ping(req.bin, {}, true);
    };

    // every 5 minutes, let's let them reconnect
    closeTimer = setTimeout(function () {
      res.end();
      req.emit('close');
    }, 15 * 60 * 1000);

    res.socket.on('close', close);
    req.on('close', close);
    res.req = req;

    // ping stats
    spike.ping(req.bin, {}, true);
  }
function ( req , res , next ) { // Check request's accepts header for event-stream. Move on if it doesn't // support it. if ( ! req . headers . accept || req . headers . accept . indexOf ( 'text/event-stream' ) === - 1 ) { return next ( ) ; } // Restore or create a session for the bin var session = utils . sessionForBin ( req . bin , true ) , key = utils . keyForBin ( req . bin ) , userSession = req . session , checksum = req . param ( 'checksum' ) , // cache the bin because req.bin is just a reference and gets changed when // the revision gets bumped. bin = req . bin , keepalive = null , close , closeTimer ; if ( isOwnerWithWrite ( userSession , bin , checksum ) && pendingToKillStreaming [ key ] ) { clearTimeout ( pendingToKillStreaming [ key ] || null ) ; } openSpikes += 1 ; metrics . gauge ( 'spike.open' , openSpikes ) ; // console.log('STREAM ACCEPTED', req.originalUrl); res . writeHead ( 200 , { 'Content-Type' : 'text/event-stream' , 'Cache-Control' : 'no-cache' } ) ; res . write ( 'id: 0\n\n' ) ; session . res . push ( res ) ; if ( req . keepLatest ) { if ( ! sessionsFromUser [ req . user . name ] ) { sessionsFromUser [ req . user . name ] = [ ] ; } sessionsFromUser [ req . user . name ] . push ( { key : key , res : res } ) ; } keepalive = setInterval ( function ( ) { if ( ! res . connection . writable ) { return close ( ) ; } res . write ( 'id: 0\n\n' ) ; } , 15 * 1000 ) ; // FIXME this will break when using /rem/last/ as req.bin won't be the same bin anymore. var closed = false ; close = function ( ) { if ( closed ) { return ; } closed = true ; if ( isOwnerWithWrite ( userSession , bin , checksum ) ) { // this means the owner has disconnected from jsbin, and it's possible // that we can remove their write access to the bin (and thus not add // streaming to full previews). So we set a timer, and if this eventsource // isn't reopened then we assume it's closed. If this eventsource *does* // reopen, then we clear the timer, and they can continue to stream. pendingToKillStreaming [ key ] = setTimeout ( function ( ) { binModel . updateBinData ( req . bin , { streaming_key : '' } , function ( error ) { if ( error ) { console . error ( error ) ; } } ) ; } , 24 * 60 * 1000 ) ; // leave it open for 24 hours } clearInterval ( keepalive ) ; clearTimeout ( closeTimer ) ; utils . removeConnection ( utils . keyForBin ( req . bin ) , res ) ; // ping stats spike . ping ( req . bin , { } , true ) ; } ; // every 5 minutes, let's let them reconnect closeTimer = setTimeout ( function ( ) { res . end ( ) ; req . emit ( 'close' ) ; } , 15 * 60 * 1000 ) ; res . socket . on ( 'close' , close ) ; req . on ( 'close' , close ) ; res . req = req ; // ping stats spike . ping ( req . bin , { } , true ) ; }
Setup an event stream for the client. This is hit on all bin endpoints.
Setup an event stream for the client . This is hit on all bin endpoints .