Access to datasets not located on the API server

2D Web Javascript API

Forewords

This chapter being quite technical before further reading, it is worth noting in which case your application is :

  • your web application and the data you want to access to are located on the same web server : cool, it is the simple case : just enter the path to data ! For instance, your web site is 'http://your.web.site/yourAppli.html', a KML file is located there 'http://your.web.site/kml/yourLayer.kml', you just have to create a layer with './kml/yourLayer.kml' as url.
  • your web application and the data you want to access to are not located on the same web server : bad luck, you need a proxy (and then read this page). In order to load the data, OpenLayers (and then the API) uses Ajax. Security rules prevent to access data from another web site the web page came from. To get data, you then need a service that your web site will ask for from them : it is the proxy (a kind of waiter handing over to you the data). This proxy must be running on your web site. For instance, your proxy url will be 'http://your.web.site/proxy/myProxy.asp' (or .php, ...). You have to set your proxy up :
    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.

Introduction

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.

Why using a proxy ?

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().

JSP proxy

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.

PHP proxy

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;
    // ----------------------------------------------------------
?>
ASP proxy

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);
%>
CGI proxy

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);
}
How to set the proxy ?

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.