Synchronicity, Asynchronicity and Request Timeouts


The HTTP action is designed for use in machine-to-machine communication. 


In most scenarios the bt.tn server makes a HTTP request and receives the response immediately, typically within a few seconds. This is what we call a synchronous HTTP action.


      bt.tn                                        server
        |                                            |
        | ----- GET /trigger HTTP/1.1 -------------> |
  waits |                                            | processes request
        | <------------------- HTTP/1.1 200 OK ----- |
        |                                            |

Synchronous HTTP action


The maximum time the bt.tn server waits for the HTTP response is limited to a short value. This is because when there are network or server related problems we want the action to fail fast and cause the bttn device to show red lights rather than leave the yellow light circling for long periods of time before changing to red to indicate a failure.


We reserve some time for possible network delays (DNS delays, poor connectivity, etc), but as a rule of thumb your server should complete the HTTP request within about 10 seconds.


In some use cases this strict time limit cannot be met because your server needs more time to do its task (connect to another server, perform heavy db operations, etc). This is where the asynchronous HTTP action comes handy.


The asynchronous HTTP action consists of two steps:

  1. bt.tn sends a HTTP request to your server, your server queues the request, sends back a HTTP response, closes the HTTP connection and starts processing the request in the background.
  2. When your server has completed processing, it passes the result data in a HTTP request back to bt.tn. This is what we call the HTTP callback.

      bt.tn                                        server
        |                                            |
        | ----- GET /trigger HTTP/1.1 -------------> |
  waits |                                            | queue request
        | <------------------- HTTP/1.1 200 OK ----- |
        |                                            | process request
        |                                            |
        |                                            |
        |                                            |
        |                                            |
        | <------- POST /callback/:id HTTP/1.1 ----- | pass result 
 process|                                            |
 result | ----- HTTP/1.1 200 OK -------------------> |
        |                                            |

Asynchronous HTTP action


The asynchronous HTTP action gives your server more time to perform it's task without binding bt.tn resources and still keeping the bttn devices responsive.


In the first step the bt.tn server generates a unique HTTP callback URL. This URL consists of the prefix "https://api.bt.tn/2014-06/callback/" followed by a long base64-encoded ID. The callback URL is passed to your server in the HTTP request along with any other arguments you have specified in the HTTP action configuration.


If your server is able to process the HTTP request, it must store the HTTP callback URL, send a HTTP 200 OK response and make sure the bt.tn server can close the connection.


In the second step your server performs an authenticated HTTP POST request to the given HTTP callback URL. Your server must add the result data to the HTTP request body in JSON format, and authenticate itself by presenting a valid bt.tn REST Api-Key in the HTTP headers.


The bt.tn server will wait for the HTTP callback for configurable maximum time. If the HTTP callback is not received within this time, then the HTTP action fails and the bttn device flashes red leds. After that any HTTP callback to that unique HTTP callback URL is ignored.


Using HTTP Callbacks


bt.tn Api-Key

When you start using the HTTP callbacks, the first thing you need to do is generate an Api-Key that you need for making authenticated requests to the bt.tn REST API. Log in to my.bt.tn, go to "Account Settings" and click on  "Generate an Api-Key for REST API access" at the bottom of the page. Give a name to your Api-Key application, click on "Generate", store the generated Api-Key on your server and click "Save".


Configuring the HTTP action

First you need to configure how the bt.tn server sends the HTTP callback URL in the HTTP request. To do this you need to either add an argument key of your choice (say "callback") with the special tag value <CALLBACKURL>, or use the tag <CALLBACKURL> in your JSON-formatted argument data. When making the HTTP request, the bt.tn server will place the dynamically generated callback URL where the <CALLBACKURL> tag is.


Next you need to toggle the "Wait for HTTP callback" switch. Select the Api-Key application that you wish to receive the callback from, fill in the expected result data and select the result compare mode, and set the timeout for the callback.


Preparing Your Server

When your server receives the HTTP request from bt.tn, it needs to store the HTTP callback URL, return a "200 OK" HTTP response and make sure the bt.tn server can close the connection. Best practice to ensure correct behavior is to send the Connection: close and the Content-Length HTTP response headers, and to flush the connection.


Next your server can perform its task without having to consider the strict HTTP action timing constraints.


Once your server has completed it's processing, it performs the HTTP callback. The HTTP callback must be a HTTP POST request, containing the Api-Key in the X-Api-Key HTTP header and the HTTP action result data in a  JSON-formatted request body.


Api-Key header format:

X-Api-Key: your Api-Key goes here


Result data format:

{ "result": "your HTTP action result data goes here" }


PHP example snippet:

<?php
/* Store the callback URL */
$callback_url = urldecode($_POST['callback']);




/* Send the response and close the connection */
$response = 'queued';
ignore_user_abort(true);
set_time_limit(60);
ob_start();
header("Connection: close");
header("Content-Length: " . mb_strlen($response));
echo $response;
ob_end_flush();
ob_flush();
flush();




/* Process the request here */
sleep(5);
$result = "positive";




/* Perform HTTP callback request */
$ci = curl_init();
curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, '30');
curl_setopt($ci, CURLOPT_TIMEOUT, '30');
curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE);
/* Add "X-Api-Key" header */
$headers = array("Content-Type: application/json",
                 "X-Api-Key: cafebabe");
curl_setopt($ci, CURLOPT_HTTPHEADER, $headers);
/* Add result data to request body */
$param = '{"result":"' . $result . '"}';
curl_setopt($ci, CURLOPT_POST, 1);
curl_setopt($ci, CURLOPT_POSTFIELDS, $param);
/* Perform callback */
curl_setopt($ci, CURLOPT_URL, $callback_url);
curl_exec($ci);
curl_close($ci);
?>