It appears Tomcat is not handling parallel uploads of the same multipart file by two different users.
Test
Two sessions/users A & B
Both upload a 20MB file with the same name foo.pdf more or less at the same time
Servlet 3.0 Request with default configuration stores the two files in the tmp folder
Both threads try to write that foo.pdf into the tmp folder
Result
The uploaded document is corrupt (two streams writing to it)
The slower request will fail with a FileNotFoundException as the tmp file was already deleted by the cleanup task of the faster request.
Is there a way to avoid this - other than setting fileSizeThreshold higher than maxFileSize so it would never be written to disk in the first place.
Side note: this is a Spring Boot 2.1 application but this is irrelevant as it uses this Servlet 3.0 implementation by default.
I have an answer but it's not really satisfactory. We didn't figure out how to make this work with Tomcat's Servlet 3.0 implementation. However, once we switched to Apache commons-fileupload all was well.
So, for Spring (Boot) applications you would
set spring.servlet.multipart.enabled: false
configure a bean of type CommonsMultipartResolver with a name of multipartResolver
add the commons-fileupload dependency
Related
I am trying to understand the Spring posted description of the problem and resolution alternatives
https://spring.io/blog/2022/03/31/spring-framework-rce-early-announcement
However, I do not understand why this would be Tomcat specific. Wouldnt the same issue exist for war files deployed to Jetty or Wildfly?
This particular exploit demostration is based on the custom custom implementation of one of tomcat specific classloaders having referrence to the servlet environment context. This is why this demo is tomcat specific especially that is exploits the fact that one of the logger valves is the first one in the processing pipeline (this is used to write custom jsp code as a log file in webapps root)
Also it requires Java9+ as it uses access trough class.getModule() which is not present in earlier versions
I have spend lots of time to understand the internal working of a multipart file upload in spring boot.
Couldn't get a clear picture on it.
Bit confused about the role of spring boot tmp directory.
I have a tmp directory named as /tmp/tomcat.4296537502689403143.8587/work/Tomcat/localhost/ROOT]
I was checked the tmp directory during the file upload, couldn't write anything here.If i delete the folder it will throw multipart error.
Can anyone explain the internal working of a file upload and the role of tmp directory.
Spring boot web framework comes with embedded web servers: Tomcat by default. Tomcat creates/uses the tmp directory to store temporary files; including uploaded files, session files, and other files.
That behavior can be changed through configuration. Alternatively, you can also configure spring boot to use a different web server.
https://github.com/spring-projects/spring-boot/blob/70eee612ff2a2b1e58cbcb18a4d46e464895c18a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java
Could be a silly question, but...
I have a Spring-based WAR application that runs 80% of the installations on Tomcat and the rest 20% on WebSphere.
I need to simply get the path of the folder where Spring's MultipartFilter (using Commons multipart resolver) stores files being uploaded. I have never set it manually, and it actually belongs to the Catalina work directory as I found out in my Tomcat installations.
For the moment, I just need to get that path. I have control of my application so no one is going to change it without notice.
I would like to know if there is a server-agnostic way to know where my Spring-based application is going to store Multipart files.
E.g. from this question I can see I can use catalina.base, but in Tomcat... not in JBoss or WebSphere. It could cost me a couple of if/else statements...
Spring tries to do it already when no default value is set
WebUtils.getTempDir(servletContext)
Which simply does:
return ((File)servletContext.getAttribute("javax.servlet.context.tempdir"));
So simply if no one overrides that location in the filter properties (and that is my case) I can rely on the default.
More in general, one may have to inspect the instance of DiskItemFileFactory to get the path to the repository
spring-boot version : 1.3.5
I set multipart.file-size-threshold=40MB in application properties.
When I am using embedded tomcat, I see that there is no write operations on disk during upload requests.
When I am using undertow, there are still write operations on disk.
How can i upload multiparts without disk writing via undertow?
Thanks.
This is a limitation of Undertow. Looking at its code it doesn't appear to honour the file size threshold and there's a todo to that effect in ManagedServlet.
I'm using a standard spring boot approach (a Java project) where we use embedded Tomcat and package as an executable jar.
I place some static resoures e.g index.html in a static folder under src/main/resources of my project. A library we use expects to load some resources using the servlet context by doing servletContext.getRealPath("XXXX").
When I run this project in Eclipse the real path for servletContext.getRealPath("/") resolves to C:\Users\<me>\workspace\springboot-project\src\main\webapp\. I have no idea why it uses this folder when it does not exist in the project? If I create the executable jar and run this then servletContext.getRealPath("/") gives C:\Users\<me>\AppData\Local\Temp\tomcat-docbase.5693809153072003983.8080\. This directory does exist but has nothing in it.
I can obviously see my resources fine if requested via HTTP I just can't get at them as expected via the servlet context.
Any ideas?
Resources in src/main/resources/static aren't served directly by Tomcat. They're served by Spring through its DispatcherServlet and a ResourceHttpRequestHandler that's auto-configured by Spring Boot. By default, this resource handler is configured to serve resources from the following locations:
classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/
It's 3 that's making your resources in src/main/resources/static available.
Using getRealPath() is always going to be problematic as it requires the resource to be available as a file on the filesystem, i.e. it can't be inside the war or jar that you're running, and there's no guarantee that that will be the case. Using ClassLoader.getResource() or ServletContext.getResource() is a much more robust way to find and load resources within your web application as it doesn't rely on the resource being directly on the filesystem.
You haven't said which library it is that's using getRealPath(). Perhaps it can be updated or be configured to use a different resource loading strategy?