Tip : Cross Domain Policy when Self-Hosting your Services

This tip is about self hosting of services with the .Net WCF or Java Restlet framework. Cross-domain access is mandatory here since the Site of Origin does not host the services.

Self hosting a service represents a quick & easy way to expose a Web Service for tests purpose. Yet, customization is extermely limited since self hosting does not provide you all the capabilities of Web Servers such as delivering static content (cross domain policy file).

The first thing you need to know is that when Silverlight attempts to access services in cross-domain, it will send a HTTP GET method on https://<domain:port>/clientaccesspolicy.xml or https://<domain:port>/crossdomain.xml (in case clientaccesspolicy.xml wasn’t found). If you plan to self host your Web Service, you will agree that there is no possible way to host a physical Cross Domain policy file at the root of the Web Server.

You must handle this request through your Service implementation. It usually matches the /clientaccesspolicy.xml or /crossdomain.xml URI pattern.

WCF way (inspired from Carlo’s blog)

Self-hosted Service

 class Program
 {
     static void Main(string[] args)
     {
         // URI of the Service
         Uri uri = new Uri("https://localhost:8081/");
  
         // create a new WebServiceHost specifying the implementation of the Service
         // and the specified uri to bind with
         WebServiceHost host = new WebServiceHost(
                 new REST_ItemsServiceLibrary.ItemsREST(),
                 uri);
  
         // add the Service Endpoint specifying the Service Interface
         host.AddServiceEndpoint(typeof(REST_ItemsServiceLibrary.IItemsREST),
                                 new WebHttpBinding(), "");
         // open the WebServiceHost
         host.Open();
  
         Console.WriteLine("Service hosted at {0}", uri.OriginalString);
         Console.ReadLine();
  
         // close the WebServiceHost
         host.Close();
  
     }
 }

Handling cross-domain file requests 

As we said earlier, we have to manage the /clientaccesspolicy.xml, specify in the Service Interface that HTTP GET method on the URI /clientaccesspolicy.xml will be handled by GetSilverlightPolicy();

 [OperationContract]
 [WebGet(UriTemplate ="/clientaccesspolicy.xml")]
 Stream GetSilverlightPolicy();

this also applies to /crossdomain.xml

Finally, in your Service implementation, return a clientaccesspolicy.xml content:

 public Stream GetSilverlightPolicy()
 {
     string result = @"<?xml version=""1.0"" encoding=""utf-8""?>
 <access-policy>
     <cross-domain-access>
         <policy>
             <allow-from http-request-headers=""*"">
                 <domain uri=""*""/>
             </allow-from>
             <grant-to>
                 <resource path=""/"" include-subpaths=""true""/>
             </grant-to>
         </policy>
     </cross-domain-access>
 </access-policy>";
     return StringToStream(result);
 }
 Stream StringToStream(string result)
 {
     WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml";
     return new MemoryStream(Encoding.UTF8.GetBytes(result));
 }

Restlet way

Self-hosted Service

 public static void main(String[] args) {
     try {
         // Create a new Component.
         Component component = new Component();
         // Add a new HTTP server listening on port 8182.
         component.getServers().add(Protocol.HTTP, 8182);
         
         // Attach the sample application.
         component.getDefaultHost().attach(
                 new FirstResourceApplication(component.getContext()));
         
         // Start the component.
         component.start();
         
     } catch (Exception e) {
         // Something is wrong.
         e.printStackTrace();
     }
 }

Handling cross-domain file requests 

Following the same philosophy as the WCF way, we need to handle HTTP GET method on /clientaccesspolicy.xml file request :

In the Application implementation you must bind the URI pattern with the Ressource that will handle it :

image

Finally, implement the ClientAccessPolicy class, specially the HTTP GET method within getRepresentation method :

 package firstResource;
  
 import java.io.IOException;
  
 import org.restlet.Context;
 import org.restlet.data.MediaType;
 import org.restlet.data.Request;
 import org.restlet.data.Response;
 import org.restlet.resource.DomRepresentation;
 import org.restlet.resource.Representation;
 import org.restlet.resource.Resource;
 import org.restlet.resource.Variant;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
  
 public class ClientAccessPolicy extends Resource{
  
     public ClientAccessPolicy(Context context, Request request,
             Response response) {
         super(context, request, response);
         getVariants().add(new Variant(MediaType.TEXT_XML));
     }
     
     @Override
     public Representation getRepresentation(Variant variant){
         if (MediaType.TEXT_XML.equals(variant.getMediaType())) {
              try {
                 DomRepresentation representation = new DomRepresentation(
                       MediaType.TEXT_XML);
                 // Generate a DOM document representing the list of
                 // items.
                 Document d = representation.getDocument();
                 Element accesspolicy = d.createElement("access-policy");
                 d.appendChild(accesspolicy);
                 
                 Element crossdomainaccess = d.createElement("cross-domain-access");
                 accesspolicy.appendChild(crossdomainaccess);
                 
                 Element policy = d.createElement("policy");
                 crossdomainaccess.appendChild(policy);
                 
                 Element allowfrom = d.createElement("allow-from");
                 allowfrom.setAttribute("http-request-headers","*");
                 
                 Element domain = d.createElement("domain");
                 domain.setAttribute("uri", "*");
                 
                 allowfrom.appendChild(domain);
                 
                 Element grantto = d.createElement("grant-to");
                 Element resource = d.createElement("resource");
                 resource.setAttribute("include-subpaths","true");
                 resource.setAttribute("path", "/");
                 
                 
                 grantto.appendChild(resource);
                 
                 policy.appendChild(allowfrom);
                 policy.appendChild(grantto);
                 
                 d.normalizeDocument();
  
  
                 // Returns the XML representation of this document.
                 return representation;
              } catch (IOException e) {
                 e.printStackTrace();
              }
           }
  
           return null;
     }
  
 }

- Ronny Kwon

Comments