<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://web.dev/</id>
  <title>Luigi Montanez on web.dev</title>
  <updated>2026-04-15T23:21:06Z</updated>
  <author>
    <name>Luigi Montanez</name>
  </author>
  <link href="https://web.dev/authors/luigimontanez/feed.xml" rel="self"/>
  <link href="https://web.dev/"/>
  <icon>https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/glvD0KI05d9PTHfzRWRw.png?auto=format</icon>
  <logo>https://web.dev/images/shared/rss-banner.png</logo>
  <subtitle>Software Developer at Sunlight Labs</subtitle>
  
  
  <entry>
    <title>Case Study - Real-time Updates in Stream Congress</title>
    <link href="https://web.dev/sunlight-streamcongress/"/>
    <updated>2011-03-17T00:00:00Z</updated>
    <id>https://web.dev/sunlight-streamcongress/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;introduction&quot;&gt;Introduction &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sunlight-streamcongress/#introduction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Through &lt;a href=&quot;http://www.html5rocks.com/tutorials/websockets/basics/&quot; rel=&quot;noopener&quot;&gt;WebSockets&lt;/a&gt; and &lt;a href=&quot;http://www.html5rocks.com/tutorials/eventsource/basics/&quot; rel=&quot;noopener&quot;&gt;EventSource&lt;/a&gt;, HTML5 enables developers to build web apps that communicate in real time with a server. &lt;a href=&quot;http://streamcongress.com/&quot; rel=&quot;noopener&quot;&gt;Stream Congress&lt;/a&gt; (available in the &lt;a href=&quot;https://chrome.google.com/webstore/detail/ahebmhmbjonbglfkghfennmigkcmpbjp&quot; rel=&quot;noopener&quot;&gt;Chrome Web Store&lt;/a&gt;) provides live updates about the workings of the United States Congress. It streams floor updates from both the House and Senate, relevant news updates, tweets from members of Congress, and other social media updates. The app is meant to be left open all day as it captures the business of Congress.&lt;/p&gt;
&lt;h2 id=&quot;starting-with-websockets&quot;&gt;Starting with WebSockets &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sunlight-streamcongress/#starting-with-websockets&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The WebSockets spec has gotten quite a bit of attention for what it enables: a stable, bi-directional &lt;a href=&quot;http://www.csc.villanova.edu/~mdamian/Sockets/TcpSockets.htm&quot; rel=&quot;noopener&quot;&gt;TCP socket&lt;/a&gt; between the browser and server. There is no data format imposed on the TCP socket; the developer is free to define a messaging protocol. In practice, passing JSON objects around as strings is most convenient. The client-side JavaScript code to listen for live updates is clean and simple:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; liveSocket &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebSocket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ws://streamcongress.com:8080/live&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;liveSocket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onmessage&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;addToStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;While browser support for WebSockets is straightforward, server-side support is still in the formative stage. &lt;a href=&quot;http://socket.io/&quot; rel=&quot;noopener&quot;&gt;Socket.IO&lt;/a&gt; on Node.js provides one of the most mature and robust server-side solutions. An event-driven server like Node.js is the right fit for WebSockets. For alternative implementations, Python developers can use &lt;a href=&quot;http://twistedmatrix.com/trac/&quot; rel=&quot;noopener&quot;&gt;Twisted&lt;/a&gt; and &lt;a href=&quot;http://www.tornadoweb.org/&quot; rel=&quot;noopener&quot;&gt;Tornado&lt;/a&gt;, while Ruby developers have &lt;a href=&quot;http://rubyeventmachine.com/&quot; rel=&quot;noopener&quot;&gt;EventMachine&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;introducing-cramp&quot;&gt;Introducing Cramp &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sunlight-streamcongress/#introducing-cramp&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/lifo/cramp&quot; rel=&quot;noopener&quot;&gt;Cramp&lt;/a&gt; is an asynchronous Ruby web framework that runs on top of EventMachine. It&#39;s written by &lt;a href=&quot;http://m.onkey.org/&quot; rel=&quot;noopener&quot;&gt;Pratik Naik&lt;/a&gt;, a member of the Ruby on Rails core team. Providing a domain specific language (DSL) for real-time web apps, Cramp is an ideal choice for Ruby web developers. Those familiar with writing controllers in Ruby on Rails will recognize Cramp&#39;s style:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;rubygems&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bundler&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Bundler&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;cramp&#39;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;http_router&#39;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;active_support/json&#39;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;thin&#39;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Cramp&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;Websocket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;backend &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:thin&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LiveSocket&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; Cramp&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;Websocket&lt;br /&gt;periodic_timer &lt;span class=&quot;token symbol&quot;&gt;:check_activities&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:every&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;check_activities&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token variable&quot;&gt;@latest_activity&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;nil&lt;/span&gt;&lt;br /&gt;    new_activities &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; find_activities_since&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@latest_activity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token variable&quot;&gt;@latest_activity&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; new_activities&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first &lt;span class=&quot;token keyword&quot;&gt;unless&lt;/span&gt; new_activities&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;empty&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;br /&gt;    render new_activities&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;to_json&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;routes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br /&gt;add&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/live&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;to&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;LiveSocket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br /&gt;run routes&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Because Cramp sits on top of the non-blocking EventMachine, there are several considerations to keep in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Non-blocking database drivers must be used, like &lt;a href=&quot;https://github.com/oldmoe/mysqlplus&quot; rel=&quot;noopener&quot;&gt;MySQLPlus&lt;/a&gt; and &lt;a href=&quot;https://github.com/bcg/em-mongo&quot; rel=&quot;noopener&quot;&gt;em-mongo&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Event-driven web servers must be used. Support is built in for &lt;a href=&quot;http://code.macournoyer.com/thin/&quot; rel=&quot;noopener&quot;&gt;Thin&lt;/a&gt; and &lt;a href=&quot;http://rainbows.rubyforge.org/&quot; rel=&quot;noopener&quot;&gt;Rainbows&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The Cramp app must be run separately from the main Rails app that powers Stream Congress, restarted and monitored independently.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;current-limitations&quot;&gt;Current Limitations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sunlight-streamcongress/#current-limitations&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;WebSockets suffered a setback on December 8, 2010 when a security vulnerability was publicized. Both &lt;a href=&quot;http://hacks.mozilla.org/2010/12/websockets-disabled-in-firefox-4/&quot; rel=&quot;noopener&quot;&gt;Firefox and Opera&lt;/a&gt; removed browser support for WebSockets. While no pure JavaScript polyfill exists, there is a &lt;a href=&quot;https://github.com/gimite/web-socket-js/&quot; rel=&quot;noopener&quot;&gt;Flash fallback&lt;/a&gt; that has been widely adopted. However, relying on Flash is far from ideal. Even though Chrome and Safari continue to support WebSockets, it became clear that to support all modern browsers without relying on Flash, WebSockets would need to be replaced.&lt;/p&gt;
&lt;h2 id=&quot;rolling-back-to-ajax-polling&quot;&gt;Rolling back to AJAX polling &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sunlight-streamcongress/#rolling-back-to-ajax-polling&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The decision was made to move away from WebSockets and back to &amp;quot;old-school&amp;quot; AJAX polling. While much less efficient from a disk and network I/O perspective, AJAX polling simplified the technical implementation of Stream Congress. Most significantly, the need for a separate Cramp app was eliminated. The AJAX endpoint was instead provided by the Rails app. The client-side code was modified to support jQuery AJAX polling:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;fillStream&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;mostRecentActivity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  $&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getJSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requestURL&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;addToStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token function&quot;&gt;fillStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;recentActivities&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token constant&quot;&gt;AJAX&lt;/span&gt; polling&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; though&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; is not without its downsides&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; Relying on the &lt;span class=&quot;token constant&quot;&gt;HTTP&lt;/span&gt; request&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;response cycle means that the server sees constant load even when there aren&lt;span class=&quot;token string&quot;&gt;&#39;t any new updates. And of course, AJAX polling doesn&#39;&lt;/span&gt;t take advantage &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; what &lt;span class=&quot;token constant&quot;&gt;HTML5&lt;/span&gt; has to offer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;## EventSource&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; The right tool &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; the job&lt;br /&gt;&lt;br /&gt;Up to &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt; point&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a key factor was ignored about the nature &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; Stream Congress&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; the app only needs to stream updates one way&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; from server to client &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; downstream&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; It didn&#39;t need to be real&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;time&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; upstream client&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;to&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;server communication&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;In &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt; sense&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; WebSockets is overkill &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; Stream Congress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; Server&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;to&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;client communication is so common that it&lt;span class=&quot;token string&quot;&gt;&#39;s been given a general term: push. In fact, many existing solutions for WebSockets, from the hosted [PusherApp](http://pusherapp.com) to the Rails library [Socky](https://github.com/socky), optimize for push and don&#39;&lt;/span&gt;t support client&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;to&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;server communication at all&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Enter EventSource&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; also called Server&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Sent Events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; The specification compares favorably to WebSockets &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; the context to server to client push&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;A&lt;/span&gt; similar&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; simple JavaScript &lt;span class=&quot;token constant&quot;&gt;API&lt;/span&gt; on the browser side&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; The open connection is &lt;span class=&quot;token constant&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;based&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; not dropping to the low level &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;TCP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Automatic reconnection when the connection is closed&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;### Going Back to Cramp&lt;br /&gt;&lt;br /&gt;In recent months&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Cramp has added support &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; EventSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; The code is very similar to the WebSockets implementation&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;`ruby&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LiveEvents&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; Cramp&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;Action&lt;br /&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;transport &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;sse&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;periodic_timer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;latest&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;every&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;def latest&lt;br /&gt;@latest_activity &lt;span class=&quot;token operator&quot;&gt;||=&lt;/span&gt; nil&lt;br /&gt;new_activities &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;find_activities_since&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;@latest_activity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;@latest_activity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; new_activities&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first unless new_activities&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;empty&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;br /&gt;render new_activities&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;to_json&lt;br /&gt;end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;routes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; HttpRouter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;new &lt;span class=&quot;token class-name&quot;&gt;do&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;LiveEvents&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;end&lt;br /&gt;run routes&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;A significant issue to keep in mind with EventSource is that cross-domain connections are not allowed. This means that the Cramp app must be served from the same streamcongress.com domain as the main Rails app. This can be accomplished with proxying at the web server. Assuming the Cramp app is powered by Thin and running on port 8000, the Apache configuration looks like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-apacheconf&quot;&gt;&lt;code class=&quot;language-apacheconf&quot;&gt;&lt;span class=&quot;token directive-inline property&quot;&gt;LoadModule&lt;/span&gt;  proxy_module             /usr/lib/apache2/modules/mod_proxy.so&lt;br /&gt;&lt;span class=&quot;token directive-inline property&quot;&gt;LoadModule&lt;/span&gt;  proxy_http_module        /usr/lib/apache2/modules/mod_proxy_http.so&lt;br /&gt;&lt;span class=&quot;token directive-inline property&quot;&gt;LoadModule&lt;/span&gt;  proxy_balancer_module    /usr/lib/apache2/modules/mod_proxy_balancer.so&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;VirtualHost&lt;/span&gt;&lt;span class=&quot;token directive-block-parameter attr-value&quot;&gt; *&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token directive-inline property&quot;&gt;ServerName&lt;/span&gt; streamcongress.com&lt;br /&gt;  &lt;span class=&quot;token directive-inline property&quot;&gt;DocumentRoot&lt;/span&gt; /projects/streamcongress/www/current/public&lt;br /&gt;  RailsEnv production&lt;br /&gt;  RackEnv production&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Directory&lt;/span&gt;&lt;span class=&quot;token directive-block-parameter attr-value&quot;&gt; /projects/streamcongress/www/current/public&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token directive-inline property&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;token directive-inline property&quot;&gt;allow&lt;/span&gt;,deny&lt;br /&gt;    &lt;span class=&quot;token directive-inline property&quot;&gt;Allow&lt;/span&gt; from all&lt;br /&gt;    &lt;span class=&quot;token directive-inline property&quot;&gt;Options&lt;/span&gt; -MultiViews&lt;br /&gt;  &lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Proxy&lt;/span&gt;&lt;span class=&quot;token directive-block-parameter attr-value&quot;&gt; balancer&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//thin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token directive-inline property&quot;&gt;BalancerMember&lt;/span&gt; http://localhost:8000&lt;br /&gt;  &lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Proxy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token directive-inline property&quot;&gt;ProxyPass&lt;/span&gt; /live balancer://thin/&lt;br /&gt;  &lt;span class=&quot;token directive-inline property&quot;&gt;ProxyPassReverse&lt;/span&gt; /live balancer://thin/&lt;br /&gt;  &lt;span class=&quot;token directive-inline property&quot;&gt;ProxyPreserveHost&lt;/span&gt; on&lt;br /&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;VirtualHost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This configuration sets an EventSource endpoint at &lt;code&gt;streamcongress.com/live&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;stable-polyfill&quot;&gt;Stable Polyfill &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sunlight-streamcongress/#stable-polyfill&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One of the most significant advantages of EventSource over WebSockets is that fallback is completely JavaScript-based, with no dependence on Flash. Remy Sharp&#39;s &lt;a href=&quot;https://github.com/remy/polyfills&quot; rel=&quot;noopener&quot;&gt;polyfill&lt;/a&gt; accomplishes this by implementing long-polling in browsers that don&#39;t support EventSource natively. Thus, EventSource works today on all modern browsers with JavaScript enabled.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sunlight-streamcongress/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;HTML5 opens the door to many new and exciting web development possibilities. With WebSockets and EventSource, web developers now have clean, well-defined standards to enable real-time web apps. But not all users run modern browsers. Graceful degradation must be considered when choosing to implement these technologies. And tooling on the server side for WebSockets and EventSource is still in the early stages. It&#39;s important to keep these factors in mind when developing real-time HTML5 apps.&lt;/p&gt;
</content>
    <author>
      <name>Luigi Montanez</name>
    </author>
  </entry>
</feed>
