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/14 00:00:48 UTC

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

     [ https://issues.apache.org/jira/browse/SHINDIG-1927?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Mike Pawlowski updated SHINDIG-1927:
------------------------------------

    Description: 
----------------------------------------------------------------------------------------
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
        - http://www.html5rocks.com/static/images/cors_server_flowchart.png
        
* 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>


  was:

----------------------------------------------------------------------------------------
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>


    
> 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
>         - http://www.html5rocks.com/static/images/cors_server_flowchart.png
>         
> * 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