One of several limitations to running applications in the browser is the so-called same-origin policy, a security restriction which prevents Javascript/ECMAScript programs in the browser from accessing resources from a different domain from that of the current page in the browser. This oftens affects OpenLayers programs.
The restriction is not a problem when loading the page, as that is controlled by the browser, not Javascript. So the initial load can include resources from as many domains as you like. Also, curiously, the restriction does not apply to <script> tags, so, when all you need is to load a file from another domain, a Javascript program can get round the restriction by simply dynamically creating a <script> tag which will then be fetched. If the resulting file is JSON (i.e. a Javascript object) and enclosed in a function call, that function will then be executed. Needless to say, this should only be used when you trust the source of the JSON function.
It's usual with AJAX-type applications to want to access data dynamically based on the browser-user's input, using XMLHttpRequest or similar. In most cases, you are only dealing with the server the page came from, so the same-origin policy is not a problem. In OpenLayers, though, it's common to want to fetch data, for example, feature data, from another domain in an AJAX-style relationship. Under the same-origin policy, this does not work.
A common way round this is to set up a proxy on the server; then, the browser tells this proxy which file/resource it wants, the proxy fetches it, and then passes it on to the browser. That way, the browser is happy because it is only dealing with the one server.
Setting up a proxy for simple HTTP GET (i.e. read) commands is very simple; for example, using Curl under PHP it can be done in a couple of lines - though you'd be well advised to restrict use to the domains you actually want to access, and prevent some outsider from using it for their own, not necessarily legal, purposes.
If you want to do the full CRUD transactions - HTTP POST, PUT and DELETE as well as GET - on the outside server, though, the proxy is more complicated, particularly if you have to set certain headers on the requests. When I wanted to set up a proxy on my own server, I searched for quite a while for one that would do all I wanted, and eventually discovered that there is a Symfony plugin which does the job admirably. This is the sfWebBrowserPlugin.
The URL requesting the resource from the proxy contains the outside URL in the query string - myproxy.html?url=resourceIWant. The list of valid hosts is set up in the application configuration file, and if the host requested is not on this list, it ignores the request. I want it to pass on authorisation and content-type headers. This should work whether Apache is running PHP as a module or as CGI - which complicates matters as in the latter case the headers are, confusingly, not in apache_request_headers but in the environment variables.
So, the action takes the request, instantiates sfWebBrowser with the appropriate headers, and passes on the request. It then echos back the responseText to the browser; of course, it returns sfView::NONE so symfony doesn't try putting it in some sort of template. Removing exception handling for simplicity, the Symfony action looks like this:
$url = $request->getParameter('url');
$bits = explode('/', $url);
if (!in_array($bits[2], sfConfig::get('app_proxy_validHosts')))
return sfView::NONE;
$oph = array();
$oph["Content-Type"] = $_SERVER['CONTENT_TYPE'];
if (strpos(php_sapi_name(), 'apache') !== false) {
$headers = apache_request_headers();
$auth = ' '.$headers['Authorization'];
}
else
$auth = $_ENV['HTTP_AUTHORIZATION'];
if ($auth)
$oph["Authorization"] = $auth;
$b = new sfWebBrowser($oph);
switch ($_SERVER['REQUEST_METHOD'])
{
case 'PUT':
$cont = file_get_contents('php://input');
$b->put($url, $cont);
break;
case 'POST':
$cont = file_get_contents("php://input");
$b->post($url, $cont);
break;
case 'DELETE':
$b->delete($url);
break;
default:
$b->get($url);
}
echo $b->getResponseText();
return sfView::NONE;