I'm running my application with spring-boot-starter-jetty: 2.1.4 (and tried with 2.1.7). My build.gradle contains:
// Spring Boot starters
compile "org.springframework.boot:spring-boot-starter-web:2.1.4.RELEASE"
compile "org.springframework.boot:spring-boot-starter-jetty:2.1.4.RELEASE"
compile "org.springframework.boot:spring-boot-devtools:2.1.4.RELEASE"
configurations {
compile.exclude module: 'spring-boot-starter-tomcat'
}
My JettyConfig:
@Configuration
@Profile("default")
public class JettyConfiguration {
@Value("classpath:jetty-env.xml")
private Resource jettyEnvResource;
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
JettyServletWebServerFactory jettyContainer = new JettyServletWebServerFactory();
jettyContainer.addServerCustomizers(jettyServerCustomizer());
return jettyContainer;
}
private JettyServerCustomizer jettyServerCustomizer() {
return server -> {
try {
WebAppContext webAppContext = (WebAppContext) server.getHandler();
new XmlConfiguration(new FileInputStream(jettyEnvResource.getFile()))
.configure(webAppContext);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
}
and jetty-env.xml:
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
<Configure id="WebAppContext" class="org.eclipse.jetty.webapp.WebAppContext">
<Call class="java.lang.System" name="setProperties">
<Arg>
<New class="java.util.Properties">
<Call name="putAll">
<Arg>
<Call class="java.lang.System" name="getProperties"/>
</Arg>
</Call>
</New>
</Arg>
</Call>
<New id="DataSource" class="org.eclipse.jetty.plus.jndi.Resource">
<Arg>jdbc/source</Arg>
<Arg>
<New class="oracle.jdbc.pool.OracleDataSource">
<Set name="DriverType">thin</Set>
<Set name="URL">@url@</Set>
<Set name="User">@db_user@</Set>
<Set name="Password">@db_password@</Set>
<Set name="connectionCachingEnabled">true</Set>
<Set name="connectionCacheProperties">
<New class="java.util.Properties">
<Call name="setProperty">
<Arg>MinLimit</Arg>
<Arg>5</Arg>
</Call>
</New>
</Set>
</New>
</Arg>
</New>
<New class="org.eclipse.jetty.plus.jndi.EnvEntry">
<Arg></Arg>
<Arg>application.encryption.key</Arg>
<Arg type="java.lang.String">@key@</Arg>
<Arg type="boolean">true</Arg>
</New>
</Configure>
My application.yml contains:
spring:
servlet:
multipart:
enabled: true
max-file-size: 20971520 #20 MB
max-request-size: 26214400 #25 MB
I'm trying to handle MaxUploadSizeExceededException using ResponseEntityExceptionHandler as follows:
@ControllerAdvice
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
@Value("${spring.servlet.multipart.max-file-size}")
private Long maxAllowedSize;
@ResponseBody
@ResponseStatus(HttpStatus.PAYLOAD_TOO_LARGE)
@ExceptionHandler(MaxUploadSizeExceededException.class)
public ResponseEntity<String> handleMaxUploadSizeExceededException(final MaxUploadSizeExceededException exception) {
String message = "Attempt to upload file with the size exceeded max allowed value = " + maxAllowedSize + " byte(s).");
return ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE).body(message);
}
}
But Spring's org.springframework.web.multipart.MultipartException is not thrown. Instead I see in logs org.eclipse.jetty.http.BadMessageException: 400: Unable to parse form content
with rootCause java.lang.IllegalStateException: Multipart Mime part query exceeds max filesize
Comment From: wilkinsona
Thanks for the report. Unfortunately, we can't afford to investigate a problem when we have to make guesses about how it occurs. Can you please turn the code snippets into a complete and minimal sample that reproduces the problem, zip it up, and attach it to this issue? As far as I can tell, there's no connection between file upload and the use of Oracle so I suspect that the Jetty configuration isn't needed. There's also no endpoint that appears to consume file uploads.
Comment From: zenonwch
Hi @wilkinsona, below you can find the link to the repo with the minimal sample of the problem (with maven instead of gradle but still reproduced): https://github.com/zenonwch/SpringBootStarterJettyMaxUploadSizeIssue
And the link to the working minimal sample with tomcat container: https://github.com/zenonwch/FileSizeErrorHandlingTomcat
Comment From: wilkinsona
Thanks for the sample.
The problem is caused by HiddenHttpMethodFilter
. It uses the request's parameters, and therefore triggers parsing of its multipart payload, before the request reaches Spring MVC's dispatcher servlet. The parsing fails due to the upload being too big, but, because the request has not reached Spring MVC, its exception handling and controller advice has no effect. You can avoid the problem by disabling the hidden HTTP method filter:
spring.mvc.hiddenmethod.filter.enabled: false
With this change in place, you'll get the expected response:
http -f POST :8080/upload file@large.txt
HTTP/1.1 413 Payload Too Large
Content-Length: 79
Content-Type: text/plain;charset=utf-8
Date: Mon, 02 Sep 2019 18:04:43 GMT
Attempt to upload file with the size exceeded max allowed value = 1024 byte(s).
HiddenHttpMethodFilter
is disabled by default as of Spring Boot 2.2.0.M5 to avoid this sort of problem.
Comment From: zenonwch
Thanks for the quick response. With the spring.mvc.hiddenmethod.filter.enabled: false
works as expected.
Comment From: elouataoui
Sorry to dig this up, but it appears that the same issue happens when using spring-security-oauth2. At one point up the filter chain (and before reaching the DispatcherServlet) the BearerTokenExtractor
calls request#getParameter
triggering the parsing of the multipart payload. As we can't disable the OAuth2 security filter, I'm not sure where to report this issue. Any pointers would be welcome.
Comment From: philwebb
@elouataoui BearerTokenExtractor
is part of https://github.com/spring-projects/spring-security-oauth which is in maintenance mode (see this blog post. I'd suggest migrating to new module that's in Spring Security itself to see if that fixes things.
Comment From: AkhilKandi
@philwebb Hello,
Even I am facing the same issue when trying to upload a file greater than 1MB getting this error.
{
"timestamp": 1588794695638,
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.IllegalStateException",
"message": "java.lang.IllegalStateException: Multipart Mime part file exceeds max filesize",
"path": "/cogs-core-engine/flatFileUpload"
}
The API worked fine when I tested in my local workspace. When committed to oneops dev environment I am getting this error. I have tried multiple workarounds but none of them is working.
Comment From: philwebb
@AkhilKandi If you're using spring-security-oauth
then I'm afraid the answer will be the same as the comment above yours.
Comment From: AkhilKandi
I am not using spring-security-oauth in my application. But I am not sure when the file is more than 1 MB the request is not even reaching till the API endpoint/ Dispatcherservlet.
Comment From: philwebb
@AkhilKandi It's hard to say if that's a bug, a configuration issue or an environmental problem. The fact that it works locally but not when pushed to "oneops dev environment" (I'm not familiar with what that is) makes me think it's something specific to your environment. If you're able to find a way to reproduce the problem is a sample application then please open a new issue and we'll take a look.