


<?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>CantRemembrances &#187; tunnel</title>
	<atom:link href="http://blog.cantremember.com/tag/tunnel/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.cantremember.com</link>
	<description>Memes of a technical vein discovered during CantRemember.com implementation</description>
	<lastBuildDate>Tue, 16 Feb 2010 06:36:02 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Dynamic Tunneling for your Facebook App</title>
		<link>http://blog.cantremember.com/dynamic-tunneling-for-your-facebook-app/</link>
		<comments>http://blog.cantremember.com/dynamic-tunneling-for-your-facebook-app/#comments</comments>
		<pubDate>Mon, 20 Apr 2009 02:14:08 +0000</pubDate>
		<dc:creator>dfoley</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[tunnel]]></category>

		<guid isPermaLink="false">http://blog.cantremember.com/?p=132</guid>
		<description><![CDATA[When I was launching Forgiveness, my first attempt at a Facebook app via RoR, I ran into one of the typical developer quandries.  How the heck to I actually make local revs to the app and proxy them through the FB back-end while still keeping the app running live for my millions of satisfied [...]]]></description>
			<content:encoded><![CDATA[<p>When I was launching <a href="http://apps.facebook.com/forgiveness-app/">Forgiveness</a>, my first attempt at a Facebook app via RoR, I ran into one of the typical developer quandries.  How the heck to I actually make local revs to the app and proxy them through the FB back-end while still keeping the app running live for my millions of satisfied customers?</p>
<p>I sought advice from <a href="http://www.workingwithrails.com/person/11513-steve-enzer">Steve Enzer</a>, and his answer was <em>&#8216;create a separate FB app id&#8217;</em>.  Yep, you can do that.  I&#8217;m sure it works just fine.  However, I wanted to come up with a single-app solution, you know, just because.</p>
<p>My app backbone was based upon <a href="http://rubyforge.org/projects/facebooker"><strong>Facebooker</strong></a>, a great gem for just such purposes.  And assuming that you&#8217;re using that gem, you&#8217;re provided with:</p>
<div class="pre_wrap">
<pre><code>rake facebooker:tunnel:start
</code></pre>
</div>
<p>Which starts up the <a href="http://blog.evanweaver.com/articles/2007/07/13/developing-a-facebook-app-locally/">traditional server tunnel</a> to your local dev box; Facebook&#8217;s proxy won&#8217;t know the difference.  I&#8217;ve also set up my canvas page URLs and the like with a fixed IP &#8212; according to FB&#8217;s best practices, because they don&#8217;t want to deal with all the DNS resolution stuff &#8212; and it listens on port 80 with a dedicated context.  So, I&#8217;ve got my <code>facebooker.yml</code> set up for port 3100:</p>
<div class="pre_wrap">
<pre><code>tunnel:
  public_host: cantremember.com
  public_port: 3100
  local_port: 3100
</code></pre>
</div>
<p>And here&#8217;s my nginx config:</p>
<div class="pre_wrap">
<pre><code>#
#	included into vhosts/localhost.conf (*.by-ip)
#		answers to :80
#
	#	registered with FaceBook
	location /forgiveness {
		#	no-trailing-/ case (the rewritten URI has a zero length)
		if ($request_filename = '') {
			rewrite  (.*)  $1/;
		}

		#	the only way i could figure to get conditional proxying
		#		proxy_pass via variables = no
		#		if { proxy_pass } = no
		#		so ... two more goofy contexts

		#	magic tunnel cookie (any value)
		if ($http_cookie ~ '_forgiveness_tunnel=1') {
			#	last : stop rewriting, but re-process through nginx (vs. break)
			rewrite  /forgiveness(.*)  /forgiveness-dev$1  last;
		}
		rewrite  /forgiveness(.*)  /forgiveness-pub$1;
	}

	location /forgiveness-pub {
		#	root contextify
		#	break to terminate re-processing through nginx
		rewrite /forgiveness-pub(.*)  $1  break;

		proxy_pass  http://cantremember_forgiveness_cluster;
	}

	location /forgiveness-dev {
		rewrite /forgiveness-dev(.*)  $1  break;

		#	same as public upstream
		proxy_pass  http://127.0.0.1:3100;
	}
</code></pre>
</div>
<p>Here I omit the upstream cluster configs, and I&#8217;ll let the comments speak for themselves &#8230; I always try to keep track of red herrings at least at some level, and there were several I bumped into while trying out this solution.  The best approach that I found was to simply set a cookie with a naming convention that nginx could parse (yes, there is a regex performance penalty here).  The development context is routed locally, which is where the tunnel is waiting to pick up and run with it.</p>
<p>Setting the cookie, well that&#8217;s another trick.  Though not much of one.  Of course your app is completely masked behind the <code>apps.facebook.com</code> domain, so trying to set a cookie to your internal domain (eg. IP) through your browser isn&#8217;t so easy, and it&#8217;s repetetive manual work.  So, just bake it into a dedicated action in your app:</p>
<div class="pre_wrap">
<pre><code>#	allows for Facebook pub / dev tunneling
#		have to set the Cookie within the scope of the app
#		can't use Firefox cookie editor to hack it
def tunnel
	#	how convenient!
	value = params[:id]

	#	this is how we get pub / dev tunneling
	#		see support/nginx
	if value == 'nil'
		cookies.delete :_forgiveness_tunnel
	else
		cookies[:_forgiveness_tunnel] = value
	end

	###render :text => "OK #{value}", :content_type => 'text/plain'
	redirect_to root_url
end
</code></pre>
</div>
<p>Then use <code>/:controller/tunnel/1</code> to engage, and <code>/:controller/tunnel/0</code> to disengage.  A little bit of auth logic, and you&#8217;re good to go.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.cantremember.com/dynamic-tunneling-for-your-facebook-app/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
