<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	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/"
	>

<channel>
	<title>everburning &#187; Article</title>
	<atom:link href="http://everburning.com/news/category/article/feed/" rel="self" type="application/rss+xml" />
	<link>http://everburning.com</link>
	<description>picking at the fringes of reality</description>
	<lastBuildDate>Wed, 04 Jan 2012 18:42:26 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Stage left: Enter Goliath</title>
		<link>http://everburning.com/news/stage-left-enter-goliath/</link>
		<comments>http://everburning.com/news/stage-left-enter-goliath/#comments</comments>
		<pubDate>Tue, 08 Mar 2011 19:27:59 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Article]]></category>
		<category><![CDATA[Goliath]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://everburning.com/?p=752</guid>
		<description><![CDATA[<p><img class="alignleft size-thumbnail wp-image-760" title="_MG_9957" src="http://everburning.com/wp-content/uploads/2011/03/MG_9957-150x150.jpg" alt="" width="150" height="150" />Over at <a href="http://labs.postrank.com">PostRank Labs</a> we&#8217;ve released the current version of our API server framework to the wild. Allow me to introduce <a href="http://goliath.io">Goliath</a>. Goliath is built on the back of the work at <a href="http://postrank.com">PostRank</a>, <a href="http://code.macournoyer.com/thin/">Thin</a>, <a href="http://www.sinatrarb.com/">Sinatra</a>, <a href="https://github.com/copiousfreetime/http-parser.rb">http-parser.rb</a> and various other projects.</p>
<p>We&#8217;ve been using a version of Goliath internally for over a year serving a sustained 500 requests/sec and shuttling around gigabytes of data.</p>
<p>How did we get here you&#8217;re asking? Our API servers from the beginning have been built on the awesome  <a href="http://rubyeventmachine.org">EventMachine</a> library. The last several iterations have used a threaded model. A request comes in, a thread is grabbed from the pool, request is processed, thread goes back to the pool. This was cool and worked quite well for us for several years. The problem we ran into, we were getting spaghetti code. A lot of our APIs call other APIs internally. Handling the callbacks, error backs and various other delayed operations was getting difficult to maintain.</p>
<p>Then along wandered <a href="http://ruby-lang.org">Ruby 1.9</a> with these cool little things called <a href="http://www.ruby-doc.org/core-1.9/classes/Fiber.html">Fibers</a>. Fibers allow us to build synchronous looking code backed by asynchronous calls. To achieve this we&#8217;re using the <a href="http://github.com/igrigorik/em-synchrony">EM-Synchrony</a> library. Tying this back into Goliath, as each request comes in, a new Fiber is created, the request is processed within that Fiber and, when completed, the Fiber goes away.</p>
<p>Once we had the foundation in place we went a step further and built a few extra little goodies into Goliath including <a href="https://github.com/postrank-labs/goliath/blob/master/examples/async_upload.rb">streaming requests</a> and <a href="https://github.com/postrank-labs/goliath/blob/master/examples/stream.rb">responses</a>. This lets us do some cool things like hook AMQP exchanges up to a Goliath API to pipe content through a streaming response.</p>
<p>Using http-parser.rb allows us to open up the option of alternate Ruby VMs. At the time of writing, Goliath has been tested with MRI, JRuby and Rubinius. MRI is currently the fastest VM for Goliath but there is lots of work going on in the alternate VMs to make them faster.</p>
<p>Ok, ok, enough of the hype, lets see some code already.</p>
<p>Well, first we need to get Goliath installed. In general this is pretty simple, you can use your system Ruby, RVM or some other system and all you need to do is:</p>
<div class="dean_ch" style="white-space: wrap;">gem install goliath</div>
<p>What we&#8217;re going to build here is an HTTP proxy that stores information about each request/response into <a href="http://www.mongodb.org/">MongoDB</a>. (The full code listing can be found in <a href="https://gist.github.com/7c2fe52d73fb290690cf">this gist</a>.)</p>
<p>Before we dig into the code, a little on how Goliath works. By default, Goliath will execute your API when you run the API file. In order to do this, the API file has to have a snake cased name based on the class name. So, our <code>HttpLog</code> API will live in a file called <code>http_log.rb</code>. When you need to add configuration to your API you&#8217;ll do this by creating a <code>config</code> directory in the same directory as your file. You then create a file in the <code>config</code> directory with the same name as your API. In our case, we&#8217;ll create a <code>config/http_log.rb</code> file to hold our configuration.</p>
<p>Goliath has been built to be <a href="http://rack.rubyforge.org/">Rack</a>-aware. This means we can make use of Rack middlewares, as long as they&#8217;re async safe or have been wrapped by <a href="https://github.com/rkh/async-rack">async-rack</a>.</p>
<p>We&#8217;re going to build some specs along with our API. Create <code>spec/http_log_spec.rb</code> and we can get this party started.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw3">require</span> <span class="st0">&#8216;rubygems&#8217;</span><br />
<span class="kw3">require</span> <span class="st0">&#8216;goliath/test_helper&#8217;</span><br />
<span class="kw3">require</span> <span class="kw4">File</span>.<span class="me1">expand_path</span><span class="br0">&#40;</span><span class="kw4">File</span>.<span class="me1">join</span><span class="br0">&#40;</span><span class="kw4">File</span>.<span class="me1">dirname</span><span class="br0">&#40;</span><span class="kw2">__FILE__</span><span class="br0">&#41;</span>, <span class="st0">&#8216;..&#8217;</span>, <span class="st0">&#8216;http_log&#8217;</span><span class="br0">&#41;</span><span class="br0">&#41;</span></p>
<p>describe HttpLog <span class="kw1">do</span><br />
&nbsp; <span class="kw1">include</span> <span class="re2">Goliath::TestHelper</span></p>
<p>&nbsp; let<span class="br0">&#40;</span><span class="re3">:err</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> <span class="kw3">Proc</span>.<span class="me1">new</span> <span class="br0">&#123;</span> |c| <span class="kw3">fail</span> <span class="st0">&quot;HTTP Request failed #{c.response}&quot;</span> <span class="br0">&#125;</span> <span class="br0">&#125;</span></p>
<p>&nbsp; it <span class="st0">&#8216;responds to requests&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>With Goliath we&#8217;ve provided a simple <code>goliath/test_helper</code> file that makes the testing of APIs a little easier. Once you&#8217;ve required the library, you just need to <code>include Goliath::TestHelper</code> and you can start using the helper methods. These include the <code>with_api</code> and <code>get_request</code> methods that are being used above. The <code>with_api</code> call will start an instance of our API running on port 9000 and anything done within its block will executing inside an EM reactor. This method will not return until <code>stop</code> is executed.</p>
<p>We&#8217;re, essentially, creating an integration test suite for our API. Everything except for the API launching will be tested by the test suite. The configuration files will be parsed, all the middlewares will be loaded and executed. Everything that your normal application would do except for the launching.</p>
<p>Calling <code>get_request</code> will send a GET to our API. The first parameter is any query parameters to send to the API. In our case, we send nothing. The second is an error handler to add. This is fired if we are unable to communicate with the API. For this example, we&#8217;re just using the RSpec <code>fail</code> method to fail the spec in the error handler.</p>
<p>The block passed to <code>get_request</code> will be executed and provided the request object when the request is complete. This allows us to look at the response headers and response body. In this case, we&#8217;re just verifying that server responds with a 200 code. When you&#8217;re using <code>get_request</code> or <code>post_request</code> a default error and callback handler will be installed that calls <code>stop</code> for you.</p>
<p>With that, running the spec should fail. Let&#8217;s create a simple API to make it pass. The following code in <code>http_log.rb</code> will give us a passing spec.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="co1">#!/usr/bin/env ruby</span></p>
<p><span class="kw3">require</span> <span class="st0">&#8216;rubygems&#8217;</span><br />
<span class="kw3">require</span> <span class="st0">&#8216;goliath&#8217;</span></p>
<p><span class="kw1">class</span> HttpLog &lt; <span class="re2">Goliath::API</span><br />
&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, <span class="br0">&#123;</span><span class="br0">&#125;</span>, <span class="st0">&quot;Hello&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>You can execute the API with <code>./http_log.rb -sv</code> which will launch the API on a default port of 9000.</p>
<p>The <code>-sv</code> flags tell the API to log to STDOUT (s) and use verbose logging (v). You can run <code>./http_log.rb -h</code> to see a list of all the default options provided by Goliath. You can add <a href="https://github.com/postrank-labs/goliath/blob/master/examples/conf_test.rb">your own options</a> as well if you need special argument handling for your API.</p>
<p>With the API running we can query the API using curl.</p>
<div class="dean_ch" style="white-space: wrap;">dj2@titania ~ $ curl localhost:<span class="nu0">9000</span><br />
Hello</div>
<p>Running the spec test again our test case should also pass at this point.</p>
<p>To make our lives easier, we&#8217;re going to use the <code>Rack::Reloader</code> middleware to handle reloading our Rack middleware on each request.</p>
<p>This is done by adding <code>use ::Rack::Reloader, 0</code> into our API. Since we only want reloading when we&#8217;re doing development we&#8217;re going to make the <code>use</code> statement conditional on the Goliath environment. By default Goliath executes in a development environment. We can confirm this by calling <code>Goliath.dev?</code> which will return <code>true</code> if the current environment is development.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">class</span> HttpLog &lt; <span class="re2">Goliath::API</span><br />
&nbsp; use ::<span class="re2">Rack::Reloader</span>, <span class="nu0">0</span> <span class="kw1">if</span> Goliath.<span class="me1">dev</span>?</p>
<p>&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, <span class="br0">&#123;</span><span class="br0">&#125;</span>, <span class="st0">&quot;Hello&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>You&#8217;ll notice that we didn&#8217;t create another file to place the middleware. With Goliath, the middleware is built into the API class itself. We&#8217;ve found this is a lot easier to deal with when you keep everything together in the same file. (You can also add <a href="https://github.com/postrank-labs/goliath/wiki/Plugins">plugins</a> in the same fashion by using the <code>plugin</code> keyword.)</p>
<p>The next step is to start forwarding to our backend server. To do this, we&#8217;ll specify in our configuration file the forwarder URL. Create <code>config/http_log.rb</code> and add the following:</p>
<div class="dean_ch" style="white-space: wrap;">config<span class="br0">&#91;</span><span class="st0">&#8216;forwarder&#8217;</span><span class="br0">&#93;</span> = <span class="st0">&#8216;http://localhost:8080&#8242;</span></div>
<p>The <code>config</code> hash allows us to store configuration data that will become available through the <code>env</code> parameter to the <code>response</code> method. In this case, we&#8217;ve specified we want our forwarder to send requests to <code>http://localhost:8080</code>.</p>
<p>In order to test this, I&#8217;m going to create a Goliath API server inside our spec files. I&#8217;ll run the server on port 8080 and check for our server response in the response from our API. Add the following to the top of the spec file.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">class</span> Responder &lt; <span class="re2">Goliath::API</span><br />
&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, <span class="br0">&#123;</span><span class="st0">&quot;Special&quot;</span> =&gt; <span class="st0">&quot;Header&quot;</span><span class="br0">&#125;</span>, <span class="st0">&quot;Hello World&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>And the following spec:</p>
<div class="dean_ch" style="white-space: wrap;">it <span class="st0">&#8216;forwards to our API server&#8217;</span> <span class="kw1">do</span><br />
&nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&#8216;Special&#8217;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&#8216;Header&#8217;</span><br />
&nbsp; &nbsp; &nbsp; c.<span class="me1">response</span>.<span class="me1">should</span> == <span class="st0">&#8216;Hello from Responder&#8217;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></div>
<p>This is similar to our previous spec except I&#8217;ve added a <code>server(Responder, 8080)</code> call to launch an instance of our <code>Responder</code> API on port 8080. Running this test should fail since we aren&#8217;t sending or receiving any data to the proxied API.</p>
<p>As a first pass, we&#8217;ll send the request and proxy the responses back.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{env.forwarder}&quot;</span><span class="br0">&#41;</span>.<span class="me1">get</span></p>
<p>&nbsp; <span class="br0">&#91;</span>req.<span class="me1">response_header</span>.<span class="me1">status</span>, req.<span class="me1">response_header</span>, req.<span class="me1">response</span><span class="br0">&#93;</span><br />
<span class="kw1">end</span></div>
<p>So, that&#8217;s cool. (Make sure you add the <code>server(Responder, 8080)</code> to the first example after doing this or it will end up failing.) There is one, non-apparent problem. It turns out the headers get transformed by <code>EM-HTTP-Request</code>. When we go to log this stuff we&#8217;ll want the non-transformed headers so we need to transform them back into the normal HTTP header format. Let&#8217;s start with a couple tests:</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;HTTP header handling&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;transforms back properly&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; hl = HttpLog.<span class="me1">new</span><br />
&nbsp; &nbsp; hl.<span class="me1">to_http_header</span><span class="br0">&#40;</span><span class="st0">&quot;SPECIAL&quot;</span><span class="br0">&#41;</span>.<span class="me1">should</span> == <span class="st0">&#8216;Special&#8217;</span><br />
&nbsp; &nbsp; hl.<span class="me1">to_http_header</span><span class="br0">&#40;</span><span class="st0">&quot;CONTENT_TYPE&quot;</span><span class="br0">&#41;</span>.<span class="me1">should</span> == <span class="st0">&#8216;Content-Type&#8217;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>Simple enough, we need to fix the casing and change <code>_</code> into <code>-</code>. Let&#8217;s add that to our HttpLog class.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> to_http_header<span class="br0">&#40;</span>k<span class="br0">&#41;</span><br />
&nbsp; k.<span class="me1">downcase</span>.<span class="kw3">split</span><span class="br0">&#40;</span><span class="st0">&#8216;_&#8217;</span><span class="br0">&#41;</span>.<span class="me1">collect</span> <span class="br0">&#123;</span> |e| e.<span class="me1">capitalize</span> <span class="br0">&#125;</span>.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&#8216;-&#8217;</span><span class="br0">&#41;</span><br />
<span class="kw1">end</span></div>
<p>That should make our transform tests pass and we can build the change into the <code>response</code> method by changing it to look like:</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{env.forwarder}&quot;</span><span class="br0">&#41;</span>.<span class="me1">get</span></p>
<p>&nbsp; response_headers = <span class="br0">&#123;</span><span class="br0">&#125;</span><br />
&nbsp; req.<span class="me1">response_header</span>.<span class="me1">each_pair</span> <span class="kw1">do</span> |k, v|<br />
&nbsp; &nbsp; response_headers<span class="br0">&#91;</span>to_http_header<span class="br0">&#40;</span>k<span class="br0">&#41;</span><span class="br0">&#93;</span> = v<br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="br0">&#91;</span>req.<span class="me1">response_header</span>.<span class="me1">status</span>, response_headers, req.<span class="me1">response</span><span class="br0">&#93;</span><br />
<span class="kw1">end</span></div>
<p>Cool, so now we&#8217;re forwarding all requests to the proxied server. There is a bit of missing information that we need, that being the headers, query parameters, request path and different types of requests from just GET.</p>
<p>Lets start with the query parameters. To test this I&#8217;m going to augment our <code>Responder</code> class to return the request parameters as part of the headers.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">class</span> Responder &lt; <span class="re2">Goliath::API</span><br />
&nbsp; use <span class="re2">Goliath::Rack::Params</span></p>
<p>&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; query_params = env.<span class="me1">params</span>.<span class="me1">collect</span> <span class="br0">&#123;</span> |param| param.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;: &quot;</span><span class="br0">&#41;</span> <span class="br0">&#125;</span><br />
&nbsp; &nbsp; headers = <span class="br0">&#123;</span><span class="st0">&quot;Special&quot;</span> =&gt; <span class="st0">&quot;Header&quot;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;Params&quot;</span> =&gt; query_params.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;|&quot;</span><span class="br0">&#41;</span><span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, headers, <span class="st0">&quot;Hello from Responder&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>We&#8217;re using another middleware here, <code>Goliath::Rack::Params</code><code>, which will parse the query and body parameters and put them into the </code><code>params</code> hash of the environment. Using that we can send the parameters back as a header in the response.</p>
<p>The test for query parameters is pretty simple, pass them in, make sure they come back:</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;query parameters&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;forwards the query parameters&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span>:query =&gt; <span class="br0">&#123;</span>:first =&gt; <span class="re3">:foo</span>, <span class="re3">:second</span> =&gt; <span class="re3">:bar</span>, <span class="re3">:third</span> =&gt; <span class="re3">:baz</span><span class="br0">&#125;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&quot;PARAMS&quot;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&quot;first: foo|second: bar|third: baz&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>To make this pass, we just need to <code>use Goliath::Rack::Params</code> and change the <code>EM::HttpRequest#get</code> request to provide the params. This is done by using: <code>req = EM::HttpRequest.new("#{env.forwarder}").get({:query =&gt; env.params})</code>.</p>
<p>Next up, let&#8217;s handle the request path. We&#8217;ll start with the spec.</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;request path&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;forwards the request path&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span>:path =&gt; <span class="st0">&#8216;/my/request/path&#8217;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&#8216;PATH&#8217;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&#8216;/my/request/path&#8217;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>Our <code>Responder</code> needs to return the path in the headers. This is done by adding <code>"Path" =&gt; env[Goliath::Request::REQUEST_PATH]</code> into the headers return hash. Making this spec pass is as simple as using the same <code>env[Goliath::Request::REQUEST_PATH]</code> data in our forwarder request.</p>
<div class="dean_ch" style="white-space: wrap;">req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{env.forwarder}#{env[Goliath::Request::REQUEST_PATH]}&quot;</span><span class="br0">&#41;</span>.<span class="me1">get</span><span class="br0">&#40;</span>params<span class="br0">&#41;</span></div>
<p>With the request path out of the way, let&#8217;s look into the headers. Goliath has built in facilities to do streaming requests. As part of that, we can hook into the <code>on_headers</code> method to receive a callback with the headers when they&#8217;re available. We can then store them into the environment and return them in a similar fashion to the query parameters.</p>
<p>We&#8217;re going to use the same <code>on_headers</code> callback in the <code>Responder</code> class to give us access to the headers. Then, the same as with query params, we&#8217;ll return them in a header for the response.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">class</span> Responder &lt; <span class="re2">Goliath::API</span><br />
&nbsp; use <span class="re2">Goliath::Rack::Params</span></p>
<p>&nbsp; <span class="kw1">def</span> on_headers<span class="br0">&#40;</span>env, headers<span class="br0">&#41;</span><br />
&nbsp; &nbsp; env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span> = headers<br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; query_params = env.<span class="me1">params</span>.<span class="me1">collect</span> <span class="br0">&#123;</span> |param| param.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;: &quot;</span><span class="br0">&#41;</span> <span class="br0">&#125;</span><br />
&nbsp; &nbsp; query_headers = env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span>.<span class="me1">collect</span> <span class="br0">&#123;</span> |param| param.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;: &quot;</span><span class="br0">&#41;</span> <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; headers = <span class="br0">&#123;</span><span class="st0">&quot;Special&quot;</span> =&gt; <span class="st0">&quot;Header&quot;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;Params&quot;</span> =&gt; query_params.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;|&quot;</span><span class="br0">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;Path&quot;</span> =&gt; env<span class="br0">&#91;</span><span class="re2">Goliath::Request::REQUEST_PATH</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;Headers&quot;</span> =&gt; query_headers.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;|&quot;</span><span class="br0">&#41;</span><span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, headers, <span class="st0">&quot;Hello from Responder&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>With a spec test similar to the query example as well.</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;headers&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;forwards the headers&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span>:head =&gt; <span class="br0">&#123;</span>:first =&gt; <span class="re3">:foo</span>, <span class="re3">:second</span> =&gt; <span class="re3">:bar</span><span class="br0">&#125;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&quot;HEADERS&quot;</span><span class="br0">&#93;</span>.<span class="me1">should</span> =~ /First: foo\|Second: bar/<br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>Let&#8217;s make the tests pass:</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> on_headers<span class="br0">&#40;</span>env, headers<span class="br0">&#41;</span><br />
&nbsp; env.<span class="me1">logger</span>.<span class="me1">info</span> <span class="st0">&#8216;proxying new request: &#8216;</span> + headers.<span class="me1">inspect</span><br />
&nbsp; env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span> = headers<br />
<span class="kw1">end</span></div>
<p>You&#8217;ll notice I added a call to <code>env.logger.info</code>. Goliath comes with built in logging capabilities. The logger is based on <code>Log4r</code> and is accessed through the environment. The Goliath environment is a subclass of <code>Hash</code> and can be accessed as such to store and retrieve information.</p>
<p>Once we&#8217;ve got the headers stored, adding them to our request is a simple process.</p>
<div class="dean_ch" style="white-space: wrap;">params = <span class="br0">&#123;</span>:head =&gt; env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span>, <span class="re3">:query</span> =&gt; env.<span class="me1">params</span><span class="br0">&#125;</span><br />
req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{env.forwarder}#{env[Goliath::Request::REQUEST_PATH]}&quot;</span><span class="br0">&#41;</span>.<span class="me1">get</span><span class="br0">&#40;</span>params<span class="br0">&#41;</span></div>
<p>One step left and our proxy is complete. Let&#8217;s send the right request method through to the proxied server (we&#8217;re just going to do GET and POST in this example). First step, add <code>"Method" =&gt; env[Goliath::Request::REQUEST_METHOD]</code> to the headers returned from our <code>Responder</code> API.</p>
<p>We can then add the specs:</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;request method&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;forwards GET requests&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&quot;METHOD&quot;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&quot;GET&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; it <span class="st0">&#8216;forwards POST requests&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; post_request<span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&quot;METHOD&quot;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&quot;POST&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>To make this pass we&#8217;ll use the <code>Goliath::Request::REQUEST_METHOD</code> to determine the right type of request to make. You&#8217;ll notice I&#8217;m putting the response into a <code>resp</code> variable, so the references to <code>req</code> in the rest of the method need to be update as well.</p>
<div class="dean_ch" style="white-space: wrap;">req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{forwarder}#{env[Goliath::Request::REQUEST_PATH]}&quot;</span><span class="br0">&#41;</span><br />
resp = <span class="kw1">case</span><span class="br0">&#40;</span>env<span class="br0">&#91;</span><span class="re2">Goliath::Request::REQUEST_METHOD</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">when</span> <span class="st0">&#8216;GET&#8217;</span> &nbsp;<span class="kw1">then</span> req.<span class="me1">get</span><span class="br0">&#40;</span>params<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">when</span> <span class="st0">&#8216;POST&#8217;</span> <span class="kw1">then</span> req.<span class="me1">post</span><span class="br0">&#40;</span>params.<span class="me1">merge</span><span class="br0">&#40;</span><span class="re3">:body</span> =&gt; env<span class="br0">&#91;</span><span class="re2">Goliath::Request::RACK_INPUT</span><span class="br0">&#93;</span>.<span class="me1">read</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">else</span> <span class="kw3">p</span> <span class="st0">&quot;UNKNOWN METHOD #{env[Goliath::Request::REQUEST_METHOD]}&quot;</span><br />
<span class="kw1">end</span></div>
<p>The only significant change from the previous version, in the POST request, we need to send the body data through to the forwarded server. The body is stored in a <code>StringIO</code> object which we can <code>read</code>. The object is stored in the environment under the <code>Goliath::Request::RACK_INPUT</code> key.</p>
<p>With that done, we should now have a functioning HTTP proxy.</p>
<p>Let&#8217;s take a look at how we can hook this up to <em>MongoDB</em> to give us the <em>Log</em> part of our the API name.</p>
<p>In order to talk to MongoDB we&#8217;re going to use the <em>em-mongo</em> gem. Since we&#8217;re working in an asynchronous environment we may have several requests being processed at the same time, so we&#8217;re going to wrap our MongoDB connection into a connection pool. There is connection pool logic built into <em>em-synchrony</em> for us to utilize.</p>
<p>I&#8217;m also, for the sake of this tutorial, going to say we don&#8217;t want to create a connection to a real Mongo instance when we&#8217;re running our spec tests. So, I&#8217;m going to restrict the connection creation to only happen in development mode.</p>
<p>Make sense? Ok, add the following to your <em>config/http_log.rb file.</em></p>
<div class="dean_ch" style="white-space: wrap;">environment<span class="br0">&#40;</span><span class="re3">:development</span><span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; config<span class="br0">&#91;</span><span class="st0">&#8216;mongo&#8217;</span><span class="br0">&#93;</span> = <span class="re2">EventMachine::Synchrony::ConnectionPool</span>.<span class="me1">new</span><span class="br0">&#40;</span>size: <span class="nu0">20</span><span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; conn = <span class="re2">EM::Mongo::Connection</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&#8216;localhost&#8217;</span>, <span class="nu0">27017</span>, <span class="nu0">1</span>, <span class="br0">&#123;</span>:reconnect_in =&gt; <span class="nu0">1</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; conn.<span class="me1">db</span><span class="br0">&#40;</span><span class="st0">&#8216;http_log&#8217;</span><span class="br0">&#41;</span>.<span class="me1">collection</span><span class="br0">&#40;</span><span class="st0">&#8216;aggregators&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>The <code>environment(:development)</code> will cause this code block to only execute if we&#8217;re in development mode. We could have also passed <code>:test</code> or <code>:production</code> depending on which mode we want. Along with a single option, you can also provide an array. So, we could have said <code>environment([:test, :development])</code> to execute in both test and development mode but not in production mode.</p>
<p>I&#8217;m going to leave it as an exercise to you, my reader, to figure out the synchrony and mongo code we&#8217;re using in the connection. Let&#8217;s just leave it at, we will now have access to a MongoDB collection object in <code>config['mongo']</code> which we can use in our application.</p>
<p>Just before we return the status, headers and body we&#8217;re going to record the request into Mongo. We&#8217;ll do that by adding:</p>
<div class="dean_ch" style="white-space: wrap;">record<span class="br0">&#40;</span>process_time, resp, env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span>, response_headers<span class="br0">&#41;</span></div>
<p>The implementation of <code>record</code> uses the various environment variables we&#8217;ve seen above to do it&#8217;s work:</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> record<span class="br0">&#40;</span>resp, client_headers, response_headers<span class="br0">&#41;</span><br />
&nbsp; e = env<br />
&nbsp; EM.<span class="me1">next_tick</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; doc = <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; request: <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; http_method: e<span class="br0">&#91;</span><span class="re2">Goliath::Request::REQUEST_METHOD</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; path: e<span class="br0">&#91;</span><span class="re2">Goliath::Request::REQUEST_PATH</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; headers: client_headers,<br />
&nbsp; &nbsp; &nbsp; &nbsp; params: e.<span class="me1">params</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>,<br />
&nbsp; &nbsp; &nbsp; response: <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; status: resp.<span class="me1">response_header</span>.<span class="me1">status</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; length: resp.<span class="me1">response</span>.<span class="me1">length</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; headers: response_headers,<br />
&nbsp; &nbsp; &nbsp; &nbsp; body: resp.<span class="me1">response</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>,<br />
&nbsp; &nbsp; &nbsp; date: <span class="kw4">Time</span>.<span class="me1">now</span>.<span class="me1">to_i</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">if</span> e<span class="br0">&#91;</span><span class="re2">Goliath::Request::RACK_INPUT</span><span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; doc<span class="br0">&#91;</span><span class="re3">:request</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="re3">:body</span><span class="br0">&#93;</span> = e<span class="br0">&#91;</span><span class="re2">Goliath::Request::RACK_INPUT</span><span class="br0">&#93;</span>.<span class="me1">read</span><br />
&nbsp; &nbsp; <span class="kw1">end</span></p>
<p>&nbsp; &nbsp; e.<span class="me1">mongo</span>.<span class="me1">insert</span><span class="br0">&#40;</span>doc<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>There are a couple of things to note, first, I&#8217;m doing this work in an <code>EM.next_tick</code> block so that I don&#8217;t block the response from returning to the client. Because I&#8217;m doing this in the next tick I&#8217;m going to lose access to the <em>env</em> so I tuck it away into a <code>e</code> variable which gets bound up with the <code>next_tick</code> block.</p>
<p>Finally, the last thing we do is call <code>e.mongo.insert(doc)</code> which will access the <code>config['mongo']</code> object we created earlier and call the <code>insert</code> method.</p>
<p>With that code added, all of our tests fail. We&#8217;re going to need to create a <em>mongo</em> key in the environment that the tests can use.</p>
<p>To do that, I&#8217;ve created a <code>mock_mongo</code> method:</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> mock_mongo<br />
&nbsp; <span class="re1">@api_server</span>.<span class="me1">config</span><span class="br0">&#91;</span><span class="st0">&#8216;mongo&#8217;</span><span class="br0">&#93;</span> = mock<span class="br0">&#40;</span><span class="st0">&#8216;mongo&#8217;</span><span class="br0">&#41;</span>.<span class="me1">as_null_object</span><br />
<span class="kw1">end</span></div>
<p>Which I call inside the <code>with_api(HttpLog)</code> block. I&#8217;ll leave it as an exercise to the reader to create some specs around the <code>record</code> method.</p>
<p>The default API server created inside <code>with_api</code> will be made available to the user through the <code>@api_server</code> variable.</p>
<p>With that, you should have a working proxy logger API. If you run webserver on port 8080 and execute the our http logger you can then make requests to port 9000 and see the results logged into mongo. Looking in mongo you should see something similar to:</p>
<pre>connecting to: test
&gt; use http_log
switched to db http_log
&gt; db.aggregators.find();
{ "_id" : ObjectId("4d6efbbad3547d28f3000001"),
  "request" : {
                "http_method" : "GET",
                "path" : "/",
                "headers" : {
                              "User-Agent" : "curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3",
                              "Host" : "localhost:9000",
                              "Accept" : "*/*",
                              "Version" : "1.1" },
                "params" : { "echo" : "test" },
                "body" : "" },
   "response" : {
                "status" : 200,
                "length" : 19,
                "headers" : {
                              "Content-Type" : "application/json; charset=utf-8",
                              "Server" : "PostRank Goliath API Server",
                              "Vary" : "Accept",
                              "Content-Length" : "19",
                              "Date" : "Thu, 03 Mar 2011 02:23:54 GMT" },
                "body" : "{\"response\":\"test\"}" },
   "process_time" : 0.007593870162963867,
   "date" : 1299119034 }</pre>
<p>With that, have fun playing with Goliath. We&#8217;re hoping people find some interesting uses for the framework.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft size-thumbnail wp-image-760" title="_MG_9957" src="http://everburning.com/wp-content/uploads/2011/03/MG_9957-150x150.jpg" alt="" width="150" height="150" />Over at <a href="http://labs.postrank.com">PostRank Labs</a> we&#8217;ve released the current version of our API server framework to the wild. Allow me to introduce <a href="http://goliath.io">Goliath</a>. Goliath is built on the back of the work at <a href="http://postrank.com">PostRank</a>, <a href="http://code.macournoyer.com/thin/">Thin</a>, <a href="http://www.sinatrarb.com/">Sinatra</a>, <a href="https://github.com/copiousfreetime/http-parser.rb">http-parser.rb</a> and various other projects.</p>
<p>We&#8217;ve been using a version of Goliath internally for over a year serving a sustained 500 requests/sec and shuttling around gigabytes of data.</p>
<p>How did we get here you&#8217;re asking? Our API servers from the beginning have been built on the awesome  <a href="http://rubyeventmachine.org">EventMachine</a> library. The last several iterations have used a threaded model. A request comes in, a thread is grabbed from the pool, request is processed, thread goes back to the pool. This was cool and worked quite well for us for several years. The problem we ran into, we were getting spaghetti code. A lot of our APIs call other APIs internally. Handling the callbacks, error backs and various other delayed operations was getting difficult to maintain.</p>
<p>Then along wandered <a href="http://ruby-lang.org">Ruby 1.9</a> with these cool little things called <a href="http://www.ruby-doc.org/core-1.9/classes/Fiber.html">Fibers</a>. Fibers allow us to build synchronous looking code backed by asynchronous calls. To achieve this we&#8217;re using the <a href="http://github.com/igrigorik/em-synchrony">EM-Synchrony</a> library. Tying this back into Goliath, as each request comes in, a new Fiber is created, the request is processed within that Fiber and, when completed, the Fiber goes away.</p>
<p>Once we had the foundation in place we went a step further and built a few extra little goodies into Goliath including <a href="https://github.com/postrank-labs/goliath/blob/master/examples/async_upload.rb">streaming requests</a> and <a href="https://github.com/postrank-labs/goliath/blob/master/examples/stream.rb">responses</a>. This lets us do some cool things like hook AMQP exchanges up to a Goliath API to pipe content through a streaming response.</p>
<p>Using http-parser.rb allows us to open up the option of alternate Ruby VMs. At the time of writing, Goliath has been tested with MRI, JRuby and Rubinius. MRI is currently the fastest VM for Goliath but there is lots of work going on in the alternate VMs to make them faster.</p>
<p>Ok, ok, enough of the hype, lets see some code already.</p>
<p>Well, first we need to get Goliath installed. In general this is pretty simple, you can use your system Ruby, RVM or some other system and all you need to do is:</p>
<div class="dean_ch" style="white-space: wrap;">gem install goliath</div>
<p>What we&#8217;re going to build here is an HTTP proxy that stores information about each request/response into <a href="http://www.mongodb.org/">MongoDB</a>. (The full code listing can be found in <a href="https://gist.github.com/7c2fe52d73fb290690cf">this gist</a>.)</p>
<p>Before we dig into the code, a little on how Goliath works. By default, Goliath will execute your API when you run the API file. In order to do this, the API file has to have a snake cased name based on the class name. So, our <code>HttpLog</code> API will live in a file called <code>http_log.rb</code>. When you need to add configuration to your API you&#8217;ll do this by creating a <code>config</code> directory in the same directory as your file. You then create a file in the <code>config</code> directory with the same name as your API. In our case, we&#8217;ll create a <code>config/http_log.rb</code> file to hold our configuration.</p>
<p>Goliath has been built to be <a href="http://rack.rubyforge.org/">Rack</a>-aware. This means we can make use of Rack middlewares, as long as they&#8217;re async safe or have been wrapped by <a href="https://github.com/rkh/async-rack">async-rack</a>.</p>
<p>We&#8217;re going to build some specs along with our API. Create <code>spec/http_log_spec.rb</code> and we can get this party started.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw3">require</span> <span class="st0">&#8216;rubygems&#8217;</span><br />
<span class="kw3">require</span> <span class="st0">&#8216;goliath/test_helper&#8217;</span><br />
<span class="kw3">require</span> <span class="kw4">File</span>.<span class="me1">expand_path</span><span class="br0">&#40;</span><span class="kw4">File</span>.<span class="me1">join</span><span class="br0">&#40;</span><span class="kw4">File</span>.<span class="me1">dirname</span><span class="br0">&#40;</span><span class="kw2">__FILE__</span><span class="br0">&#41;</span>, <span class="st0">&#8216;..&#8217;</span>, <span class="st0">&#8216;http_log&#8217;</span><span class="br0">&#41;</span><span class="br0">&#41;</span></p>
<p>describe HttpLog <span class="kw1">do</span><br />
&nbsp; <span class="kw1">include</span> <span class="re2">Goliath::TestHelper</span></p>
<p>&nbsp; let<span class="br0">&#40;</span><span class="re3">:err</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> <span class="kw3">Proc</span>.<span class="me1">new</span> <span class="br0">&#123;</span> |c| <span class="kw3">fail</span> <span class="st0">&quot;HTTP Request failed #{c.response}&quot;</span> <span class="br0">&#125;</span> <span class="br0">&#125;</span></p>
<p>&nbsp; it <span class="st0">&#8216;responds to requests&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>With Goliath we&#8217;ve provided a simple <code>goliath/test_helper</code> file that makes the testing of APIs a little easier. Once you&#8217;ve required the library, you just need to <code>include Goliath::TestHelper</code> and you can start using the helper methods. These include the <code>with_api</code> and <code>get_request</code> methods that are being used above. The <code>with_api</code> call will start an instance of our API running on port 9000 and anything done within its block will executing inside an EM reactor. This method will not return until <code>stop</code> is executed.</p>
<p>We&#8217;re, essentially, creating an integration test suite for our API. Everything except for the API launching will be tested by the test suite. The configuration files will be parsed, all the middlewares will be loaded and executed. Everything that your normal application would do except for the launching.</p>
<p>Calling <code>get_request</code> will send a GET to our API. The first parameter is any query parameters to send to the API. In our case, we send nothing. The second is an error handler to add. This is fired if we are unable to communicate with the API. For this example, we&#8217;re just using the RSpec <code>fail</code> method to fail the spec in the error handler.</p>
<p>The block passed to <code>get_request</code> will be executed and provided the request object when the request is complete. This allows us to look at the response headers and response body. In this case, we&#8217;re just verifying that server responds with a 200 code. When you&#8217;re using <code>get_request</code> or <code>post_request</code> a default error and callback handler will be installed that calls <code>stop</code> for you.</p>
<p>With that, running the spec should fail. Let&#8217;s create a simple API to make it pass. The following code in <code>http_log.rb</code> will give us a passing spec.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="co1">#!/usr/bin/env ruby</span></p>
<p><span class="kw3">require</span> <span class="st0">&#8216;rubygems&#8217;</span><br />
<span class="kw3">require</span> <span class="st0">&#8216;goliath&#8217;</span></p>
<p><span class="kw1">class</span> HttpLog &lt; <span class="re2">Goliath::API</span><br />
&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, <span class="br0">&#123;</span><span class="br0">&#125;</span>, <span class="st0">&quot;Hello&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>You can execute the API with <code>./http_log.rb -sv</code> which will launch the API on a default port of 9000.</p>
<p>The <code>-sv</code> flags tell the API to log to STDOUT (s) and use verbose logging (v). You can run <code>./http_log.rb -h</code> to see a list of all the default options provided by Goliath. You can add <a href="https://github.com/postrank-labs/goliath/blob/master/examples/conf_test.rb">your own options</a> as well if you need special argument handling for your API.</p>
<p>With the API running we can query the API using curl.</p>
<div class="dean_ch" style="white-space: wrap;">dj2@titania ~ $ curl localhost:<span class="nu0">9000</span><br />
Hello</div>
<p>Running the spec test again our test case should also pass at this point.</p>
<p>To make our lives easier, we&#8217;re going to use the <code>Rack::Reloader</code> middleware to handle reloading our Rack middleware on each request.</p>
<p>This is done by adding <code>use ::Rack::Reloader, 0</code> into our API. Since we only want reloading when we&#8217;re doing development we&#8217;re going to make the <code>use</code> statement conditional on the Goliath environment. By default Goliath executes in a development environment. We can confirm this by calling <code>Goliath.dev?</code> which will return <code>true</code> if the current environment is development.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">class</span> HttpLog &lt; <span class="re2">Goliath::API</span><br />
&nbsp; use ::<span class="re2">Rack::Reloader</span>, <span class="nu0">0</span> <span class="kw1">if</span> Goliath.<span class="me1">dev</span>?</p>
<p>&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, <span class="br0">&#123;</span><span class="br0">&#125;</span>, <span class="st0">&quot;Hello&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>You&#8217;ll notice that we didn&#8217;t create another file to place the middleware. With Goliath, the middleware is built into the API class itself. We&#8217;ve found this is a lot easier to deal with when you keep everything together in the same file. (You can also add <a href="https://github.com/postrank-labs/goliath/wiki/Plugins">plugins</a> in the same fashion by using the <code>plugin</code> keyword.)</p>
<p>The next step is to start forwarding to our backend server. To do this, we&#8217;ll specify in our configuration file the forwarder URL. Create <code>config/http_log.rb</code> and add the following:</p>
<div class="dean_ch" style="white-space: wrap;">config<span class="br0">&#91;</span><span class="st0">&#8216;forwarder&#8217;</span><span class="br0">&#93;</span> = <span class="st0">&#8216;http://localhost:8080&#8242;</span></div>
<p>The <code>config</code> hash allows us to store configuration data that will become available through the <code>env</code> parameter to the <code>response</code> method. In this case, we&#8217;ve specified we want our forwarder to send requests to <code>http://localhost:8080</code>.</p>
<p>In order to test this, I&#8217;m going to create a Goliath API server inside our spec files. I&#8217;ll run the server on port 8080 and check for our server response in the response from our API. Add the following to the top of the spec file.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">class</span> Responder &lt; <span class="re2">Goliath::API</span><br />
&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, <span class="br0">&#123;</span><span class="st0">&quot;Special&quot;</span> =&gt; <span class="st0">&quot;Header&quot;</span><span class="br0">&#125;</span>, <span class="st0">&quot;Hello World&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>And the following spec:</p>
<div class="dean_ch" style="white-space: wrap;">it <span class="st0">&#8216;forwards to our API server&#8217;</span> <span class="kw1">do</span><br />
&nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&#8216;Special&#8217;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&#8216;Header&#8217;</span><br />
&nbsp; &nbsp; &nbsp; c.<span class="me1">response</span>.<span class="me1">should</span> == <span class="st0">&#8216;Hello from Responder&#8217;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></div>
<p>This is similar to our previous spec except I&#8217;ve added a <code>server(Responder, 8080)</code> call to launch an instance of our <code>Responder</code> API on port 8080. Running this test should fail since we aren&#8217;t sending or receiving any data to the proxied API.</p>
<p>As a first pass, we&#8217;ll send the request and proxy the responses back.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{env.forwarder}&quot;</span><span class="br0">&#41;</span>.<span class="me1">get</span></p>
<p>&nbsp; <span class="br0">&#91;</span>req.<span class="me1">response_header</span>.<span class="me1">status</span>, req.<span class="me1">response_header</span>, req.<span class="me1">response</span><span class="br0">&#93;</span><br />
<span class="kw1">end</span></div>
<p>So, that&#8217;s cool. (Make sure you add the <code>server(Responder, 8080)</code> to the first example after doing this or it will end up failing.) There is one, non-apparent problem. It turns out the headers get transformed by <code>EM-HTTP-Request</code>. When we go to log this stuff we&#8217;ll want the non-transformed headers so we need to transform them back into the normal HTTP header format. Let&#8217;s start with a couple tests:</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;HTTP header handling&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;transforms back properly&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; hl = HttpLog.<span class="me1">new</span><br />
&nbsp; &nbsp; hl.<span class="me1">to_http_header</span><span class="br0">&#40;</span><span class="st0">&quot;SPECIAL&quot;</span><span class="br0">&#41;</span>.<span class="me1">should</span> == <span class="st0">&#8216;Special&#8217;</span><br />
&nbsp; &nbsp; hl.<span class="me1">to_http_header</span><span class="br0">&#40;</span><span class="st0">&quot;CONTENT_TYPE&quot;</span><span class="br0">&#41;</span>.<span class="me1">should</span> == <span class="st0">&#8216;Content-Type&#8217;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>Simple enough, we need to fix the casing and change <code>_</code> into <code>-</code>. Let&#8217;s add that to our HttpLog class.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> to_http_header<span class="br0">&#40;</span>k<span class="br0">&#41;</span><br />
&nbsp; k.<span class="me1">downcase</span>.<span class="kw3">split</span><span class="br0">&#40;</span><span class="st0">&#8216;_&#8217;</span><span class="br0">&#41;</span>.<span class="me1">collect</span> <span class="br0">&#123;</span> |e| e.<span class="me1">capitalize</span> <span class="br0">&#125;</span>.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&#8216;-&#8217;</span><span class="br0">&#41;</span><br />
<span class="kw1">end</span></div>
<p>That should make our transform tests pass and we can build the change into the <code>response</code> method by changing it to look like:</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{env.forwarder}&quot;</span><span class="br0">&#41;</span>.<span class="me1">get</span></p>
<p>&nbsp; response_headers = <span class="br0">&#123;</span><span class="br0">&#125;</span><br />
&nbsp; req.<span class="me1">response_header</span>.<span class="me1">each_pair</span> <span class="kw1">do</span> |k, v|<br />
&nbsp; &nbsp; response_headers<span class="br0">&#91;</span>to_http_header<span class="br0">&#40;</span>k<span class="br0">&#41;</span><span class="br0">&#93;</span> = v<br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="br0">&#91;</span>req.<span class="me1">response_header</span>.<span class="me1">status</span>, response_headers, req.<span class="me1">response</span><span class="br0">&#93;</span><br />
<span class="kw1">end</span></div>
<p>Cool, so now we&#8217;re forwarding all requests to the proxied server. There is a bit of missing information that we need, that being the headers, query parameters, request path and different types of requests from just GET.</p>
<p>Lets start with the query parameters. To test this I&#8217;m going to augment our <code>Responder</code> class to return the request parameters as part of the headers.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">class</span> Responder &lt; <span class="re2">Goliath::API</span><br />
&nbsp; use <span class="re2">Goliath::Rack::Params</span></p>
<p>&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; query_params = env.<span class="me1">params</span>.<span class="me1">collect</span> <span class="br0">&#123;</span> |param| param.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;: &quot;</span><span class="br0">&#41;</span> <span class="br0">&#125;</span><br />
&nbsp; &nbsp; headers = <span class="br0">&#123;</span><span class="st0">&quot;Special&quot;</span> =&gt; <span class="st0">&quot;Header&quot;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;Params&quot;</span> =&gt; query_params.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;|&quot;</span><span class="br0">&#41;</span><span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, headers, <span class="st0">&quot;Hello from Responder&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>We&#8217;re using another middleware here, <code>Goliath::Rack::Params</code><code>, which will parse the query and body parameters and put them into the </code><code>params</code> hash of the environment. Using that we can send the parameters back as a header in the response.</p>
<p>The test for query parameters is pretty simple, pass them in, make sure they come back:</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;query parameters&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;forwards the query parameters&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span>:query =&gt; <span class="br0">&#123;</span>:first =&gt; <span class="re3">:foo</span>, <span class="re3">:second</span> =&gt; <span class="re3">:bar</span>, <span class="re3">:third</span> =&gt; <span class="re3">:baz</span><span class="br0">&#125;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&quot;PARAMS&quot;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&quot;first: foo|second: bar|third: baz&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>To make this pass, we just need to <code>use Goliath::Rack::Params</code> and change the <code>EM::HttpRequest#get</code> request to provide the params. This is done by using: <code>req = EM::HttpRequest.new("#{env.forwarder}").get({:query =&gt; env.params})</code>.</p>
<p>Next up, let&#8217;s handle the request path. We&#8217;ll start with the spec.</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;request path&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;forwards the request path&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span>:path =&gt; <span class="st0">&#8216;/my/request/path&#8217;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&#8216;PATH&#8217;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&#8216;/my/request/path&#8217;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>Our <code>Responder</code> needs to return the path in the headers. This is done by adding <code>"Path" =&gt; env[Goliath::Request::REQUEST_PATH]</code> into the headers return hash. Making this spec pass is as simple as using the same <code>env[Goliath::Request::REQUEST_PATH]</code> data in our forwarder request.</p>
<div class="dean_ch" style="white-space: wrap;">req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{env.forwarder}#{env[Goliath::Request::REQUEST_PATH]}&quot;</span><span class="br0">&#41;</span>.<span class="me1">get</span><span class="br0">&#40;</span>params<span class="br0">&#41;</span></div>
<p>With the request path out of the way, let&#8217;s look into the headers. Goliath has built in facilities to do streaming requests. As part of that, we can hook into the <code>on_headers</code> method to receive a callback with the headers when they&#8217;re available. We can then store them into the environment and return them in a similar fashion to the query parameters.</p>
<p>We&#8217;re going to use the same <code>on_headers</code> callback in the <code>Responder</code> class to give us access to the headers. Then, the same as with query params, we&#8217;ll return them in a header for the response.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">class</span> Responder &lt; <span class="re2">Goliath::API</span><br />
&nbsp; use <span class="re2">Goliath::Rack::Params</span></p>
<p>&nbsp; <span class="kw1">def</span> on_headers<span class="br0">&#40;</span>env, headers<span class="br0">&#41;</span><br />
&nbsp; &nbsp; env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span> = headers<br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; query_params = env.<span class="me1">params</span>.<span class="me1">collect</span> <span class="br0">&#123;</span> |param| param.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;: &quot;</span><span class="br0">&#41;</span> <span class="br0">&#125;</span><br />
&nbsp; &nbsp; query_headers = env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span>.<span class="me1">collect</span> <span class="br0">&#123;</span> |param| param.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;: &quot;</span><span class="br0">&#41;</span> <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; headers = <span class="br0">&#123;</span><span class="st0">&quot;Special&quot;</span> =&gt; <span class="st0">&quot;Header&quot;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;Params&quot;</span> =&gt; query_params.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;|&quot;</span><span class="br0">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;Path&quot;</span> =&gt; env<span class="br0">&#91;</span><span class="re2">Goliath::Request::REQUEST_PATH</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;Headers&quot;</span> =&gt; query_headers.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;|&quot;</span><span class="br0">&#41;</span><span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, headers, <span class="st0">&quot;Hello from Responder&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>With a spec test similar to the query example as well.</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;headers&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;forwards the headers&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span>:head =&gt; <span class="br0">&#123;</span>:first =&gt; <span class="re3">:foo</span>, <span class="re3">:second</span> =&gt; <span class="re3">:bar</span><span class="br0">&#125;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&quot;HEADERS&quot;</span><span class="br0">&#93;</span>.<span class="me1">should</span> =~ /First: foo\|Second: bar/<br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>Let&#8217;s make the tests pass:</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> on_headers<span class="br0">&#40;</span>env, headers<span class="br0">&#41;</span><br />
&nbsp; env.<span class="me1">logger</span>.<span class="me1">info</span> <span class="st0">&#8216;proxying new request: &#8216;</span> + headers.<span class="me1">inspect</span><br />
&nbsp; env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span> = headers<br />
<span class="kw1">end</span></div>
<p>You&#8217;ll notice I added a call to <code>env.logger.info</code>. Goliath comes with built in logging capabilities. The logger is based on <code>Log4r</code> and is accessed through the environment. The Goliath environment is a subclass of <code>Hash</code> and can be accessed as such to store and retrieve information.</p>
<p>Once we&#8217;ve got the headers stored, adding them to our request is a simple process.</p>
<div class="dean_ch" style="white-space: wrap;">params = <span class="br0">&#123;</span>:head =&gt; env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span>, <span class="re3">:query</span> =&gt; env.<span class="me1">params</span><span class="br0">&#125;</span><br />
req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{env.forwarder}#{env[Goliath::Request::REQUEST_PATH]}&quot;</span><span class="br0">&#41;</span>.<span class="me1">get</span><span class="br0">&#40;</span>params<span class="br0">&#41;</span></div>
<p>One step left and our proxy is complete. Let&#8217;s send the right request method through to the proxied server (we&#8217;re just going to do GET and POST in this example). First step, add <code>"Method" =&gt; env[Goliath::Request::REQUEST_METHOD]</code> to the headers returned from our <code>Responder</code> API.</p>
<p>We can then add the specs:</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;request method&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;forwards GET requests&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&quot;METHOD&quot;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&quot;GET&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; it <span class="st0">&#8216;forwards POST requests&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; post_request<span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&quot;METHOD&quot;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&quot;POST&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>To make this pass we&#8217;ll use the <code>Goliath::Request::REQUEST_METHOD</code> to determine the right type of request to make. You&#8217;ll notice I&#8217;m putting the response into a <code>resp</code> variable, so the references to <code>req</code> in the rest of the method need to be update as well.</p>
<div class="dean_ch" style="white-space: wrap;">req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{forwarder}#{env[Goliath::Request::REQUEST_PATH]}&quot;</span><span class="br0">&#41;</span><br />
resp = <span class="kw1">case</span><span class="br0">&#40;</span>env<span class="br0">&#91;</span><span class="re2">Goliath::Request::REQUEST_METHOD</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">when</span> <span class="st0">&#8216;GET&#8217;</span> &nbsp;<span class="kw1">then</span> req.<span class="me1">get</span><span class="br0">&#40;</span>params<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">when</span> <span class="st0">&#8216;POST&#8217;</span> <span class="kw1">then</span> req.<span class="me1">post</span><span class="br0">&#40;</span>params.<span class="me1">merge</span><span class="br0">&#40;</span><span class="re3">:body</span> =&gt; env<span class="br0">&#91;</span><span class="re2">Goliath::Request::RACK_INPUT</span><span class="br0">&#93;</span>.<span class="me1">read</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">else</span> <span class="kw3">p</span> <span class="st0">&quot;UNKNOWN METHOD #{env[Goliath::Request::REQUEST_METHOD]}&quot;</span><br />
<span class="kw1">end</span></div>
<p>The only significant change from the previous version, in the POST request, we need to send the body data through to the forwarded server. The body is stored in a <code>StringIO</code> object which we can <code>read</code>. The object is stored in the environment under the <code>Goliath::Request::RACK_INPUT</code> key.</p>
<p>With that done, we should now have a functioning HTTP proxy.</p>
<p>Let&#8217;s take a look at how we can hook this up to <em>MongoDB</em> to give us the <em>Log</em> part of our the API name.</p>
<p>In order to talk to MongoDB we&#8217;re going to use the <em>em-mongo</em> gem. Since we&#8217;re working in an asynchronous environment we may have several requests being processed at the same time, so we&#8217;re going to wrap our MongoDB connection into a connection pool. There is connection pool logic built into <em>em-synchrony</em> for us to utilize.</p>
<p>I&#8217;m also, for the sake of this tutorial, going to say we don&#8217;t want to create a connection to a real Mongo instance when we&#8217;re running our spec tests. So, I&#8217;m going to restrict the connection creation to only happen in development mode.</p>
<p>Make sense? Ok, add the following to your <em>config/http_log.rb file.</em></p>
<div class="dean_ch" style="white-space: wrap;">environment<span class="br0">&#40;</span><span class="re3">:development</span><span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; config<span class="br0">&#91;</span><span class="st0">&#8216;mongo&#8217;</span><span class="br0">&#93;</span> = <span class="re2">EventMachine::Synchrony::ConnectionPool</span>.<span class="me1">new</span><span class="br0">&#40;</span>size: <span class="nu0">20</span><span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; conn = <span class="re2">EM::Mongo::Connection</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&#8216;localhost&#8217;</span>, <span class="nu0">27017</span>, <span class="nu0">1</span>, <span class="br0">&#123;</span>:reconnect_in =&gt; <span class="nu0">1</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; conn.<span class="me1">db</span><span class="br0">&#40;</span><span class="st0">&#8216;http_log&#8217;</span><span class="br0">&#41;</span>.<span class="me1">collection</span><span class="br0">&#40;</span><span class="st0">&#8216;aggregators&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>The <code>environment(:development)</code> will cause this code block to only execute if we&#8217;re in development mode. We could have also passed <code>:test</code> or <code>:production</code> depending on which mode we want. Along with a single option, you can also provide an array. So, we could have said <code>environment([:test, :development])</code> to execute in both test and development mode but not in production mode.</p>
<p>I&#8217;m going to leave it as an exercise to you, my reader, to figure out the synchrony and mongo code we&#8217;re using in the connection. Let&#8217;s just leave it at, we will now have access to a MongoDB collection object in <code>config['mongo']</code> which we can use in our application.</p>
<p>Just before we return the status, headers and body we&#8217;re going to record the request into Mongo. We&#8217;ll do that by adding:</p>
<div class="dean_ch" style="white-space: wrap;">record<span class="br0">&#40;</span>process_time, resp, env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span>, response_headers<span class="br0">&#41;</span></div>
<p>The implementation of <code>record</code> uses the various environment variables we&#8217;ve seen above to do it&#8217;s work:</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> record<span class="br0">&#40;</span>resp, client_headers, response_headers<span class="br0">&#41;</span><br />
&nbsp; e = env<br />
&nbsp; EM.<span class="me1">next_tick</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; doc = <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; request: <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; http_method: e<span class="br0">&#91;</span><span class="re2">Goliath::Request::REQUEST_METHOD</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; path: e<span class="br0">&#91;</span><span class="re2">Goliath::Request::REQUEST_PATH</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; headers: client_headers,<br />
&nbsp; &nbsp; &nbsp; &nbsp; params: e.<span class="me1">params</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>,<br />
&nbsp; &nbsp; &nbsp; response: <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; status: resp.<span class="me1">response_header</span>.<span class="me1">status</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; length: resp.<span class="me1">response</span>.<span class="me1">length</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; headers: response_headers,<br />
&nbsp; &nbsp; &nbsp; &nbsp; body: resp.<span class="me1">response</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>,<br />
&nbsp; &nbsp; &nbsp; date: <span class="kw4">Time</span>.<span class="me1">now</span>.<span class="me1">to_i</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">if</span> e<span class="br0">&#91;</span><span class="re2">Goliath::Request::RACK_INPUT</span><span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; doc<span class="br0">&#91;</span><span class="re3">:request</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="re3">:body</span><span class="br0">&#93;</span> = e<span class="br0">&#91;</span><span class="re2">Goliath::Request::RACK_INPUT</span><span class="br0">&#93;</span>.<span class="me1">read</span><br />
&nbsp; &nbsp; <span class="kw1">end</span></p>
<p>&nbsp; &nbsp; e.<span class="me1">mongo</span>.<span class="me1">insert</span><span class="br0">&#40;</span>doc<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>There are a couple of things to note, first, I&#8217;m doing this work in an <code>EM.next_tick</code> block so that I don&#8217;t block the response from returning to the client. Because I&#8217;m doing this in the next tick I&#8217;m going to lose access to the <em>env</em> so I tuck it away into a <code>e</code> variable which gets bound up with the <code>next_tick</code> block.</p>
<p>Finally, the last thing we do is call <code>e.mongo.insert(doc)</code> which will access the <code>config['mongo']</code> object we created earlier and call the <code>insert</code> method.</p>
<p>With that code added, all of our tests fail. We&#8217;re going to need to create a <em>mongo</em> key in the environment that the tests can use.</p>
<p>To do that, I&#8217;ve created a <code>mock_mongo</code> method:</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> mock_mongo<br />
&nbsp; <span class="re1">@api_server</span>.<span class="me1">config</span><span class="br0">&#91;</span><span class="st0">&#8216;mongo&#8217;</span><span class="br0">&#93;</span> = mock<span class="br0">&#40;</span><span class="st0">&#8216;mongo&#8217;</span><span class="br0">&#41;</span>.<span class="me1">as_null_object</span><br />
<span class="kw1">end</span></div>
<p>Which I call inside the <code>with_api(HttpLog)</code> block. I&#8217;ll leave it as an exercise to the reader to create some specs around the <code>record</code> method.</p>
<p>The default API server created inside <code>with_api</code> will be made available to the user through the <code>@api_server</code> variable.</p>
<p>With that, you should have a working proxy logger API. If you run webserver on port 8080 and execute the our http logger you can then make requests to port 9000 and see the results logged into mongo. Looking in mongo you should see something similar to:</p>
<pre>connecting to: test
&gt; use http_log
switched to db http_log
&gt; db.aggregators.find();
{ "_id" : ObjectId("4d6efbbad3547d28f3000001"),
  "request" : {
                "http_method" : "GET",
                "path" : "/",
                "headers" : {
                              "User-Agent" : "curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3",
                              "Host" : "localhost:9000",
                              "Accept" : "*/*",
                              "Version" : "1.1" },
                "params" : { "echo" : "test" },
                "body" : "" },
   "response" : {
                "status" : 200,
                "length" : 19,
                "headers" : {
                              "Content-Type" : "application/json; charset=utf-8",
                              "Server" : "PostRank Goliath API Server",
                              "Vary" : "Accept",
                              "Content-Length" : "19",
                              "Date" : "Thu, 03 Mar 2011 02:23:54 GMT" },
                "body" : "{\"response\":\"test\"}" },
   "process_time" : 0.007593870162963867,
   "date" : 1299119034 }</pre>
<p>With that, have fun playing with Goliath. We&#8217;re hoping people find some interesting uses for the framework.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/stage-left-enter-goliath/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Download and XML parsing with HotCocoa</title>
		<link>http://everburning.com/news/download-and-xml-parsing-with-hotcocoa/</link>
		<comments>http://everburning.com/news/download-and-xml-parsing-with-hotcocoa/#comments</comments>
		<pubDate>Mon, 08 Feb 2010 05:47:16 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Article]]></category>
		<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[HotCocoa]]></category>
		<category><![CDATA[MacRuby]]></category>

		<guid isPermaLink="false">http://everburning.com/?p=700</guid>
		<description><![CDATA[<p><a href="http://everburning.com/wp-content/uploads/2010/02/IMG_7535.jpg"><img src="http://everburning.com/wp-content/uploads/2010/02/IMG_7535-150x150.jpg" alt="" title="IMG_7535" width="150" height="150" class="alignleft size-thumbnail wp-image-702" /></a>I&#8217;ve been working on <a href="http://github.com/dj2/Rife">Rife</a>, a <a href="http://reader.google.com">Google Reader</a> client, over the last few days and have been digging my way through some more <a href="http://github.com/richkilmer/hotcocoa">HotCocoa</a> mappings. I figured the best way to remember some of this stuff is to write it down so, the following will look at synchronous and asynchronous downloads and writing a XML parser in HotCocoa/<a href="http://macruby.org">MacRuby</a>.</p>
<p>So, what are we creating you ask? Well, as I said, I&#8217;ve been playing with Google Reader APIs and we&#8217;re going to do a synchronous request to Google for an identifier token. Once we&#8217;ve successfully authenticated we&#8217;re going to make an asynchronous request for our unread items. We&#8217;ll then parse the resulting XML document and spit the titles out to the console.</p>
<p>As with any HotCocoa application the easiest way to get started is to have system setup the shell of our application. We&#8217;ll use the <code>hotcocoa</code> command to create our application which I&#8217;m calling <em>titles</em>.</p>
<pre><code>titania:example dj2$ hotcocoa titles</code></pre>
<p>In order to authenticate to Google we&#8217;re going to need your username and password. Since I&#8217;m going to do the output to the console for demonstration purposes I&#8217;ll use the main application window to show the fields for username and password and a save button.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw3">require</span> <span class="st0">&#8216;rubygems&#8217;</span><br />
<span class="kw3">require</span> <span class="st0">&#8216;hotcocoa&#8217;</span></p>
<p><span class="kw1">class</span> Application<br />
&nbsp; <span class="kw1">include</span> HotCocoa<br />
&nbsp; <br />
&nbsp; <span class="kw1">def</span> start<br />
&nbsp; &nbsp; application<span class="br0">&#40;</span><span class="re3">:name</span> =&gt; <span class="st0">&quot;Titles&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |app|<br />
&nbsp; &nbsp; &nbsp; app.<span class="me1">delegate</span> = <span class="kw2">self</span></p>
<p>&nbsp; &nbsp; &nbsp; window<span class="br0">&#40;</span><span class="re3">:frame</span> =&gt; <span class="br0">&#91;</span><span class="nu0">100</span>, <span class="nu0">100</span>, <span class="nu0">200</span>, <span class="nu0">200</span><span class="br0">&#93;</span>, <span class="re3">:title</span> =&gt; <span class="st0">&quot;Titles&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |win|<br />
&nbsp; &nbsp; &nbsp; &nbsp; win.<span class="me1">center</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; win.<span class="me1">will_close</span> <span class="br0">&#123;</span> <span class="kw3">exit</span> <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; label<span class="br0">&#40;</span><span class="re3">:text</span> =&gt; <span class="st0">&quot;Username&quot;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">false</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; <span class="re1">@username_field</span> = text_field<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">false</span>, <span class="re3">:expand</span> =&gt; <span class="br0">&#91;</span><span class="re3">:width</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; label<span class="br0">&#40;</span><span class="re3">:text</span> =&gt; <span class="st0">&quot;Password&quot;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">false</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; <span class="re1">@password_field</span> = secure_text_field<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">false</span>, <span class="re3">:expand</span> =&gt; <span class="br0">&#91;</span><span class="re3">:width</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; save = button<span class="br0">&#40;</span><span class="re3">:title</span> =&gt; <span class="st0">&quot;save&quot;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">true</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |button|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; button.<span class="me1">on_action</span> <span class="br0">&#123;</span> authenticate <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">@username_field</span>.<span class="me1">setNextKeyView</span><span class="br0">&#40;</span>@password_field<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">@password_field</span>.<span class="me1">setNextKeyView</span><span class="br0">&#40;</span>save<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; save.<span class="me1">setNextKeyView</span><span class="br0">&#40;</span>@username_field<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> authenticate<br />
&nbsp; &nbsp; <span class="kw3">puts</span> <span class="st0">&quot;DO AUTH #{@username_field.to_s} #{@password_field.to_s}&quot;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></p>
<p>Application.<span class="me1">new</span>.<span class="me1">start</span></div>
<p>If you run the application by executing <code>macrake</code> in the <em>titles</em> directory you should see the main application window. Typing something into the <em>username</em> and <em>password</em> fields and pressing <em>save</em> you should see something similar to the following in your terminal.</p>
<pre><code>DO AUTH test test</code></pre>
<p>With our framework setup let&#8217;s get to the interesting stuff. First up, authenticating so we can retrieve our identifier from Google. We&#8217;re going to make a synchronous request to retrieve the identifier and, if successful, call a method to start retrieving our reading list.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> authenticate<br />
&nbsp; username = <span class="re1">@username_field</span>.<span class="me1">stringValue</span><br />
&nbsp; password = <span class="re1">@password_field</span>.<span class="me1">stringValue</span></p>
<p>&nbsp; query = <span class="st0">&quot;https://www.google.com/accounts/ClientLogin?&quot;</span> +<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;Email=#{CGI.escape(username)}&amp;Passwd=#{CGI.escape(password.to_s)}&quot;</span> +<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;&amp;source=HotCocoaExample&amp;service=reader&quot;</span></p>
<p>&nbsp; url = NSURL.<span class="me1">URLWithString</span><span class="br0">&#40;</span>query<span class="br0">&#41;</span><br />
&nbsp; request = NSMutableURLRequest.<span class="me1">requestWithURL</span><span class="br0">&#40;</span>url<span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;HotCocoaExample&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;source&quot;</span><span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;2&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;GData-Version&quot;</span><span class="br0">&#41;</span></p>
<p>&nbsp; response = Pointer.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;@&quot;</span><span class="br0">&#41;</span><br />
&nbsp; data = NSURLConnection.<span class="me1">sendSynchronousRequest</span><span class="br0">&#40;</span>request, returningResponse:response, error:<span class="kw2">nil</span><span class="br0">&#41;</span><br />
&nbsp; data = NSString.<span class="me1">alloc</span>.<span class="me1">initWithData</span><span class="br0">&#40;</span>data, encoding:NSUTF8StringEncoding<span class="br0">&#41;</span></p>
<p>&nbsp; <span class="kw1">if</span> data =~ /^SID=<span class="br0">&#40;</span>.<span class="me1">*</span><span class="br0">&#41;</span>\n/<br />
&nbsp; &nbsp; <span class="re1">@sid</span> = $<span class="nu0">1</span></p>
<p>&nbsp; &nbsp; retrieve_reading_list<br />
&nbsp; <span class="kw1">else</span><br />
&nbsp; &nbsp; <span class="kw3">raise</span> <span class="kw4">Exception</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;Authentication failed with: #{data}&quot;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></p>
<p><span class="kw1">def</span> retrieve_reading_list<br />
&nbsp; <span class="kw3">puts</span> <span class="re1">@sid</span><br />
<span class="kw1">end</span></div>
<p>If you add the above to your application, and add <code>require 'cgi'</code>, you should be able to run the program, put in your username and password and get a long line of characters spit out on the terminal. Those characters are your Google <em>SID</em>.</p>
<p>Let&#8217;s look a bit closer at what we&#8217;re doing in the <code>authenticate</code> method. We start by grabbing the <code>stringValue</code> for the username and password fields. Then, using these values, we build the query string needed for authentication. This query string is used to build a URL object by calling <code>NSURL.URLWithString(query)</code>. With the URL in hand we can start building our request object. This is done by calling <code>NSMutableURLRequest.requestWithURL(url)</code>. I&#8217;m using the mutable version of the request as I want to add a few extra header values. These are both added with <code>addValue(value, forHTTPHeaderField:field)</code>.</p>
<p>When we execute our request the system is going to want to put our response object somewhere. In the Cocoa version the method accepts a <code>NSURLResponse **response</code> parameter. In order to handle the response we need to create a <code>Pointer</code> object which is a MacRuby object for handling these pointers to objects. We want our pointer to point to an object so we use <code>Pointer.new("@")</code>.</p>
<p>With the response setup we call <code>NSURLConnection.sendSynchronousRequest</code> and provide our request and response objects. I don&#8217;t care about the error, but if you do, you&#8217;d want to pass in something similar to our response pointer. The request will return a <code>NSData</code> object which we convert to a string using the <code>initWithData</code> initialization method of <code>NSString</code>.</p>
<p>With the string in hand we try to extract our <em>SID</em> and, if successful, execute the <code>retrieve_reading_list</code> method which just spits out the <em>SID</em>.</p>
<p>OK, cool, we&#8217;ve now got our authentication token and are ready to move onto the asynchronous request to get our reading list.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> retrieve_reading_list<br />
&nbsp; query = <span class="st0">&quot;https://www.google.com/reader/atom/user/-/state/com.google/reading-list?&quot;</span> +<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;xt=user/-/state/com.google/read&amp;ck=#{Time.now.to_i * 1000}&amp;n=2&quot;</span></p>
<p>&nbsp; url = NSURL.<span class="me1">URLWithString</span><span class="br0">&#40;</span>query<span class="br0">&#41;</span><br />
&nbsp; request = NSMutableURLRequest.<span class="me1">requestWithURL</span><span class="br0">&#40;</span>url<span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;HotCocoaExample&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;source&quot;</span><span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;2&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;GData-Version&quot;</span><span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;SID=#{@sid}&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;Cookie&quot;</span><span class="br0">&#41;</span></p>
<p>&nbsp; NSURLConnection.<span class="me1">connectionWithRequest</span><span class="br0">&#40;</span>request, delegate:<span class="kw2">self</span><span class="br0">&#41;</span><br />
<span class="kw1">end</span></p>
<p><span class="kw1">def</span> connectionDidFinishLoading<span class="br0">&#40;</span>conn<span class="br0">&#41;</span><br />
&nbsp; <span class="kw3">puts</span> NSString.<span class="me1">alloc</span>.<span class="me1">initWithData</span><span class="br0">&#40;</span>@receivedData, encoding:NSUTF8StringEncoding<span class="br0">&#41;</span><br />
<span class="kw1">end</span></p>
<p><span class="kw1">def</span> connection<span class="br0">&#40;</span>conn, didReceiveResponse:response<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">if</span> response.<span class="me1">statusCode</span> != <span class="nu0">200</span><br />
&nbsp; &nbsp; <span class="kw3">puts</span> <span class="st0">&quot;BAD STATUS: #{response.statusCode}&quot;</span><br />
&nbsp; &nbsp; <span class="kw3">p</span> response.<span class="me1">allHeaderFields</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></p>
<p><span class="kw1">def</span> connection<span class="br0">&#40;</span>conn, didReceiveData:data<span class="br0">&#41;</span><br />
&nbsp; <span class="re1">@receivedData</span> ||= NSMutableData.<span class="me1">new</span><br />
&nbsp; <span class="re1">@receivedData</span>.<span class="me1">appendData</span><span class="br0">&#40;</span>data<span class="br0">&#41;</span><br />
<span class="kw1">end</span></div>
<p>Similar to the synchronous method we start by building our query string, <code>NSURL</code> and <code>NSMutableURLRequest</code>. We&#8217;ve added a cookie to our request object to hold the <em>SID</em> retrieved earlier from Google.</p>
<p>We fire the request by calling <code>NSURLConnection.connectionWithRequest(request, delegate:self)</code>. We specific ourselves as the delegate for the connection. There are a few delegate methods we can implement to receive data and get notified of request states. These are:</p>
<ul>
<li><code>connectionDidFinishLoading(connection)</code></li>
<li><code>connection(connection, didReceiveResponse:response)</code></li>
<li><code>connection(connection, didReceiveData:data)</code></li>
</ul>
<p>We&#8217;ll look at our implementation of each of these callbacks in turn. First, in <code>connectionDidFinishLoading(conn)</code> we&#8217;re just printing out the data retrieved. We need to convert the data, similar to what we did in the synchronous request, from a <code>NSData</code> object to a <code>NSString</code> object.</p>
<p>In <code>connection(conn, didReceiveResponse:response)</code> we&#8217;re just checking to see if we got a 200 response code from the server. In all other cases we print an error.</p>
<p>The main work is done in <code>connection(conn, didReceiveData:data)</code> where we create a <code>NSMutableData</code> object if needed and append any data received into the mutable data object.</p>
<p>Running the code at this point should dump the first two items in your reading list to the console. The data will be a big mess of XML but we&#8217;ll look at parsing that in the next step.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> connectionDidFinishLoading<span class="br0">&#40;</span>conn<span class="br0">&#41;</span><br />
&nbsp; xml = HotCocoa.<span class="me1">xml_parser</span><span class="br0">&#40;</span><span class="re3">:data</span> =&gt; <span class="re1">@receivedData</span><span class="br0">&#41;</span><br />
&nbsp; <span class="re1">@receivedData</span> = <span class="kw2">nil</span></p>
<p>&nbsp; xml.<span class="me1">on_start_document</span> <span class="br0">&#123;</span> <span class="kw3">puts</span> <span class="st0">&quot;Starting Parse&quot;</span> <span class="br0">&#125;</span><br />
&nbsp; xml.<span class="me1">on_end_document</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; HotCocoa.<span class="me1">notification</span><span class="br0">&#40;</span><span class="re3">:post</span> =&gt; <span class="kw2">true</span>, <span class="re3">:name</span> =&gt; <span class="st0">&quot;all_entries_loaded&quot;</span>, <span class="re3">:object</span> =&gt; <span class="kw2">nil</span>, <span class="re3">:info</span> =&gt; <span class="kw2">nil</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
&nbsp; xml.<span class="me1">on_parse_error</span> <span class="br0">&#123;</span> |err| <span class="kw3">puts</span> <span class="st0">&quot;Parse error #{err.inspect}&quot;</span> <span class="br0">&#125;</span></p>
<p>&nbsp; xml.<span class="me1">on_cdata</span> <span class="br0">&#123;</span> |cdata| <span class="re1">@elem_text</span> += cdata.<span class="me1">to_s</span> <span class="br0">&#125;</span><br />
&nbsp; xml.<span class="me1">on_characters</span> <span class="br0">&#123;</span> |chars| <span class="re1">@elem_text</span> += chars.<span class="me1">to_s</span> <span class="br0">&#125;</span></p>
<p>&nbsp; xml.<span class="me1">on_start_element</span> <span class="kw1">do</span> |element, namespace, qualified_name, attributes|<br />
&nbsp; &nbsp; <span class="re1">@elem_text</span> = <span class="st0">&#8221;</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; xml.<span class="me1">on_end_element</span> <span class="kw1">do</span> |element, namespace, qualified_name|<br />
&nbsp; &nbsp; <span class="kw3">puts</span> <span class="re1">@elem_text</span> <span class="kw1">if</span> element == <span class="st0">&#8216;title&#8217;</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; xml.<span class="me1">parse</span><br />
<span class="kw1">end</span></div>
<p>We&#8217;re finally getting into some HotCocoa specific code with our XML parser. HotCocoa defines a mapping wrapper around <code>NSXMLParser</code> and provides a set of delegate methods. These delegates mean we don&#8217;t have to set our class as the delegate and create a bunch of methods. They mean we can attach our code as blocks on our XML object. All the better if you want to define a few parsers in one class.</p>
<p>We start off by creating a <code>HotCocoa.xml_parser</code>. The parser accepts <code>NSData</code> objects so we don&#8217;t need to convert our response data to a string. We then setup  eight callbacks. There are actually a bunch more callbacks that can be hooked up and you should look at the <code>xml_parser</code> mapping code to see if you need any of them. For our purposes, we only really care about eight.</p>
<p>The <code>on_start_document</code>, <code>on_end_document</code> and <code>on_parse_error</code> callbacks, as you can probably guess, get called when we start parsing, when we finish parsing and when we receive a parse error, respectively. We don&#8217;t really care about start in this example, but I put it in anyway. When we&#8217;ve completed parsing we send a notification and other application code can then listen for this notification and do anything it needs. If we wanted we could store the entries as they&#8217;re parsed and provide them to the <code>:object</code> key. This would make those entries available to anyone that receives the notification.</p>
<p>If we receive either CDATA, with <code>on_cdata</code>, or text, with <code>on_characters</code>, we append the content to our current elements text. When we receive the open tag of a new element, <code>on_start_element</code>, we dump our current element text as we&#8217;ve started a new element. We can also take a look at the elements name, attributes, namespace and qualified name, if desired.</p>
<p>Finally, in <code>on_end_element</code> we print out the current element text if the element we&#8217;re finishing has a name of <em>title</em>.</p>
<p>With all the callbacks configured we use <code>xml.parse</code> to start the parser. You should, if you run this example, see the titles and authors of the first two posts in your reading list. (The author name is also called title and I&#8217;m not bothering to check that the parent element is <em>entry</em> before spitting it out.)</p>
<p>That&#8217;s it. You can now make synchronous and asynchronous requests for content and parse any resulting XML.</p>
<p>One last thing before you go. Both of the requests we did above were <em>GET</em> requests. You can do other types of requests using the same methods as above you just need a slightly different setup for the request. You can see a <em>POST</em> request below.</p>
<div class="dean_ch" style="white-space: wrap;">request = NSMutableURLRequest.<span class="me1">requestWithURL</span><span class="br0">&#40;</span>url<span class="br0">&#41;</span><br />
request.<span class="me1">addValue</span><span class="br0">&#40;</span>SOURCE, forHTTPHeaderField:<span class="st0">&quot;source&quot;</span><span class="br0">&#41;</span><br />
request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;2&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;GData-Version&quot;</span><span class="br0">&#41;</span><br />
request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;SID=#{@sid}&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;Cookie&quot;</span><span class="br0">&#41;</span></p>
<p>body=<span class="st0">&quot;first=1&amp;second=2&amp;third=3&quot;</span></p>
<p>request.<span class="me1">setHTTPMethod</span><span class="br0">&#40;</span><span class="st0">&#8216;POST&#8217;</span><span class="br0">&#41;</span><br />
request.<span class="me1">setValue</span><span class="br0">&#40;</span><span class="st0">&#8216;application/x-www-form-urlencoded&#8217;</span>, forHTTPHeaderField:<span class="st0">&#8216;Content-Type&#8217;</span><span class="br0">&#41;</span><br />
request.<span class="me1">setValue</span><span class="br0">&#40;</span>body.<span class="me1">length</span>.<span class="me1">to_s</span>, forHTTPHeaderField:<span class="st0">&#8216;Content-Length&#8217;</span><span class="br0">&#41;</span><br />
request.<span class="me1">setHTTPBody</span><span class="br0">&#40;</span>body.<span class="me1">dataUsingEncoding</span><span class="br0">&#40;</span>NSASCIIStringEncoding<span class="br0">&#41;</span><span class="br0">&#41;</span></div>
<p>The first few lines should look familiar from creating our asynchronous request above. Since we&#8217;re going to be posting the data we use <code>setHTTPMethod('POST')</code> to setup the request method. We&#8217;ve form encoded the data so we set the appropriate <em>Content-Type</em> and set the <em>Content-Length</em>. Note, we convert the length to a string before sending to <code>setValue</code>. Finally, we set the body of the post with <code>setHTTPBody</code>. You need to convert the body string into a <code>NSData</code> object which we do with the <code>dataUsingEncoding</code> method. If you don&#8217;t convert the body to <code>NSData</code> you&#8217;ll end up sending a <code>nil</code> body with your post request.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p><a href="http://everburning.com/wp-content/uploads/2010/02/IMG_7535.jpg"><img src="http://everburning.com/wp-content/uploads/2010/02/IMG_7535-150x150.jpg" alt="" title="IMG_7535" width="150" height="150" class="alignleft size-thumbnail wp-image-702" /></a>I&#8217;ve been working on <a href="http://github.com/dj2/Rife">Rife</a>, a <a href="http://reader.google.com">Google Reader</a> client, over the last few days and have been digging my way through some more <a href="http://github.com/richkilmer/hotcocoa">HotCocoa</a> mappings. I figured the best way to remember some of this stuff is to write it down so, the following will look at synchronous and asynchronous downloads and writing a XML parser in HotCocoa/<a href="http://macruby.org">MacRuby</a>.</p>
<p>So, what are we creating you ask? Well, as I said, I&#8217;ve been playing with Google Reader APIs and we&#8217;re going to do a synchronous request to Google for an identifier token. Once we&#8217;ve successfully authenticated we&#8217;re going to make an asynchronous request for our unread items. We&#8217;ll then parse the resulting XML document and spit the titles out to the console.</p>
<p>As with any HotCocoa application the easiest way to get started is to have system setup the shell of our application. We&#8217;ll use the <code>hotcocoa</code> command to create our application which I&#8217;m calling <em>titles</em>.</p>
<pre><code>titania:example dj2$ hotcocoa titles</code></pre>
<p>In order to authenticate to Google we&#8217;re going to need your username and password. Since I&#8217;m going to do the output to the console for demonstration purposes I&#8217;ll use the main application window to show the fields for username and password and a save button.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw3">require</span> <span class="st0">&#8216;rubygems&#8217;</span><br />
<span class="kw3">require</span> <span class="st0">&#8216;hotcocoa&#8217;</span></p>
<p><span class="kw1">class</span> Application<br />
&nbsp; <span class="kw1">include</span> HotCocoa<br />
&nbsp; <br />
&nbsp; <span class="kw1">def</span> start<br />
&nbsp; &nbsp; application<span class="br0">&#40;</span><span class="re3">:name</span> =&gt; <span class="st0">&quot;Titles&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |app|<br />
&nbsp; &nbsp; &nbsp; app.<span class="me1">delegate</span> = <span class="kw2">self</span></p>
<p>&nbsp; &nbsp; &nbsp; window<span class="br0">&#40;</span><span class="re3">:frame</span> =&gt; <span class="br0">&#91;</span><span class="nu0">100</span>, <span class="nu0">100</span>, <span class="nu0">200</span>, <span class="nu0">200</span><span class="br0">&#93;</span>, <span class="re3">:title</span> =&gt; <span class="st0">&quot;Titles&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |win|<br />
&nbsp; &nbsp; &nbsp; &nbsp; win.<span class="me1">center</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; win.<span class="me1">will_close</span> <span class="br0">&#123;</span> <span class="kw3">exit</span> <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; label<span class="br0">&#40;</span><span class="re3">:text</span> =&gt; <span class="st0">&quot;Username&quot;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">false</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; <span class="re1">@username_field</span> = text_field<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">false</span>, <span class="re3">:expand</span> =&gt; <span class="br0">&#91;</span><span class="re3">:width</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; label<span class="br0">&#40;</span><span class="re3">:text</span> =&gt; <span class="st0">&quot;Password&quot;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">false</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; <span class="re1">@password_field</span> = secure_text_field<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">false</span>, <span class="re3">:expand</span> =&gt; <span class="br0">&#91;</span><span class="re3">:width</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; save = button<span class="br0">&#40;</span><span class="re3">:title</span> =&gt; <span class="st0">&quot;save&quot;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">true</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |button|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; button.<span class="me1">on_action</span> <span class="br0">&#123;</span> authenticate <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">@username_field</span>.<span class="me1">setNextKeyView</span><span class="br0">&#40;</span>@password_field<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">@password_field</span>.<span class="me1">setNextKeyView</span><span class="br0">&#40;</span>save<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; save.<span class="me1">setNextKeyView</span><span class="br0">&#40;</span>@username_field<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> authenticate<br />
&nbsp; &nbsp; <span class="kw3">puts</span> <span class="st0">&quot;DO AUTH #{@username_field.to_s} #{@password_field.to_s}&quot;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></p>
<p>Application.<span class="me1">new</span>.<span class="me1">start</span></div>
<p>If you run the application by executing <code>macrake</code> in the <em>titles</em> directory you should see the main application window. Typing something into the <em>username</em> and <em>password</em> fields and pressing <em>save</em> you should see something similar to the following in your terminal.</p>
<pre><code>DO AUTH test test</code></pre>
<p>With our framework setup let&#8217;s get to the interesting stuff. First up, authenticating so we can retrieve our identifier from Google. We&#8217;re going to make a synchronous request to retrieve the identifier and, if successful, call a method to start retrieving our reading list.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> authenticate<br />
&nbsp; username = <span class="re1">@username_field</span>.<span class="me1">stringValue</span><br />
&nbsp; password = <span class="re1">@password_field</span>.<span class="me1">stringValue</span></p>
<p>&nbsp; query = <span class="st0">&quot;https://www.google.com/accounts/ClientLogin?&quot;</span> +<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;Email=#{CGI.escape(username)}&amp;Passwd=#{CGI.escape(password.to_s)}&quot;</span> +<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;&amp;source=HotCocoaExample&amp;service=reader&quot;</span></p>
<p>&nbsp; url = NSURL.<span class="me1">URLWithString</span><span class="br0">&#40;</span>query<span class="br0">&#41;</span><br />
&nbsp; request = NSMutableURLRequest.<span class="me1">requestWithURL</span><span class="br0">&#40;</span>url<span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;HotCocoaExample&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;source&quot;</span><span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;2&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;GData-Version&quot;</span><span class="br0">&#41;</span></p>
<p>&nbsp; response = Pointer.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;@&quot;</span><span class="br0">&#41;</span><br />
&nbsp; data = NSURLConnection.<span class="me1">sendSynchronousRequest</span><span class="br0">&#40;</span>request, returningResponse:response, error:<span class="kw2">nil</span><span class="br0">&#41;</span><br />
&nbsp; data = NSString.<span class="me1">alloc</span>.<span class="me1">initWithData</span><span class="br0">&#40;</span>data, encoding:NSUTF8StringEncoding<span class="br0">&#41;</span></p>
<p>&nbsp; <span class="kw1">if</span> data =~ /^SID=<span class="br0">&#40;</span>.<span class="me1">*</span><span class="br0">&#41;</span>\n/<br />
&nbsp; &nbsp; <span class="re1">@sid</span> = $<span class="nu0">1</span></p>
<p>&nbsp; &nbsp; retrieve_reading_list<br />
&nbsp; <span class="kw1">else</span><br />
&nbsp; &nbsp; <span class="kw3">raise</span> <span class="kw4">Exception</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;Authentication failed with: #{data}&quot;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></p>
<p><span class="kw1">def</span> retrieve_reading_list<br />
&nbsp; <span class="kw3">puts</span> <span class="re1">@sid</span><br />
<span class="kw1">end</span></div>
<p>If you add the above to your application, and add <code>require 'cgi'</code>, you should be able to run the program, put in your username and password and get a long line of characters spit out on the terminal. Those characters are your Google <em>SID</em>.</p>
<p>Let&#8217;s look a bit closer at what we&#8217;re doing in the <code>authenticate</code> method. We start by grabbing the <code>stringValue</code> for the username and password fields. Then, using these values, we build the query string needed for authentication. This query string is used to build a URL object by calling <code>NSURL.URLWithString(query)</code>. With the URL in hand we can start building our request object. This is done by calling <code>NSMutableURLRequest.requestWithURL(url)</code>. I&#8217;m using the mutable version of the request as I want to add a few extra header values. These are both added with <code>addValue(value, forHTTPHeaderField:field)</code>.</p>
<p>When we execute our request the system is going to want to put our response object somewhere. In the Cocoa version the method accepts a <code>NSURLResponse **response</code> parameter. In order to handle the response we need to create a <code>Pointer</code> object which is a MacRuby object for handling these pointers to objects. We want our pointer to point to an object so we use <code>Pointer.new("@")</code>.</p>
<p>With the response setup we call <code>NSURLConnection.sendSynchronousRequest</code> and provide our request and response objects. I don&#8217;t care about the error, but if you do, you&#8217;d want to pass in something similar to our response pointer. The request will return a <code>NSData</code> object which we convert to a string using the <code>initWithData</code> initialization method of <code>NSString</code>.</p>
<p>With the string in hand we try to extract our <em>SID</em> and, if successful, execute the <code>retrieve_reading_list</code> method which just spits out the <em>SID</em>.</p>
<p>OK, cool, we&#8217;ve now got our authentication token and are ready to move onto the asynchronous request to get our reading list.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> retrieve_reading_list<br />
&nbsp; query = <span class="st0">&quot;https://www.google.com/reader/atom/user/-/state/com.google/reading-list?&quot;</span> +<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;xt=user/-/state/com.google/read&amp;ck=#{Time.now.to_i * 1000}&amp;n=2&quot;</span></p>
<p>&nbsp; url = NSURL.<span class="me1">URLWithString</span><span class="br0">&#40;</span>query<span class="br0">&#41;</span><br />
&nbsp; request = NSMutableURLRequest.<span class="me1">requestWithURL</span><span class="br0">&#40;</span>url<span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;HotCocoaExample&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;source&quot;</span><span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;2&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;GData-Version&quot;</span><span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;SID=#{@sid}&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;Cookie&quot;</span><span class="br0">&#41;</span></p>
<p>&nbsp; NSURLConnection.<span class="me1">connectionWithRequest</span><span class="br0">&#40;</span>request, delegate:<span class="kw2">self</span><span class="br0">&#41;</span><br />
<span class="kw1">end</span></p>
<p><span class="kw1">def</span> connectionDidFinishLoading<span class="br0">&#40;</span>conn<span class="br0">&#41;</span><br />
&nbsp; <span class="kw3">puts</span> NSString.<span class="me1">alloc</span>.<span class="me1">initWithData</span><span class="br0">&#40;</span>@receivedData, encoding:NSUTF8StringEncoding<span class="br0">&#41;</span><br />
<span class="kw1">end</span></p>
<p><span class="kw1">def</span> connection<span class="br0">&#40;</span>conn, didReceiveResponse:response<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">if</span> response.<span class="me1">statusCode</span> != <span class="nu0">200</span><br />
&nbsp; &nbsp; <span class="kw3">puts</span> <span class="st0">&quot;BAD STATUS: #{response.statusCode}&quot;</span><br />
&nbsp; &nbsp; <span class="kw3">p</span> response.<span class="me1">allHeaderFields</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></p>
<p><span class="kw1">def</span> connection<span class="br0">&#40;</span>conn, didReceiveData:data<span class="br0">&#41;</span><br />
&nbsp; <span class="re1">@receivedData</span> ||= NSMutableData.<span class="me1">new</span><br />
&nbsp; <span class="re1">@receivedData</span>.<span class="me1">appendData</span><span class="br0">&#40;</span>data<span class="br0">&#41;</span><br />
<span class="kw1">end</span></div>
<p>Similar to the synchronous method we start by building our query string, <code>NSURL</code> and <code>NSMutableURLRequest</code>. We&#8217;ve added a cookie to our request object to hold the <em>SID</em> retrieved earlier from Google.</p>
<p>We fire the request by calling <code>NSURLConnection.connectionWithRequest(request, delegate:self)</code>. We specific ourselves as the delegate for the connection. There are a few delegate methods we can implement to receive data and get notified of request states. These are:</p>
<ul>
<li><code>connectionDidFinishLoading(connection)</code></li>
<li><code>connection(connection, didReceiveResponse:response)</code></li>
<li><code>connection(connection, didReceiveData:data)</code></li>
</ul>
<p>We&#8217;ll look at our implementation of each of these callbacks in turn. First, in <code>connectionDidFinishLoading(conn)</code> we&#8217;re just printing out the data retrieved. We need to convert the data, similar to what we did in the synchronous request, from a <code>NSData</code> object to a <code>NSString</code> object.</p>
<p>In <code>connection(conn, didReceiveResponse:response)</code> we&#8217;re just checking to see if we got a 200 response code from the server. In all other cases we print an error.</p>
<p>The main work is done in <code>connection(conn, didReceiveData:data)</code> where we create a <code>NSMutableData</code> object if needed and append any data received into the mutable data object.</p>
<p>Running the code at this point should dump the first two items in your reading list to the console. The data will be a big mess of XML but we&#8217;ll look at parsing that in the next step.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> connectionDidFinishLoading<span class="br0">&#40;</span>conn<span class="br0">&#41;</span><br />
&nbsp; xml = HotCocoa.<span class="me1">xml_parser</span><span class="br0">&#40;</span><span class="re3">:data</span> =&gt; <span class="re1">@receivedData</span><span class="br0">&#41;</span><br />
&nbsp; <span class="re1">@receivedData</span> = <span class="kw2">nil</span></p>
<p>&nbsp; xml.<span class="me1">on_start_document</span> <span class="br0">&#123;</span> <span class="kw3">puts</span> <span class="st0">&quot;Starting Parse&quot;</span> <span class="br0">&#125;</span><br />
&nbsp; xml.<span class="me1">on_end_document</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; HotCocoa.<span class="me1">notification</span><span class="br0">&#40;</span><span class="re3">:post</span> =&gt; <span class="kw2">true</span>, <span class="re3">:name</span> =&gt; <span class="st0">&quot;all_entries_loaded&quot;</span>, <span class="re3">:object</span> =&gt; <span class="kw2">nil</span>, <span class="re3">:info</span> =&gt; <span class="kw2">nil</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
&nbsp; xml.<span class="me1">on_parse_error</span> <span class="br0">&#123;</span> |err| <span class="kw3">puts</span> <span class="st0">&quot;Parse error #{err.inspect}&quot;</span> <span class="br0">&#125;</span></p>
<p>&nbsp; xml.<span class="me1">on_cdata</span> <span class="br0">&#123;</span> |cdata| <span class="re1">@elem_text</span> += cdata.<span class="me1">to_s</span> <span class="br0">&#125;</span><br />
&nbsp; xml.<span class="me1">on_characters</span> <span class="br0">&#123;</span> |chars| <span class="re1">@elem_text</span> += chars.<span class="me1">to_s</span> <span class="br0">&#125;</span></p>
<p>&nbsp; xml.<span class="me1">on_start_element</span> <span class="kw1">do</span> |element, namespace, qualified_name, attributes|<br />
&nbsp; &nbsp; <span class="re1">@elem_text</span> = <span class="st0">&#8221;</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; xml.<span class="me1">on_end_element</span> <span class="kw1">do</span> |element, namespace, qualified_name|<br />
&nbsp; &nbsp; <span class="kw3">puts</span> <span class="re1">@elem_text</span> <span class="kw1">if</span> element == <span class="st0">&#8216;title&#8217;</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; xml.<span class="me1">parse</span><br />
<span class="kw1">end</span></div>
<p>We&#8217;re finally getting into some HotCocoa specific code with our XML parser. HotCocoa defines a mapping wrapper around <code>NSXMLParser</code> and provides a set of delegate methods. These delegates mean we don&#8217;t have to set our class as the delegate and create a bunch of methods. They mean we can attach our code as blocks on our XML object. All the better if you want to define a few parsers in one class.</p>
<p>We start off by creating a <code>HotCocoa.xml_parser</code>. The parser accepts <code>NSData</code> objects so we don&#8217;t need to convert our response data to a string. We then setup  eight callbacks. There are actually a bunch more callbacks that can be hooked up and you should look at the <code>xml_parser</code> mapping code to see if you need any of them. For our purposes, we only really care about eight.</p>
<p>The <code>on_start_document</code>, <code>on_end_document</code> and <code>on_parse_error</code> callbacks, as you can probably guess, get called when we start parsing, when we finish parsing and when we receive a parse error, respectively. We don&#8217;t really care about start in this example, but I put it in anyway. When we&#8217;ve completed parsing we send a notification and other application code can then listen for this notification and do anything it needs. If we wanted we could store the entries as they&#8217;re parsed and provide them to the <code>:object</code> key. This would make those entries available to anyone that receives the notification.</p>
<p>If we receive either CDATA, with <code>on_cdata</code>, or text, with <code>on_characters</code>, we append the content to our current elements text. When we receive the open tag of a new element, <code>on_start_element</code>, we dump our current element text as we&#8217;ve started a new element. We can also take a look at the elements name, attributes, namespace and qualified name, if desired.</p>
<p>Finally, in <code>on_end_element</code> we print out the current element text if the element we&#8217;re finishing has a name of <em>title</em>.</p>
<p>With all the callbacks configured we use <code>xml.parse</code> to start the parser. You should, if you run this example, see the titles and authors of the first two posts in your reading list. (The author name is also called title and I&#8217;m not bothering to check that the parent element is <em>entry</em> before spitting it out.)</p>
<p>That&#8217;s it. You can now make synchronous and asynchronous requests for content and parse any resulting XML.</p>
<p>One last thing before you go. Both of the requests we did above were <em>GET</em> requests. You can do other types of requests using the same methods as above you just need a slightly different setup for the request. You can see a <em>POST</em> request below.</p>
<div class="dean_ch" style="white-space: wrap;">request = NSMutableURLRequest.<span class="me1">requestWithURL</span><span class="br0">&#40;</span>url<span class="br0">&#41;</span><br />
request.<span class="me1">addValue</span><span class="br0">&#40;</span>SOURCE, forHTTPHeaderField:<span class="st0">&quot;source&quot;</span><span class="br0">&#41;</span><br />
request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;2&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;GData-Version&quot;</span><span class="br0">&#41;</span><br />
request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;SID=#{@sid}&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;Cookie&quot;</span><span class="br0">&#41;</span></p>
<p>body=<span class="st0">&quot;first=1&amp;second=2&amp;third=3&quot;</span></p>
<p>request.<span class="me1">setHTTPMethod</span><span class="br0">&#40;</span><span class="st0">&#8216;POST&#8217;</span><span class="br0">&#41;</span><br />
request.<span class="me1">setValue</span><span class="br0">&#40;</span><span class="st0">&#8216;application/x-www-form-urlencoded&#8217;</span>, forHTTPHeaderField:<span class="st0">&#8216;Content-Type&#8217;</span><span class="br0">&#41;</span><br />
request.<span class="me1">setValue</span><span class="br0">&#40;</span>body.<span class="me1">length</span>.<span class="me1">to_s</span>, forHTTPHeaderField:<span class="st0">&#8216;Content-Length&#8217;</span><span class="br0">&#41;</span><br />
request.<span class="me1">setHTTPBody</span><span class="br0">&#40;</span>body.<span class="me1">dataUsingEncoding</span><span class="br0">&#40;</span>NSASCIIStringEncoding<span class="br0">&#41;</span><span class="br0">&#41;</span></div>
<p>The first few lines should look familiar from creating our asynchronous request above. Since we&#8217;re going to be posting the data we use <code>setHTTPMethod('POST')</code> to setup the request method. We&#8217;ve form encoded the data so we set the appropriate <em>Content-Type</em> and set the <em>Content-Length</em>. Note, we convert the length to a string before sending to <code>setValue</code>. Finally, we set the body of the post with <code>setHTTPBody</code>. You need to convert the body string into a <code>NSData</code> object which we do with the <code>dataUsingEncoding</code> method. If you don&#8217;t convert the body to <code>NSData</code> you&#8217;ll end up sending a <code>nil</code> body with your post request.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/download-and-xml-parsing-with-hotcocoa/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>A Mathematician’s Lament</title>
		<link>http://everburning.com/news/a-mathematician%e2%80%99s-lament/</link>
		<comments>http://everburning.com/news/a-mathematician%e2%80%99s-lament/#comments</comments>
		<pubDate>Thu, 13 Mar 2008 15:41:29 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Article]]></category>

		<guid isPermaLink="false">http://everburning.com/news/a-mathematician%e2%80%99s-lament/</guid>
		<description><![CDATA[<p>From what I&#8217;ve read so far, I&#8217;m about 5 pages in, an excellent article on the state of mathematics education.  Give it a read: <a href="http://www.maa.org/devlin/LockhartsLament.pdf">A Mathematician&#8217;s Lament</a>.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fa-mathematician%25e2%2580%2599s-lament%2F&amp;title=A+Mathematician%E2%80%99s+Lament" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fa-mathematician%25e2%2580%2599s-lament%2F&amp;title=A+Mathematician%E2%80%99s+Lament" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fa-mathematician%25e2%2580%2599s-lament%2F&amp;title=A+Mathematician%E2%80%99s+Lament" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fa-mathematician%25e2%2580%2599s-lament%2F&amp;title=A+Mathematician%E2%80%99s+Lament" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fa-mathematician%25e2%2580%2599s-lament%2F&amp;title=A+Mathematician%E2%80%99s+Lament', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fa-mathematician%25e2%2580%2599s-lament%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fa-mathematician%25e2%2580%2599s-lament%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fa-mathematician%25e2%2580%2599s-lament%2F&amp;title=A+Mathematician%E2%80%99s+Lament" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fa-mathematician%25e2%2580%2599s-lament%2F&amp;title=A+Mathematician%E2%80%99s+Lament" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p>From what I&#8217;ve read so far, I&#8217;m about 5 pages in, an excellent article on the state of mathematics education.  Give it a read: <a href="http://www.maa.org/devlin/LockhartsLament.pdf">A Mathematician&#8217;s Lament</a>.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fa-mathematician%25e2%2580%2599s-lament%2F&amp;title=A+Mathematician%E2%80%99s+Lament" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fa-mathematician%25e2%2580%2599s-lament%2F&amp;title=A+Mathematician%E2%80%99s+Lament" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fa-mathematician%25e2%2580%2599s-lament%2F&amp;title=A+Mathematician%E2%80%99s+Lament" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fa-mathematician%25e2%2580%2599s-lament%2F&amp;title=A+Mathematician%E2%80%99s+Lament" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fa-mathematician%25e2%2580%2599s-lament%2F&amp;title=A+Mathematician%E2%80%99s+Lament', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fa-mathematician%25e2%2580%2599s-lament%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fa-mathematician%25e2%2580%2599s-lament%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fa-mathematician%25e2%2580%2599s-lament%2F&amp;title=A+Mathematician%E2%80%99s+Lament" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fa-mathematician%25e2%2580%2599s-lament%2F&amp;title=A+Mathematician%E2%80%99s+Lament" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/a-mathematician%e2%80%99s-lament/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Pick your poison.</title>
		<link>http://everburning.com/news/pick-your-poison/</link>
		<comments>http://everburning.com/news/pick-your-poison/#comments</comments>
		<pubDate>Fri, 12 Oct 2007 04:28:55 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Article]]></category>
		<category><![CDATA[Computers]]></category>

		<guid isPermaLink="false">http://everburning.com/news/pick-your-poison/</guid>
		<description><![CDATA[<p>Stumbled across, well, it showed up in my blogroll, an interesting article on the <a href="http://www.cerias.purdue.edu/weblogs/">Cerias</a> blog about problem solving and our tendency to try to cure the symptoms instead of solving underlying issues. Mostly computer stuff but nothing technical and maps to pretty much any culture.</p>
<blockquote><p>As a result, we develop fragile monocultures that have a particular set of vulnerabilities, and then we need to spend a huge amount to protect them. ~ <a href="http://www.cerias.purdue.edu/weblogs/spaf/kudos-opinions-rants/post-124/solving-some-of-the-wrong-problems/">Solving Some of the Wrong Problems</a>.</p></blockquote>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fpick-your-poison%2F&amp;title=Pick+your+poison." title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fpick-your-poison%2F&amp;title=Pick+your+poison." title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fpick-your-poison%2F&amp;title=Pick+your+poison." title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fpick-your-poison%2F&amp;title=Pick+your+poison." title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fpick-your-poison%2F&amp;title=Pick+your+poison.', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fpick-your-poison%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fpick-your-poison%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fpick-your-poison%2F&amp;title=Pick+your+poison." title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fpick-your-poison%2F&amp;title=Pick+your+poison." title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p>Stumbled across, well, it showed up in my blogroll, an interesting article on the <a href="http://www.cerias.purdue.edu/weblogs/">Cerias</a> blog about problem solving and our tendency to try to cure the symptoms instead of solving underlying issues. Mostly computer stuff but nothing technical and maps to pretty much any culture.</p>
<blockquote><p>As a result, we develop fragile monocultures that have a particular set of vulnerabilities, and then we need to spend a huge amount to protect them. ~ <a href="http://www.cerias.purdue.edu/weblogs/spaf/kudos-opinions-rants/post-124/solving-some-of-the-wrong-problems/">Solving Some of the Wrong Problems</a>.</p></blockquote>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fpick-your-poison%2F&amp;title=Pick+your+poison." title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fpick-your-poison%2F&amp;title=Pick+your+poison." title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fpick-your-poison%2F&amp;title=Pick+your+poison." title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fpick-your-poison%2F&amp;title=Pick+your+poison." title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fpick-your-poison%2F&amp;title=Pick+your+poison.', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fpick-your-poison%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fpick-your-poison%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fpick-your-poison%2F&amp;title=Pick+your+poison." title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fpick-your-poison%2F&amp;title=Pick+your+poison." title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/pick-your-poison/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic Page Served (once) in 1.314 seconds -->

