001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.web.server; 018 019import java.util.Collection; 020import java.util.Collections; 021import java.util.Iterator; 022import java.util.LinkedHashMap; 023import java.util.Map; 024 025import org.springframework.util.Assert; 026 027/** 028 * Simple server-independent abstraction for mime mappings. Roughly equivalent to the 029 * {@literal <mime-mapping>} element traditionally found in web.xml. 030 * 031 * @author Phillip Webb 032 * @since 2.0.0 033 */ 034public final class MimeMappings implements Iterable<MimeMappings.Mapping> { 035 036 /** 037 * Default mime mapping commonly used. 038 */ 039 public static final MimeMappings DEFAULT; 040 041 static { 042 MimeMappings mappings = new MimeMappings(); 043 mappings.add("abs", "audio/x-mpeg"); 044 mappings.add("ai", "application/postscript"); 045 mappings.add("aif", "audio/x-aiff"); 046 mappings.add("aifc", "audio/x-aiff"); 047 mappings.add("aiff", "audio/x-aiff"); 048 mappings.add("aim", "application/x-aim"); 049 mappings.add("art", "image/x-jg"); 050 mappings.add("asf", "video/x-ms-asf"); 051 mappings.add("asx", "video/x-ms-asf"); 052 mappings.add("au", "audio/basic"); 053 mappings.add("avi", "video/x-msvideo"); 054 mappings.add("avx", "video/x-rad-screenplay"); 055 mappings.add("bcpio", "application/x-bcpio"); 056 mappings.add("bin", "application/octet-stream"); 057 mappings.add("bmp", "image/bmp"); 058 mappings.add("body", "text/html"); 059 mappings.add("cdf", "application/x-cdf"); 060 mappings.add("cer", "application/pkix-cert"); 061 mappings.add("class", "application/java"); 062 mappings.add("cpio", "application/x-cpio"); 063 mappings.add("csh", "application/x-csh"); 064 mappings.add("css", "text/css"); 065 mappings.add("dib", "image/bmp"); 066 mappings.add("doc", "application/msword"); 067 mappings.add("dtd", "application/xml-dtd"); 068 mappings.add("dv", "video/x-dv"); 069 mappings.add("dvi", "application/x-dvi"); 070 mappings.add("eot", "application/vnd.ms-fontobject"); 071 mappings.add("eps", "application/postscript"); 072 mappings.add("etx", "text/x-setext"); 073 mappings.add("exe", "application/octet-stream"); 074 mappings.add("gif", "image/gif"); 075 mappings.add("gtar", "application/x-gtar"); 076 mappings.add("gz", "application/x-gzip"); 077 mappings.add("hdf", "application/x-hdf"); 078 mappings.add("hqx", "application/mac-binhex40"); 079 mappings.add("htc", "text/x-component"); 080 mappings.add("htm", "text/html"); 081 mappings.add("html", "text/html"); 082 mappings.add("ief", "image/ief"); 083 mappings.add("jad", "text/vnd.sun.j2me.app-descriptor"); 084 mappings.add("jar", "application/java-archive"); 085 mappings.add("java", "text/x-java-source"); 086 mappings.add("jnlp", "application/x-java-jnlp-file"); 087 mappings.add("jpe", "image/jpeg"); 088 mappings.add("jpeg", "image/jpeg"); 089 mappings.add("jpg", "image/jpeg"); 090 mappings.add("js", "application/javascript"); 091 mappings.add("jsf", "text/plain"); 092 mappings.add("json", "application/json"); 093 mappings.add("jspf", "text/plain"); 094 mappings.add("kar", "audio/midi"); 095 mappings.add("latex", "application/x-latex"); 096 mappings.add("m3u", "audio/x-mpegurl"); 097 mappings.add("mac", "image/x-macpaint"); 098 mappings.add("man", "text/troff"); 099 mappings.add("mathml", "application/mathml+xml"); 100 mappings.add("me", "text/troff"); 101 mappings.add("mid", "audio/midi"); 102 mappings.add("midi", "audio/midi"); 103 mappings.add("mif", "application/x-mif"); 104 mappings.add("mov", "video/quicktime"); 105 mappings.add("movie", "video/x-sgi-movie"); 106 mappings.add("mp1", "audio/mpeg"); 107 mappings.add("mp2", "audio/mpeg"); 108 mappings.add("mp3", "audio/mpeg"); 109 mappings.add("mp4", "video/mp4"); 110 mappings.add("mpa", "audio/mpeg"); 111 mappings.add("mpe", "video/mpeg"); 112 mappings.add("mpeg", "video/mpeg"); 113 mappings.add("mpega", "audio/x-mpeg"); 114 mappings.add("mpg", "video/mpeg"); 115 mappings.add("mpv2", "video/mpeg2"); 116 mappings.add("ms", "application/x-wais-source"); 117 mappings.add("nc", "application/x-netcdf"); 118 mappings.add("oda", "application/oda"); 119 mappings.add("odb", "application/vnd.oasis.opendocument.database"); 120 mappings.add("odc", "application/vnd.oasis.opendocument.chart"); 121 mappings.add("odf", "application/vnd.oasis.opendocument.formula"); 122 mappings.add("odg", "application/vnd.oasis.opendocument.graphics"); 123 mappings.add("odi", "application/vnd.oasis.opendocument.image"); 124 mappings.add("odm", "application/vnd.oasis.opendocument.text-master"); 125 mappings.add("odp", "application/vnd.oasis.opendocument.presentation"); 126 mappings.add("ods", "application/vnd.oasis.opendocument.spreadsheet"); 127 mappings.add("odt", "application/vnd.oasis.opendocument.text"); 128 mappings.add("otg", "application/vnd.oasis.opendocument.graphics-template"); 129 mappings.add("oth", "application/vnd.oasis.opendocument.text-web"); 130 mappings.add("otp", "application/vnd.oasis.opendocument.presentation-template"); 131 mappings.add("ots", "application/vnd.oasis.opendocument.spreadsheet-template "); 132 mappings.add("ott", "application/vnd.oasis.opendocument.text-template"); 133 mappings.add("ogx", "application/ogg"); 134 mappings.add("ogv", "video/ogg"); 135 mappings.add("oga", "audio/ogg"); 136 mappings.add("ogg", "audio/ogg"); 137 mappings.add("otf", "application/x-font-opentype"); 138 mappings.add("spx", "audio/ogg"); 139 mappings.add("flac", "audio/flac"); 140 mappings.add("anx", "application/annodex"); 141 mappings.add("axa", "audio/annodex"); 142 mappings.add("axv", "video/annodex"); 143 mappings.add("xspf", "application/xspf+xml"); 144 mappings.add("pbm", "image/x-portable-bitmap"); 145 mappings.add("pct", "image/pict"); 146 mappings.add("pdf", "application/pdf"); 147 mappings.add("pgm", "image/x-portable-graymap"); 148 mappings.add("pic", "image/pict"); 149 mappings.add("pict", "image/pict"); 150 mappings.add("pls", "audio/x-scpls"); 151 mappings.add("png", "image/png"); 152 mappings.add("pnm", "image/x-portable-anymap"); 153 mappings.add("pnt", "image/x-macpaint"); 154 mappings.add("ppm", "image/x-portable-pixmap"); 155 mappings.add("ppt", "application/vnd.ms-powerpoint"); 156 mappings.add("pps", "application/vnd.ms-powerpoint"); 157 mappings.add("ps", "application/postscript"); 158 mappings.add("psd", "image/vnd.adobe.photoshop"); 159 mappings.add("qt", "video/quicktime"); 160 mappings.add("qti", "image/x-quicktime"); 161 mappings.add("qtif", "image/x-quicktime"); 162 mappings.add("ras", "image/x-cmu-raster"); 163 mappings.add("rdf", "application/rdf+xml"); 164 mappings.add("rgb", "image/x-rgb"); 165 mappings.add("rm", "application/vnd.rn-realmedia"); 166 mappings.add("roff", "text/troff"); 167 mappings.add("rtf", "application/rtf"); 168 mappings.add("rtx", "text/richtext"); 169 mappings.add("sfnt", "application/font-sfnt"); 170 mappings.add("sh", "application/x-sh"); 171 mappings.add("shar", "application/x-shar"); 172 mappings.add("sit", "application/x-stuffit"); 173 mappings.add("snd", "audio/basic"); 174 mappings.add("src", "application/x-wais-source"); 175 mappings.add("sv4cpio", "application/x-sv4cpio"); 176 mappings.add("sv4crc", "application/x-sv4crc"); 177 mappings.add("svg", "image/svg+xml"); 178 mappings.add("svgz", "image/svg+xml"); 179 mappings.add("swf", "application/x-shockwave-flash"); 180 mappings.add("t", "text/troff"); 181 mappings.add("tar", "application/x-tar"); 182 mappings.add("tcl", "application/x-tcl"); 183 mappings.add("tex", "application/x-tex"); 184 mappings.add("texi", "application/x-texinfo"); 185 mappings.add("texinfo", "application/x-texinfo"); 186 mappings.add("tif", "image/tiff"); 187 mappings.add("tiff", "image/tiff"); 188 mappings.add("tr", "text/troff"); 189 mappings.add("tsv", "text/tab-separated-values"); 190 mappings.add("ttf", "application/x-font-ttf"); 191 mappings.add("txt", "text/plain"); 192 mappings.add("ulw", "audio/basic"); 193 mappings.add("ustar", "application/x-ustar"); 194 mappings.add("vxml", "application/voicexml+xml"); 195 mappings.add("xbm", "image/x-xbitmap"); 196 mappings.add("xht", "application/xhtml+xml"); 197 mappings.add("xhtml", "application/xhtml+xml"); 198 mappings.add("xls", "application/vnd.ms-excel"); 199 mappings.add("xml", "application/xml"); 200 mappings.add("xpm", "image/x-xpixmap"); 201 mappings.add("xsl", "application/xml"); 202 mappings.add("xslt", "application/xslt+xml"); 203 mappings.add("xul", "application/vnd.mozilla.xul+xml"); 204 mappings.add("xwd", "image/x-xwindowdump"); 205 mappings.add("vsd", "application/vnd.visio"); 206 mappings.add("wav", "audio/x-wav"); 207 mappings.add("wbmp", "image/vnd.wap.wbmp"); 208 mappings.add("wml", "text/vnd.wap.wml"); 209 mappings.add("wmlc", "application/vnd.wap.wmlc"); 210 mappings.add("wmls", "text/vnd.wap.wmlsc"); 211 mappings.add("wmlscriptc", "application/vnd.wap.wmlscriptc"); 212 mappings.add("wmv", "video/x-ms-wmv"); 213 mappings.add("woff", "application/font-woff"); 214 mappings.add("woff2", "application/font-woff2"); 215 mappings.add("wrl", "model/vrml"); 216 mappings.add("wspolicy", "application/wspolicy+xml"); 217 mappings.add("z", "application/x-compress"); 218 mappings.add("zip", "application/zip"); 219 DEFAULT = unmodifiableMappings(mappings); 220 } 221 222 private final Map<String, Mapping> map; 223 224 /** 225 * Create a new empty {@link MimeMappings} instance. 226 */ 227 public MimeMappings() { 228 this.map = new LinkedHashMap<>(); 229 } 230 231 /** 232 * Create a new {@link MimeMappings} instance from the specified mappings. 233 * @param mappings the source mappings 234 */ 235 public MimeMappings(MimeMappings mappings) { 236 this(mappings, true); 237 } 238 239 /** 240 * Create a new {@link MimeMappings} from the specified mappings. 241 * @param mappings the source mappings with extension as the key and mime-type as the 242 * value 243 */ 244 public MimeMappings(Map<String, String> mappings) { 245 Assert.notNull(mappings, "Mappings must not be null"); 246 this.map = new LinkedHashMap<>(); 247 mappings.forEach(this::add); 248 } 249 250 /** 251 * Internal constructor. 252 * @param mappings source mappings 253 * @param mutable if the new object should be mutable. 254 */ 255 private MimeMappings(MimeMappings mappings, boolean mutable) { 256 Assert.notNull(mappings, "Mappings must not be null"); 257 this.map = (mutable ? new LinkedHashMap<>(mappings.map) 258 : Collections.unmodifiableMap(mappings.map)); 259 } 260 261 @Override 262 public Iterator<Mapping> iterator() { 263 return getAll().iterator(); 264 } 265 266 /** 267 * Returns all defined mappings. 268 * @return the mappings. 269 */ 270 public Collection<Mapping> getAll() { 271 return this.map.values(); 272 } 273 274 /** 275 * Add a new mime mapping. 276 * @param extension the file extension (excluding '.') 277 * @param mimeType the mime type to map 278 * @return any previous mapping or {@code null} 279 */ 280 public String add(String extension, String mimeType) { 281 Mapping previous = this.map.put(extension, new Mapping(extension, mimeType)); 282 return (previous != null) ? previous.getMimeType() : null; 283 } 284 285 /** 286 * Get a mime mapping for the given extension. 287 * @param extension the file extension (excluding '.') 288 * @return a mime mapping or {@code null} 289 */ 290 public String get(String extension) { 291 Mapping mapping = this.map.get(extension); 292 return (mapping != null) ? mapping.getMimeType() : null; 293 } 294 295 /** 296 * Remove an existing mapping. 297 * @param extension the file extension (excluding '.') 298 * @return the removed mime mapping or {@code null} if no item was removed 299 */ 300 public String remove(String extension) { 301 Mapping previous = this.map.remove(extension); 302 return (previous != null) ? previous.getMimeType() : null; 303 } 304 305 @Override 306 public boolean equals(Object obj) { 307 if (obj == null) { 308 return false; 309 } 310 if (obj == this) { 311 return true; 312 } 313 if (obj instanceof MimeMappings) { 314 MimeMappings other = (MimeMappings) obj; 315 return this.map.equals(other.map); 316 } 317 return false; 318 } 319 320 @Override 321 public int hashCode() { 322 return this.map.hashCode(); 323 } 324 325 /** 326 * Create a new unmodifiable view of the specified mapping. Methods that attempt to 327 * modify the returned map will throw {@link UnsupportedOperationException}s. 328 * @param mappings the mappings 329 * @return an unmodifiable view of the specified mappings. 330 */ 331 public static MimeMappings unmodifiableMappings(MimeMappings mappings) { 332 return new MimeMappings(mappings, false); 333 } 334 335 /** 336 * A single mime mapping. 337 */ 338 public static final class Mapping { 339 340 private final String extension; 341 342 private final String mimeType; 343 344 public Mapping(String extension, String mimeType) { 345 Assert.notNull(extension, "Extension must not be null"); 346 Assert.notNull(mimeType, "MimeType must not be null"); 347 this.extension = extension; 348 this.mimeType = mimeType; 349 } 350 351 public String getExtension() { 352 return this.extension; 353 } 354 355 public String getMimeType() { 356 return this.mimeType; 357 } 358 359 @Override 360 public boolean equals(Object obj) { 361 if (obj == null) { 362 return false; 363 } 364 if (obj == this) { 365 return true; 366 } 367 if (obj instanceof Mapping) { 368 Mapping other = (Mapping) obj; 369 return this.extension.equals(other.extension) 370 && this.mimeType.equals(other.mimeType); 371 } 372 return false; 373 } 374 375 @Override 376 public int hashCode() { 377 return this.extension.hashCode(); 378 } 379 380 @Override 381 public String toString() { 382 return "Mapping [extension=" + this.extension + ", mimeType=" + this.mimeType 383 + "]"; 384 } 385 386 } 387 388}