Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@reiern70
Copy link

@reiern70 reiern70 commented May 13, 2025

We add a way for users to plug in listeners into upload processing so that custom applications can hook into multipart requests and provide custom progress reporting.

See

https://bz.apache.org/bugzilla/show_bug.cgi?id=69690

This appear in the context of

https://issues.apache.org/jira/browse/WICKET-7154

Changes on tomcat 11 make wicket support for multipart very "shaky" as any call to get POST parameters before wicket logic for multi-part processing takes place would make tomcat consume the request. With this changes I plan to introduce "native" tomcat processing into wicket as an option (with progress reporting that wicket now supports).

I have created a PR to wicket allowing to use the work in this PR in order to do uploads and report progress. This seems to work in our application. See

apache/wicket#1167

@reiern70 reiern70 marked this pull request as draft May 14, 2025 10:57
@reiern70 reiern70 force-pushed the reiern70/allow-users-to-plug-in-progress-listener branch from 78d3e5f to 2a649a8 Compare May 14, 2025 18:45
@ChristopherSchultz
Copy link
Contributor

What do you think about being able to register more than one listener? Should the application be able to register a listener for just a single request, then deregister it? Or is your concept that the application has one single listener, and then it's up to the application to figure out the details from there?

@reiern70
Copy link
Author

What do you think about being able to register more than one listener? Should the application be able to register a listener for just a single request, then deregister it? Or is your concept that the application has one single listener, and then it's up to the application to figure out the details from there?

Many thanks for your comments.

I still need to experiment with this and Wicket and see what is more convenient. I just used this POC in order to plug in a very simple listener on my customer's app.

image

My next step is try to roll a "tomcat native" way to do upload progress in Wicket. That is, Wicket just sets a listener and tomcat does the dirty job... and wicket part just reports to the UI as it does now.

To be totally honest for wicket would be best if tomcat behaves as "before". From wicket's perpective this is just "a hack". Because this solution will be only for tomcat (that is what my customer's application uses). But other application server might still need to use default fileupload2 approach.

Why I'm trying to do this. Now I have fixed wicket + our application to not call getParameters before our uploads take place (uploads with progress). But tomorrow we introduce a new filter in front of wicket filter and this new filter calls getParameters.... and our uploads will be broken and we will be scratching our heads wondering how. This can be as simple as a library upgrade...

Here I have added a factory for listeners... Each ServletContext can define one via init parameters. For each multipart request this factory is asked if a listener is needed. My, guess is, in the wicket context, this will be something calling the upload progress updating machinery wicket has (that is page specific). Thus, factory just creates some ephemeral listener for that request in particular. I need to try.

Regarding many or one. Factory is one and listener per request is one. But a Listener can be anything and also be compound (more than one listener).

Hum... I'm new to tomcat code-base I don't know if coyote Requests are reusable... I need to check. If so we maybe need to do some clean up.

I will prepare a wicket PR alongside this one and see how flexible is that I defined here.

@reiern70
Copy link
Author

What do you think about being able to register more than one listener? Should the application be able to register a listener for just a single request, then deregister it? Or is your concept that the application has one single listener, and then it's up to the application to figure out the details from there?

Many thanks for your comments.

I still need to experiment with this and Wicket and see what is more convenient. I just used this POC in order to plug in a very simple listener on my customer's app.

image

My next step is try to roll a "tomcat native" way to do upload progress in Wicket. That is, Wicket just sets a listener and tomcat does the dirty job... and wicket part just reports to the UI as it does now.

To be totally honest for wicket would be best if tomcat behaves as "before". From wicket's perpective this is just "a hack". Because this solution will be only for tomcat (that is what my customer's application uses). But other application server might still need to use default fileupload2 approach.

Why I'm trying to do this. Now I have fixed wicket + our application to not call getParameters before our uploads take place (uploads with progress). But tomorrow we introduce a new filter in front of wicket filter and this new filter calls getParameters.... and our uploads will be broken and we will be scratching our heads wondering how. This can be as simple as a library upgrade...

Here I have added a factory for listeners... Each ServletContext can define one via init parameters. For each multipart request this factory is asked if a listener is needed. My, guess is, in the wicket context, this will be something calling the upload progress updating machinery wicket has (that is page specific). Thus, factory just creates some ephemeral listener for that request in particular. I need to try.

Regarding many or one. Factory is one and listener per request is one. But a Listener can be anything and also be compound (more than one listener).

Hum... I'm new to tomcat code-base I don't know if coyote Requests are reusable... I need to check. If so we maybe need to do some clean up.

I will prepare a wicket PR alongside this one and see how flexible is that I defined here.

It would be nice if the specification defines some standard way to listen for uploads progress... Then Wicket or any other framework just hook into this standard

@reiern70 reiern70 force-pushed the reiern70/allow-users-to-plug-in-progress-listener branch from 47674e2 to 38c55b2 Compare May 18, 2025 15:05
@reiern70
Copy link
Author

@ChristopherSchultz I have implemented

apache/wicket#1167

A way to get Wicket's support for upload with progress using work in this PR.

@reiern70 reiern70 marked this pull request as ready for review May 19, 2025 08:53
@reiern70
Copy link
Author

@ChristopherSchultz any chances to get this PR merged into Tomcat 11?

  • Any other work to be done from my side?
  • Add tests?
  • Back-port this to previous tomcat versions?

Even without having wicket PR merged this would help solve our problem as I can do the same developments I did for wicket in our application and once new changes are introduced in Wicket (if they are) revert to using what Wicket offers.

@rmaucher
Copy link
Contributor

Personally I am not a fan at all right now:

  • About the way it is hooked in Request and esp ApplicationContext.
  • It changes fileupload, which is a separate component (fileupload would need to accept the changes).
  • This capability if desired should probably be added to the Servlet specification itself.

@markt-asf
Copy link
Contributor

See jakartaee/servlet#66

@rmaucher
Copy link
Contributor

Let's see how the feature proposal progresses first then.

@reiern70
Copy link
Author

Personally I am not a fan at all right now:

Ok.

  • About the way it is hooked in Request and esp ApplicationContext.

I'm totally new to Tomcat code-base. I tried to solve a problem. If the way I did it is not correct then I'm all ears about changing code in the right way.

  • It changes fileupload, which is a separate component (fileupload would need to accept the changes).

What shall I do with respect to this?

  • This capability if desired should probably be added to the Servlet specification itself.

I do agree about adding this to specification... But way Tomcat behaves now is not good because:

  1. if someone calls getParameter then the application will not be able to call getInputStream and parse it (in this case Wicket and fileupload2)
  2. I managed to get our application working (upload + upload progress) by hunting down all calls to getParameter (in wicket and our application).
  3. Solution in 2 is very fragile. A web applications are normally a stack of filters... One filter calls getParameter before Wicket filter and our application will suddenly fail. My fear is version X of a third party filter works Ok. Then we upgrade to version X + 1 and this new filter now calls getParameters and our application will be broken and we don't know why. We will also need to monitor how we develop in our application just not to break uploads.

@reiern70
Copy link
Author

See jakartaee/servlet#66

Thanks for the link. See

jakartaee/servlet#66 (comment)

IMHO what is described there is not what we need... because it has the same problem we have now.

@markt-asf
Copy link
Contributor

IMHO what is described there is not what we need... because it has the same problem we have now.

Which is what, exactly?

@reiern70
Copy link
Author

IMHO what is described there is not what we need... because it has the same problem we have now.

Which is what, exactly?

The problem now is: 1) "someone" calls getParamaters 2) then tomcat parse the parts 3) when wicket calls getInputStream if is already used.

What is proposed this is the way to set the listener:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
   {
       if(request-is-large)
       {
           MyProgressListener listener = new MyProgressListener(request);
           request.getSession().addAttribute("progressListener", listener);
           request.setProgressListener(listener);
       }
   }

Ok. Assume we do this in Wicket filter... Then

  1. If there is no filter before wicket calling getParameters. The above will work
  2. If the is a filter before wicket that does call getParameters... then as Tomcat 11 works now the above will not work because request will be already "used".

What I propose here is:

The above is safe because the listener is created by tomcat itself via the factory. Thus, no matter who triggers the parsing of the parts the listener will receive the notifications. Another problem with that design is:

  1. You have an stack of filters. F1 -> F2 -> F3 and
  2. F1 adds a Listener then F3 adds a listener (the replaces the previous one)?

Thus... IMHO... this PR is also not correct. You Should maybe have some stack of factories and each factory adds a Listener and then tomcat call each of them in order.

@reiern70
Copy link
Author

IMHO what is described there is not what we need... because it has the same problem we have now.

Which is what, exactly?

You suggested to avoid using multipart... but again. Your servlet is not ISOLATED. E.g. in case of Wicket many thing can happen before you arrive to parse yourself multipart... E.g. as soon as someone requests parameter value Tomcat will fail.

aused by: java.lang.IllegalStateException: Unable to process parts as no multi-part configuration has been provided
at org.apache.catalina.connector.Request.parseParts(Request.java:2446) ~[catalina.jar:12.0.0-M1-dev]
at org.apache.catalina.connector.Request.doParseParameters(Request.java:2818) ~[catalina.jar:12.0.0-M1-dev]
at org.apache.catalina.connector.Request.parseParameters(Request.java:2776) ~[catalina.jar:12.0.0-M1-dev]
at org.apache.catalina.connector.Request.getParameterNames(Request.java:1092) ~[catalina.jar:12.0.0-M1-dev]
at org.apache.catalina.connector.Request.getParameterMap(Request.java:1077) ~[catalina.jar:12.0.0-M1-dev]
at org.apache.catalina.connector.RequestFacade.getParameterMap(RequestFacade.java:173) ~[catalina.jar:12.0.0-M1-dev]
at jakarta.servlet.ServletRequestWrapper.getParameterMap(ServletRequestWrapper.java:168) ~[servlet-api.jar:6.2DRAFT]
at org.apache.wicket.protocol.http.servlet.ServletWebRequest.generatePostParameters(ServletWebRequest.java:285) ~[wicket-core-10.6.0-SNAPSHOT.jar:10.6.0-SNAPSHOT]
at org.apache.wicket.protocol.http.servlet.ServletWebRequest.getPostRequestParameters(ServletWebRequest.java:335) ~[wicket-core-10.6.0-SNAPSHOT.jar:10.6.0-SNAPSHOT]
at org.apache.wicket.protocol.http.servlet.ServletWebRequest$1.getParameterValue(ServletWebRequest.java:363) ~[wicket-core-10.6.0-SNAPSHOT.jar:10.6.0-SNAPSHOT]
at org.apache.wicket.request.parameter.CombinedRequestParametersAdapter.getParameterValue(CombinedRequestParametersAdapter.java:70) ~[wicket-request-10.6.0-SNAPSHOT.jar:10.6.0-SNAPSHOT]

What I propose here will work (does work as I have our application working with these changes).

@markt-asf
Copy link
Contributor

markt-asf commented May 19, 2025

It looks like Tomcat parsing parts for multipart/form-data with no <multipart-config> as a result of calling getParameter() and friends is a bug although I need to check the TCK. Fix that and the Wicket is free to call getInputStream().

@reiern70
Copy link
Author

reiern70 commented May 19, 2025

It looks like Tomcat parsing parts for multipart/form-data with no <multipart-config> as a result of calling getParameter() and friends is a bug although I need to check the TCK. Fix that and the Wicket is free to call getInputStream().

This is how all started ...

https://bz.apache.org/bugzilla/show_bug.cgi?id=69689

I reported the above. Because it is not a backwards compatible behavior. Then it was closed as work as expected as per specification. Then I decided to do this work even when I found a work around for our application + some changes in wicket... because that was a very weak way to "fix" the problem. If this ISSUE is fixed in tomcat them this work is not needed at all and wicket will work as it is now (parsing the input stream using fileupload2). Anyways...

jakartaee/servlet#66

would be nice to have because then wicket will not need this custom logic to support multipart progress

@markt-asf
Copy link
Contributor

This is how all started ...

Not quite. The provided reproducer uses the @MultipartConfig annotation. Therefore, for that configuration, it is working as designed.

@reiern70
Copy link
Author

This is how all started ...

Not quite. The provided reproducer uses the @MultipartConfig annotation. Therefore, for that configuration, it is working as designed.

Not following you.

  1. If you do not set the casualMultipartConfig or the @MultipartConfig tomcat will complain when getParameters is called
  2. If you set any of them tomcat will try to parse the PARTS.

@markt-asf
Copy link
Contributor

  • If you do not set the casualMultipartConfig or the @MultipartConfig tomcat will complain when getParameters is called

That looks like a Tomcat bug but I need to confirm that. Assuming it is a bug, once that bug is fixed, this becomes the solution.

  • If you set any of them tomcat will try to parse the PARTS.

That is what the provided reproducer does so the behaviour of the reproducer as as designed.

@reiern70
Copy link
Author

  • If you do not set the casualMultipartConfig or the @MultipartConfig tomcat will complain when getParameters is called

That looks like a Tomcat bug but I need to confirm that. Assuming it is a bug, once that bug is fixed, this becomes the solution.

I do agree... Many thanks for all you answers!

  • If you set any of them tomcat will try to parse the PARTS.

That is what the provided reproducer does so the behaviour of the reproducer as as designed.

@markt-asf
Copy link
Contributor

Fixed for 11.0.8 onwards.

@markt-asf markt-asf closed this May 21, 2025
@reiern70
Copy link
Author

Fixed for 11.0.8 onwards.

Saw the commit. Many thanks. Thus now applications can work as before. I will test our application + Latest wicket 10.x against unreleased tomcat and confirm.

@reiern70
Copy link
Author

Fixed for 11.0.8 onwards.

Saw the commit. Many thanks. Thus now applications can work as before. I will test our application + Latest wicket 10.x against unreleased tomcat and confirm.

Yes. This fixes our ISSUE! Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants