Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

DataAsyncBlock (DAB)

4.91/5 (7 votes)
10 Jan 2014CPOL2 min read 26.2K   107  
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.
 

JavaScript
  // 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 the GET request on the server in PHP.
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("'", '&#39;', str_replace('"', '&#34;', 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("'", '&#39;', str_replace('"', '&#34;', $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 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

  1. Sept 20, 2013 - Created DAB idea for public use.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)