You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@shindig.apache.org by "Mike Pawlowski (JIRA)" <ji...@apache.org> on 2013/08/13 23:56:47 UTC

[jira] [Created] (SHINDIG-1927) JSON RPC servlet does not support CORS properly

Mike Pawlowski created SHINDIG-1927:
---------------------------------------

             Summary: JSON RPC servlet does not support CORS properly
                 Key: SHINDIG-1927
                 URL: https://issues.apache.org/jira/browse/SHINDIG-1927
             Project: Shindig
          Issue Type: Bug
          Components: Java
    Affects Versions: 2.5.0
         Environment: * Jetty 9.0.3.v20130506
* Apache Tomcat 7.0.42 (64-bit)

            Reporter: Mike Pawlowski
            Priority: Minor



----------------------------------------------------------------------------------------
Problem
----------------------------------------------------------------------------------------

* Like other customers, we have the following requirement: Shindig running on different domain than container REVISITED
   => http://markmail.org/message/qqfnyjg6dfunfw3h

* JSON RPC servlet does not support CORS properly.
  => org.apache.shindig.protocol.JsonRpcServlet
  => Causes cross-domain XHRs to get gadget metadata to fail
        - Servlet does not handle OPTIONS preflight requests, so CORS requests automatically fail
        
* However, the RPC servlet does specify the "Access-Control-Allow-Origin" header 
   => This interferes with Jetty / Tomcat CORS servlet filters and causes the actual POST request to fail
   => Cause: "If the response includes zero or more than one Access-Control-Allow-Origin header values, return fail and terminate this algorithm."
         - http://www.w3.org/TR/cors/#resource-sharing-check          
         - The servlet filter is already providing a value for the "Access-Control-Allow-Origin" header downstream
 
----------------------------------------------------------------------------------------
Patch
----------------------------------------------------------------------------------------

Since, the CORS support is incomplete and does not work out-of-the-box, it is probably safer to just remove the CORS specific header setting in the RPC servlet altogether

i.e.   HttpUtil.setCORSheader(servletResponse, containerConfig.<String>getList(token.getContainer(), "gadgets.parentOrigins"));

Will attach patch.
          
----------------------------------------------------------------------------------------
Background Information
----------------------------------------------------------------------------------------

* Shindig does not have out-of-the-box (OOTB) support for OpenSocial gadget containers hosted from one domain and the OpenSocial gadget renderer hosted from another domain
   => e.g. 
         - Examples application (OS gadget container): http://localhost:9081/examples/gadgets 
         - Shindig application (OS gadget renderer): http://localhost:9082/rpc, http://localhost:9082/gadgets/ifr, etc.
   => Deploying Shindig as a separate application in the Jazz Platform ecosystem is advantageous
         - Prevents the requirement for each application that hosts OpenSocial gadgets to serve it's own local copy of Shindig
         - Facilitates the sandboxing / compartmentalization / segregation of Shindig binaries from Jazz Platform related binaries

* Only the request to get gadget metadata needs to be executed via a cross-domain XHR
  => Executed by common container API (osapi.container) outside of the yet-to-be constructed gadget iframe before making a request to render the gadget iframe (needs gadget metadata to render the gadget)
  
* Possible solutions to fix the cross-domain XHR from the OS gadget container to OS gadget renderer to get the gadget metadata
   => (1) Use a transparent proxy on the gadget container server to proxy the metadata request to Shindig
          - Complicates server configuration & deployment
          - Causes support issues for customers with custom server setups (e.g. proxies, firewalls)
   => (2) Use HTML5 Cross-Origin Resource Sharing (CORS) support
          - http://www.w3.org/TR/cors/
   => (3) Use JSONP 
          - http://www.ibm.com/developerworks/library/wa-aj-jsonp1/
          - Only supports GET HTTP requests
          - RPC servlet relies on POST HTTP requests
   => (4) Request metadata from server-side JSP and inline the JSON response in the gadget container page response
          - Not dynamic. Cannot add a gadget to the container on-the-fly via AJAX (i.e. without a full page reload)
   => (5) Use a hidden iframe source from the Shindig server on the gadget container page to get the gadget metadata
          - Need to define a special document on the Shindig server to fetch the gadget metadata and communicate back to the parent container via HTML5 postMessage API
   => (6) Inject custom JavaScript into the gadget iframe (when constructed) to get the gadget metadata
         - "Chicken & the egg problem". Need the gadget metadata in order to render the gadget iframe
         - Too hacky. Need to interfere with the internal workings & flow of control of the common container

----------------------------------------------------------------------------------------
Selected Solution (CORS)
----------------------------------------------------------------------------------------

* Decided to implement option (2) CORS approach
  => Use common container preloadGadget API to fetch the gadget metadata and force Shindig to cache the response
        - http://opensocial-resources.googlecode.com/svn/spec/2.5/Core-Container.xml#osapi.container.Container.preloadGadget
  => Use common container navigateGadget API to render the gadget (e.g. Create iframe, etc.) using cached gadget metadata
        - http://opensocial-resources.googlecode.com/svn/spec/2.5/Core-Container.xml#osapi.container.Container.navigateGadget
        - Note: If preloadGadget call is omitted, then the navigateGadget call will try to fetch the gadget metadata itself and fail due to cross-domain security failures 

* On Jetty, use built-in Cross Origin Filter feature
   http://download.eclipse.org/jetty/7.6.11.v20130520/apidocs/org/eclipse/jetty/servlets/CrossOriginFilter.html
   http://wiki.eclipse.org/Jetty/Feature/Cross_Origin_Filter

* On Tomcat, use built-in CORS filter feature
   http://tomcat.apache.org/tomcat-7.0-doc/config/filter.html#CORS_Filter

----------------------------------------------------------------------------------------
Jetty Cross Origin Filter Configuration
----------------------------------------------------------------------------------------

  <filter>
    <filter-name>cross-origin</filter-name>
    <filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class>
    <init-param>
      <param-name>allowedOrigins</param-name>
      <param-value>*</param-value>
    </init-param>
    <init-param>
      <param-name>allowedMethods</param-name>
      <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
    </init-param>
    <init-param>
      <param-name>allowedHeaders</param-name>
      <param-value>Content-Type,X-Requested-With,Accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
    </init-param>    
    <init-param>
      <param-name>preflightMaxAge</param-name>
      <param-value>1800</param-value>
    </init-param>    
    <init-param>
      <param-name>allowCredentials</param-name>
      <param-value>true</param-value>
    </init-param>    
    <init-param>
      <param-name>chainPreflight</param-name>
      <param-value>false</param-value>
    </init-param>    
  </filter>

  <filter-mapping>
    <filter-name>cross-origin</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

----------------------------------------------------------------------------------------
Tomcat CORS Filter Configuration
----------------------------------------------------------------------------------------

<filter>
  <filter-name>CorsFilter</filter-name>
  <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
  <init-param>
    <param-name>cors.allowed.origins</param-name>
    <param-value>*</param-value>
  </init-param>
  <init-param>
    <param-name>cors.allowed.methods</param-name>
    <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
  </init-param>
  <init-param>
    <param-name>cors.allowed.headers</param-name>
    <param-value>Content-Type,X-Requested-With,Accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
  </init-param>
  <init-param>
    <param-name>cors.support.credentials</param-name>
    <param-value>true</param-value>
  </init-param>
  <init-param>
    <param-name>cors.preflight.maxage</param-name>
    <param-value>1800</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>CorsFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>


--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira