Skip to content

Commit 9f4cb13

Browse files
authored
[staticfiles] Add mimeTypes option to staticFiles
and switch to text/javascript (javalin#1824)
1 parent f38fb52 commit 9f4cb13

File tree

7 files changed

+73
-4
lines changed

7 files changed

+73
-4
lines changed

javalin/src/main/java/io/javalin/http/ContentType.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ enum class ContentType(
2525
TEXT_CSS("text/css", true, "css"),
2626
TEXT_CSV("text/csv", false, "csv"),
2727
TEXT_HTML("text/html", true, "html", "htm"),
28+
TEXT_JS("text/javascript", true, "js", "mjs"),
2829
TEXT_MARKDOWN("text/markdown", true, "md"),
2930
TEXT_PROPERTIES("text/x-java-properties", true, "properties"),
3031
TEXT_XML("text/xml", true, "xml"),
@@ -73,7 +74,12 @@ enum class ContentType(
7374
APPLICATION_DOCX("application/vnd.openxmlformats-officedocument.wordprocessingml.document", false, "docx"),
7475
APPLICATION_EPUB("application/epub+zip", false, "epub"),
7576
APPLICATION_GZ("application/gzip", false, "gz"),
76-
APPLICATION_JS("application/javascript", true, "js", "mjs"),
77+
@Deprecated(
78+
message = "use TEXT_JS instead.",
79+
replaceWith = ReplaceWith("io.javalin.http.ContentType.TEXT_JS"),
80+
level = DeprecationLevel.ERROR
81+
)
82+
APPLICATION_JS("application/javascript", true, "application/javascript"),
7783
APPLICATION_JSON("application/json", true, "json"),
7884
APPLICATION_MPKG("application/vnd.apple.installer+xml", false, "mpkg"),
7985
APPLICATION_JAR("application/java-archive", false, "jar"),
@@ -104,7 +110,14 @@ enum class ContentType(
104110
const val HTML = "text/html"
105111
const val XML = "text/xml"
106112
const val OCTET_STREAM = "application/octet-stream"
113+
@Deprecated(
114+
message = "represents the older application/javascript mimetype. This will change to represent text/javascript instead. Please use either JAVASCRIPT_LEGACY or JAVASCRIPT_MODERN instead to be explicit",
115+
replaceWith = ReplaceWith("io.javalin.http.ContentType.JAVASCRIPT_MODERN"),
116+
level = DeprecationLevel.WARNING
117+
)
107118
const val JAVASCRIPT = "application/javascript"
119+
const val JAVASCRIPT_LEGACY = "application/javascript"
120+
const val JAVASCRIPT_MODERN = "text/javascript"
108121
const val JSON = "application/json"
109122
const val FORM_DATA = "multipart/form-data"
110123

javalin/src/main/java/io/javalin/http/staticfiles/StaticFileConfig.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.javalin.http.staticfiles
22

3+
import io.javalin.http.ContentType
34
import io.javalin.http.Header
45
import jakarta.servlet.http.HttpServletRequest
56
import org.eclipse.jetty.server.handler.ContextHandler.AliasCheck
@@ -14,8 +15,29 @@ data class StaticFileConfig(
1415
@JvmField var aliasCheck: AliasCheck? = null,
1516
@JvmField var headers: Map<String, String> = mutableMapOf(Header.CACHE_CONTROL to "max-age=0"),
1617
@JvmField var skipFileFunction: (HttpServletRequest) -> Boolean = { false },
18+
@JvmField val mimeTypes: MimeTypesConfig = MimeTypesConfig()
1719
) {
1820
internal fun refinedToString(): String {
1921
return this.toString().replace(", skipFileFunction=(jakarta.servlet.http.HttpServletRequest) -> kotlin.Boolean", "")
2022
}
2123
}
24+
25+
class MimeTypesConfig {
26+
private val extensionToMimeType: MutableMap<String, String> = mutableMapOf()
27+
28+
fun getMapping(): Map<String, String> = extensionToMimeType.toMap()
29+
30+
fun add(contentType: ContentType) {
31+
add(contentType.mimeType, *contentType.extensions)
32+
}
33+
34+
fun add(contentType: ContentType, vararg extensions: String) {
35+
add(contentType.mimeType, *extensions)
36+
}
37+
38+
fun add(mimeType: String, vararg extensions: String) {
39+
extensions.forEach { ext ->
40+
extensionToMimeType[ext] = mimeType
41+
}
42+
}
43+
}

javalin/src/main/java/io/javalin/jetty/JettyResourceHandler.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
package io.javalin.jetty
88

99
import io.javalin.config.PrivateConfig
10+
import io.javalin.http.ContentType
1011
import io.javalin.http.staticfiles.Location
1112
import io.javalin.http.staticfiles.StaticFileConfig
1213
import io.javalin.util.JavalinException
1314
import io.javalin.util.JavalinLogger
1415
import jakarta.servlet.http.HttpServletRequest
1516
import jakarta.servlet.http.HttpServletResponse
17+
import org.eclipse.jetty.http.MimeTypes
1618
import org.eclipse.jetty.server.Request
1719
import org.eclipse.jetty.server.Server
1820
import org.eclipse.jetty.server.handler.ResourceHandler
@@ -76,6 +78,15 @@ open class ConfigurableHandler(val config: StaticFileConfig, jettyServer: Server
7678
isDirAllowed = false
7779
isEtags = true
7880
server = jettyServer
81+
mimeTypes = MimeTypes()
82+
// TODO: the next two lines can be removed once Jetty releases a version with their new mimetype mappings.
83+
// Ref: https://github.com/eclipse/jetty.project/issues/9342
84+
// Ref: https://github.com/eclipse/jetty.project/pull/9347
85+
mimeTypes.addMimeMapping("js", ContentType.JAVASCRIPT_MODERN)
86+
mimeTypes.addMimeMapping("mjs", ContentType.JAVASCRIPT_MODERN)
87+
config.mimeTypes.getMapping().forEach { (ext, mimeType) ->
88+
mimeTypes.addMimeMapping(ext, mimeType)
89+
}
7990
start()
8091
}
8192

javalin/src/test/java/io/javalin/staticfiles/TestSinglePageMode.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class TestSinglePageMode {
8181

8282
@Test
8383
fun `SinglePageHandler doesn't affect static files - classpath`() = TestUtil.test(rootSinglePageApp_classPath) { _, http ->
84-
assertThat(http.htmlGet("/script.js").headers.getFirst(Header.CONTENT_TYPE)).contains("application/javascript")
84+
assertThat(http.htmlGet("/script.js").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.JAVASCRIPT_MODERN)
8585
assertThat(http.htmlGet("/webjars/swagger-ui/${TestDependency.swaggerVersion}/swagger-ui.css").headers.getFirst(
8686
Header.CONTENT_TYPE)).contains(ContentType.CSS)
8787
assertThat(http.htmlGet("/webjars/swagger-ui/${TestDependency.swaggerVersion}/swagger-ui.css").httpCode()).isEqualTo(OK)

javalin/src/test/java/io/javalin/staticfiles/TestStaticFiles.kt

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,17 @@ class TestStaticFiles {
143143
@Test
144144
fun `serving JS from classpath works`() = TestUtil.test(defaultStaticResourceApp) { _, http ->
145145
assertThat(http.get("/script.js").httpCode()).isEqualTo(OK)
146-
assertThat(http.get("/script.js").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.JAVASCRIPT)
146+
assertThat(http.get("/script.js").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.JAVASCRIPT_MODERN)
147147
assertThat(http.getBody("/script.js")).contains("JavaScript works")
148148
}
149149

150+
@Test
151+
fun `serving mjs from classpath works`() = TestUtil.test(defaultStaticResourceApp) { _, http ->
152+
assertThat(http.get("/module.mjs").httpCode()).isEqualTo(OK)
153+
assertThat(http.get("/module.mjs").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.JAVASCRIPT_MODERN)
154+
assertThat(http.getBody("/module.mjs")).contains("export function test()").contains("mjs works")
155+
}
156+
150157
@Test
151158
fun `serving CSS from classpath works`() = TestUtil.test(defaultStaticResourceApp) { _, http ->
152159
assertThat(http.get("/styles.css").httpCode()).isEqualTo(OK)
@@ -207,7 +214,7 @@ class TestStaticFiles {
207214
assertThat(http.get("/html.html").status).isEqualTo(200)
208215
assertThat(http.get("/html.html").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.HTML)
209216
assertThat(http.getBody("/html.html")).contains("HTML works")
210-
assertThat(http.get("/script.js").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.JAVASCRIPT)
217+
assertThat(http.get("/script.js").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.JAVASCRIPT_MODERN)
211218
assertThat(http.get("/styles.css").headers.getFirst(Header.CONTENT_TYPE)).contains(ContentType.CSS)
212219
}
213220

@@ -340,4 +347,16 @@ class TestStaticFiles {
340347
assertThat(http.getBody("/html.html")).contains("HTML works")
341348
}
342349

350+
@Test
351+
fun `can add custom mimetype mappings`() = TestUtil.test(Javalin.create { config ->
352+
config.staticFiles.add {
353+
it.directory = "/public"
354+
it.location = Location.CLASSPATH
355+
it.mimeTypes.add("application/x-javalin", "javalin")
356+
}
357+
}) { _, http ->
358+
assertThat(http.get("/file.javalin").httpCode()).isEqualTo(OK)
359+
assertThat(http.get("/file.javalin").headers.getFirst(Header.CONTENT_TYPE)).contains("application/x-javalin")
360+
assertThat(http.getBody("/file.javalin")).contains("TESTFILE.javalin")
361+
}
343362
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
TESTFILE.javalin
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function test() {
2+
return 'mjs works'
3+
}

0 commit comments

Comments
 (0)