This chapter being quite technical before further reading, it is worth noting in which case your application is :
myMap.setProxyUrl('http://your.web.site/proxy/myProxy.asp');Where to find such a proxy service ? for instance there. You could also copy-paste the source code found on this page.
In order to load a remote layer, a proxy service is required in order to retrieve data for the end-user. Basically, all data needed to be processed by the Javascript library on the client needs this functionality. So, layers like WFS, KML, GPX, OpenLS, ... (written differently, all XML responses from remote services) are ruled by this "proxyfication". OpenLayers add the URL to proxy to the proxy URL. The examples found below use the GET method with the url parameter to pass the target url.
OpenLayers uses AJAX request to retrieve data from various services. For security reasons, it is not possible to make such AJAX requests to services located in a different domain name (FQDN) than the API server (client of the those services). Therefore, it is not possible to access services from a server distinct from the API server (where the API is loaded).
This restriction can be suppressed via the use of a proxy located on the API server (the one that brings the map to the end-users). The hint is to send the AJAX request to the map server directly where the proxy runs, instead of the targetted service. The former forwards the requests to the targetted service, receives the responses and forwards them back to the OpenLayers clients.
The Geoportal API is based on that principal. The Geoportal.Map class supports setProxyUrl() method that takes one argument representing the proxy's URL to query as a characters string. In order to use layers not located on the API server, it is then mandatory to have a proxy service running along with this server. Its URL will be transmitted via a call to instance.setProxyUrl().
The Geoportal API used to expose a JSP proxy and now use a servlet called xmlProxy. If you run a servlets engine at your API server side, you just may copy this JSP proxy into your servlets engine directory.
<%
if("GET".equals(request.getMethod())){
//execute the GET
String serverUrl= org.apache.commons.httpclient.util.URIUtil.decode(request.getParameter("url"));
java.net.URL url= new java.net.URL(serverUrl);
if (!"http".equals(url.getProtocol())) {
throw new javax.servlet.ServletException(
"only use HTTP Url's, please don't hack this server!");
}
java.io.InputStream in= url.openStream();
response.setContentType("text/xml");
byte[] buff= new byte[1024];
int count;
java.io.OutputStream o= response.getOutputStream();
while ((count= in.read(buff)) > -1) {
o.write(buff, 0, count);
}
o.flush();
o.close();
}else{
//execute the POST
try {
// Transfer bytes from in to out
java.io.PrintWriter o= response.getWriter();
java.io.InputStream in= request.getInputStream();
String serverUrl= org.apache.commons.httpclient.util.URIUtil.decode(request.getParameter("url"));
org.apache.commons.httpclient.HttpClient client= new org.apache.commons.httpclient.HttpClient();
org.apache.commons.httpclient.methods.PostMethod httppost=
new org.apache.commons.httpclient.methods.PostMethod(serverUrl);
String referrer= request.getHeader("referer");
if (referrer!=null) {
org.apache.commons.httpclient.Header h= new org.apache.commons.httpclient.Header("referer", referrer);
httppost.setRequestHeader("referer", referrer);
}
String ct= request.getHeader("Content-Type");
if (ct==null) {
ct= "application/xml";
}
httppost.setRequestHeader("Content-Type", ct);
httppost.setRequestEntity(new org.apache.commons.httpclient.methods.InputStreamRequestEntity(in));
String proxyHost = System.getProperty("http.proxyHost");
String proxyPort = System.getProperty("http.proxyPort");
client.getHostConfiguration().setProxy(proxyHost,Integer.parseInt(proxyPort));
client.executeMethod(httppost);
int code= httppost.getStatusCode();
if ((code >= org.apache.commons.httpclient.HttpStatus.SC_OK &&
code < org.apache.commons.httpclient.HttpStatus.SC_MULTIPLE_CHOICES)) {
response.setContentType("text/xml");
String responseBody= httppost.getResponseBodyAsString();
response.setContentLength(responseBody.length());
o.print( responseBody );
} else {
throw new javax.servlet.ServletException("Unexpected failure: " + httppost.getStatusLine().toString());
}
httppost.releaseConnection();
o.flush();
o.close();
} catch (java.io.IOException e) {
throw new javax.servlet.ServletException(e);
}
}
%>On the other hand, the servlets engine JVM (tomcat or other engine) might be set to use a proxy Lan to Wan service. For that, one has to use the following options :
-Dhttp.proxyHost=<proxy_address> -Dhttp.proxyPort=<proxy_port>.
When using Eclipse environment, these options are found in the Window/Preferences menu, Tomcat item, JVM parameters, add parameters to the JVM.
Using a PHP proxy is also possible for webmasters that don't use servlets engine. The following code shows how to set up such a proxy :
<?php
// proxy http
// auteur: Marc Gauthier
// 02/09/2009
// les erreurs dans les log du serveur
// http://www.papygeek.com/download/53/
// Transfer-Encoding: chunked
//
// Didier Richard - IGN - dérivation pour publication
// (c) IGN 2010
// License : http://creativecommons.org/licenses/by-nc-sa/2.0/fr/
// ----------------------------------------------------------
// global variables :
$debug= 0;
$debug_html= 0;
$sUrl= '';
$sReponse= '';
// via un proxy d'entreprise/IP
$proxy_host= '';
$proxy_port= 80;
$content_types= array(
'application/vnd.google-earth.kml+xml',
'application/vnd.google-earth.kml',
//'application/vnd.google-earth.kmz', # TODO needs to unzip response ...
'application/vnd.ogc.se_xml',
'application/vnd.ogc.wms_xml',
'application/vnd.ogc.wfs_xml',
'application/vnd.ogc.gml',
'application/soap+xml',
'application/xml',
'text/xml',
'text/plain',
'text/html');
// ----------------------------------------------------------
//
// écriture d'un message de log
function carp($msg) {
global $debug;
global $debug_html;
if ($debug) {
if ($debug_html) {
print "__FILE__.:$msg.<br>\n";
}
error_log(__FILE__.": $msg", 0);
}
}
//
// écriture d'un message de log et erreur http
function confess($msg) {
carp($msg);
header("HTTP/1.0 500 $msg");
exit;
}
//
// mode chunk
function unchunk($result) {
return preg_replace_callback(
'/(?:(?:\r\n|\n)|^)([0-9A-F]+)(?:\r\n|\n){1,2}(.*?)'.
'((?:\r\n|\n)(?:[0-9A-F]+(?:\r\n|\n))|$)/si',
create_function(
'$matches',
'return hexdec($matches[1]) == strlen($matches[2]) ? $matches[2] : $matches[0];'
),
$result
);
}
//
// la fonction proxy
// 2 grandes étapes:
// - émission de la requête
// - réception de la réponse
function proxy($sUrl) {
global $proxy_host, $proxy_port, $chunked, $content_types;
//
// analyse de l'url et construction de la requête
$sUrl= urldecode($sUrl);
$referrer= (isset($_SERVER["HTTP_REFERER"])? $_SERVER["HTTP_REFERER"] : "");
$userAgent= (isset($_SERVER["HTTP_USER_AGENT"])? $_SERVER["HTTP_USER_AGENT"] : "");
$aUrl= @parse_url($sUrl);
if (!isset($aUrl['scheme'])) {
//try absolute and relative path:
confess("proxy scheme missing");
}
// construction de la requete
$acceptH= (isset($_SERVER['HTTP_ACCEPT'])? $_SERVER['HTTP_ACCEPT'] : "");
#$pragmaH= (isset($_SERVER['HTTP_PRAGMA'])? $_SERVER['HTTP_PRAGMA'] : "");
#$cacheControlH= (isset($_SERVER['HTTP_CACHE_CONTROL'])? $_SERVER['HTTP_CACHE_CONTROL'] : "");
$acceptLanguageH= (isset($_SERVER['HTTP_ACCEPT_CHARSET'])? $_SERVER['HTTP_ACCEPT_CHARSET'] : "");
$keepAliveH= (isset($_SERVER['HTTP_KEEP_ALIVE'])? $_SERVER['HTTP_KEEP_ALIVE'] : "");
#$acceptEncodingH= (isset($_SERVER['HTTP_ACCEPT_ENCODING'])? $_SERVER['HTTP_ACCEPT_ENCODING'] : "");//TODO decompression
#$connectionH= (isset($_SERVER['HTTP_CONNECTION'])? $_SERVER['HTTP_CONNECTION'] : "");
#$SOAPActionH= (isset($_SERVER['HTTP_SOAPACTION'])? $_SERVER['HTTP_SOAPACTION'] : "");
$acceptCharsetH= (isset($_SERVER['HTTP_ACCEPT_CHARSET'])? $_SERVER['HTTP_ACCEPT_CHARSET'] : "");
#$gppKeyH= (isset($_SERVER['gppkey'];#if gppKey in HTTP Header
$Hs= "Host: ".$aUrl['host'].(isset($aUrl['port']) && !empty($aUrl['port']) ? ":".$aUrl['port'] : "")."\r\n"
. (strlen($acceptH)>0? "Accept: ".$acceptH : join(",", $content_types))."\r\n"
#. (strlen($pragmaH)>0? "Pragma: ".$pragmaH."\r\n" : "")
#. (strlen($cacheControlH)>0? "Cache-Control: ".$cacheControlH."\r\n" : "")
. (strlen($acceptLanguageH)>0? "Accept-Language: ".$acceptLanguageH."\r\n" : "")
. (strlen($keepAliveH)>0? "Keep-Alive: ".$keepAliveH."\r\n" : "")
#. (strlen($acceptEncodingH)>0? "Accept-Encoding: ".$acceptEncodingH."\r\n" : "")
#. (strlen($connectionH)>0? "Connection: ".$connectionH."\r\n" : "")
#. (strlen($SOAPActionH)>0? "SOAPAction: ".$SOAPActionH."\r\n" : "")
. (strlen($acceptCharsetH)>0? "Accept-Charset: ".$acceptCharsetH."\r\n" : "")
#. (strlen($gppKeyH)>0? "gppkey: ".$gppKeyH."\r\n" : "")
. (strlen($referrer)>0? "Referer: ".$referrer."\r\n" : "")
. (strlen($userAgent)>0? "User-Agent: ".$userAgent."\r\n" : "")
;
carp($Hs);
if ($_SERVER["REQUEST_METHOD"]==='GET') {
$sReq= "GET $sUrl HTTP/1.0\r\n"
. $Hs
;
} else {
if ($_SERVER["REQUEST_METHOD"]==='POST') {
$data= '';
if (count($_POST)){
while (list($key, $val)= each($_POST)){
$data.="$key : $val\n";
}
} else {
$data= trim(file_get_contents('php://input'));
}
$sReq= "POST $sUrl HTTP/1.0\r\n"
. $Hs
. "Content-Type: text/xml\r\n"
. "Content-length: ".strlen($data)."\r\n"
. "\r\n"
. $data
;
}
}
$sReq.= "\r\n";
if (empty($proxy_host)) {
$host= $aUrl["host"];
$port= (isset($aUrl["port"]) && !empty($aUrl["port"])? $aUrl["port"] : 80);
} else {
$host= $proxy_host;
$port= $proxy_port;
}
// envoi de la requête
carp("host:$host port:$port url:$sUrl");
$fp= @fsockopen($host, $port, $errno, $errstr, 5);
if (!$fp) {
confess("fsockopen failed: $errstr ($errno)");
}
carp("sReq:$sReq");
fwrite($fp, $sReq);
// attente de la réponse
$headers= '';
$sReponse= '';
ob_start();
while (!feof($fp)) {
$sReponse.= fread($fp, 4096);
}
fclose ($fp);
$eoh= strpos($sReponse, "\r\n\r\n");
$headers= substr($sReponse, 0, $eoh);
$sReponse= substr($sReponse, $eoh+4);
$Hs= preg_split('/(?:\r\n|\n)/', $headers);
carp("Hs=[".count($hs)."]");
for ($i= 0, $l= count($hs); $i<$l; $i++) {
if (preg_match('/^Content-Length/i', $hs[$i])) {
continue;
}
if (preg_match('/^Transfer-Encoding: chunked/i', $hs[$i])) {
// Transfer-Encoding: chunked
carp("chunked response");
$sReponse= unchunck($sReponse);
continue;
}
#carp("header=[$hs[$i]]");
header($hs[$i]);
}
header("Content-Length: ".strlen($sReponse));
print $sReponse;
}
// ----------------------------------------------------------
// programme principal:
// on accept que GET/POST (pour l'instant)
if (($_SERVER["REQUEST_METHOD"]==='GET' or $_SERVER["REQUEST_METHOD"]==='POST') &&
(isset($_REQUEST["url"]) && strlen($_REQUEST["url"])>0)) {
$sUrl= substr($_SERVER["QUERY_STRING"],4);
carp("Proxying:$sUrl");
proxy($sUrl);
exit;
}
// on ne traite pas la demande :
carp("REQUEST_METHOD:".$_SERVER["REQUEST_METHOD"]." QUERY_STRING:".$_SERVER["QUERY_STRING"] );
if ($debug) {
phpinfo(INFO_VARIABLES);
}
exit;
// ----------------------------------------------------------
?>Using an ASP proxy is also feasible. The following code uses the Coalesys (http://www.coalesys.com/products/httpclient/features/default.asp) component :
<%@ LANGUAGE=JScript %>
<%
//Creation of the component
var objHTTP =Server.CreateObject("Coalesys.CSHttpClient.1");
//HTTP variables
var HTTPResponseBody = new String();
var HTTPResponseHeaders = new String();
var HTTPResponseStatus = new String();
//Target URL
objHTTP.RequestURL = String(Request.Querystring).substr(4);
//Possibly, use a proxy (LAN 2 WAN)
//objHTTP.ProxyAddress = "<proxy_adress>";
objHTTP.ProxyPort = <proxy_port>;
objHTTP.AppendHost = true;
//Execution of the request
objHTTP.Execute("GET");
//Status reception
HTTPResponseStatus = objHTTP.ResponseStatus;
//Headers reception
HTTPResponseHeaders = objHTTP.ResponseHeaders;
//Response body reception
HTTPResponseBody = objHTTP.ResponseBody(0);
//Forwarding of the response
Response.ContentType = "text/xml";
Response.write(HTTPResponseBody);
%>It is also possible to use proxies developed in other languages (Unix Shells, Python, Perl, ...) via CGI. OpenLayers delivers such a proxy written in Python. With Perl, the following URL http://search.cpan.org/~book/HTTP-Proxy/lib/HTTP/Proxy.pm delivers a library to set up ones own proxy.
Here is a Perl example using the cURL library again :
#!/usr/bin/perl -T
#
# (c) 2008 IGN - Didier.Richard@ign.fr
# License : http://creativecommons.org/licenses/by-nc-sa/2.0/fr/
#
#use diagnostics;
use strict;
use warnings;
# modules
use CGI qw/:cgi/;# to dialog over http
$CGI::POST_MAX=1024 * 1000; # max 1000Kb posts
use LWP::UserAgent;
use HTTP::Headers;
use HTTP::Message;
use HTTP::Request;
use HTTP::Response;
use Encode;
use IO::Uncompress::Unzip qw(unzip $UnzipError);
# variables globales
our $VERSION= '0.0.1';
# TODO : default configuration file
# The SetPerlVar directive can override this :
my $configfile= '';
#
# Error outputs
#
sub notify_proxy_failure {
my ($cgi, $status, $msg)= @_;
print $cgi->header(
-type => 'text/plain;charset:utf-8',
-status => "$status",
-Content_Length => defined($msg)? length($msg) : 0
);
if (defined($msg)) {
print $msg;
}
exit 0;
}# notify_proxy_failure
#
# LWP Proxy (Web)
#
sub proxyfy_remote {
my ($cgi, $url)= @_;
my $hds= HTTP::Headers->new() or notify_proxy_failure($cgi,500);
my @content_types= (
'application/vnd.google-earth.kml+xml',
'application/vnd.google-earth.kml',
'application/vnd.google-earth.kmz',
'application/vnd.ogc.wms_xml',
'application/vnd.ogc.wfs_xml',
'application/vnd.ogc.gml',
'application/soap+xml',
'application/xml',
'text/xml'
);
$hds->header(Accept => \@content_types);
for my $h ('Pragma', 'Cache-Control', 'Accept-Language', 'Keep-Alive',
'Accept-Encoding', 'Connection', 'SOAPAction', 'Accept-Charset') {
my $hv= $cgi->http($h);
if (defined($hv)) {
$hds->header($h => $hv);
}
}
$hds->referer($cgi->referer()) if defined($cgi->referer());
my $rqst= HTTP::Request->new($cgi->request_method(), $url, $hds) or notify_proxy_failure($cgi,500);;
my $xml;
if ($cgi->request_method() eq 'POST') {
$xml= $cgi->param('POSTDATA');
if (!defined($xml)) {
$xml= '';
$rqst->header('Content-Type', $cgi->content_type());
for my $k ($cgi->param) {
my @v= $cgi->param($k);
$xml.= $k.'='.join(',',@v).'&';
}
}
$rqst->content($xml);
$rqst->content_length(length($xml));
} else {
$xml= $cgi->query_string;
}
my $bot= LWP::UserAgent->new() or notify_proxy_failure($cgi,500);
$bot->agent($cgi->user_agent()) if defined($cgi->user_agent());
$bot->max_size(1000*1024); # 1Mo
#$bot->show_progress(0);
$bot->env_proxy;#if behind an enterprise proxy
my $resp= $bot->request($rqst);
if (!$resp->is_success) {
notify_proxy_failure($cgi,400,'remote('.$xml.') failed : '.$resp->status_line);# RC_BAD_REQUEST
}
my $content= $resp->content_ref;
my $content_length= $resp->header('Content-Length');
my $content_type= $resp->header('Content-Type');
if (!defined($content_type)) {
notify_proxy_failure($cgi,400,'empty content-type ('.
$$content.') for ('.$xml.')');# RC_BAD_REQUEST
}
chomp($content_type);
$content_type=~s/\x0d//g;
if ($content_type!~m/^ *([^;]+)/i) {
notify_proxy_failure($cgi,400,'unknown content-type : '.$content_type.' ('.
$$content.') for ('.$xml.')');# RC_BAD_REQUEST
}
my ($mimetype)= split(/,/, $1); # only the first ?
$mimetype=~s/\x0d//g;
$mimetype=~s/\x0a//g;
if (scalar(grep {$_=~m{^\Q$mimetype\E$}} @content_types)==0) {
notify_proxy_failure($cgi,400,'unknown mime-type : '.$mimetype.' ('.
$$content.') for ('.$xml.')');# RC_BAD_REQUEST
}
my $charset= 'UTF-8';
if ($content_type=~m/^.*; *charset=(.*)$/i) {
$charset= $1;
$charset=~s/\x0d//g;
$charset=~s/\x0a//g;
}
my $uncompressIt= 0;
if ($mimetype eq 'application/vnd.google-earth.kmz') {
# unzip it :
$mimetype= 'application/vnd.google-earth.kml+xml';
$uncompressIt= 1;
}
if ($resp->header('Content-Encoding') eq 'gzip') {
$uncompressIt= 1;
}
if ($uncompressIt) {
my $unzip_returned_content= '';
my $uz= new IO::Uncompress::Unzip $content, InputLength => $content_length, Append => 1
or notify_proxy_failure($cgi,500);
my $unzip_returned_content_length= 0;
my $read_count= 0;
while (($read_count= $uz->read($unzip_returned_content))>0) {
$unzip_returned_content_length+= $read_count;
}
# read_count < 0 : error
# read_count = 0 : eof
notify_proxy_failure($cgi,500) if $read_count!=0;
$content= \$unzip_returned_content;
$content_length= $unzip_returned_content_length;
}
print $cgi->header(
-type => "$mimetype;charset=$charset",
-Content_Length => $content_length
);
print $$content;
}# proxyfy_remote
#
# LWP Proxy (local)
#
sub proxyfy_local {
my ($cgi, $url)= @_;
my $hds= HTTP::Headers->new() or notify_proxy_failure($cgi,500);
my $rqst= HTTP::Request->new('GET', $url, $hds) or notify_proxy_failure($cgi,500);;
my $bot= LWP::UserAgent->new() or notify_proxy_failure($cgi,500);
$bot->max_size(1000*1024); # 1Mo
#$bot->show_progress(0);
my $resp= $bot->request($rqst);
if (!$resp->is_success) {
notify_proxy_failure($cgi,400,'local('.$url.') failed : '.$resp->status_line);# RC_BAD_REQUEST
}
my $mimetype= 'text/xml';
my $charset= 'UTF-8';
my $content= $resp->content_ref;
my $content_length= $resp->header('Content-Length');
if ($url=~m/\.kml$/i) {
$mimetype= 'application/vnd.google-earth.kml+xml';
} elsif ($url=~m/\.kmz$/i) {
# unzip it :
$mimetype= 'application/vnd.google-earth.kml+xml';
my $unzip_returned_content= '';
my $uz= new IO::Uncompress::Unzip $content, InputLength => $content_length, Append => 1
or notify_proxy_failure($cgi,500);
my $unzip_returned_content_length= 0;
my $read_count= 0;
while (($read_count= $uz->read($unzip_returned_content))>0) {
$unzip_returned_content_length+= $read_count;
}
# read_count < 0 : error
# read_count = 0 : eof
notify_proxy_failure($cgi,500) if $read_count!=0;
$content= \$unzip_returned_content;
$content_length= $unzip_returned_content_length;
} elsif ($url=~m/\.js$/i) {
$mimetype= 'application/javascript';
}
print $cgi->header(
-type => "$mimetype;charset=$charset",
-Content_Length => $content_length,
);
print $$content;
}# proxyfy_local
#
# Main program :
#
# First, we get GlobalRequest
my $gr= undef;
my $cgi= undef;
if ($ENV{MOD_PERL}) {
$gr= shift @_;
$cgi= CGI->new($gr);
$configfile= $gr->dir_config('configfile');
} else {
$cgi= CGI->new();
}
my $url;
if ($cgi->request_method() eq 'GET') {
$url= $cgi->url_param('url');
} elsif ($cgi->request_method() eq 'POST') {
$url= $cgi->url_param('url');
} else {
notify_proxy_failure($cgi,400,'unknown method : '.$cgi->request_method());# RC_BAD_REQUEST
}
if ($url=~m/^https?:\/\//) {
proxyfy_remote($cgi,$url);
} else {
proxyfy_local($cgi,$url);
}Just call the setProxyUrl() method as follows :
function initGeoportalMap() {
...
geoportalLoadmyMap(mapdiv,mapmode,modterritory);
// now myMap is defined :
myMap.getMap().setProxyUrl(proxyUrl);
...
}Starting with 1.0beta4 version, one can specify the proxy at creation time :
function initGeoportalMap() {
...
geoportalLoadmyMap(mapdiv,mapmode,modterritory,null,null,proxyUrl);
...
}In the upcoming release of the Geoportal's API, most of the services will support the JSONP protocol when practicable, allowing to get JSON response embedding XML answers.