Cross Domain AJAX Using A Proxy

The web is full of people stating that you cannot make an XMLHttpRequest (or the Microsoft equivalent) for another domain. However, this is only a half truth, because you can with a proxy server. JavaScript strictly prohibits making an XMLHttpRequest directly to another domain, but you can always forward a remote request through a proxy service running on your own domain. Here is the script that I use:

Example 1: PHP Proxy Script

<?php
// add domains here to prevent proxy chaining by nefarious people; default allows all domains
$domainWhitelist = array("core.localhost", "localhost", "mattsnider.com");
$isDomainValid = true;
if (sizeof($domainWhitelist)) {
	$domain = preg_replace("/^www\./", "", $_SERVER["HTTP_HOST"]);
	// this attempts to prevent proxy chaining
	$isXMLHttpRequest = array_key_exists("HTTP_X_REQUESTED_WITH", $_SERVER) && "XMLHttpRequest" === $_SERVER["HTTP_X_REQUESTED_WITH"];
	$isDomainValid = $isXMLHttpRequest && in_array($domain, $domainWhitelist);
}

if ($isDomainValid) {
// Get the url of to be proxied
// Is it a POST or a GET?
$isPost = array_key_exists("url", $_POST);
$url = ($isPost) ? $_POST["url"] : $_GET["url"];
$headers = "";
$mimeType = "";

if ($isPost) {
	if (array_key_exists("headers", $_POST)) {$headers = $_POST["headers"];}
	if (array_key_exists("mimeType", $_POST)) {$mimeType = $_POST["mimeType"];}
}
else {
	if (array_key_exists("headers", $_GET)) {$headers = $_GET["headers"];}
	if (array_key_exists("mimeType", $_GET)) {$mimeType = $_GET["mimeType"];}
}

//Start the Curl session
$session = curl_init($url);

// If it’s a POST, put the POST data in the body
if ($isPost) {
	$postvars = "";
	while ($element = current($_POST)) {
		$postvars .= key($_POST)."=".$element."&";
		next($_POST);
	}
	curl_setopt ($session, CURLOPT_POST, true);
	curl_setopt ($session, CURLOPT_POSTFIELDS, $postvars);
}

// Don’t return HTTP headers. Do return the contents of the call
curl_setopt($session, CURLOPT_HEADER, ($headers == "true") ? true : false);

curl_setopt($session, CURLOPT_FOLLOWLOCATION, true);
// prevents an accidental or intentional DoS attack
curl_setopt($session, CURLOPT_MAXREDIRS, 2);
//curl_setopt($ch, CURLOPT_TIMEOUT, 4);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);

// Make the call
$response = curl_exe\c($session); // remove the "\" between the "exe" and "c", this was causing issues with wordpress

if ($mimeType != "") {
	// The web service returns XML. Set the Content-Type appropriately
	header("Content-Type: ".$mimeType);
}

echo $response;
curl_close($session);

} ?>

This is a modified version of PHP Proxy Script for Cross Domain Requests by Abdul Qabiz. It is very simple to use, just pass the request that you would have sent to the remote URL to this script instead, storing the remote URL in the url parameter. If it is a post request, then this script forwards the post parameters as well. If the remote URL response provides a mimeType then this script will return the same mimeType as the proxied response.

The script has improved handling of POST and GET detection, as I believe older versions of PHP (when Abdul’s script was originally written) handled missing associated array keys without throwing an error. As per some good advice by Steve Webster, I have added support for a whitelist of domains that can use the proxy. If you do not use the whitelist, then nefarious developers can hijack your proxy to mask malicious activity, making it seem like their malicious attacks comes from your servers. Example 1 shows that I have whitelisted two of my local servers and "mattsnider.com". Lastly, when using CURLOPT_FOLLOWLOCATION one should also set CURLOPT_MAXREDIRS, so that you cannot accidentally setup an infinite loop by redirecting to your own proxy.

Below is an example URL that you could use to get the IP address of a visitor to your site:

Example 2: A Test Url

// cross-domain url = http://jsonip.appspot.com/?callback=getip
// "proxy.php?url=" + encodeURIComponent("http://jsonip.appspot.com/?callback=getip")
var AjaxUrl = "proxy.php?url=http%3A%2F%2Fjsonip.appspot.com%2F%3Fcallback%3Dgetip";

This script uses the curl library, which is not usually defaulted on in PHP. To turn it on, simply uncomment the "extension=php_curl.dll" line in your "php.ini" file and restart your server.

CSS String Truncation with Ellipsis

Today’s article is brought to us by guest writer, Justin Maxwell. Justin will explain the technique he fine tuned for Mint.com to ellipsis text using just CSS. For more information about ...

Augmented Configuration Pattern

When a JavaScript object can be configured in a variety of ways, it is best-practice to include a configuration object in the constructor, where all configuration values are optional. This way, the developer can configure an object when it is instantiated, without having to provide parameters that are common to most instances of an object.

Example 1: Using A Configuration Object

 var YL = YAHOO.lang; var testFunction = function(conf) { var cfg = ...

Using an EventProvider

For the Client-Side Storage problem that I have been working on with YUI, I was introduced to the EventProvider Interface, which provides a better way of handling the CustomEvents attached to an object. In Yahoo!s own words:

EventProvider is designed to be used with YAHOO.augment to wrap CustomEvents in an interface that allows events to be subscribed to and fired by name. This makes it possible for implementing code to subscribe to an event ...

AJAX Poller

A friend of mine needed a simple AJAX polling system, so I developed the AjaxPoller widget for him. The object is instantiated with a configuration object, which should include at the very least the URL to poll (url). Additionally, the developer can optionally specify: the length of time before the AJAX message times out (timeout), the length of time to wait between polls (period), a function to trigger when to stop polling dymanically (stopfx), and ...

Import JavaScript Using DOM Insertion.txt

The most common and efficient way to dynamically insert JavaScript is to use a DOM insertion technique. When the page loads, provide some basic JavaScript architecture, most likely namespace, DOM, and event management, plus a JavaScript import method (YUI has a loader utility YUI Loader and all 4 packages in ). Then as JavaScript is executed on the page and additional features are required, a JavaScript insert method should be called to append ...

Import JavaScript Using iFrames

There are a variety of ways to dynamically import JavaScript files on demand, the most elegant solution is to add additional script tags into the header. Todays article introduces a different technique, a hack if you will, leveraging the fact that an iFrame can load and run its own JavaScript, entirely independent of the parent context. So for each JavaScript file to include, beyond the main library, an iframe must be appended to the end ...

Leverating YAHOO.env._id_counter for Unique Ids

This topic was originally covered in the Unique Id Generator article, where you can find more a heavy-handed solution, allowing a developer to determine the character set used for and length of the generated id. However, as most of the commenters pointed out, there is a simpler way to create unique numbers that will work for most developers; that is to increment a global number each time it is used. YUI already has a ...

Improved Cookie Size Calculation

The Yahoo! User Interface Library (YUI) has a great cookie management system, cookie.js, that handles basic cookie read/write, as well as supporting sub-cookies, which allows multiple pieces of data to be stored in a single cookie. In the article, Augmenting Cookie, I proposed several helpful functions to augment the cookie object, including: getNumberOfCookies, getCookieSize, and isCookiesEnabled. Today’s article revisits the getCookieSize, improving the performance and accuracy of the calculation.

Article ...

Fast, NonObtrusive Way to Capture Click Events Before Loading JS Library

Many JavaScript developers are learning to optimize their webpages by moving JavaScript includes to the bottom of the HTML document, just before the closing of the body tag. In addition, most developers believe in separation of implementation from design, so all JavaScript event handlers will be inside those included JavaScript files, instead of being written (or attached) directly in the HTML. What this creates is a window of time, depending on how long it takes ...