DataAsyncBlock (DAB)
How to send async blocks of form data to a remote server without using Ajax.
Introduction
This article describes an alternative way to post asynchronous data to a remote server without using Ajax.
Background
I needed a way to send blocks of data to a remote server, bypassing the security sandbox of Ajax. Tinkering with different ideas, I came up with an actual solution that works and is fairly easy to implement. I call it DataAsyncBlock
or DAB
for short.
It uses JSONP
, JSON
, P3P
, PHP
, JavaScript
and the DOM
to inject script tags into the webpage header, sending blocks of data, through server GET
requests. I have tested and it seems to work across multiple browsers. It can send unlimited amounts of asynchronous data to any remote server that has code to handle unordered blocks of data. It is not blocked by browser URI
Max length either, because the data is sent in blocks with an index and length. Async data can be handled by the server and collected randomly and put back together by using the index identifier and a key or session.
Security
There are definite concerns in using this method to send personal data. Please be aware and only use this code appropriately for your project.
Client Side Code
Below is the needed code for the client side to send data to a remote server.
// Checks for null, undefined objects and empty strings.
function isnull(o){return (o==undefined||o==null||(typeof(o)=='string'&&o==''))?true:false;}
// For turning json to objects.
String.prototype.run = function(){ return (new Function('with(this) { return ' + this + '}' )).call({}); };
// Encodes data to be sent via querystring.
function dabsafe(s)
{
return encodeURIComponent(s).replace(/!/g, '%21').replace("/'/g", '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
}
// The dab sending method.
function dab(webpage, callback, data)
{
// Variables
var head = document.head || document.getElementsByTagName('head')[0];
var buffer = 50;
// Encode url
var m = dabsafe(data);
var ml = (m.length <= buffer) ? 1 : (Math.floor(m.length / buffer) + 1);
for(var i = 0; i < ml; i++)
{
var pos = i * buffer;
var d = m.substring(pos, pos + buffer);
// Create script child and append to head
var s = document.createElement('script');
s.id = callback + i;
s.src = webpage + '?format=json&i=' + (i+1) +
'&l=' + (ml+1) + '&c=' + callback +
'&d=' + d;
head.appendChild(s);
}
}
// The dab callback
function dabcallback(d)
{
if(isnull(d)){ return; }
// Create data object.
var s = d.run();
if(!isnull(s.e))
{
// Alert error message
alert(s.e);
return;
}
else
{
alert(s.s);
}
// Remove script
var o = document.getElementById('dabcallback' + s.i);
o.parentNode.removeChild(o);
}
var fruits = 'blackcurrant | redcurrant | gooseberry | tomato | eggplant | guava | ' +
'lucuma | chili pepper | pomegranate | kiwifruit | grape | cranberry | ' +
'blueberry | pumpkin | gourd | cucumber | melon | orange | lemon | lime | ' +
'grapefruit | blackberry | raspberry | boysenberry | pineapple | fig | ' +
'mulberry | hedge apple | apple | rose hip | strawberry';
dab('http://yoursite.com/serverside.php', 'dabcallback', fruits);
Server Side Code
Below is the code needed to process theGET
request on the server in PHP
.
<?php
// Start session if one doesn't exist and setup dat packet array.
if(!$_SESSION){ session_start(); }
if(!isset($_SESSION['dat'])){ $_SESSION['dat'] = array(''=>''); }
// Header needed to parse and collect data.
header("Content-Type: application/javascript");
header('P3P: CP="NOI ADM DEV COM NAV OUR STP"');
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
// Checks for nulls
function isnull($data){ return (isset($data) && !empty($data)) ? false : true; }
// Encodes data for json response.
function utf8_encode_all($dat)
{
if (is_string($dat)) return str_replace("'", ''', str_replace('"', '"', utf8_encode($dat)));
if (!is_array($dat)) return $dat;
$ret = array();
foreach($dat as $i=>$d) $ret[$i] = utf8_encode_all($d);
return str_replace("'", ''', str_replace('"', '"', $ret));
}
// Send a response to a webpage.
function response($_callback, $_data, $_err, $_index)
{
$_callback = utf8_encode_all($_callback);
$_data = utf8_encode_all($_data);
$_err = utf8_encode_all($_err);
$_index = utf8_encode_all($_index);
$data = array("s" => $_data, "e" => $_err, "i" => $_index);
echo $_callback . "('" . json_encode($data) ."');";
}
// Process the form
function process()
{
// Variables
$id = '';
$err = '';
$status = '';
// Get request query
$l = (!isnull($_GET['l'])) ? intval($_GET['l']) : '';
$c = (!isnull($_GET['c'])) ? $_GET['c'] : '';
$d = (!isnull($_GET['d'])) ? $_GET['d'] : '';
$i = (!isnull($_GET['i'])) ? intval($_GET['i']) . '' : '';
if($i==1){ unset($_SESSION['dat']); $_SESSION['dat'] = array(''=>''); }
// Add buffer to array
$_SESSION['dat'][$i] = $d;
// Sort array and pop it into one string.
ksort($_SESSION['dat']);
$arr = implode($_SESSION['dat']);
// Decode all special characters
$arr = urldecode($arr);
// Check if total packets have been captured.
if(count($_SESSION['dat'])==$l)
{
try
{
if(isnull($arr)){ throw new Exception('Fruit is required!'); }
$status = ucwords($arr);
}
catch (Exception $e)
{
$err = $e->getMessage();
}
// Write response back.
response($c, $status, $err, $i);
// Remove the current session.
session_destroy();
}
}
// Start the process
process();
?>
Running the Code
When you run the client side code, it will send the fruit string to the server in blocks of 50 characters. It will send the packets inGET
requests using the querystring. The server will create a unique session for the incoming data and create an array to hold the packets of data. Once the server has collected all of the packets it will process the fruit string capitalizing the first word of each and send the string to the defined callback method as a JSON
object. The callback will then display it through a JavaScript
alert. This is a simple demo, but it can be extended to handle large projects. With this demo, I put the buffer at 50 characters to show you how it sends multiple packets. Most browsers allow MAX URI
length of 2048 or more characters for GET
requests. So change the setting as needed.
Code Output
Blackcurrant | Redcurrant | Gooseberry | Tomato | Eggplant | Guava | Lucuma | Chili Pepper | Pomegranate | Kiwifruit | Grape | Cranberry | Blueberry | Pumpkin | Gourd | Cucumber | Melon | Orange | Lemon | Lime | Grapefruit | Blackberry | Raspberry | Boysenberry | Pineapple | Fig | Mulberry | Hedge Apple | Apple | Rose Hip | Strawberry
History
- Sept 20, 2013 - Created DAB idea for public use.