You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@jena.apache.org by "Nouwt, B. (Barry)" <ba...@tno.nl.INVALID> on 2020/01/28 19:28:46 UTC

Enable Cross-origin resource sharing (CORS) on embedded Fuseki (jetty)

Hi all,

I enabled CORS on embedded Fuseki (when handling YASGUI SPARQL requests) and wanted to share the way I did it for others' benefit. It took me a while to figure out how to do this. I found instructions on how to enable CORS via the web.xml, but the embedded Fuseki does not use a web.xml (as far as I am aware). I also found a lot of examples on how to add the CORS filter in Jetty when you control when filters are added. The code at the end of the email enables it in my setup.

Some remarks:
- as far as I could see, Fuseki builder does not allow custom filters to be chained/configured before the FusekiFilter (which intercepts all traffic to datasets).
- adding the CORS filter after the FusekiFilter does not work in case of a SPARQL request to a particular dataset, because FusekiFilter intercepts its and prevents it from continuing through the filter chain.
- the above two remarks forced me to add the CORS filter directly to the Jetty Server, however, it does not allow insertion of filters before other filters, it only supports adding it to the end of the chain.
- After succeeding to add my filter before the FusekiFilter, it still did not work, because (after some searching) a FilterMapping (to a particular path) is also required.
- So, the not so elegant code below retrieves both the filters and the filtermappings and inserts the filter (and filtermapping) before the FusekiFilter.

Maybe someone can check whether there is an easier way to enable it and hopefully it saves somebody a few hours in the future!

Regards, Barry

----------- code to enable CORS for embedded fuseki (note that this code is not directly runnable)------------------------------
//... configure server through builder ...

server = builder.build();

Server jettyServer = server.getJettyServer();

/*
 * trying to add the cross origin filter. Fuseki adds its FusekiFilter as the
 * first and this one consumes a SPARQL request to a particular dataset. So, to
 * be able to add the cross origin filter, we have to add it BEFORE the
 * FusekiFilter. This is not as easy as it looks, because you need to add the
 * filter as a filter and as a filtermapping to the /* pathspec.
 */
Handler handler = jettyServer.getHandler();
FilterHolder holder = new FilterHolder(CrossOriginFilter.class);
// we use the default CrossOriginFilter settings, but you can specify them.
//holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
//holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
//holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,POST,HEAD,OPTIONS");
// holder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM,
// 			"X-Requested-With,Content-Type,Accept,Origin");
assert (handler instanceof ServletContextHandler);
ServletContextHandler h = (ServletContextHandler) handler;
ServletHandler servletHandler = h.getServletHandler();
List<FilterMapping> mappings = new ArrayList<FilterMapping>(Arrays.asList(servletHandler.getFilterMappings()));
List<FilterHolder> holders = new ArrayList<FilterHolder>(Arrays.asList(servletHandler.getFilters()));
FilterMapping mapping = new FilterMapping();
mapping.setFilterName(holder.getName());
mapping.setPathSpec("/*");
mapping.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST));
mappings.add(0, mapping);
holders.add(0, holder);
FilterMapping[] mappings3 = new FilterMapping[mappings.size()];
mappings3 = mappings.toArray(mappings3);
FilterHolder[] holders3 = new FilterHolder[holders.size()];
holders3 = holders.toArray(holders3);
servletHandler.setFilters(holders3);
servletHandler.setFilterMappings(mappings3);

server.start();
This message may contain information that is not intended for you. If you are not the addressee or if this message was sent to you by mistake, you are requested to inform the sender and delete the message. TNO accepts no liability for the content of this e-mail, for the manner in which you use it and for damage of any kind resulting from the risks inherent to the electronic transmission of messages.


RE: Enable Cross-origin resource sharing (CORS) on embedded Fuseki (jetty)

Posted by "Nouwt, B. (Barry)" <ba...@tno.nl.INVALID>.
Hi Andy, thanks for the pointers. I've tested the 3.15.0-SNAPSHOT and I can confirm that your FusekiBuilder.enableCors(true) property indeed enables Cross-Origin Resource Sharing on the Fuseki Server. If Fuseki receives a request from the YASGUI instance on another domain, it returns the following cors headers:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://localhost:1234
Access-Control-Expose-Headers: Cache-Control, Content-Language, Content-Length, Content-Type, Expires, Last-Modified, Pragma

Once Apache Jena 3.15.0 comes out I will remove my ugly CORS code that I posted before and replace it by your elegant builder property.

Thank you very much!

Regards, Barry

-----Original Message-----
From: Andy Seaborne <an...@apache.org> 
Sent: vrijdag 28 februari 2020 15:39
To: users@jena.apache.org
Subject: Re: Enable Cross-origin resource sharing (CORS) on embedded Fuseki (jetty)

Yes, JENA-1846. I have no idea why I wrote 1294.

Development builds are at https://repository.apache.org/snapshots

Jena version 3.15.0-SNAPSHOT

       <repositories>

         <repository>
           <id>apache.snapshots</id>
           <name>Apache Snapshot Repository</name>
           <url>https://repository.apache.org/snapshots</url>
           <releases>
             <enabled>false</enabled>
           </releases>
           <snapshots>
             <enabled>true</enabled>
           </snapshots>
         </repository>

       </repositories>


     Thanks
     Andy

On 28/02/2020 09:41, Nouwt, B. (Barry) wrote:
> Hi Andy, thanks for filing the JIRA. I'm unsure whether JENA-1294 is the correct issue, it seems JENA-1846 describes it better.
> 
> Is it possible to use a development build using maven? If so, I might be able to test it.
> 
> Regards, Barry
> 
> -----Original Message-----
> From: Andy Seaborne <an...@apache.org>
> Sent: dinsdag 25 februari 2020 19:38
> To: users@jena.apache.org
> Subject: Re: Enable Cross-origin resource sharing (CORS) on embedded 
> Fuseki (jetty)
> 
> JENA-1294
> 
> I've put in a fixed setup activated by "--cors" - I'd be obliged if people would try it out and let us know if it works.
> 
> It's in the latest development build #1834.
> 
>       Thanks
>       Andy
> 
> 
> On 29/01/2020 11:28, Andy Seaborne wrote:
>> Barry - thanks for the report
>>
>> On 28/01/2020 19:28, Nouwt, B. (Barry) wrote:
>>> Hi all,
>>>
>>> I enabled CORS on embedded Fuseki (when handling YASGUI SPARQL
>>> requests) and wanted to share the way I did it for others' benefit.
>>> It took me a while to figure out how to do this. I found 
>>> instructions on how to enable CORS via the web.xml, but the embedded 
>>> Fuseki does not use a web.xml (as far as I am aware). I also found a 
>>> lot of examples on how to add the CORS filter in Jetty when you 
>>> control when filters are added. The code at the end of the email enables it in my setup.
>>
>> You are correct - Fuseki main is not a webapp, no war file.
>> Fuseki Full, a webapp, does put the CORS filter in.
>>
>> I thought that servlet filters got added before the Fuseki main 
>> filter but it sounds like that is not your experience. That would be a bug.
>> Could you file a JIRA please? As your code show, getting the order 
>> right is ... a bit opaque.
>>
>> The complication is that the Fuseki filter catches "/*" (any URL) 
>> because the valid names can change as datasets are added to registry 
>> which is possible at run time.
>>
>> It might need both "before" and "after" filters.
>>
>> And/or : It might be a good idea to have an "enableCORS" setting to 
>> the Fuseki Server builder which will need its own configuration.
>>
>>       Andy
>>
>>>
>>> Some remarks:
>>> - as far as I could see, Fuseki builder does not allow custom 
>>> filters to be chained/configured before the FusekiFilter (which 
>>> intercepts all traffic to datasets).
>>> - adding the CORS filter after the FusekiFilter does not work in 
>>> case of a SPARQL request to a particular dataset, because 
>>> FusekiFilter intercepts its and prevents it from continuing through the filter chain.
>>> - the above two remarks forced me to add the CORS filter directly to 
>>> the Jetty Server, however, it does not allow insertion of filters 
>>> before other filters, it only supports adding it to the end of the chain.
>>> - After succeeding to add my filter before the FusekiFilter, it 
>>> still did not work, because (after some searching) a FilterMapping 
>>> (to a particular path) is also required.
>>> - So, the not so elegant code below retrieves both the filters and 
>>> the filtermappings and inserts the filter (and filtermapping) before 
>>> the FusekiFilter.
>>>
>>> Maybe someone can check whether there is an easier way to enable it 
>>> and hopefully it saves somebody a few hours in the future!
>>>
>>> Regards, Barry
>>>
>>> ----------- code to enable CORS for embedded fuseki (note that this 
>>> code is not directly runnable)------------------------------
>>> //... configure server through builder ...
>>>
>>> server = builder.build();
>>>
>>> Server jettyServer = server.getJettyServer();
>>>
>>> /*
>>>    * trying to add the cross origin filter. Fuseki adds its 
>>> FusekiFilter as the
>>>    * first and this one consumes a SPARQL request to a particular 
>>> dataset. So, to
>>>    * be able to add the cross origin filter, we have to add it 
>>> BEFORE the
>>>    * FusekiFilter. This is not as easy as it looks, because you need 
>>> to add the
>>>    * filter as a filter and as a filtermapping to the /* pathspec.
>>>    */
>>> Handler handler = jettyServer.getHandler(); FilterHolder holder = 
>>> new FilterHolder(CrossOriginFilter.class);
>>> // we use the default CrossOriginFilter settings, but you can 
>>> specify them.
>>> //holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM,
>>> "*");
>>> //holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORI
>>> G
>>> IN_HEADER,
>>> "*");
>>> //holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM,
>>> "GET,POST,HEAD,OPTIONS");
>>> // holder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM,
>>> //             "X-Requested-With,Content-Type,Accept,Origin");
>>> assert (handler instanceof ServletContextHandler); 
>>> ServletContextHandler h = (ServletContextHandler) handler; 
>>> ServletHandler servletHandler = h.getServletHandler(); 
>>> List<FilterMapping> mappings = new 
>>> ArrayList<FilterMapping>(Arrays.asList(servletHandler.getFilterMappi
>>> n
>>> gs()));
>>>
>>> List<FilterHolder> holders = new
>>> ArrayList<FilterHolder>(Arrays.asList(servletHandler.getFilters()));
>>> FilterMapping mapping = new FilterMapping(); 
>>> mapping.setFilterName(holder.getName());
>>> mapping.setPathSpec("/*");
>>> mapping.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST));
>>> mappings.add(0, mapping);
>>> holders.add(0, holder);
>>> FilterMapping[] mappings3 = new FilterMapping[mappings.size()];
>>> mappings3 = mappings.toArray(mappings3); FilterHolder[] holders3 = 
>>> new FilterHolder[holders.size()];
>>> holders3 = holders.toArray(holders3); 
>>> servletHandler.setFilters(holders3);
>>> servletHandler.setFilterMappings(mappings3);
>>>
>>> server.start();
>>> This message may contain information that is not intended for you. 
>>> If you are not the addressee or if this message was sent to you by 
>>> mistake, you are requested to inform the sender and delete the 
>>> message. TNO accepts no liability for the content of this e-mail, 
>>> for the manner in which you use it and for damage of any kind 
>>> resulting from the risks inherent to the electronic transmission of messages.
>>>

Re: Enable Cross-origin resource sharing (CORS) on embedded Fuseki (jetty)

Posted by Andy Seaborne <an...@apache.org>.
Yes, JENA-1846. I have no idea why I wrote 1294.

Development builds are at https://repository.apache.org/snapshots

Jena version 3.15.0-SNAPSHOT

       <repositories>

         <repository>
           <id>apache.snapshots</id>
           <name>Apache Snapshot Repository</name>
           <url>https://repository.apache.org/snapshots</url>
           <releases>
             <enabled>false</enabled>
           </releases>
           <snapshots>
             <enabled>true</enabled>
           </snapshots>
         </repository>

       </repositories>


     Thanks
     Andy

On 28/02/2020 09:41, Nouwt, B. (Barry) wrote:
> Hi Andy, thanks for filing the JIRA. I'm unsure whether JENA-1294 is the correct issue, it seems JENA-1846 describes it better.
> 
> Is it possible to use a development build using maven? If so, I might be able to test it.
> 
> Regards, Barry
> 
> -----Original Message-----
> From: Andy Seaborne <an...@apache.org>
> Sent: dinsdag 25 februari 2020 19:38
> To: users@jena.apache.org
> Subject: Re: Enable Cross-origin resource sharing (CORS) on embedded Fuseki (jetty)
> 
> JENA-1294
> 
> I've put in a fixed setup activated by "--cors" - I'd be obliged if people would try it out and let us know if it works.
> 
> It's in the latest development build #1834.
> 
>       Thanks
>       Andy
> 
> 
> On 29/01/2020 11:28, Andy Seaborne wrote:
>> Barry - thanks for the report
>>
>> On 28/01/2020 19:28, Nouwt, B. (Barry) wrote:
>>> Hi all,
>>>
>>> I enabled CORS on embedded Fuseki (when handling YASGUI SPARQL
>>> requests) and wanted to share the way I did it for others' benefit.
>>> It took me a while to figure out how to do this. I found instructions
>>> on how to enable CORS via the web.xml, but the embedded Fuseki does
>>> not use a web.xml (as far as I am aware). I also found a lot of
>>> examples on how to add the CORS filter in Jetty when you control when
>>> filters are added. The code at the end of the email enables it in my setup.
>>
>> You are correct - Fuseki main is not a webapp, no war file.
>> Fuseki Full, a webapp, does put the CORS filter in.
>>
>> I thought that servlet filters got added before the Fuseki main filter
>> but it sounds like that is not your experience. That would be a bug.
>> Could you file a JIRA please? As your code show, getting the order
>> right is ... a bit opaque.
>>
>> The complication is that the Fuseki filter catches "/*" (any URL)
>> because the valid names can change as datasets are added to registry
>> which is possible at run time.
>>
>> It might need both "before" and "after" filters.
>>
>> And/or : It might be a good idea to have an "enableCORS" setting to
>> the Fuseki Server builder which will need its own configuration.
>>
>>       Andy
>>
>>>
>>> Some remarks:
>>> - as far as I could see, Fuseki builder does not allow custom filters
>>> to be chained/configured before the FusekiFilter (which intercepts
>>> all traffic to datasets).
>>> - adding the CORS filter after the FusekiFilter does not work in case
>>> of a SPARQL request to a particular dataset, because FusekiFilter
>>> intercepts its and prevents it from continuing through the filter chain.
>>> - the above two remarks forced me to add the CORS filter directly to
>>> the Jetty Server, however, it does not allow insertion of filters
>>> before other filters, it only supports adding it to the end of the chain.
>>> - After succeeding to add my filter before the FusekiFilter, it still
>>> did not work, because (after some searching) a FilterMapping (to a
>>> particular path) is also required.
>>> - So, the not so elegant code below retrieves both the filters and
>>> the filtermappings and inserts the filter (and filtermapping) before
>>> the FusekiFilter.
>>>
>>> Maybe someone can check whether there is an easier way to enable it
>>> and hopefully it saves somebody a few hours in the future!
>>>
>>> Regards, Barry
>>>
>>> ----------- code to enable CORS for embedded fuseki (note that this
>>> code is not directly runnable)------------------------------
>>> //... configure server through builder ...
>>>
>>> server = builder.build();
>>>
>>> Server jettyServer = server.getJettyServer();
>>>
>>> /*
>>>    * trying to add the cross origin filter. Fuseki adds its
>>> FusekiFilter as the
>>>    * first and this one consumes a SPARQL request to a particular
>>> dataset. So, to
>>>    * be able to add the cross origin filter, we have to add it BEFORE
>>> the
>>>    * FusekiFilter. This is not as easy as it looks, because you need
>>> to add the
>>>    * filter as a filter and as a filtermapping to the /* pathspec.
>>>    */
>>> Handler handler = jettyServer.getHandler(); FilterHolder holder = new
>>> FilterHolder(CrossOriginFilter.class);
>>> // we use the default CrossOriginFilter settings, but you can specify
>>> them.
>>> //holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM,
>>> "*");
>>> //holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIG
>>> IN_HEADER,
>>> "*");
>>> //holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM,
>>> "GET,POST,HEAD,OPTIONS");
>>> // holder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM,
>>> //             "X-Requested-With,Content-Type,Accept,Origin");
>>> assert (handler instanceof ServletContextHandler);
>>> ServletContextHandler h = (ServletContextHandler) handler;
>>> ServletHandler servletHandler = h.getServletHandler();
>>> List<FilterMapping> mappings = new
>>> ArrayList<FilterMapping>(Arrays.asList(servletHandler.getFilterMappin
>>> gs()));
>>>
>>> List<FilterHolder> holders = new
>>> ArrayList<FilterHolder>(Arrays.asList(servletHandler.getFilters()));
>>> FilterMapping mapping = new FilterMapping();
>>> mapping.setFilterName(holder.getName());
>>> mapping.setPathSpec("/*");
>>> mapping.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST));
>>> mappings.add(0, mapping);
>>> holders.add(0, holder);
>>> FilterMapping[] mappings3 = new FilterMapping[mappings.size()];
>>> mappings3 = mappings.toArray(mappings3); FilterHolder[] holders3 =
>>> new FilterHolder[holders.size()];
>>> holders3 = holders.toArray(holders3);
>>> servletHandler.setFilters(holders3);
>>> servletHandler.setFilterMappings(mappings3);
>>>
>>> server.start();
>>> This message may contain information that is not intended for you. If
>>> you are not the addressee or if this message was sent to you by
>>> mistake, you are requested to inform the sender and delete the
>>> message. TNO accepts no liability for the content of this e-mail, for
>>> the manner in which you use it and for damage of any kind resulting
>>> from the risks inherent to the electronic transmission of messages.
>>>

RE: Enable Cross-origin resource sharing (CORS) on embedded Fuseki (jetty)

Posted by "Nouwt, B. (Barry)" <ba...@tno.nl.INVALID>.
Hi Andy, thanks for filing the JIRA. I'm unsure whether JENA-1294 is the correct issue, it seems JENA-1846 describes it better.

Is it possible to use a development build using maven? If so, I might be able to test it.

Regards, Barry

-----Original Message-----
From: Andy Seaborne <an...@apache.org> 
Sent: dinsdag 25 februari 2020 19:38
To: users@jena.apache.org
Subject: Re: Enable Cross-origin resource sharing (CORS) on embedded Fuseki (jetty)

JENA-1294

I've put in a fixed setup activated by "--cors" - I'd be obliged if people would try it out and let us know if it works.

It's in the latest development build #1834.

     Thanks
     Andy


On 29/01/2020 11:28, Andy Seaborne wrote:
> Barry - thanks for the report
> 
> On 28/01/2020 19:28, Nouwt, B. (Barry) wrote:
>> Hi all,
>>
>> I enabled CORS on embedded Fuseki (when handling YASGUI SPARQL
>> requests) and wanted to share the way I did it for others' benefit. 
>> It took me a while to figure out how to do this. I found instructions 
>> on how to enable CORS via the web.xml, but the embedded Fuseki does 
>> not use a web.xml (as far as I am aware). I also found a lot of 
>> examples on how to add the CORS filter in Jetty when you control when 
>> filters are added. The code at the end of the email enables it in my setup.
> 
> You are correct - Fuseki main is not a webapp, no war file.
> Fuseki Full, a webapp, does put the CORS filter in.
> 
> I thought that servlet filters got added before the Fuseki main filter 
> but it sounds like that is not your experience. That would be a bug.
> Could you file a JIRA please? As your code show, getting the order 
> right is ... a bit opaque.
> 
> The complication is that the Fuseki filter catches "/*" (any URL) 
> because the valid names can change as datasets are added to registry 
> which is possible at run time.
> 
> It might need both "before" and "after" filters.
> 
> And/or : It might be a good idea to have an "enableCORS" setting to 
> the Fuseki Server builder which will need its own configuration.
> 
>      Andy
> 
>>
>> Some remarks:
>> - as far as I could see, Fuseki builder does not allow custom filters 
>> to be chained/configured before the FusekiFilter (which intercepts 
>> all traffic to datasets).
>> - adding the CORS filter after the FusekiFilter does not work in case 
>> of a SPARQL request to a particular dataset, because FusekiFilter 
>> intercepts its and prevents it from continuing through the filter chain.
>> - the above two remarks forced me to add the CORS filter directly to 
>> the Jetty Server, however, it does not allow insertion of filters 
>> before other filters, it only supports adding it to the end of the chain.
>> - After succeeding to add my filter before the FusekiFilter, it still 
>> did not work, because (after some searching) a FilterMapping (to a 
>> particular path) is also required.
>> - So, the not so elegant code below retrieves both the filters and 
>> the filtermappings and inserts the filter (and filtermapping) before 
>> the FusekiFilter.
>>
>> Maybe someone can check whether there is an easier way to enable it 
>> and hopefully it saves somebody a few hours in the future!
>>
>> Regards, Barry
>>
>> ----------- code to enable CORS for embedded fuseki (note that this 
>> code is not directly runnable)------------------------------
>> //... configure server through builder ...
>>
>> server = builder.build();
>>
>> Server jettyServer = server.getJettyServer();
>>
>> /*
>>   * trying to add the cross origin filter. Fuseki adds its 
>> FusekiFilter as the
>>   * first and this one consumes a SPARQL request to a particular 
>> dataset. So, to
>>   * be able to add the cross origin filter, we have to add it BEFORE 
>> the
>>   * FusekiFilter. This is not as easy as it looks, because you need 
>> to add the
>>   * filter as a filter and as a filtermapping to the /* pathspec.
>>   */
>> Handler handler = jettyServer.getHandler(); FilterHolder holder = new 
>> FilterHolder(CrossOriginFilter.class);
>> // we use the default CrossOriginFilter settings, but you can specify 
>> them.
>> //holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, 
>> "*"); 
>> //holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIG
>> IN_HEADER,
>> "*");
>> //holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM,
>> "GET,POST,HEAD,OPTIONS");
>> // holder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM,
>> //             "X-Requested-With,Content-Type,Accept,Origin");
>> assert (handler instanceof ServletContextHandler); 
>> ServletContextHandler h = (ServletContextHandler) handler; 
>> ServletHandler servletHandler = h.getServletHandler(); 
>> List<FilterMapping> mappings = new 
>> ArrayList<FilterMapping>(Arrays.asList(servletHandler.getFilterMappin
>> gs()));
>>
>> List<FilterHolder> holders = new
>> ArrayList<FilterHolder>(Arrays.asList(servletHandler.getFilters()));
>> FilterMapping mapping = new FilterMapping(); 
>> mapping.setFilterName(holder.getName());
>> mapping.setPathSpec("/*");
>> mapping.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST));
>> mappings.add(0, mapping);
>> holders.add(0, holder);
>> FilterMapping[] mappings3 = new FilterMapping[mappings.size()];
>> mappings3 = mappings.toArray(mappings3); FilterHolder[] holders3 = 
>> new FilterHolder[holders.size()];
>> holders3 = holders.toArray(holders3); 
>> servletHandler.setFilters(holders3);
>> servletHandler.setFilterMappings(mappings3);
>>
>> server.start();
>> This message may contain information that is not intended for you. If 
>> you are not the addressee or if this message was sent to you by 
>> mistake, you are requested to inform the sender and delete the 
>> message. TNO accepts no liability for the content of this e-mail, for 
>> the manner in which you use it and for damage of any kind resulting 
>> from the risks inherent to the electronic transmission of messages.
>>

Re: Enable Cross-origin resource sharing (CORS) on embedded Fuseki (jetty)

Posted by Andy Seaborne <an...@apache.org>.
JENA-1294

I've put in a fixed setup activated by "--cors" - I'd be obliged if 
people would try it out and let us know if it works.

It's in the latest development build #1834.

     Thanks
     Andy


On 29/01/2020 11:28, Andy Seaborne wrote:
> Barry - thanks for the report
> 
> On 28/01/2020 19:28, Nouwt, B. (Barry) wrote:
>> Hi all,
>>
>> I enabled CORS on embedded Fuseki (when handling YASGUI SPARQL 
>> requests) and wanted to share the way I did it for others' benefit. It 
>> took me a while to figure out how to do this. I found instructions on 
>> how to enable CORS via the web.xml, but the embedded Fuseki does not 
>> use a web.xml (as far as I am aware). I also found a lot of examples 
>> on how to add the CORS filter in Jetty when you control when filters 
>> are added. The code at the end of the email enables it in my setup.
> 
> You are correct - Fuseki main is not a webapp, no war file.
> Fuseki Full, a webapp, does put the CORS filter in.
> 
> I thought that servlet filters got added before the Fuseki main filter 
> but it sounds like that is not your experience. That would be a bug. 
> Could you file a JIRA please? As your code show, getting the order right 
> is ... a bit opaque.
> 
> The complication is that the Fuseki filter catches "/*" (any URL) 
> because the valid names can change as datasets are added to registry 
> which is possible at run time.
> 
> It might need both "before" and "after" filters.
> 
> And/or : It might be a good idea to have an "enableCORS" setting to the 
> Fuseki Server builder which will need its own configuration.
> 
>      Andy
> 
>>
>> Some remarks:
>> - as far as I could see, Fuseki builder does not allow custom filters 
>> to be chained/configured before the FusekiFilter (which intercepts all 
>> traffic to datasets).
>> - adding the CORS filter after the FusekiFilter does not work in case 
>> of a SPARQL request to a particular dataset, because FusekiFilter 
>> intercepts its and prevents it from continuing through the filter chain.
>> - the above two remarks forced me to add the CORS filter directly to 
>> the Jetty Server, however, it does not allow insertion of filters 
>> before other filters, it only supports adding it to the end of the chain.
>> - After succeeding to add my filter before the FusekiFilter, it still 
>> did not work, because (after some searching) a FilterMapping (to a 
>> particular path) is also required.
>> - So, the not so elegant code below retrieves both the filters and the 
>> filtermappings and inserts the filter (and filtermapping) before the 
>> FusekiFilter.
>>
>> Maybe someone can check whether there is an easier way to enable it 
>> and hopefully it saves somebody a few hours in the future!
>>
>> Regards, Barry
>>
>> ----------- code to enable CORS for embedded fuseki (note that this 
>> code is not directly runnable)------------------------------
>> //... configure server through builder ...
>>
>> server = builder.build();
>>
>> Server jettyServer = server.getJettyServer();
>>
>> /*
>>   * trying to add the cross origin filter. Fuseki adds its 
>> FusekiFilter as the
>>   * first and this one consumes a SPARQL request to a particular 
>> dataset. So, to
>>   * be able to add the cross origin filter, we have to add it BEFORE the
>>   * FusekiFilter. This is not as easy as it looks, because you need to 
>> add the
>>   * filter as a filter and as a filtermapping to the /* pathspec.
>>   */
>> Handler handler = jettyServer.getHandler();
>> FilterHolder holder = new FilterHolder(CrossOriginFilter.class);
>> // we use the default CrossOriginFilter settings, but you can specify 
>> them.
>> //holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
>> //holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, 
>> "*");
>> //holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, 
>> "GET,POST,HEAD,OPTIONS");
>> // holder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM,
>> //             "X-Requested-With,Content-Type,Accept,Origin");
>> assert (handler instanceof ServletContextHandler);
>> ServletContextHandler h = (ServletContextHandler) handler;
>> ServletHandler servletHandler = h.getServletHandler();
>> List<FilterMapping> mappings = new 
>> ArrayList<FilterMapping>(Arrays.asList(servletHandler.getFilterMappings())); 
>>
>> List<FilterHolder> holders = new 
>> ArrayList<FilterHolder>(Arrays.asList(servletHandler.getFilters()));
>> FilterMapping mapping = new FilterMapping();
>> mapping.setFilterName(holder.getName());
>> mapping.setPathSpec("/*");
>> mapping.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST));
>> mappings.add(0, mapping);
>> holders.add(0, holder);
>> FilterMapping[] mappings3 = new FilterMapping[mappings.size()];
>> mappings3 = mappings.toArray(mappings3);
>> FilterHolder[] holders3 = new FilterHolder[holders.size()];
>> holders3 = holders.toArray(holders3);
>> servletHandler.setFilters(holders3);
>> servletHandler.setFilterMappings(mappings3);
>>
>> server.start();
>> This message may contain information that is not intended for you. If 
>> you are not the addressee or if this message was sent to you by 
>> mistake, you are requested to inform the sender and delete the 
>> message. TNO accepts no liability for the content of this e-mail, for 
>> the manner in which you use it and for damage of any kind resulting 
>> from the risks inherent to the electronic transmission of messages.
>>

Re: Enable Cross-origin resource sharing (CORS) on embedded Fuseki (jetty)

Posted by Andy Seaborne <an...@apache.org>.
Barry - thanks for the report

On 28/01/2020 19:28, Nouwt, B. (Barry) wrote:
> Hi all,
> 
> I enabled CORS on embedded Fuseki (when handling YASGUI SPARQL requests) and wanted to share the way I did it for others' benefit. It took me a while to figure out how to do this. I found instructions on how to enable CORS via the web.xml, but the embedded Fuseki does not use a web.xml (as far as I am aware). I also found a lot of examples on how to add the CORS filter in Jetty when you control when filters are added. The code at the end of the email enables it in my setup.

You are correct - Fuseki main is not a webapp, no war file.
Fuseki Full, a webapp, does put the CORS filter in.

I thought that servlet filters got added before the Fuseki main filter 
but it sounds like that is not your experience. That would be a bug. 
Could you file a JIRA please? As your code show, getting the order right 
is ... a bit opaque.

The complication is that the Fuseki filter catches "/*" (any URL) 
because the valid names can change as datasets are added to registry 
which is possible at run time.

It might need both "before" and "after" filters.

And/or : It might be a good idea to have an "enableCORS" setting to the 
Fuseki Server builder which will need its own configuration.

     Andy

> 
> Some remarks:
> - as far as I could see, Fuseki builder does not allow custom filters to be chained/configured before the FusekiFilter (which intercepts all traffic to datasets).
> - adding the CORS filter after the FusekiFilter does not work in case of a SPARQL request to a particular dataset, because FusekiFilter intercepts its and prevents it from continuing through the filter chain.
> - the above two remarks forced me to add the CORS filter directly to the Jetty Server, however, it does not allow insertion of filters before other filters, it only supports adding it to the end of the chain.
> - After succeeding to add my filter before the FusekiFilter, it still did not work, because (after some searching) a FilterMapping (to a particular path) is also required.
> - So, the not so elegant code below retrieves both the filters and the filtermappings and inserts the filter (and filtermapping) before the FusekiFilter.
> 
> Maybe someone can check whether there is an easier way to enable it and hopefully it saves somebody a few hours in the future!
> 
> Regards, Barry
> 
> ----------- code to enable CORS for embedded fuseki (note that this code is not directly runnable)------------------------------
> //... configure server through builder ...
> 
> server = builder.build();
> 
> Server jettyServer = server.getJettyServer();
> 
> /*
>   * trying to add the cross origin filter. Fuseki adds its FusekiFilter as the
>   * first and this one consumes a SPARQL request to a particular dataset. So, to
>   * be able to add the cross origin filter, we have to add it BEFORE the
>   * FusekiFilter. This is not as easy as it looks, because you need to add the
>   * filter as a filter and as a filtermapping to the /* pathspec.
>   */
> Handler handler = jettyServer.getHandler();
> FilterHolder holder = new FilterHolder(CrossOriginFilter.class);
> // we use the default CrossOriginFilter settings, but you can specify them.
> //holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
> //holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
> //holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,POST,HEAD,OPTIONS");
> // holder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM,
> // 			"X-Requested-With,Content-Type,Accept,Origin");
> assert (handler instanceof ServletContextHandler);
> ServletContextHandler h = (ServletContextHandler) handler;
> ServletHandler servletHandler = h.getServletHandler();
> List<FilterMapping> mappings = new ArrayList<FilterMapping>(Arrays.asList(servletHandler.getFilterMappings()));
> List<FilterHolder> holders = new ArrayList<FilterHolder>(Arrays.asList(servletHandler.getFilters()));
> FilterMapping mapping = new FilterMapping();
> mapping.setFilterName(holder.getName());
> mapping.setPathSpec("/*");
> mapping.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST));
> mappings.add(0, mapping);
> holders.add(0, holder);
> FilterMapping[] mappings3 = new FilterMapping[mappings.size()];
> mappings3 = mappings.toArray(mappings3);
> FilterHolder[] holders3 = new FilterHolder[holders.size()];
> holders3 = holders.toArray(holders3);
> servletHandler.setFilters(holders3);
> servletHandler.setFilterMappings(mappings3);
> 
> server.start();
> This message may contain information that is not intended for you. If you are not the addressee or if this message was sent to you by mistake, you are requested to inform the sender and delete the message. TNO accepts no liability for the content of this e-mail, for the manner in which you use it and for damage of any kind resulting from the risks inherent to the electronic transmission of messages.
>