<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>IONCANNON</title>
	
	<link>http://www.ioncannon.net</link>
	<description>Thoughts on Software Development and Engineering</description>
	<lastBuildDate>Tue, 08 May 2012 15:20:59 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
	
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/ioncannon" /><feedburner:info uri="ioncannon" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://www.ioncannon.net/?pushpress=hub" /><item>
		<title>Embed Ruby in Your iOS Apps Using mruby</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/2ELV_m45Mu0/</link>
		<comments>http://www.ioncannon.net/programming/1524/embed-ruby-in-your-ios-apps-using-mruby/#comments</comments>
		<pubDate>Tue, 08 May 2012 15:20:59 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[embed]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[mruby]]></category>
		<category><![CDATA[objective-c]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=1524</guid>
		<description><![CDATA[I&#039;ve been playing with mruby for the past week or so. If you haven&#039;t seen it yet it is an embeddable version of Ruby. The first thing I wonder about when I heard about mruby last year a RubyConf was embedding it in iOS apps. Now that the initial version has been released I figured [...]]]></description>
			<content:encoded><![CDATA[<p>I&#039;ve been playing with <a href="https://github.com/mruby/mruby">mruby</a> for the past week or so. If you haven&#039;t seen it yet it is an embeddable version of Ruby. The first thing I wonder about when I heard about mruby last year a RubyConf was embedding it in iOS apps. Now that the initial version has been released I figured I would give it a try.</p>
<p>There are a few things to take into account before diving into this. The first is that the mruby project is very new and there are a number of gaps in the language support right now but the goal is to support the ISO definition of Ruby at some point. The second thing to know is that I&#039;m talking about embedding Ruby here and not writing iOS apps using Ruby. I&#039;m more interested in the potential of Ruby as a scripting language for something like a game. If you want to look into writing iOS apps using Ruby check out <a href="http://www.rubymotion.com/">RubyMotion</a> or the <a href="http://mobiruby.org/">MobiRuby</a> project (MobiRuby is based on mruby).</p>
<p><span id="more-1524"></span></p>
<p>Step one on the path to embedding mruby in an iOS app is getting the library compiled as a framework for XCode. Although the earliest version of mruby had support for building an iOS version that support was removed and there seems to be no current push to add it back. Because of that I created a simple <a href="https://github.com/carsonmcdonald/ios-ruby-embedded">iOS mruby build setup</a> on github. The instructions for using it are included with the project. Once the framework has been created it is easy enough to include in your XCode project just like any other external framework using the &#034;Add Other&#034; option in the &#034;Build Phases&#034; -> &#034;Link Binary With Libraries&#034; section.</p>
<p>There are two ways of using mruby, you can run a script as source or you can pre-compile the source into bytecode first. I&#039;m going to focus on the pre-compiled way because I believe it is the less likely of the two to get an app rejected. This requires that you have the mruby tools available. If you use the build setup I created you can find the tools in the bin directory.</p>
<p>And finally I have created a simple <a href="https://github.com/carsonmcdonald/MRubyiOSExample">mruby iOS example</a> XCode project that you can use to get started. It includes a pre-compiled version of the framework created with the build setup mentioned before and examples of calling objective-c from Ruby and Ruby from objective-c. The following is a quick overview of the example:</p>
<ul>
<li><b>FooUtil.h/m</b> &#8211; This is where all the interaction with mruby happens. It coordinates different parts of the examples as well.</li>
<li><b>example.rb</b> &#8211; This is the Ruby code for the example and has to be compiled into example.mrb before changes will take place.</li>
<li><b>example.mrb</b> &#8211; This is the compiled version of example.rb.</li>
<li><b>FooData.h/m</b> &#8211; This is an example data class that is wrapped using a Ruby class named FooData.</li>
</ul>
<p>Everything is driven from the FooUtil singleton to make it easier to find each example. There are five methods to understand in FooUtil:</p>
<ul>
<li><b>setDebugBlock</b> &#8211; This static method takes a block that will be passed a debug string. This gives an easy way of changing the output of the example.</li>
<li><b>loadFromBundle</b> &#8211; This is an example of loading a pre-compiled script. It assumes it can find the given filename in the app bundle.</li>
<li><b>execute</b> &#8211; This is an example of executing a loaded script. It assumes loadFromBundle has been called. Calling this method will cause the Ruby to objective-c examples to execute.</li>
<li><b>updateBarLocation</b> &#8211; This is an example of executing Ruby code from objective-c.</li>
<li><b>cleanup</b> &#8211; This method will force a garbage collection. It was added to demonstrate the release of the wrapped FooData class.</li>
</ul>
<p>The example code doesn&#039;t as much error checking as you should have in a real app but it hopefully is enough to get you started. I plan on adding more examples to the github project as features get added to mruby and as I have time. As a reminder, you have to recompile the Ruby code if you make changes or they won&#039;t be seen.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=2ELV_m45Mu0:KSU7o5vZZn0:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=2ELV_m45Mu0:KSU7o5vZZn0:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=2ELV_m45Mu0:KSU7o5vZZn0:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=2ELV_m45Mu0:KSU7o5vZZn0:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=2ELV_m45Mu0:KSU7o5vZZn0:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=2ELV_m45Mu0:KSU7o5vZZn0:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/2ELV_m45Mu0" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/1524/embed-ruby-in-your-ios-apps-using-mruby/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/programming/1524/embed-ruby-in-your-ios-apps-using-mruby/</feedburner:origLink></item>
		<item>
		<title>Segmenting WebM Video and the MediaSource API</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/movdlpt6HeM/</link>
		<comments>http://www.ioncannon.net/utilities/1515/segmenting-webm-video-and-the-mediasource-api/#comments</comments>
		<pubDate>Tue, 03 Jan 2012 13:59:08 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[utilities]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=1515</guid>
		<description><![CDATA[For a while now I&#039;ve seen people ask when support for Apple&#039;s Pantos HTTP live streaming would make it past Safari and iOS. The answer seems to have been that it wasn&#039;t clear that Pantos streaming was the best option and something else would come about eventually that would be more flexible. There have been [...]]]></description>
			<content:encoded><![CDATA[<p>For a while now I&#039;ve seen people ask when support for Apple&#039;s <a href="http://tools.ietf.org/html/draft-pantos-http-live-streaming-07">Pantos HTTP live streaming</a> would make it past Safari and iOS. The answer seems to have been that it wasn&#039;t clear that Pantos streaming was the best option and something else would come about eventually that would be more flexible. There have been other options but they involve either Flash or Silverlight and most people want something that works with html5 video. After a long wait it seems like the time is getting close now with the <a href="http://html5-mediasource-api.googlecode.com/svn/trunk/draft-spec/mediasource-draft-spec.html">MediaSource API</a>.</p>
<p>The MediaSource API has experimental support in Chrome and can be enabled by using the chrome://flags option. To see it in action you can go to the <a href="http://d28nuaxr58rcpu.cloudfront.net/MediaSourceAPIDemo/demo.html">MediaSource demo page</a>. You can also read a litte more about it <a href="http://updates.html5rocks.com/2011/11/Stream-video-using-the-MediaSource-API">here</a> although the spec linked to above is probably a better place to learn about it.</p>
<p>A while ago I created a tool for <a href="http://www.ioncannon.net/projects/http-live-video-stream-segmenter-and-distributor/">segmenting H264 video</a> in a Pantos compliant way. When I saw the MediaSource API I wondered how the same type of tool might fit in. The first thing to note is that the Pantos draft describes a complete technique for video streaming while the MediaSource API gives you the tools to stream video and leaves the technique. What follows is a simple technique for segmenting a WebM video in a way that allows standard streaming with the MediaSource API in the same fasion as the Pantos draft technique. While this example will not support variable rate streams it could be expanded to do so and would be the next logical step.</p>
<p><span id="more-1515"></span></p>
<p>The basics of how the Pantos technique works are simple. A video is broken down into chunks and each of those chunks is downloaded, buffered and played. In older drafts this ment splitting a video file into multiple files but in later drafts support was added for range requests allowing the large file to remain intact. With that understanding there are two major parts that need to be addressed:</p>
<ul>
<li>Segmenting a WebM video &#8211; This will require some understanding of the container format for WebM and the result will be something like a playlist for the video.</li>
<li>Javascript to play the video &#8211; This will use the playlist created by segmenting the WebM video to drive the MediaSource API.</li>
</ul>
<h2>Segmenting a WebM Video</h2>
<p>Before reading on take a second to read the <a href="http://www.webmproject.org/about/">about WebM</a> page. It describes that WebM specifies a video/audio codec and a container format. We are only interested in the format of the container. If you want all the details you can read the <a href="http://www.webmproject.org/code/specs/container/">WebM container</a> format specs. It is enough to know that the WebM container is a modified version of the <a href="http://matroska.org/">Matroska Container</a> format and both containers use <a href="http://ebml.sourceforge.net/specs/">EBML</a> to specify their structure.</p>
<p>EBML is basically a binary XML format. There are a number of tools available for readin EBML and for my perposes I found the Python <a href="https://github.com/jspiros/python-ebml">python-ebml</a> implementation to be the easiest to get working.</p>
<p>The MediaSource API wants to be fed chunks of video data in a specific way. First it wants header information and then it wants what are called <a href="http://www.webmproject.org/code/specs/container/#cluster">clusters</a> from the WebM container. Each cluster contains various information but the most likely reason for wanting a cluster as input is that each cluster starts with a keyframe. A keyframe is an important reference point in the video stream and one that gets built on until the next keyframe.</p>
<p>The following code will take as input a WebM file and output a playlist for that file in JSON format. The playlist will contain the offsets of each cluster in the given WebM video and we will use it later to fetch the header and each cluster as chunks and feed those to a video element:</p>
<pre class="brush: python; title: ; notranslate">
from ebml.schema import EBMLDocument, UnknownElement, CONTAINER, BINARY

def fill_video_info(element, offset, video_info):
  if element.name == 'Duration':
    video_info['duration'] = element.value

  if element.name == 'DisplayWidth':
    video_info['width'] = element.value

  if element.name == 'DisplayHeight':
    video_info['height'] = element.value

  if element.name == 'Cluster':
    video_info['clusters'].append({'offset': offset})

  if element.name == 'Timecode':
    video_info['clusters'][-1]['timecode'] = element.value

  if element.type == CONTAINER:
    for sub_el in element.value:
      fill_video_info(sub_el, offset + element.head_size, video_info)
      offset += sub_el.size

if __name__ == '__main__':
  import sys
  import json
  import os

  mod_name, _, cls_name = 'ebml.schema.matroska.MatroskaDocument'.rpartition('.')
  try:
    doc_mod = __import__(mod_name, fromlist=[cls_name])
    doc_cls = getattr(doc_mod, cls_name)
  except ImportError:
    parser.error('unable to import module %s' % mod_name)
  except AttributeError:
    parser.error('unable to import class %s from %s' % (cls_name, mod_name))

  video_info = {}
  video_info['filename'] = sys.argv[1]
  video_info['total_size'] = os.stat(sys.argv[1]).st_size
  video_info['clusters'] = []

  with open(sys.argv[1], 'rb') as stream:
    doc = doc_cls(stream)
    offset = 0
    for el in doc.roots:
      fill_video_info(el, offset, video_info)
      offset += el.size

  print json.dumps(video_info)
</pre>
<p>To run the script you will want to save the above script and install the module then do something like this:</p>
<pre class="brush: bash; title: ; notranslate">
git clone https://github.com/jspiros/python-ebml.git
PYTHONPATH=python-ebml python create_playlist.py test.webm
</pre>
<p>Running the script will result in JSON output that can be piped to a file and used as a playlist.</p>
<h2>Javascript to Play the WebM Segmented Playlist</h2>
<p>Feeding data to the MediaSource API is done with Javascript. The first thing to do is read in the JSON playlist file created by the Python script when the page is finished loading. To do that I&#039;m using the Snack JS framework, it is micro-framework that has some of the features of JQuery. After reading in the playlist chunks of the WebM file need to be read in with range requests.</p>
<p>A while ago I made created a post about <a href="http://www.ioncannon.net/programming/1506/range-requests-with-ajax/">range requests with ajax</a> and that comes in handy here. The main difference between that post and what we need here is that the MediaSource API expects a typed array as input so we need to use <a href="http://www.w3.org/TR/XMLHttpRequest2/">XHR2</a>.</p>
<p>The following Javascript code is bare bones. One of the most obvious limitations is that it doesn&#039;t have the ability to seek to a portion of the stream, it will only play the stream start to finish or if the player has the content buffered it should allow for seeking in the buffered portion. It demonstrates that the API works and should be a good starting point for experimenting more with multi-rate streaming which is my short term goal.</p>
<pre class="brush: jscript; title: ; notranslate">
  /**
   * This fetches a chunk of video using a range request.
   *   video_name - The relative filename of the video to fetch
   *   start_bytes - The start byte of the chunk to fetch
   *   end_bytes - The last byte of the chunk to fetch
   *   is_last - True if this is the last chunk to fetch
   */
  function fetch_chunk(video, video_name, start_bytes, end_bytes, is_last)
  {
    var range_req = 'bytes=' + start_bytes + '-' + end_bytes;

    var xhr = new XMLHttpRequest();
    xhr.open('GET', video_name, true);
    xhr.setRequestHeader(&quot;Range&quot;, range_req);
    xhr.responseType = 'arraybuffer';

    xhr.onload = function(e)
    {
      video.webkitSourceAppend(new Uint8Array(this.response));

      if(is_last)
      {
        video.webkitSourceEndOfStream(HTMLMediaElement.EOS_NO_ERROR);
      }
    };

    xhr.send();
  }

  /**
   * Fetch the header portion of the video file.
   */
  function fetch_header(video, video_info)
  {
    fetch_chunk(video, video_info.filename, 0, video_info.clusters[0].offset-1, video_info.clusters.length == 1);
  }

  var current_cluster = 0;

  /**
   * Fetch the next cluster of the video file.
   */
  function fetch_next_cluster(video, video_info)
  {
    if(video_info.clusters.length == current_cluster+1)
    {
      fetch_chunk(video, video_info.filename, video_info.clusters[current_cluster].offset, video_info.total_size, true);
    }
    else
    {
      fetch_chunk(video, video_info.filename, video_info.clusters[current_cluster].offset, video_info.clusters[current_cluster+1].offset-1, false);
    }

    current_cluster++;
  }

  /**
   * Enable the video for MediaSource and attach event listeners to drive the fetching of video chunks.
   */
  function video_setup(video_info)
  {
    current_cluster = 0;

    video.src = video.webkitMediaSourceURL;

    video.addEventListener('webkitsourceopen', function(e)
    {
      var video = this;

      fetch_header(video, video_info);
      fetch_next_cluster(video, video_info);
    }, false);

    video.addEventListener('progress', function(e)
    {
      var video = this;

      if( video.webkitSourceState != HTMLMediaElement.SOURCE_ENDED )
      {
        fetch_next_cluster(video, video_info);
      }
    });
  }

  /**
   * When the page is done loading read in the JSON playlist and get the video set up.
   */
  snack.ready(function()
  {
    if (!video.webkitMediaSourceURL)
    {
      alert('webkitMediaSourceURL is not available');
      return;
    }

    snack.request({ method: 'get', url: 'test.json', }, function (err, res)
    {
      if (err)
      {
        alert(err);
        return;
      }

      video_setup(snack.parseJSON(res));
    });
  });
</pre>
<p>Assuming you have enabled support in Chrome (currently the only browser to support the API) you can see everything in action by checking out my <a href="http://d28nuaxr58rcpu.cloudfront.net/MediaSourceAPIDemo/demo.html">MediaSource API demo</a> page.</p>
<p>I have glossed over a lot of details but the main take aways are that the MediaSource API gives you the interface to build a Pantos streaming system, segment WebM on clusters to feed to the MediaSource API and use XHR2 to fetch the WebM data with range requests. As one last point a lot of the best practices for encoding video for Pantos streaming will work here as well.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=movdlpt6HeM:n4QaFxIVj-Y:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=movdlpt6HeM:n4QaFxIVj-Y:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=movdlpt6HeM:n4QaFxIVj-Y:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=movdlpt6HeM:n4QaFxIVj-Y:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=movdlpt6HeM:n4QaFxIVj-Y:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=movdlpt6HeM:n4QaFxIVj-Y:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/movdlpt6HeM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/utilities/1515/segmenting-webm-video-and-the-mediasource-api/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/utilities/1515/segmenting-webm-video-and-the-mediasource-api/</feedburner:origLink></item>
		<item>
		<title>Range Requests with Ajax</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/DGgzNLNfrU0/</link>
		<comments>http://www.ioncannon.net/programming/1506/range-requests-with-ajax/#comments</comments>
		<pubDate>Tue, 22 Nov 2011 16:03:58 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=1506</guid>
		<description><![CDATA[I ran across something the other day that made wonder about doing range requests using ajax. For some reason it wasn&#039;t obvious at first if this would be easy but as it turns out it is. If you aren&#039;t familiar with range requests head over to the HTTP RFC and check out the range header. [...]]]></description>
			<content:encoded><![CDATA[<p>I ran across something the other day that made wonder about doing range requests using ajax. For some reason it wasn&#039;t obvious at first if this would be easy but as it turns out it is.</p>
<p>If you aren&#039;t familiar with range requests head over to the HTTP RFC and check out the <a href="http://tools.ietf.org/html/rfc2616#section-14.35.2">range header</a>. Your web server needs to support range requests for this to be useful but most do so that shouldn&#039;t be a huge issue. As a bonus you will find that some CDNs support range request as well (Amazon S3 for example).</p>
<p><span id="more-1506"></span></p>
<p>To show how this works I&#039;ll start off by getting the offsets of a text file, then use curl to show the actual request and then put it into an ajax request. Even though I&#039;m using a text file the same thing could be done with a binary file as well. Here are the contents of the file I&#039;ve called range-test.txt:</p>
<pre class="brush: plain; title: ; notranslate">
First part of the file.

More text here.

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

Some text after Lorem Ipsum.

End of the file.
</pre>
<p>Using egrep we can get the offset of each line pretty easy like so:</p>
<pre class="brush: plain; title: ; notranslate">
&gt; egrep -b &quot;\r|\n&quot; range-test.txt
0:First part of the file.
25:More text here.
42:Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
618:Some text after Lorem Ipsum.
660:End of the file.
</pre>
<p>Quickly verify that the range looks right in isolation using hexdump:</p>
<pre class="brush: plain; title: ; notranslate">
&gt; hexdump -c -s 618 -n 30 range-test.txt
000026a   S   o   m   e       t   e   x   t       a   f   t   e   r
000027a   L   o   r   e   m       I   p   s   u   m   .  \n  \n
0000288
</pre>
<p>Now we have a sample range so put the text file on your web server and use curl to send the range request headers (note that the hexdump takes an offset and count of bytes while the range header takes an start and end offset):</p>
<pre class="brush: plain; title: ; notranslate">
&gt; curl -H 'Range:bytes=618-647' http://localhost/range-test.txt
Some text after Lorem Ipsum.
</pre>
<p>Now that it makes sense how this range is used to make a request for part of a file we can move on to doing it with an ajax request. I&#039;m going to use <a href="http://jquery.com/">jQuery</a>&#039;s <a href="http://api.jquery.com/jQuery.ajax/">ajax</a> function to make this really simple. It has a feature that lets you set headers really easily. The following is all you need:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;html&gt;
  &lt;head&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;jquery-1.7.min.js&quot;&gt;&lt;/script&gt;
    &lt;script type=&quot;text/javascript&quot;&gt;
    $(function() {
      $.ajax({
        url: 'range-test.txt',
        headers: {Range: &quot;bytes=618-647&quot;},
        success: function( data ) { $('#results').html( data ); }
      });
    });
    &lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div id=&quot;results&quot;&gt;&lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>And that is all there is to it. This could be useful for all kinds of things like displaying just a section of a large log file at a time. This might also be useful in a situation where you want to stream binary data back to the browser a chunk at a time.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=DGgzNLNfrU0:OzLB4UJV3Sw:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=DGgzNLNfrU0:OzLB4UJV3Sw:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=DGgzNLNfrU0:OzLB4UJV3Sw:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=DGgzNLNfrU0:OzLB4UJV3Sw:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=DGgzNLNfrU0:OzLB4UJV3Sw:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=DGgzNLNfrU0:OzLB4UJV3Sw:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/DGgzNLNfrU0" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/1506/range-requests-with-ajax/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/programming/1506/range-requests-with-ajax/</feedburner:origLink></item>
		<item>
		<title>Using WebP to Reduce Native iOS App Size</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/j1uRits2cPk/</link>
		<comments>http://www.ioncannon.net/programming/1483/using-webp-to-reduce-native-ios-app-size/#comments</comments>
		<pubDate>Wed, 29 Jun 2011 12:23:40 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[WebP]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=1483</guid>
		<description><![CDATA[Last year Google released WebM as an alternative to h264 encoded video. They followed that up with the release of WebP as an alternative to JPG. Ever since the release I&#039;ve been thinking about giving it a try on iOS to see how well it might work to reduce application size. As a bonus to [...]]]></description>
			<content:encoded><![CDATA[<p>Last year Google released <a href="http://www.webmproject.org/">WebM</a> as an alternative to h264 encoded video. They followed that up with the release of <a href="http://code.google.com/speed/webp/">WebP</a> as an alternative to JPG. Ever since the release I&#039;ve been thinking about giving it a try on iOS to see how well it might work to reduce application size. As a bonus to reduced size, WebP also supports an alpha channel that JPG doesn&#039;t (there is more information available on the original <a href="http://blog.chromium.org/2010/09/webp-new-image-format-for-web.html">release</a> blog post).</p>
<p><span id="more-1483"></span></p>
<p>To follow along you will need to grab the source for WebP and compile it. The easiest way to do that is to follow the official <a href="http://code.google.com/speed/webp/docs/compiling.html">compiling WebP</a> instructions. After compiling the source you will be able to encode WebP images, later I describe how to build the same source for use with iOS. </p>
<p>Before trying to build libwebp for iOS I did some testing to see just how much of a reduction I could achieve over JPG. I used ImageMagick to convert the source images into JPG and pushed the quality of the JPG down as far as seemed reasonable to reduce the resulting size. I tried a number of combinations but ended up using the following command to convert the JPGs:</p>
<pre class="brush: bash; title: ; notranslate">
convert -quality 75 -resize 1024x680 inputfile.png outputfile.jpg
</pre>
<p>Here are a couple samples produced using that command:</p>
<p><a href="http://www.ioncannon.net/wp-content/uploads/2011/06/test2.jpg"><img src="http://www.ioncannon.net/wp-content/uploads/2011/06/test2-300x199.jpg" alt="Egg Carton" title="Egg Carton" width="300" height="199" class="alignnone size-medium wp-image-1485" /></a></p>
<p><a href="http://www.ioncannon.net/wp-content/uploads/2011/06/test9.jpg"><img src="http://www.ioncannon.net/wp-content/uploads/2011/06/test9-300x199.jpg" alt="Elephants" title="Elephants" width="300" height="199" class="alignnone size-medium wp-image-1485" /></a></p>
<p>For WebP I made the quality 90 and encoded the same images with the following command:</p>
<pre class="brush: bash; title: ; notranslate">
cwebp -q 90 -resize 1024 680 inputfile.png -o outputfile.webp
</pre>
<p>Here are a couple example images produced using that command (at the time of this post these will only be visible using Chrome):</p>
<p><a href="http://www.ioncannon.net/wp-content/uploads/2011/06/test2.webp"><img src="http://www.ioncannon.net/wp-content/uploads/2011/06/test2-300x199.webp" alt="Egg Carton" title="Egg Carton" width="300" height="199" class="alignnone size-medium wp-image-1485" /></a></p>
<p><a href="http://www.ioncannon.net/wp-content/uploads/2011/06/test9.webp"><img src="http://www.ioncannon.net/wp-content/uploads/2011/06/test9-300x199.webp" alt="Elephants" title="Elephants" width="300" height="199" class="alignnone size-medium wp-image-1485" /></a></p>
<p>Even with the advantage I was giving the JPG images by compressing them with a lower quality I was able to get a 25% size reduction in the WebP version. Generally the JPG images would have been usable but some ended up with artifacts that would have required them to be re-encoded at a higher quality. The WebP versions ended up looking better in every case.</p>
<p>This was just my quick attempt to verify that it would be worth the effort of using WebP. A more in depth overview of JPG vs WebP can be found in the <a href="http://code.google.com/speed/webp/gallery.html">example gallery</a>.</p>
<p>With that simple test out of the way I created a script that would compile the libwebp source into a framework compatible with the iOS simulators, iPad and iPhone:</p>
<pre class="brush: bash; title: ; notranslate">
SDK=4.3
PLATFORMS=&quot;iPhoneSimulator iPhoneOS-V6 iPhoneOS-V7&quot;
DEVELOPER=/Developer
TOPDIR=`pwd`
BUILDDIR=&quot;$TOPDIR/tmp&quot;
FINALDIR=&quot;$TOPDIR/WebP.framework&quot;
LIBLIST=''

mkdir -p $BUILDDIR
mkdir -p $FINALDIR
mkdir $FINALDIR/Headers/

for PLATFORM in ${PLATFORMS}
do
  if [ &quot;${PLATFORM}&quot; == &quot;iPhoneOS-V7&quot; ]
  then
    PLATFORM=&quot;iPhoneOS&quot;
    ARCH=&quot;armv7&quot;
  elif [ &quot;${PLATFORM}&quot; == &quot;iPhoneOS-V6&quot; ]
  then
    PLATFORM=&quot;iPhoneOS&quot;
    ARCH=&quot;armv6&quot;
  else
    ARCH=&quot;i386&quot;
  fi

  ROOTDIR=&quot;${BUILDDIR}/${PLATFORM}-${SDK}-${ARCH}&quot;
  rm -rf &quot;${ROOTDIR}&quot;
  mkdir -p &quot;${ROOTDIR}&quot;

  export DEVROOT=&quot;${DEVELOPER}/Platforms/${PLATFORM}.platform/Developer&quot;
  export SDKROOT=&quot;${DEVROOT}/SDKs/${PLATFORM}${SDK}.sdk&quot;
  export CC=${DEVROOT}/usr/bin/gcc
  export LD=${DEVROOT}/usr/bin/ld
  export CPP=${DEVROOT}/usr/bin/cpp
  export CXX=${DEVROOT}/usr/bin/g++
  export AR=${DEVROOT}/usr/bin/ar
  export AS=${DEVROOT}/usr/bin/as
  export NM=${DEVROOT}/usr/bin/nm
  export CXXCPP=$DEVROOT/usr/bin/cpp
  export RANLIB=$DEVROOT/usr/bin/ranlib
  export LDFLAGS=&quot;-arch ${ARCH} -pipe -no-cpp-precomp -isysroot ${SDKROOT} -L${ROOTDIR}/lib&quot;
  export CFLAGS=&quot;-arch ${ARCH} -pipe -no-cpp-precomp -isysroot ${SDKROOT} -I${ROOTDIR}/include&quot;
  export CXXFLAGS=&quot;-arch ${ARCH} -pipe -no-cpp-precomp -isysroot ${SDKROOT} -I${ROOTDIR}/include&quot;

  rm -rf libwebp-0.1.2
  tar xzf libwebp-0.1.2.tar.gz
  cd libwebp-0.1.2

  sh autogen.sh

  ./configure --host=${ARCH}-apple-darwin --prefix=${ROOTDIR} --disable-shared --enable-static
  make
  make install

  LIBLIST=&quot;${LIBLIST} ${ROOTDIR}/lib/libwebp.a&quot;
  cp -Rp ${ROOTDIR}/include/webp/* $FINALDIR/Headers/

  cd ..
done

${DEVROOT}/usr/bin/lipo -create $LIBLIST -output $FINALDIR/WebP

rm -rf libwebp-0.1.2
rm -rf ${BUILDDIR}
</pre>
<p>The script assumes that the source for libwebp is in the same directory as the script is executed from. If you skipped the first part of the post or want to make sure you are using the same version I used then use the following curl command:</p>
<pre class="brush: bash; title: ; notranslate">
curl http://webp.googlecode.com/files/libwebp-0.1.2.tar.gz &gt; libwebp-0.1.2.tar.gz
</pre>
<p>After you run the script you will have a WebP.framework directory that represents the library. You can use this as a framework in XCode.</p>
<p>Using the library is easy. The following snipit shows how to get the current WebP library version, get the width and height of an image and then read the image into a buffer:</p>
<pre class="brush: cpp; title: ; notranslate">
    // Get the current version of the WebP decoder
    int rc = WebPGetDecoderVersion();

    NSLog(@&quot;Version: %d&quot;, rc);

    // Get the width and height of the selected WebP image
    int width = 0;
    int height = 0;
    WebPGetInfo([myData bytes], [myData length], &amp;width, &amp;height);

    NSLog(@&quot;Image Width: %d Image Height: %d&quot;, width, height); 

    // Decode the WebP image data into a RGBA value array
    uint8_t *data = WebPDecodeRGBA([myData bytes], [myData length], &amp;width, &amp;height);
</pre>
<p>There is more documentation on the <a href="http://code.google.com/speed/webp/docs/api.html">WebP API</a> and I&#039;ve put together a complete example on Github. If you grab the <a href="https://github.com/carsonmcdonald/WebP-iOS-example">WebP iOS example</a> from Github you will find the build script as well as the full example. First build the library from a command line then open the example project in XCode. If you don&#039;t first build the library before opening the project you will see that the WebP.framework is missing.</p>
<p>At this point you are on your way to having smaller iOS applications. There is at least one drawback at this point and that is speed. Decoding is a little slow and I think this is because the library isn&#039;t constructed to take advantage of any hardware acceleration. The next step is to see if it can be sped up by using the accelerate framework.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=j1uRits2cPk:ERTZJikzG_w:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=j1uRits2cPk:ERTZJikzG_w:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=j1uRits2cPk:ERTZJikzG_w:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=j1uRits2cPk:ERTZJikzG_w:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=j1uRits2cPk:ERTZJikzG_w:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=j1uRits2cPk:ERTZJikzG_w:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/j1uRits2cPk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/1483/using-webp-to-reduce-native-ios-app-size/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/programming/1483/using-webp-to-reduce-native-ios-app-size/</feedburner:origLink></item>
		<item>
		<title>Using the Google Closure Compiler in Java</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/meUYJPQUZsc/</link>
		<comments>http://www.ioncannon.net/programming/1447/using-the-google-closure-compiler-in-java/#comments</comments>
		<pubDate>Tue, 08 Mar 2011 14:29:55 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[closure]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=1447</guid>
		<description><![CDATA[I recently had a chance to try out Google&#039;s closure compiler. The closure compiler is similar to the YUI compressor except that along with minimizing it may rewrite the JavaScript. If you want to understand more about what it does start at the overview documentation and then go from there. What I needed was a [...]]]></description>
			<content:encoded><![CDATA[<p>I recently had a chance to try out <a href="http://code.google.com/closure/compiler/">Google&#039;s closure compiler</a>. The closure compiler is similar to the <a href="http://developer.yahoo.com/yui/compressor/">YUI compressor</a> except that along with minimizing it may rewrite the JavaScript. If you want to understand more about what it does start at the <a href="http://code.google.com/closure/compiler/docs/overview.html">overview documentation</a> and then go from there.</p>
<p>What I needed was a way to use the closure compiler in an Ant task. The Ant task that comes with the library is good but there wasn&#039;t a way for me to integrate it into an existing system that wasn&#039;t going to change. After looking around for some example code and not finding any I went into the library&#039;s Ant task and figured out how to wire it all up myself.</p>
<p><span id="more-1447"></span></p>
<p>It isn&#039;t that hard to use the compiler library in your own Java code but without a simple example it takes some work to figure out what is needed and what isn&#039;t. The following code is just about as bare bones as you can get. It takes a number of JavaScript files and compile them using the medium setting, more about the choice of settings after the code:</p>
<pre class="brush: java; title: ; notranslate">
import com.google.javascript.jscomp.*;

import java.io.FileWriter;
import java.util.List;
import java.util.ArrayList;
import java.util.logging.Level;

public class Test
{
  public static void main(String[] args) throws Exception
  {
    // These are external JavaScript files you reference but don't want changed
    String externalJavascriptResources[] = {
        &quot;jquery.js&quot;,
        &quot;jqueryui.js&quot;
    };
    // These are the files you want optimized
    String primaryJavascriptToCompile[] = {
        &quot;somejavascript.js&quot;,
        &quot;otherjavascript.js&quot;
    };
    // This is where the optimized code will end up
    String outputFilename = &quot;combined.min.js&quot;;

    com.google.javascript.jscomp.Compiler.setLoggingLevel(Level.INFO);
    com.google.javascript.jscomp.Compiler compiler = new com.google.javascript.jscomp.Compiler();

    CompilerOptions options = new CompilerOptions();
    CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options);

    WarningLevel.VERBOSE.setOptionsForWarningLevel(options);

    List&lt;JSSourceFile&gt; externalJavascriptFiles = new ArrayList&lt;JSSourceFile&gt;();
    for (String filename : externalJavascriptResources)
    {
      externalJavascriptFiles.add(JSSourceFile.fromFile(filename));
    }

    List&lt;JSSourceFile&gt; primaryJavascriptFiles = new ArrayList&lt;JSSourceFile&gt;();
    for (String filename : primaryJavascriptToCompile)
    {
      primaryJavascriptFiles.add(JSSourceFile.fromFile(filename));
    }

    compiler.compile(externalJavascriptFiles, primaryJavascriptFiles, options);

    for (JSError message : compiler.getWarnings())
    {
      System.err.println(&quot;Warning message: &quot; + message.toString());
    }

    for (JSError message : compiler.getErrors())
    {
      System.err.println(&quot;Error message: &quot; + message.toString());
    }

    FileWriter outputFile = new FileWriter(outputFilename);
    outputFile.write(compiler.toSource());
    outputFile.close();
  }
}
</pre>
<p>The above code is doing a number of things:</p>
<ul>
<li>It takes both external resources you don&#039;t want optimized as well as resources you do want optimized that refer to those external resources. You may not need to list external resources depending on what level of optimization you use.</li>
<li>It combines all the input files that are not external into one output file.</li>
<li>It compiles the javascript code given to it using the SIMPLE_OPTIMIZATIONS setting.</li>
<li>It lists any warnings or errors it found while compiling the code.</li>
</ul>
<p>One of the nice side effects of using the closure compiler is that because it compiles the code it can alert you to errors or potential issues using warnings.</p>
<p>What is listed above is using the middle of the road optimizations. There is a level above SIMPLE_OPTIMIZATIONS that you can set by changing that line in the above code to:</p>
<pre class="brush: java; title: ; notranslate">
  CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
</pre>
<p>Be aware that the advanced optimizations will do things, renaming your variables and functions to name two, that can break your code. The compiler gives you a way of alerting it to things you don&#039;t want changed using externs and exports. Before using the advanced option you should read the <a href="http://code.google.com/closure/compiler/docs/api-tutorial3.html">advanced options tutorial</a>.</p>
<p>If you do not like seeing all the steps the compiler takes you can set the logging level to QUIET by changing the WarningLevel line to the following:</p>
<pre class="brush: java; title: ; notranslate">
  WarningLevel.QUIET.setOptionsForWarningLevel(options);
</pre>
<p>One last note about the compiler is that the <a href="http://code.google.com/closure/compiler/docs/inspector.html">closure inspector</a> is a Firebug plugin that will let you see what the original line of code looked like before it was compiled. This can help a lot when something goes wrong.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=meUYJPQUZsc:xGp5KGqy8LU:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=meUYJPQUZsc:xGp5KGqy8LU:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=meUYJPQUZsc:xGp5KGqy8LU:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=meUYJPQUZsc:xGp5KGqy8LU:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=meUYJPQUZsc:xGp5KGqy8LU:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=meUYJPQUZsc:xGp5KGqy8LU:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/meUYJPQUZsc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/1447/using-the-google-closure-compiler-in-java/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/programming/1447/using-the-google-closure-compiler-in-java/</feedburner:origLink></item>
		<item>
		<title>Google Analytics Dashboard WordPress Plugin Version 2.0 Released</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/FwkWjcbUV4g/</link>
		<comments>http://www.ioncannon.net/utilities/1460/google-analytics-dashboard-wordpress-plugi-version-2-0-released/#comments</comments>
		<pubDate>Tue, 22 Feb 2011 14:13:22 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[utilities]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=1460</guid>
		<description><![CDATA[It has taken me a while but I&#039;ve finally been able to release version 2 of the Google Analytics Dashboard WordPress Plugin. The primary enhancement of this version is that it no longer blocks the dashboard, posts or pages interfaces while loading. The next major change is an upgrade to using Google&#039;s OAuth login system [...]]]></description>
			<content:encoded><![CDATA[<p>It has taken me a while but I&#039;ve finally been able to release version 2 of the <a href="http://www.ioncannon.net/projects/google-analytics-dashboard-wordpress-widget/">Google Analytics Dashboard WordPress Plugin</a>. The primary enhancement of this version is that it no longer blocks the dashboard, posts or pages interfaces while loading. The next major change is an upgrade to using Google&#039;s OAuth login system (see my <a href="http://www.ioncannon.net/programming/1443/google-oauth-for-installed-apps-php-example/">Google OAuth example using PHP</a> for more information on how I did that if you are interested). The old login system is still available but the OAuth login is the one to use going forward and I may remove the old one at some point. The move to OAuth along with the refactoring I did to the code will allow support for other Google sites such as Feedburner. As a bonus I also moved the caching system to the newer WordPress transient storage interface. The use of the transient storage interface should fix one of the biggest issues people have seen in the past so no more worrying about finding a temp directory that is writable.</p>
<p>Here are a list of the major changes:</p>
<ul>
<li>The dashboard panel now loads asynchronously so the entire dashboard doesn&#039;t block while it is loading</li>
<li>Made the analytics column in posts and pages load asynchronously so that it doesn&#039;t block the loading of the page</li>
<li>Added support for Google OAuth logins</li>
<li>Use transient API support with wordpress version 2.8+</li>
<li>Added ability to support multiple analytics sources</li>
</ul>
<p>Some other minor changes:</p>
<ul>
<li>Stop unlink warnings when caching won&#039;t work</li>
<li>Refactored code so that major parts are split into classes</li>
<li>Refactored code to better seperate UI code</li>
<li>Fixed mime type not being sent correctly for admin area javascript file</li>
<li>Fix bug in wordpress version checking</li>
</ul>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=FwkWjcbUV4g:HkEneAbKULs:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=FwkWjcbUV4g:HkEneAbKULs:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=FwkWjcbUV4g:HkEneAbKULs:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=FwkWjcbUV4g:HkEneAbKULs:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=FwkWjcbUV4g:HkEneAbKULs:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=FwkWjcbUV4g:HkEneAbKULs:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/FwkWjcbUV4g" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/utilities/1460/google-analytics-dashboard-wordpress-plugi-version-2-0-released/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/utilities/1460/google-analytics-dashboard-wordpress-plugi-version-2-0-released/</feedburner:origLink></item>
		<item>
		<title>Google OAuth for Installed Apps PHP Example</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/zqNMvMcVDVo/</link>
		<comments>http://www.ioncannon.net/programming/1443/google-oauth-for-installed-apps-php-example/#comments</comments>
		<pubDate>Tue, 08 Feb 2011 16:53:20 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[oauth]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=1443</guid>
		<description><![CDATA[I have been working on a long needed update to the Google analytics dashboard plugin for WordPress and one of the items I had on my TODO list was using Google&#039;s OAuth login instead of the old ClientLogin. Setting OAuth up for a WordPress plugin is complicated because it isn&#039;t a hosted application and as [...]]]></description>
			<content:encoded><![CDATA[<p>I have been working on a long needed update to the <a href="http://www.ioncannon.net/projects/google-analytics-dashboard-wordpress-widget/">Google analytics dashboard plugin for WordPress</a> and one of the items I had on my TODO list was using Google&#039;s OAuth login instead of the old ClientLogin. Setting OAuth up for a WordPress plugin is complicated because it isn&#039;t a hosted application and as such I can&#039;t register it to get OAuth keys. That is where a special way of doing OAuth comes in called <a href="http://code.google.com/apis/accounts/docs/OAuthForInstalledApps.html">OAuth for installed apps</a>.</p>
<p>There seems to be a lot of general documentation on how to do OAuth but there wasn&#039;t much about using it for installed apps so what follows is an example using PHP that is basically what went into the plugin update.</p>
<p><span id="more-1443"></span></p>
<p>It helps to first know the basic steps that will make this work. From the documentation those steps are:</p>
<ol>
<li>Your application gets an unauthorized request token from Google&#039;s authorization server.</li>
<li>Google asks the user to grant you access to the required data.</li>
<li>Your application gets an authorized request token from the authorization server.</li>
<li>You exchange the authorized request token for an access token.</li>
<li>You use the access token to request data from Google&#039;s service access servers.</li>
</ol>
<p>I will reference these steps in the code that follows.</p>
<p>Before trying this example out grab the <a href="http://oauth.googlecode.com/svn/code/php/">OAuth PHP library</a>. The only file that is needed to execute the examples is OAuth.php. </p>
<p>I have split the code into two files that I named phase1.php and phase2.php. In phase1.php the resulting URL would be used to grant access to the given scope (note that I&#039;ve marked parts of the code that would need to be customized with a &#034;Customize this&#034; comment):</p>
<pre class="brush: php; title: ; notranslate">
require_once(&quot;OAuth.php&quot;);

// This is the setup for step 1
// 1. Your application gets an unauthorized request token from Google's authorization server.
$signature_method = new OAuthSignatureMethod_HMAC_SHA1();

$params = array();

// The callback is where the user comes back after authentication
$params['oauth_callback'] = 'http://example.com/phase2.php'; // Customize this

// The scope is what you are asking for access to
$params['scope'] = 'https://www.google.com/analytics/feeds/'; // Customize this

// Setting xoauth_displayname will show the given name on the authentication screen
$params['xoauth_displayname'] = 'My Test App'; // Customize this

// Set up an anonymous oauth consumer
$consumer = new OAuthConsumer('anonymous', 'anonymous', NULL);

// Set up the request for a request token
$req_req = OAuthRequest::from_consumer_and_token($consumer, NULL, 'GET', 'https://www.google.com/accounts/OAuthGetRequestToken', $params);

// Sign the request
$req_req-&gt;sign_request($signature_method, $consumer, NULL);

// Set up curl and have it get the token to use for the authenication call
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $req_req-&gt;to_url());

// This tells curl to return the response as one string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 

// Run curl and grab the output and the return code
// This is the execution of step 1
$return = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if($http_code == 200)
{
  // If the call was good parse out the response parameters into an array
  $access_params = array();
  $param_pairs = explode('&amp;', $return);
  foreach($param_pairs as $param_pair)
  {
    if (trim($param_pair) == '') { continue; }
    list($key, $value) = explode('=', $param_pair);
    $access_params[$key] = urldecode($value);
  }

  // Print out the authentication URL that needs to be opened in a browser
  echo &quot;Open this link: https://www.google.com/accounts/OAuthAuthorizeToken?oauth_token=&quot; . urlencode($access_params['oauth_token']) . &quot;\n&quot;;

  // This is the oauth token secret that is needed for the second part
  echo &quot;\nRemember the folowing value: &quot; . $access_params['oauth_token_secret'] . &quot;\n&quot;;

  // This is where the user would perform step 2
  // 2. Google asks the user to grant you access to the required data.
}
else
{
  echo &quot;Error: $http_code and $return\n&quot;;
}
</pre>
<p>The above code performs steps 1 and 2. There are a few notes that are good to know:</p>
<ul>
<li>The OAuth consumer is set to &#034;anonymous&#034; here because this is an installed app, that is used again in the second phase.</li>
<li>There can be multiple scope values, just seperate them with a space.</li>
<li>Make sure to save the oauth_token_secret value, it is required for the second phase.</li>
</ul>
<p>At this point the user goes off to authorize the application on Google&#039;s system. When they are done they are redirected back to your callback URL specified in phase 1 as oauth_callback. I put the following code in a file named phase2.php (again I&#039;ve noted places that would need to be customized):</p>
<pre class="brush: php; title: ; notranslate">
require_once(&quot;OAuth.php&quot;);

// This is where step 3 comes in, this would be in the callback and would be called
// after the user authenticates.
// 3. Your application gets an authorized request token from the authorization server.
$oauth_verifier = urldecode($_REQUEST['oauth_verifier']);
$oauth_token = urldecode($_REQUEST['oauth_token']);

// The token secret would need to be looked up from the previous step
$ouath_token_secret = ''; // Customize this

// This is where setup for step 4 starts
// 4. You exchange the authorized request token for an access token.
$signature_method = new OAuthSignatureMethod_HMAC_SHA1();

$params = array();

$params['oauth_verifier'] = $oauth_verifier;

// Set up the anonymous consumer again
$consumer = new OAuthConsumer('anonymous', 'anonymous', NULL);

// Use the oauth token and token secret to set up the consumer for the access token
$final_consumer = new OAuthConsumer($oauth_token, $ouath_token_secret);

// Set up the call to get the access token
$acc_req = OAuthRequest::from_consumer_and_token($consumer, $final_consumer, 'GET', 'https://www.google.com/accounts/OAuthGetAccessToken', $params);

$acc_req-&gt;sign_request($signature_method, $consumer, $final_consumer);

// Set up curl and have it get the final token and secret
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $acc_req-&gt;to_url());

// This tells curl to return the response as one string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 

// Run curl and grab the output and the return code
// This is the execution of step 4
$return = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if($http_code == 200)
{
  // If the call was good parse out the response parameters into an array
  $access_params = array();
  $param_pairs = explode('&amp;', $return);
  foreach($param_pairs as $param_pair)
  {
    if (trim($param_pair) == '') { continue; }
    list($key, $value) = explode('=', $param_pair);
    $access_params[$key] = urldecode($value);
  }

  // Print out the final information needed for access to the service that was in &quot;scope&quot;
  echo &quot;Access info: &quot; . $access_params['oauth_token'] . &quot; and &quot; . $access_params['oauth_token_secret'] . &quot;\n&quot;;
}
else
{
  echo &quot;Error: $http_code and $return\n&quot;;
}
</pre>
<p>At this point you have a good oauth_token and oauth_token_secret that would need to be stored securely. These two bits of information allow you to make calls later to services provided under the given scope listed in the initial token request. Here is an example of making a request:</p>
<pre class="brush: php; title: ; notranslate">
require_once(&quot;OAuth.php&quot;);

// This is step 5
// 5. You use the access token to request data from Google's service access servers.

$url = 'https://www.google.com/analytics/feeds/accounts/default';

$signature_method = new OAuthSignatureMethod_HMAC_SHA1();

$params = array();

$consumer = new OAuthConsumer('anonymous', 'anonymous', NULL);

// Here the saved oauth_token and oauth_token_secret are used
$token = new OAuthConsumer($saved_oauth_token, $saved_oauth_token_secret);

$oauth_req = OAuthRequest::from_consumer_and_token($consumer, $token, 'GET', $url, $params);

$oauth_req-&gt;sign_request($signature_method, $consumer, $token);

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);

// Use the oauth request to create the authenication header
curl_setopt($ch, CURLOPT_HTTPHEADER, array($oauth_req-&gt;to_header())));

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

$return = curl_exec($ch);
$this-&gt;http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if($this-&gt;http_code == 200)
{
  echo $return;
}
else
{
  echo $return;
}
</pre>
<p>For a lot more detail check out <a href="http://code.google.com/apis/gdata/articles/oauth.html">Google&#039;s OAuth documentation</a>. I also found it helpful to use the <a href="http://googlecodesamples.com/oauth_playground/">Google&#039;s OAuth playground</a> to test things out.</p>
<p>I also found a handy list of <a href="http://code.google.com/apis/gdata/faq.html#AuthScopes">all the define Google OAuth scopes</a>.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=zqNMvMcVDVo:8GEHIH6eBDg:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=zqNMvMcVDVo:8GEHIH6eBDg:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=zqNMvMcVDVo:8GEHIH6eBDg:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=zqNMvMcVDVo:8GEHIH6eBDg:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=zqNMvMcVDVo:8GEHIH6eBDg:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=zqNMvMcVDVo:8GEHIH6eBDg:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/zqNMvMcVDVo" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/1443/google-oauth-for-installed-apps-php-example/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/programming/1443/google-oauth-for-installed-apps-php-example/</feedburner:origLink></item>
		<item>
		<title>Java AirPlay Client</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/Q2XVCHZ77cM/</link>
		<comments>http://www.ioncannon.net/utilities/1436/java-airplay-client/#comments</comments>
		<pubDate>Tue, 25 Jan 2011 14:36:04 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[utilities]]></category>
		<category><![CDATA[airplay]]></category>
		<category><![CDATA[java]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=1436</guid>
		<description><![CDATA[Ever since getting one of the new AppleTV devices I have been wanting to fiddle with AirPlay. I finally got around to looking at a dump of the traffic between an iPad and the AppleTV over Christmas and was surprised at how simple it was. Soon after I noticed a blog post about AirFlick for [...]]]></description>
			<content:encoded><![CDATA[<p>Ever since getting one of the new AppleTV devices I have been wanting to fiddle with AirPlay. I finally got around to looking at a dump of the traffic between an iPad and the AppleTV over Christmas and was surprised at how simple it was. Soon after I noticed a blog post about <a href="http://ericasadun.com/ftp/AirPlay/">AirFlick</a> for the Mac. AirFlick was close to what I was wanting at the time but I really wanted something that would let me control AirPlay from Linux or Windows.</p>
<p>I decided to make something that could run anywhere so I created my own AirPlay client called <a href="http://www.ioncannon.net/projects/ap4j-player-java-airplay-player/">AP4J</a>. I used Java and a pure Java Bonjour implementation called <a href="http://jmdns.sourceforge.net/">JmDNS</a> so AP4J can run anywhere Java runs.</p>
<p>The current version only has the ability to control an AirPlay device. That means you have to supply a location that has a compatible video (h264 encoded) but once playing you will have control over the video just as you would using the iPad or iPhone. The next step will be to add the ability to directly serve videos instead of only being able to control the playback of videos. My goal will be the ability to run AP4J on my Windows Home Media server where I can have it stream videos to my AppleTV.</p>
<p>I have tested AP4J on Linux, Windows and Mac but only extensively on Linux. I have also tested a number of sites that have compatible videos available, a few of those are listed here:</p>
<ul>
<li><a href="http://blip.tv/">http://blip.tv/</a></li>
<li><a href="http://blip.tv/">http://www.archives.org/</a></li>
<li><a href="http://blip.tv/">http://confreaks.net/</a></li>
</ul>
<p>Now for a couple screen shots. This is what you see after starting the server and going to the web interface:</p>
<p><a href="http://www.ioncannon.net/wp-content/uploads/2011/01/ap4jmain.png"><img src="http://www.ioncannon.net/wp-content/uploads/2011/01/ap4jmain.png" alt="" title="Java AirPlay Application Main Menu" width="353" height="204" class="alignnone size-full wp-image-1431" /></a></p>
<p>This is what it looks like when a video is playing:</p>
<p><a href="http://www.ioncannon.net/wp-content/uploads/2011/01/ap4jpopup.png"><img src="http://www.ioncannon.net/wp-content/uploads/2011/01/ap4jpopup.png" alt="" title="Java AirPlay Application Play Popup" width="698" height="407" class="alignnone size-full wp-image-1432" /></a> <br/></p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=Q2XVCHZ77cM:L88RBvAX6DA:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=Q2XVCHZ77cM:L88RBvAX6DA:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=Q2XVCHZ77cM:L88RBvAX6DA:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=Q2XVCHZ77cM:L88RBvAX6DA:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=Q2XVCHZ77cM:L88RBvAX6DA:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=Q2XVCHZ77cM:L88RBvAX6DA:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/Q2XVCHZ77cM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/utilities/1436/java-airplay-client/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/utilities/1436/java-airplay-client/</feedburner:origLink></item>
		<item>
		<title>Browser Based Push Notifications with Mongrel2 and EventSource</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/Zf_ujVVgyuY/</link>
		<comments>http://www.ioncannon.net/programming/1410/browser-based-push-notifications-with-mongrel2-and-eventsource/#comments</comments>
		<pubDate>Tue, 04 Jan 2011 19:00:05 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[mongrel2]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=1410</guid>
		<description><![CDATA[One of the interesting things about Mongrel2 is its ability to send output to multiple clients with a single handler message. This has a lot of potential for push applications and while I was investigating Mongrel2 a new version of iOS came out that included changes to Safari. While looking at the list of Safari [...]]]></description>
			<content:encoded><![CDATA[<p>One of the interesting things about <a href="http://mongrel2.org/">Mongrel2</a> is its ability to send output to multiple clients with a single handler message. This has a lot of potential for push applications and while I was investigating Mongrel2 a new version of iOS came out that included changes to Safari. While looking at the list of <a href="http://www.mobilexweb.com/blog/safari-ios-accelerometer-websockets-html5">Safari changes in iOS 4.2</a> I noticed something called <a href="http://dev.w3.org/html5/eventsource/">EventSource</a> and went to investigate what it was.</p>
<p>As it turns out EventSource is a newer way of doing browser push currently supported by Chrome, Opera and Safari (mobile Safari as well). There is a good <a href="http://www.html5rocks.com/tutorials/eventsource/basics/">HTML5Rocks post on Server-Sent Events</a> that goes into more detail on the differences of using it over something like WebSockets. One of the differences is that EventSource specially addresses mobile device use with the ability to do a &#034;Connectionless push&#034; through a proxy so the end device can sleep but still receive push notifications.</p>
<p>Before reading on check out my <a href="http://www.ioncannon.net/programming/1384/example-mongrel2-handler-in-ruby/">example Mongrel2 ruby handler</a> post if you haven&#039;t already. The following examples will be based on the code from that post. I&#039;m also going to use Modernizr to detect support for EventSource so check out my post on <a href="http://www.ioncannon.net/programming/1369/using-modernizr-simple-guide-and-examples/">using Modernizr to detect browser support</a> as well.</p>
<p><span id="more-1410"></span></p>
<p>The first thing to do is configure Mongrel2. The following configuration will tell Mongrel2 to serve static content from a directory named &#034;html&#034; and connect the /esupdates path to a ruby handler we will write later (also note that it listens on more than one host, make sure you put your IP in place of the example IP):</p>
<pre class="brush: plain; title: ; notranslate">
root_dir = Dir(base='html/', index_file='index.html', default_ctype='text/plain')

esupdates = Handler(send_spec='tcp://127.0.0.1:9999', send_ident='54c6755b-9628-40a4-9a2d-cc82a816345e',
                    recv_spec='tcp://127.0.0.1:9998', recv_ident='')

routes = {
    '/esupdates': esupdates,
    '/': root_dir
}

main = Server(
    uuid=&quot;2f62bd5-9e59-49cd-993c-3b6013c28f05&quot;,
    access_log=&quot;/logs/access.log&quot;,
    error_log=&quot;/logs/error.log&quot;,
    chroot=&quot;./&quot;,
    pid_file=&quot;/run/mongrel2.pid&quot;,
    default_host=&quot;localhost&quot;,
    name=&quot;main&quot;,
    port=6767,
    hosts=[ Host(name=&quot;192.168.1.1&quot;, routes=routes)
            Host(name=&quot;localhost&quot;, routes=routes) ]
)

settings = {&quot;zeromq.threads&quot;: 1}

servers = [main]
</pre>
<p>The next step is to create the page that will set up the EventSource connection and display any push messages. In this example I&#039;m using a modified version of Modernizr to detect EventSource support but you could do the test by hand or assume the user has support. The current version of Modernizr doesn&#039;t support EventSource yet but you can grab <a href="https://github.com/carsonmcdonald/Modernizr">my fork of Modernizr</a> where I have added it.</p>
<pre class="brush: xml; title: ; notranslate">
&lt;!doctype html&gt;
&lt;html style=&quot;no-js&quot;&gt;
  &lt;head&gt;
    &lt;title&gt;EventSource Test&lt;/title&gt;

    &lt;style type=&quot;text/css&quot; media=&quot;screen&quot;&gt;
      div.esno, div.esyes { display: none }
      .no-eventsource div.esno { display: block }
      .eventsource div.esyes { display: block }
    &lt;/style&gt;

    &lt;script src=&quot;https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;modernizr-min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
    &lt;script type=&quot;text/javascript&quot;&gt;
      function startUpdates()
      {
        var source = new EventSource('/esupdates');
        source.onmessage = function (event)
        {
          $(&quot;#events&quot;).innerHTML += event.data + '&lt;br/&gt;';
        };

        source.onerror = function (event)
        {
          $(&quot;#events&quot;).innerHTML += 'error&lt;br/&gt;';
        };

        source.onopen = function (event)
        {
           $(&quot;#events&quot;).innerHTML += 'open&lt;br/&gt;';
        };
      }

      $(document).ready(function()
      {
        if (Modernizr.eventsource)
        {
          setTimeout(startUpdates, 10);
        }
      });
    &lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;

    &lt;div class=&quot;esno&quot;&gt;
      Your browser does not support EventSource.
    &lt;/div&gt;

    &lt;div class=&quot;esyes&quot;&gt;
      Messages:&lt;br/&gt;
      &lt;div id=&quot;events&quot;&gt;&lt;/div&gt;
    &lt;/div&gt;

  &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>The above creates the EventSource connection to the /esupdates URL and when any event comes in it appends that event to the div with the id of &#034;events&#034;.  This and the Modernizr javascript need to go in the &#034;html&#034; directory.</p>
<p>And last comes the handler code. I have modified the ruby handler example from my previous post by adding a new ZMQ queue called &#034;eventsource_queue&#034; as well as a beacon thread that pushes a message to the &#034;eventsource_queue&#034; every 5 seconds. Messages coming in on the &#034;eventsource_queue&#034; are sent to every connected client:</p>
<pre class="brush: ruby; title: ; notranslate">
require 'zmq'
require 'json'

handler_thread = Thread.new do
  handler_ctx = ZMQ::Context.new(1)

  receive_queue = handler_ctx.socket(ZMQ::PULL)
  receive_queue.connect(&quot;tcp://127.0.0.1:9999&quot;)

  response_publisher = handler_ctx.socket(ZMQ::PUB)
  response_publisher.connect(&quot;tcp://127.0.0.1:9998&quot;)
  response_publisher.setsockopt(ZMQ::IDENTITY, &quot;82209006-86FF-4982-B5EA-D1E29E55D481&quot;)

  stop_queue = handler_ctx.socket(ZMQ::PULL)
  stop_queue.connect(&quot;ipc://shutdown_queue&quot;)

  eventsource_queue = handler_ctx.socket(ZMQ::PULL)
  eventsource_queue.connect(&quot;ipc://eventsource_queue&quot;)

  connections = []

  stopped = false
  until stopped do
    selected_queue = ZMQ.select([receive_queue, stop_queue, eventsource_queue])
    if selected_queue[0][0] == stop_queue # Anything on the stop_queue ends processing
      stop_queue.close
      receive_queue.close
      eventsource_queue.close
      response_publisher.close
      handler_ctx.close
      stopped = true
    elsif selected_queue[0][0] == eventsource_queue # A message that goes out to all clients
      next if connections.size == 0 

      # The following must be the send_ident value used in the Handler config file entry
      sender_uuid = '54c6755b-9628-40a4-9a2d-cc82a816345e' 

      es_message = eventsource_queue.recv(0)

      # Note: This can only handle 128 connections without modification
      client_str = connections.join(' ')

      response_value = &quot;#{sender_uuid} #{client_str.length}:#{client_str}, #{es_message}&quot;
      response_publisher.send(response_value, 0)
    else
      # Request comes in as &quot;UUID ID PATH SIZE:HEADERS,SIZE:BODY,&quot;
      sender_uuid, client_id, request_path, request_message = receive_queue.recv(0).split(' ', 4)
      len, rest = request_message.split(':', 2)
      headers = JSON.parse(rest[0...len.to_i])
      len, rest = rest[(len.to_i+1)..-1].split(':', 2)
      body = rest[0...len.to_i]

      if headers['METHOD'] == 'JSON' and JSON.parse(body)['type'] == 'disconnect'
        connections.delete(client_id) # A client has disconnected, remove them from the list
        next
      end

      # Respond with the opening EventSource information
      connections &lt;&lt; client_id

      headers = {}
      headers['Content-Type'] = 'text/event-stream'
      headers['Expires'] = 'Fri, 01 Jan 1990 00:00:00 GMT'
      headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
      headers['Pragma'] = 'no-cache'
      headers_s = headers.map{|k, v| &quot;%s: %s&quot; % [k,v]}.join(&quot;\r\n&quot;)

      content_body = &quot;: time stream\nretry: 5000\n\n&quot;;

      response_value = &quot;#{sender_uuid} #{client_id.to_s.length}:#{client_id}, HTTP/1.1 200 OK\r\n#{headers_s}\r\n\r\n#{content_body}&quot;
      response_publisher.send(response_value, 0)
    end
  end
end

# This thread sends out a message every 5 seconds to all connected clients
beacon_thread = Thread.new do
  beacon_ctx = ZMQ::Context.new(1)
  eventsource_queue = beacon_ctx.socket(ZMQ::PUSH)
  eventsource_queue.bind(&quot;ipc://eventsource_queue&quot;)

  index = 1
  loop do
    eventsource_queue.send(&quot;id: #{index}\r\ndata: Timestamp #{Time.now}\r\n\r\n&quot;)
    sleep(5)
    index = index + 1
  end
end

ctx = ZMQ::Context.new(1)
stop_push_queue = ctx.socket(ZMQ::PUSH)
trap('INT') do # Send a message to shutdown on SIGINT
  stop_push_queue.bind(&quot;ipc://shutdown_queue&quot;)
  stop_push_queue.send(&quot;shutdown&quot;)
end

handler_thread.join

beacon_thread.kill
beacon_thread.join

stop_push_queue.close
</pre>
<p>There is a limitation in the above of 128 client connections at one time. This is something that could be fixed by splitting the set of connections into subsets of 128 each and sending each subset a message. I left that out to try to reduce the complexity of this example.</p>
<p>Although this example shows a single event going out to every client there is no need to limit it to just that. You could easily do the same thing for subsets of the full set of clients.</p>
<p>As a next step it might be interesting to see how well this setup could handle the <a href="http://www.kegel.com/c10k.html">C10k problem</a> or even the <a href="http://blog.urbanairship.com/blog/2010/08/24/c500k-in-action-at-urban-airship/">C500k problem</a>.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=Zf_ujVVgyuY:LxjxmE6cA4w:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=Zf_ujVVgyuY:LxjxmE6cA4w:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=Zf_ujVVgyuY:LxjxmE6cA4w:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=Zf_ujVVgyuY:LxjxmE6cA4w:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=Zf_ujVVgyuY:LxjxmE6cA4w:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=Zf_ujVVgyuY:LxjxmE6cA4w:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/Zf_ujVVgyuY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/1410/browser-based-push-notifications-with-mongrel2-and-eventsource/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/programming/1410/browser-based-push-notifications-with-mongrel2-and-eventsource/</feedburner:origLink></item>
		<item>
		<title>Example Mongrel2 Handler in Ruby</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/aQCAwPqYPmw/</link>
		<comments>http://www.ioncannon.net/programming/1384/example-mongrel2-handler-in-ruby/#comments</comments>
		<pubDate>Mon, 06 Dec 2010 14:27:35 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[mongrel2]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[zeromq]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=1384</guid>
		<description><![CDATA[Right before I went to Rubyconf I started looking at Mongrel2 so I had something to hack on while I was there. I grabbed the two Ruby handlers listed on the main site, Ruby Mongrel2 and Rack Mongrel2, to get started. I noticed right away that I couldn&#039;t kill the handler process once it started [...]]]></description>
			<content:encoded><![CDATA[<p>Right before I went to <a href="http://rubyconf.org">Rubyconf</a> I started looking at <a href="http://mongrel2.org/">Mongrel2</a> so I had something to hack on while I was there. I grabbed the two Ruby handlers listed on the main site, <a href="https://github.com/perplexes/m2r">Ruby Mongrel2</a> and <a href="https://github.com/darkhelmet/rack-mongrel2">Rack Mongrel2</a>, to get started. I noticed right away that I couldn&#039;t kill the handler process once it started using either one of these libraries. I started down the path of figuring out why they wouldn&#039;t respond to SIGINT and eventually to writing my own handler in Ruby that demonstrates how to fix the issue</p>
<p><span id="more-1384"></span></p>
<p>Before getting started with the handler itself it is important to note that Mongrel2 uses <a href="http://www.zeromq.org/">ZeroMQ</a> to communicate with handlers. At least some idea of how ZeroMQ works is needed to follow along with the handler code so take a few minutes to read over the <a href="http://www.zeromq.org/intro:read-the-manual">manual</a>. After looking at the manual take a look at the <a href="http://www.zeromq.org/bindings:ruby">ZeroMQ Ruby bindings</a>. You may even want to take a few minutes and check out some <a href="https://github.com/andrewvc/learn-ruby-zeromq">examples using ZeroMQ with Ruby</a>.</p>
<p>The first significant difference between the following example handler and the two Ruby handlers list above is that it uses the ZeroMQ Ruby bindings. The other two use the <a href="https://github.com/chuckremes/ffi-rzmq">FFI RZMQ</a> bindings. I decided to use the ZeroMQ Ruby bindings after reading the &#034;FEATURES/PROBLEMS&#034; section of the FFI RZMQ bindings. The main drawback in going this route is that the example handler won&#039;t run under JRuby. After installing the ZeroMQ library you can use the following command to install the correct gem needed for the handler:</p>
<pre class="brush: plain; title: ; notranslate">
gem install zmq
</pre>
<p>You will also need the ZeroMQ library installed before installing Mongrel2.</p>
<p>Before starting on the handler it is important to understand how Mongrel2 is configured. A good place to start is either the <a href="http://mongrel2.org/wiki?name=GettingStarted">Mongrel2 quickstart guide</a> or the <a href="http://mongrel2.org/doc/tip/docs/manual/book.wiki">Mongrel2 manual</a>. The example handler was tested using mongrel2-1.4 and the following Mongrel2 configuration will work with the example handler:</p>
<pre class="brush: plain; title: ; notranslate">
demo = Handler(send_spec='tcp://127.0.0.1:9999', send_ident='54c6755b-9628-40a4-9a2d-cc82a816345e',
               recv_spec='tcp://127.0.0.1:9998', recv_ident='')

routes = {
    '/demo': demo
}

main = Server(
    uuid=&quot;2f62bd5-9e59-49cd-993c-3b6013c28f05&quot;,
    access_log=&quot;/logs/access.log&quot;,
    error_log=&quot;/logs/error.log&quot;,
    chroot=&quot;./&quot;,
    pid_file=&quot;/run/mongrel2.pid&quot;,
    default_host=&quot;localhost&quot;,
    name=&quot;main&quot;,
    port=6767,
    hosts=[ Host(name=&quot;localhost&quot;, routes=routes) ]
)

settings = {&quot;zeromq.threads&quot;: 1}

servers = [main]
</pre>
<p>Assuming you have Mongrel2 installed and working you can copy the above into a file called m2.conf and then run the following command:</p>
<pre class="brush: bash; title: ; notranslate">
m2sh load --db config.sqlite -config m2.conf
</pre>
<p>This configuration sets up a route at /demo that maps to the example handler. The handler is expected to use TCP to communicate with Mongrel2 on localhost ports 9999 and 9998.</p>
<p>With all that out of the way it is finally time for the handler. The following is the outer shell of the handler. It creates a thread that will handle the incoming messages and the response. It then sets up an interrupt trap on the main thread and waits for the handler thread to end:</p>
<pre class="brush: ruby; title: ; notranslate">
handler_thread = Thread.new do

# This is where the zeromq interface will go

  stopped = false
  until stopped do

# This is where messages will be processed

  end
end

trap('INT') do # Send a message to shutdown on SIGINT

# This is where the interrupt signal will be handled

end

handler_thread.join
</pre>
<p>With the shell in place the next part to go in is the communication with Mongrel2. Here we open two ZeroMQ sockets to Mongrel2. The first is a &#034;PULL&#034; socket that will have incoming request messages. The second is a &#034;PUB&#034; socket that will be used to push responses back. The incoming messages are parsed and the response is sent back containing &#034;Hello world!&#034;:</p>
<pre class="brush: ruby; title: ; notranslate">
require 'zmq'
require 'json'

handler_thread = Thread.new do
  handler_ctx = ZMQ::Context.new(1)

  receive_queue = handler_ctx.socket(ZMQ::PULL)
  receive_queue.connect(&quot;tcp://127.0.0.1:9999&quot;)

  response_publisher = handler_ctx.socket(ZMQ::PUB)
  response_publisher.connect(&quot;tcp://127.0.0.1:9998&quot;)
  response_publisher.setsockopt(ZMQ::IDENTITY, &quot;82209006-86FF-4982-B5EA-D1E29E55D481&quot;)

  stopped = false
  until stopped do
    # Request comes in as &quot;UUID ID PATH SIZE:HEADERS,SIZE:BODY,&quot;
    sender_uuid, client_id, request_path, request_message = receive_queue.recv(0).split(' ', 4)
    len, rest = request_message.split(':', 2)
    headers = JSON.parse(rest[0...len.to_i])
    len, rest = rest[(len.to_i+1)..-1].split(':', 2)
    body = rest[0...len.to_i]

    if headers['METHOD'] == 'JSON' and JSON.parse(body)['type'] == 'disconnect'
      next # A client has disconnected, might want to do something here...
    end

    # Response goes out as &quot;UUID SIZE:ID ID ID, BODY&quot;
    content_body = &quot;Hello world!&quot;
    response_value = &quot;#{sender_uuid} 1:#{client_id}, HTTP/1.1 200 OK\r\nContent-Length: #{content_body.size}\r\n\r\n#{content_body}&quot;
    response_publisher.send(response_value, 0)
  end
end

trap('INT') do # Send a message to shutdown on SIGINT

# This is where the interrupt signal will be handled

end

handler_thread.join
</pre>
<p>At this point the handler will work just fine but it won&#039;t die without a kill -9. Fixing that is where the trap(&#039;INT&#039;) and handler thread come in to play. The following version is the final version and uses a third internal queue called the &#034;shutdown_queue&#034; to signal that the handler thread should stop. One change you should note from the above example is the use of the select call that watches multiple sockets:</p>
<pre class="brush: ruby; title: ; notranslate">
require 'zmq'
require 'json'

handler_thread = Thread.new do
  handler_ctx = ZMQ::Context.new(1)

  receive_queue = handler_ctx.socket(ZMQ::PULL)
  receive_queue.connect(&quot;tcp://127.0.0.1:9999&quot;)

  response_publisher = handler_ctx.socket(ZMQ::PUB)
  response_publisher.connect(&quot;tcp://127.0.0.1:9998&quot;)
  response_publisher.setsockopt(ZMQ::IDENTITY, &quot;82209006-86FF-4982-B5EA-D1E29E55D481&quot;)

  stop_queue = handler_ctx.socket(ZMQ::PULL)
  stop_queue.connect(&quot;ipc://shutdown_queue&quot;)

  stopped = false
  until stopped do
    selected_queue = ZMQ.select([receive_queue, stop_queue])
    if selected_queue[0][0] == stop_queue # Anything on the stop_queue ends processing
      stop_queue.close
      receive_queue.close
      response_publisher.close
      handler_ctx.close
      stopped = true
    else
      # Request comes in as &quot;UUID ID PATH SIZE:HEADERS,SIZE:BODY,&quot;
      sender_uuid, client_id, request_path, request_message = receive_queue.recv(0).split(' ', 4)
      len, rest = request_message.split(':', 2)
      headers = JSON.parse(rest[0...len.to_i])
      len, rest = rest[(len.to_i+1)..-1].split(':', 2)
      body = rest[0...len.to_i]

      if headers['METHOD'] == 'JSON' and JSON.parse(body)['type'] == 'disconnect'
        next # A client has disconnected, might want to do something here...
      end

      # Response goes out as &quot;UUID SIZE:ID ID ID, BODY&quot;
      content_body = &quot;Hello world!&quot;
      response_value = &quot;#{sender_uuid} 1:#{client_id}, HTTP/1.1 200 OK\r\nContent-Length: #{content_body.size}\r\n\r\n#{content_body}&quot;
      response_publisher.send(response_value, 0)
    end
  end
end

ctx = ZMQ::Context.new(1)
stop_push_queue = ctx.socket(ZMQ::PUSH)
trap('INT') do # Send a message to shutdown on SIGINT
  stop_push_queue.bind(&quot;ipc://shutdown_queue&quot;)
  stop_push_queue.send(&quot;shutdown&quot;)
end

handler_thread.join

stop_push_queue.close
</pre>
<p>This is the bare minimum handler and doesn&#039;t use all the features that Mongrel2 has. One of the most interesting features of Mongrel2 is the ability to have the handler respond to multiple client requests with a single response message. I hope to use this feature in the near future to test out server side event push with Javascript using <a href="http://dev.w3.org/html5/eventsource/">EventSource</a>. </p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=aQCAwPqYPmw:UpkHUdOWtMg:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=aQCAwPqYPmw:UpkHUdOWtMg:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=aQCAwPqYPmw:UpkHUdOWtMg:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=aQCAwPqYPmw:UpkHUdOWtMg:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=aQCAwPqYPmw:UpkHUdOWtMg:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=aQCAwPqYPmw:UpkHUdOWtMg:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/aQCAwPqYPmw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/1384/example-mongrel2-handler-in-ruby/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/programming/1384/example-mongrel2-handler-in-ruby/</feedburner:origLink></item>
	</channel>
</rss><!-- Dynamic page generated in 0.343 seconds. --><!-- Cached page generated by WP-Super-Cache on 2012-05-08 10:28:52 -->

