Testing using an external API can be a PITA, especially if the API uses any HTTP Callbacks or redirects such as OAuth or WebHooks. If your using any callback functionality like this then the Service Provider needs a way to callback your application and therefore be accessible to the public Internet.
When you start integrating these APIs, it’s much easier to work on your local development machine, but these are usually behind firewalls, NAT, or are otherwise not able to provide a public URL and it’s not really feasible to push to a staging environment every time you want to test something.
So we need a way to make our local applications available to the Internet; there are a few good services and tools out there to help with this such as: Tunnlr, ProxyLocal, showoff.io or you can setup your own reverse SSH Tunnel if you already have a remote system to forward you requests.
Ruby, Ruby, Ruby!
LocalTunnel can be installed by using RubyGems.
Mac – If you are using a Mac then your all set as it’s installed by default.
Linux – If you are on a Linux machine, you can run the following command to install
$ sudo apt-get install ruby ruby1.8-dev rubygems1.8 libopenssl-ruby
Windows – Windows is a different animal, full instructions on installing RubyGems and LocalTunnel can be found here: http://blog.wearemammoth.com/2011/09/localtunnel-windows.html
Once RubyGems installed, either operating sytem can install LocalTunnel via the following command:
Drop the sudo if your using Windows.
Creating public and private keys
All these handy services for creating tunnels require a private and public key pair to enable your computer to securely communicate with theirs.To generate your public/private key pair, you can run the following command:
You should get output similar to the following:
$ ssh-keygen<br> Generating public/private rsa key pair.<br> Enter file in which to save the key (/Users/Ryan/.ssh/id_rsa):<br> Enter passphrase (empty for no passphrase):<br> Enter same passphrase again:<br>
First it will prompt you for a location to store the keys, and then a passphrase and confirmation of the passphrase. Here we have just hit enter 3 times, excepting the default location and using an empty passphrase.
First run with LocalTunnel
The first time you ever use LocalTunnel, you will have to upload the public key we generated earlier.
As you can see from the previous output, my key is stored in /Users/Ryan/.ssh/id_rsa. Using this path we can run localtunnel and upload they key using the following command:
localtunnel -k /Users/kevin/.ssh/id_rsa.pub 8081
-k indicates the key to upload followed by the path of the key. The last parameter “8081” is the local port we want localtunnel to forward to. So whatever port your app is running on should replace this value.
You only have to specify the key the first time you run localtunnel. Future tunnels can be setup as simple as the following command:
Each time you run the command you should get output similar to the following:
<code>This localtunnel service is brought to you by Twilio
.<br> Port 8081 is now publicly accessible from http://Rj1z.localtunnel.com ...<br>
Take note of the URL provided as we’ll use this later to configure our callbacks.
Configuring the Callback
As Localtunnel is kindly provided by Twilio, to demonstrate I will use the Twilio Cloud Connector. Twilio has an awesome WebHook implementation with great debugging tools. Twilio uses callbacks to tell you about the status of your requests; When you use Twilio to a place a phone call or send an SMS the Twilio API allows you to send a URL where you’ll receive information about the phone call once it ends or the status of the outbound SMS message after it’s processed.
This example uses the Twilio Cloud Connector to send a simple SMS message. The most important thing to note is that the “status-callback-flow-ref” attribute. All connector operations that support callback’s will have an optional attribute ending in “-flow-ref”. In this case : “status-callback-flow-ref”. As the name suggests, this attribute should reference a flow. This value must be a valid flow id from within your configuration. It is this flow that will be used to listen for the callback.
Notice that the flow has no inbound endpoint? This is where the magic happens; when Twilio process the SMS message it will send a callback automatically to that flow without you having to define an inbound endpoint. The connector automatically generates an inbound endpoint and sends the auto generated URL to Twilio for you.
Customizing the Callback
The URL generated for the callback URL is built using ‘localhost’ as the host, the ‘http.port’ environment variable or ‘localPort’ value as the port and the path of the URL is typically just a random generated string or static value. So if I run this locally it would send Twilio my non public address, something like http://localhost:80/fjer4380j3fkl00vv3v3er342fvvn.
Each connector that accepts HTTP callbacks will provide you with an optional http-callback-config child element to override these settings. These settings can be set at the connector’s config level as follows:
Here we have amended the previous example to add the additonal http-callback-config configuration. The configuration takes three mandatory arguments: domain, lo-calPort and remotePort. These settings will be used to constuct the URL that is passed to the external system. The URL will be the same as the default generated URL of the HTTP inbound-endpoint except that the host is replaced by the ‘domain’ setting (or its default value) and the port is replaced by the ‘remotePort’ setting (or its default value).
In this case we have used the domain from the URL that LocalTunnel generated for us earlier: Rj1z.localtunnel.com and set the remotePort to 80 as LocalTunnel just used the default 80 port.
And that’s it! If you rerun this configuration you should start seeing your callback being printed to the console. The same goes for any OAuth connectors too. If your using any OAuth connectors built using the DevKit OAuth modules, such as the LinkedIn connector, you can configure the OAuth callback in a similar way: