Luis Lebolo opened SPR-14368 and commented
I upgraded from Spring 4.2.6 to 4.3.0 and now I'm receiving the following error in Chrome when my web page tries to load static javascript files.
Refused to execute script from
http://.../someJsFile.js
because its MIME type ('application/octet-stream') is not executable, and strict MIME type checking is enabled.
I double-checked the response header (using Spring 4.3.0) and I indeed see Content-Type:application/octet-stream
. However, when I drop back down to Spring 4.2.6 I see Content-Type:application/javascript
.
The files are included in my page like
<!DOCTYPE html>
<html lang="en">
<!-- ... -->
<body>
<!-- ... -->
<!-- Adding type="text/javascript" does not help -->
<script src="/resources/js/someJsFile.js"></script>
</body>
</html>
and my config looks like
<!-- ... -->
<context:component-scan base-package="some.package.spec" />
<mvc:resources mapping="/resources/**" location="/resources/" />
<mvc:annotation-driven />
<!-- ... -->
I tried looking at the Spring 4.3.0 release notes, but nothing immediately stood out (maybe #17493).
Any ideas what changes in 4.3.0 are affecting me? How do I tell Spring 4.3.0 to set the correct content type for static javascript files? Let me know if you need more details about configuration, etc. Unfortunately, I don't think I could package up the project - it's quite complex and lots of proprietary info.
Affects: 4.3 GA
Reference URL: http://stackoverflow.com/questions/37822460/upgraded-to-spring-4-3-0-and-static-js-files-are-now-served-as-application-octe
Issue Links: - #18233 ResourceHttpRequestHandler does not take into account mime types configured in WebMvcConfigurerAdapter
Referenced from: commits https://github.com/spring-projects/spring-framework/commit/e38623df87cf6cbc529724d6cf1170874c05bcf0
0 votes, 5 watchers
Comment From: spring-projects-issues
Brian Clozel commented
Hello Luis Lebolo
This might be related to #18233, but I don't see why this is behaving like this.
Did you define a custom ContentNegotiationManager
in your configuration or used a ContentNegotiationConfigurer
to customize it?
What JVM / container are you using for this application?
Is the Java Activation Framework available in your JVM?
I've added a repro project for this and couldn't reproduce the problem with JDK8 on Tomcat 8.0 and Jetty 9.3. Could you try this repro project in your environment or adapt it to reproduce the issue?
Thanks
Comment From: spring-projects-issues
Rossen Stoyanchev commented
If it's not easy to provide a repro project, perhaps you can debug your application. See what happens in ResourceHttpRequestHandler#getMediaType
. Comparing 4.2.6 and 4.3 will probably show the difference.
Comment From: spring-projects-issues
Luis Lebolo commented
Thanks for looking into this guys.
Brian, the only configuration I can think of around content negotiation is
<!-- ContentNegotiatingViewResolver delegates to other ViewResolvers based on application context/media type -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">
<property name="prefixJson" value="true" />
</bean>
</list>
</property>
</bean>
I'm using Tomcat 8.0.29 and Java 8 (jdk1.8.0_66). JAF is available. The only JVM settings I add when running Tomcat are -Xmx3g
and -Xms1g
.
I'll try to reproduce the issue in the repo you've created, but first I'll try to debug ResourceHttpRequestHandler#getMediaType
as suggested by Rossen.
Comment From: spring-projects-issues
Rossen Stoyanchev commented
In 4.2.6 the media type for a resources was checked against the ServletContext#getMimeType
and then as a fallback against JAF. In 4.3.0 we are delegating to the ServletPathExtensionContentNegotiationStrategy
which seems to call super first (registered extensions and/or JAF) and then the ServletContext. That's probably something we need to address in any case but do tell us what you find out through debugging.
Comment From: spring-projects-issues
Luis Lebolo commented
I stepped through the debugger and the issue is definitely related to changes made in #18233. I tried configuring a custom content negotiating manager like this, but that didn't work.
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" />
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="mediaTypes">
<value>js=application/javascript</value>
</property>
</bean>
!-- ContentNegotiatingViewResolver delegates to other ViewResolvers based on application context/media type-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="contentNegotiationManager" ref="contentNegotiationManager"/>
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">
<property name="prefixJson" value="true" />
</bean>
</list>
</property>
</bean>
For completeness (excuse the verbosity :D), I looked at [ResourceHttpRequestHandler#getMediaType](https://github.com/spring-projects/spring-framework/blob/v4.3.0.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java#L517)
for v4.3.0.RELEASE during a static resource request for a javascript file.
I get all the way to line 530 with mediaType == null
(PathExtensionContentNegotiationStrategy.getMediaTypeForResource(resource) == null
). Then the (default?) content negotiation manager returns an empty list of media types (getContentNegotiationManager().resolveMediaTypes(webRequest) == []
). So the final return value is null.
If I step down further into [PathExtensionContentNegotiationStrategy](https://github.com/spring-projects/spring-framework/blob/v4.3.0.RELEASE/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java#L147)
, getMediaTypeForResource()
calls lookupMediaType(".js".)
which returns null because this.mediaTypes
is empty. Then JafMediaTypeFactory.getMediaType(filename)
is called which returns an application/octet-stream media type, that ultimately gets set to null in the subsequent if branch and returned.
If I step down further into [ContentNegotiationManager](https://github.com/spring-projects/spring-framework/blob/v4.3.0.RELEASE/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManager.java#L121)
, resovleMediaTypes()
loops through the (default?) strategies. PathExtensionContentNegotiationStrategy
ultimately returns an empty list of media types, presumably due to the same logic as above. Then the HeaderContentNegotiationStrategy
returns * / *
(MEDIA_TYPE_ALL) which is skipped in the subsequent if branch. So an empty list of media types is ultimately returned.
Comment From: spring-projects-issues
Luis Lebolo commented
In [ResourceHttpRequestHandler#getMediaType](https://github.com/spring-projects/spring-framework/blob/v4.2.6.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java#L391)
for v4.2.6.RELEASE, getServletContext().getMimeType(resource.getFilename())
returns application/javascript
, which causes MediaType.parseMediaType(mimeType)
to also return a application/javascript meadia type (i.e. the subsequent JAF branch is never entered).
Comment From: spring-projects-issues
Rossen Stoyanchev commented
If I step down further into PathExtensionContentNegotiationStrategy
What's the actual instance? It should be ServletPathExtensionContentNegotiationStrategy
which calls super first in getMetdiaTypeForResource
and then servletContext.getMimeType(..)
, i.e. the same as in 4.2.6. Somehow that must not be the actual instance type or else it shouldn't fall through to the ContentNegotiationManager. This is set up in ContentNegotiationManagerFactoryBean, you can check what happens there.
I'm also surprised that setting up a mapping for "js" does not work. Any idea what happens in those same steps?
I'm starting to suspect something about the URL that's throwing off perhaps the determination of the extension?
Comment From: spring-projects-issues
Luis Lebolo commented
I'll be away for a few days, but will check on this at the end of the week.
Comment From: spring-projects-issues
Rossen Stoyanchev commented
I'm actually awaiting feedback from you (see my last comment).
Comment From: spring-projects-issues
Luis Lebolo commented
I'm back and able to devote some time. The actual instance is PathExtensionContentNegotiationStrategy
. In ContentNegotiationManagerFactoryBean, this.servletContext == null
so strategy
gets instantiated as a PathExtensionContentNegotiationStrategy
.
This is using the configuration I mentioned here.
Comment From: spring-projects-issues
Rossen Stoyanchev commented
Okay thanks I see the issue. I'll have a snapshot for you to try later today.
Comment From: spring-projects-issues
Rossen Stoyanchev commented
There is a fix in place that should address the issue. When the current build finishes please give it a try with a 4.3.1 snapshot and thanks!
Comment From: spring-projects-issues
Luis Lebolo commented
Sorry, I'm not too familiar with the Spring build system. Can you point me to the status of the current build? And if it's not too much trouble (I can Google around), how would I use the latest snapshot? I'm using Gradle to get Spring from mavenCentral, e.g.
apply plugin: 'java'
apply plugin: 'war'
repositories {
mavenCentral()
}
dependencies {
compile 'org.springframework:spring-web:4.2.+'
// ...
}
Comment From: spring-projects-issues
Rossen Stoyanchev commented
The build is done. You need repo.spring.io/snapshot/ and version 4.3.1.BUILD-SNAPSHOT.
Comment From: spring-projects-issues
Luis Lebolo commented
Ok, things are back to normal with 4.3.1.BUILD-SNAPSHOT (i.e. javascript files have Content-Type:application/javascript
). Thanks for the timely solution!
Comment From: spring-projects-issues
Steve commented
I am using Spring mvc 4.3.2 and Spring Security 4.1.3 and am still experiencing the same issue. All static js resource are being served as 'application/octet-stream'.
Comment From: spring-projects-issues
Steve commented
Previous comment, that is with an webapp with @EnableWebMvc
. I have another spring app, same version but without EnableWebMvc and it is serving static js resources fine.
Comment From: spring-projects-issues
Brian Clozel commented
An issue related to that, #19146, has been fixed and will be released with 4.3.3. In the meantime, can you test your application with the 4.3.3.BUILD-SNAPSHOT version?
If you can still reproduce the problem with 4.3.3 snapshot, please open a new issue with a small repro project so we can investigate.
Thanks!
Comment From: spring-projects-issues
Steve commented
I can confirm that 4.3.3 fixes the issue and static js files are now served correctly.
Comment From: mffonseca
@spring-issuemaster I'm new with spring and I have the same problem. Could you give an example of how pom.xml should look?
Comment From: bclozel
@mffonseca your pom.xml should look like a project generated from start.spring.io. If you need help with a problem in your application, you can ask a question on StackOverflow.
Comment From: simararneja
This issue seems to have reappeared in Spring 5.3.23 https://stackoverflow.com/questions/73882172/spring-5-3-23-static-js-files-served-as-application-octet-stream-instead-of-a
Comment From: bclozel
@simararneja Could you create a minimal sample application (using start.spring.io) that replicates the problem? Then could you create a new issue for that? Thanks!
Comment From: rstoyanchev
Not much has changed in the code that was touched for this issue, so it's probably not the same issue. Trying to isolate it with a minimal sample and creating a new issue is the way to go.