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.
function isnull(o){return (o==undefined||o==null||(typeof(o)=='string'&&o==''))?true:false;}
String.prototype.run = function(){ return (new Function('with(this) { return ' + this + '}' )).call({}); };
function dabsafe(s)
{
return encodeURIComponent(s).replace(/!/g, '%21').replace("/'/g", '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
}
function dab(webpage, callback, data)
{
var head = document.head || document.getElementsByTagName('head')[0];
var buffer = 50;
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);
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);
}
}
function dabcallback(d)
{
if(isnull(d)){ return; }
var s = d.run();
if(!isnull(s.e))
{
alert(s.e);
return;
}
else
{
alert(s.s);
}
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 the
GET
request on the server in
PHP
.
<?php
if(!$_SESSION){ session_start(); }
if(!isset($_SESSION['dat'])){ $_SESSION['dat'] = array(''=>''); }
header("Content-Type: application/javascript");
header('P3P: CP="NOI ADM DEV COM NAV OUR STP"');
header("Cache-Control: no-cache, must-revalidate");
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
function isnull($data){ return (isset($data) && !empty($data)) ? false : true; }
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));
}
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) ."');";
}
function process()
{
$id = '';
$err = '';
$status = '';
$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(''=>''); }
$_SESSION['dat'][$i] = $d;
ksort($_SESSION['dat']);
$arr = implode($_SESSION['dat']);
$arr = urldecode($arr);
if(count($_SESSION['dat'])==$l)
{
try
{
if(isnull($arr)){ throw new Exception('Fruit is required!'); }
$status = ucwords($arr);
}
catch (Exception $e)
{
$err = $e->getMessage();
}
response($c, $status, $err, $i);
session_destroy();
}
}
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 in
GET
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.