Stream: dev

Topic: CorsFilter Servlet Filter not discovered


view this post on Zulip Balázs Pataki (Feb 05 2026 at 10:30):

I'm not sure if it is container related, however it seems that the CorsFilter is not picked up automatically although the @WebFilter("/*") should do that:

@WebFilter("/*")
public class CorsFilter implements Filter

Instead I had to manually add to web.xml this:

    <filter>
        <filter-name>edu.harvard.iq.dataverse.filter.CorsFilter</filter-name>
        <filter-class>edu.harvard.iq.dataverse.filter.CorsFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>edu.harvard.iq.dataverse.filter.CorsFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

I also checked in the debugger and without the explicit web.xml doFilter() is not get called.

view this post on Zulip Notification Bot (Feb 05 2026 at 13:12):

This topic was moved here from #containers > CorsFilter problem by Oliver Bertuch.

view this post on Zulip Oliver Bertuch (Feb 05 2026 at 13:12):

As this is not purely container related, I moved this here @Balázs Pataki

view this post on Zulip Oliver Bertuch (Feb 05 2026 at 13:13):

Are there any API tests for the filter?

view this post on Zulip Oliver Bertuch (Feb 05 2026 at 13:13):

It would be good to have a reproducer.

view this post on Zulip Balázs Pataki (Feb 05 2026 at 13:14):

I just tried calling a DV endpoint from a browser component, but try to come up with a curl.

view this post on Zulip Oliver Bertuch (Feb 05 2026 at 13:25):

It would be good to have an API test included in our regular suite, so we can also test this is in CI

view this post on Zulip Balázs Pataki (Feb 05 2026 at 16:39):

Actually, I don't know how to make curl fail with CORS, because it never does as it doesn't care, only browsers do, right? For now I just vibe coded a simple single page app which tries to create a dataset with POST and it does fail for me with stock develop branch DV (without manually adding the filter to webxml):

cors-tester.html

Just open it in a browser, set the api token and press "POST JSON" to see if it fails or not. (It uses the dataset-finch1.json from the DV API guides)

view this post on Zulip Oliver Bertuch (Feb 05 2026 at 18:23):

I was suggesting creating an IT test that checks for presence of the headers.

view this post on Zulip Oliver Bertuch (Feb 05 2026 at 18:24):

(As you said - anything that's not a browser won't care. But we'd only want to verify the servlet filter is picked up)

view this post on Zulip Balázs Pataki (Feb 06 2026 at 10:13):

Ok, I created a CorsIT test checking for CORS headers.

It turns out that the result is kind of random based on whether the server happens to pick up CorsFilter or not.

Here's my actual test session: I run the app in docker. When it is started I run the test manually. Then I stop the server with ctrl+c and either start it again with run, or first do an explicit stop. None of these seem to matter, the result seems to be random:

mvn -Pct docker:stop

mvn -Pct docker:run --> FAIL

ctrl+c

mvn -Pct docker:run --> FAIL

ctrl+c

mvn -Pct docker:stop

mvn -Pct docker:run --> PASS

ctrl+c

mvn -Pct docker:run --> PASS

ctrl+c

mvn -Pct docker:run --> FAIL

I don't know how to proceed from here. One thing is sure: If add the explicit web.xml filter and filter-mapping it works consistently.

view this post on Zulip Balázs Pataki (Feb 06 2026 at 10:40):

This is the test I run:

CorsIT.java

In the docker-compose-dev.yml I set these for dev_dataverse just to see if I get specific headers I configure:

      DATAVERSE_CORS_ORIGIN: "*"
      DATAVERSE_CORS_HEADERS_ALLOW: "Accept,Content-Type,X-Dataverse-key,Authorization,cedar-client-session-id,cedar-debug"
``

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:06):

In your test I see that you are targeting an API endpoint.

view this post on Zulip Balázs Pataki (Feb 06 2026 at 11:07):

Yes, /api/dataverses/root/datasets

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:07):

Are we sure the filter actually will correctly work with JAX-RS

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:07):

What happens when hitting JSF or other pure servlets?

view this post on Zulip Balázs Pataki (Feb 06 2026 at 11:08):

Yes, sometimes it does, sometimes it doesn't. I don't know what kind of race condition could
it be.

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:08):

What happens if we hit a 404 in a servlet

view this post on Zulip Balázs Pataki (Feb 06 2026 at 11:08):

I don't know about JSF, but I want to access api endpoints from a webapp

view this post on Zulip Balázs Pataki (Feb 06 2026 at 11:09):

What url do you suggest to test?

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:12):

Technically, JAX-RS is still a servlet, but it's special. We may be in trouble here due to filter ordering etc.

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:12):

Also, AI tells me there may be a problem with the mixed style of having web.xml registered filters and annotation based. Not sure if this really is a thing, but lets keep it in mind.

view this post on Zulip Balázs Pataki (Feb 06 2026 at 11:13):

Yes, I think it definitely affects it, although as I read about it should be still deterministic

view this post on Zulip Balázs Pataki (Feb 06 2026 at 11:15):

SwordFilter is the only one configured in web.xml. Maybe removing it from there and adding @WebFilter annotation on the class makes it clear.

view this post on Zulip Balázs Pataki (Feb 06 2026 at 11:15):

I try this.

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:17):

Technically for JAX-RS we should be using @ContainerRequestFilter to be within the consistent filter framework of JAX-RS

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:18):

See ApiBlockingFilter in api.filter package.

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:18):

So this may be another culprit, why things my go sideways - the servlet filter and the JAX-RS filter may conflict.

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:19):

At least if both are discovered by annotations scanning.

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:19):

Maybe making the filter explicit in web.xml makes it load before the other or sth

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:24):

Hmm coming to think about it: any servlet filters will be applied first, then the JAX-RS internal filters will apply. These two don't mix-n-match, they are at different layers. So should be nothing to worry about here.

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:27):

@Balázs Pataki could you please verify something for me...

view this post on Zulip Balázs Pataki (Feb 06 2026 at 11:27):

Yes, sure.

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:27):

Could you run the tests against /api/v1/...

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:28):

I had a look at all the filters we already have and ApiRouter popped up. Asking AI about it gave me this:

The ApiRouter is doing an internal RequestDispatcher.forward(...) from:
/api/.../api/v1/... (your JAX‑RS app is mounted at ) @ApplicationPath("api/v1")

That has a big side effect in Servlet-land: a forward is a second dispatch with dispatcher type FORWARD, and many servlet filters only apply to dispatcher type REQUEST unless explicitly configured.

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:30):

So let's rule out that the router may mess up our filter chain

view this post on Zulip Balázs Pataki (Feb 06 2026 at 11:34):

In the meantime I tested putting @WebFilter onto SwordFilter and removing the web.xml config --> same random test behaviour

I now try the /api/v1 behaviour

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:35):

Great to verify that! We doubted it would be related, glad to see it was indeed not.

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:36):

My money is on Router vs Cors, as both affect the same endpoints.

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:37):

Also, you might want to do the testing with the sword filter as well :wink: Not sure if the order matters with those two. IMHO filters should avoid being dependent on order. But of course we'd need to take care of types (forward, request)

view this post on Zulip Balázs Pataki (Feb 06 2026 at 11:45):

Yep. So, when /api/dataverses/root/datasets fails /api/v1/dataverses/root/datasets passes the test.

view this post on Zulip Balázs Pataki (Feb 06 2026 at 11:46):

Now what? :thinking:

view this post on Zulip Balázs Pataki (Feb 06 2026 at 11:50):

In case of /api/v1 it first reaches CorsFilter then ApiBlockingFilter
In case of /api only reaches ApiBlockingFilter

(doFilter(), I mean)

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:51):

Ha! So Api Router is the culprit

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:53):

But you're somewhat wrong. The chain is:
/api -> Router -> forward -> Cors -> ApiBlocking (which breaks at Cors because forward)
/api/v1 -> Router -> Cors -> ApiBlocking

view this post on Zulip Balázs Pataki (Feb 06 2026 at 11:54):

Maybe that's how it should be, but no, in case of /api Cors is not ALWAYS reached.

view this post on Zulip Balázs Pataki (Feb 06 2026 at 11:55):

So in case of

RequestDispatcher dsp = request.getRequestDispatcher(newRequestUri);
dsp.forward(req, sr1);

wherever it goes it won't always goes thro CorsFilter, it is not in that filter chain, or sg.

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:55):

So what we need to do: add both "FORWARD" and "REQUEST" to CorsFilter:

@WebFilter(value = "/*", dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.FORWARD})

We should also look at the types ERROR and INCLUDE later on to avoid breaking CORS when serving error pages etc

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:56):

Balázs Pataki said:

Maybe that's how it should be, but no, in case of /api Cors is not ALWAYS reached.

It goes there, but will not match because of dispatcher type forward!

view this post on Zulip Balázs Pataki (Feb 06 2026 at 11:56):

Ok, I check now adding DispatcherType.FORWARD

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 11:57):

Please make sure to use the above example, otherwise we'd break REQUEST :wink:

view this post on Zulip Balázs Pataki (Feb 06 2026 at 11:57):

yeah, i copy pasted that :-)

view this post on Zulip Balázs Pataki (Feb 06 2026 at 12:06):

Oh yeah, it works consistently now! :tada:

And the filter orders are as follows:

/api
cors -> router -> cors -> apiblocking

/api/v1
cors -> router -> apiblocking

view this post on Zulip Philip Durbin 🚀 (Feb 06 2026 at 12:30):

Good morning. :coffee: You've been busy!

view this post on Zulip Balázs Pataki (Feb 06 2026 at 12:57):

Submitted a PR:

https://github.com/IQSS/dataverse/pull/12151

Should we add anything else to it, @Oliver Bertuch ?

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 12:58):

Can you test the error pages?

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 12:58):

And SWORD maybe?

view this post on Zulip Balázs Pataki (Feb 06 2026 at 12:59):

You mean to add more URL-s to test in CorsIT, right?

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 12:59):

Yeah, lets have more tests in there

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 13:00):

Might be an @Parameterized one or single ones.

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 13:00):

Not sure we can make RestAssured hit the other endpoints that easily.

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 13:19):

@Don Sizemore this may be worth another backport to 6.9

view this post on Zulip Balázs Pataki (Feb 06 2026 at 13:40):

I made it ParameterizedTest and now it tests these endpoints

"/api/dataverses/root/datasets",
"/api/v1/dataverses/root/datasets",
"/page_doesnt_exist",
"/dvn/api/data-deposit/v1.1/swordv2/collection/dataverse/root"

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 14:10):

You probably should add CorsIT to the list of tests at https://github.com/IQSS/dataverse/blob/develop/tests/integration-tests.txt

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 14:10):

I've always meant to refactor the API tests to properly use Maven Failsafe and JUnit 5 Suites, but didn't find the time so far...

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 14:14):

I left two comments on the PR

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 14:28):

Edit: added another one.

view this post on Zulip Oliver Bertuch (Feb 06 2026 at 14:48):

@Balázs Pataki I've marked your PR as a draft for now and added all the necessary labels (put didn't put it on the board yet @Philip Durbin 🚀). Keep them changes coming, I'll be the ambassador on including it in 6.10

view this post on Zulip Philip Durbin 🚀 (Feb 06 2026 at 14:59):

I'd say we can safely put it in the "Ready for Triage" column so it comes up on Triage Tuesday. Any objections? Thanks for the PR! :heart:

view this post on Zulip Philip Durbin 🚀 (Apr 02 2026 at 20:24):

@Balázs Pataki @Oliver Bertuch I'm reviewing #12151 and I rebased the branch. I hope you don't mind.

view this post on Zulip Philip Durbin 🚀 (Apr 02 2026 at 20:25):

@Oliver Bertuch I added a note about requirements.txt changes that doc writers will need to know about.

view this post on Zulip Philip Durbin 🚀 (Apr 02 2026 at 20:26):

@Don Sizemore tests are failing on Jenkins. Please see https://github.com/IQSS/dataverse/pull/12151#discussion_r3030000487

Should we enable CORS on the Dataverse instances Jenkins spins up? Can I co-assign you to the PR until we decide how to handle this?

view this post on Zulip Philip Durbin 🚀 (Apr 02 2026 at 21:06):

@Balázs Pataki @Oliver Bertuch I have questions about #12151 and left reviews for you.


Last updated: Apr 03 2026 at 06:08 UTC