1 // 2 // ======================================================================== 3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. 4 // ------------------------------------------------------------------------ 5 // All rights reserved. This program and the accompanying materials 6 // are made available under the terms of the Eclipse Public License v1.0 7 // and Apache License v2.0 which accompanies this distribution. 8 // 9 // The Eclipse Public License is available at 10 // http://www.eclipse.org/legal/epl-v10.html 11 // 12 // The Apache License v2.0 is available at 13 // http://www.opensource.org/licenses/apache2.0.php 14 // 15 // You may elect to redistribute this code under either of these licenses. 16 // ======================================================================== 17 // 18 19 package org.eclipse.jetty.http; 20 21 import java.util.Enumeration; 22 import java.util.HashMap; 23 import java.util.Iterator; 24 import java.util.Map; 25 import java.util.MissingResourceException; 26 import java.util.ResourceBundle; 27 28 import org.eclipse.jetty.io.Buffer; 29 import org.eclipse.jetty.io.BufferCache; 30 import org.eclipse.jetty.io.BufferCache.CachedBuffer; 31 import org.eclipse.jetty.util.StringUtil; 32 import org.eclipse.jetty.util.log.Log; 33 import org.eclipse.jetty.util.log.Logger; 34 35 36 /* ------------------------------------------------------------ */ 37 /** 38 * 39 */ 40 public class MimeTypes 41 { 42 private static final Logger LOG = Log.getLogger(MimeTypes.class); 43 44 public final static String 45 FORM_ENCODED="application/x-www-form-urlencoded", 46 MESSAGE_HTTP="message/http", 47 MULTIPART_BYTERANGES="multipart/byteranges", 48 49 TEXT_HTML="text/html", 50 TEXT_PLAIN="text/plain", 51 TEXT_XML="text/xml", 52 TEXT_JSON="text/json", 53 54 TEXT_HTML_8859_1="text/html;charset=ISO-8859-1", 55 TEXT_PLAIN_8859_1="text/plain;charset=ISO-8859-1", 56 TEXT_XML_8859_1="text/xml;charset=ISO-8859-1", 57 58 TEXT_HTML_UTF_8="text/html;charset=UTF-8", 59 TEXT_PLAIN_UTF_8="text/plain;charset=UTF-8", 60 TEXT_XML_UTF_8="text/xml;charset=UTF-8", 61 TEXT_JSON_UTF_8="text/json;charset=UTF-8"; 62 63 private final static String 64 TEXT_HTML__8859_1="text/html; charset=ISO-8859-1", 65 TEXT_PLAIN__8859_1="text/plain; charset=ISO-8859-1", 66 TEXT_XML__8859_1="text/xml; charset=ISO-8859-1", 67 TEXT_HTML__UTF_8="text/html; charset=UTF-8", 68 TEXT_PLAIN__UTF_8="text/plain; charset=UTF-8", 69 TEXT_XML__UTF_8="text/xml; charset=UTF-8", 70 TEXT_JSON__UTF_8="text/json; charset=UTF-8"; 71 72 private final static int 73 FORM_ENCODED_ORDINAL=1, 74 MESSAGE_HTTP_ORDINAL=2, 75 MULTIPART_BYTERANGES_ORDINAL=3, 76 77 TEXT_HTML_ORDINAL=4, 78 TEXT_PLAIN_ORDINAL=5, 79 TEXT_XML_ORDINAL=6, 80 TEXT_JSON_ORDINAL=7, 81 82 TEXT_HTML_8859_1_ORDINAL=8, 83 TEXT_PLAIN_8859_1_ORDINAL=9, 84 TEXT_XML_8859_1_ORDINAL=10, 85 86 TEXT_HTML_UTF_8_ORDINAL=11, 87 TEXT_PLAIN_UTF_8_ORDINAL=12, 88 TEXT_XML_UTF_8_ORDINAL=13, 89 TEXT_JSON_UTF_8_ORDINAL=14; 90 91 private static int __index=15; 92 93 public final static BufferCache CACHE = new BufferCache(); 94 95 public final static CachedBuffer 96 FORM_ENCODED_BUFFER=CACHE.add(FORM_ENCODED,FORM_ENCODED_ORDINAL), 97 MESSAGE_HTTP_BUFFER=CACHE.add(MESSAGE_HTTP, MESSAGE_HTTP_ORDINAL), 98 MULTIPART_BYTERANGES_BUFFER=CACHE.add(MULTIPART_BYTERANGES,MULTIPART_BYTERANGES_ORDINAL), 99 100 TEXT_HTML_BUFFER=CACHE.add(TEXT_HTML,TEXT_HTML_ORDINAL), 101 TEXT_PLAIN_BUFFER=CACHE.add(TEXT_PLAIN,TEXT_PLAIN_ORDINAL), 102 TEXT_XML_BUFFER=CACHE.add(TEXT_XML,TEXT_XML_ORDINAL), 103 TEXT_JSON_BUFFER=CACHE.add(TEXT_JSON,TEXT_JSON_ORDINAL), 104 105 TEXT_HTML_8859_1_BUFFER=CACHE.add(TEXT_HTML_8859_1,TEXT_HTML_8859_1_ORDINAL), 106 TEXT_PLAIN_8859_1_BUFFER=CACHE.add(TEXT_PLAIN_8859_1,TEXT_PLAIN_8859_1_ORDINAL), 107 TEXT_XML_8859_1_BUFFER=CACHE.add(TEXT_XML_8859_1,TEXT_XML_8859_1_ORDINAL), 108 109 TEXT_HTML_UTF_8_BUFFER=CACHE.add(TEXT_HTML_UTF_8,TEXT_HTML_UTF_8_ORDINAL), 110 TEXT_PLAIN_UTF_8_BUFFER=CACHE.add(TEXT_PLAIN_UTF_8,TEXT_PLAIN_UTF_8_ORDINAL), 111 TEXT_XML_UTF_8_BUFFER=CACHE.add(TEXT_XML_UTF_8,TEXT_XML_UTF_8_ORDINAL), 112 TEXT_JSON_UTF_8_BUFFER=CACHE.add(TEXT_JSON_UTF_8,TEXT_JSON_UTF_8_ORDINAL), 113 114 TEXT_HTML__8859_1_BUFFER=CACHE.add(TEXT_HTML__8859_1,TEXT_HTML_8859_1_ORDINAL), 115 TEXT_PLAIN__8859_1_BUFFER=CACHE.add(TEXT_PLAIN__8859_1,TEXT_PLAIN_8859_1_ORDINAL), 116 TEXT_XML__8859_1_BUFFER=CACHE.add(TEXT_XML__8859_1,TEXT_XML_8859_1_ORDINAL), 117 118 TEXT_HTML__UTF_8_BUFFER=CACHE.add(TEXT_HTML__UTF_8,TEXT_HTML_UTF_8_ORDINAL), 119 TEXT_PLAIN__UTF_8_BUFFER=CACHE.add(TEXT_PLAIN__UTF_8,TEXT_PLAIN_UTF_8_ORDINAL), 120 TEXT_XML__UTF_8_BUFFER=CACHE.add(TEXT_XML__UTF_8,TEXT_XML_UTF_8_ORDINAL), 121 TEXT_JSON__UTF_8_BUFFER=CACHE.add(TEXT_JSON__UTF_8,TEXT_JSON_UTF_8_ORDINAL); 122 123 124 /* ------------------------------------------------------------ */ 125 /* ------------------------------------------------------------ */ 126 private final static Map __dftMimeMap = new HashMap(); 127 private final static Map __encodings = new HashMap(); 128 static 129 { 130 try 131 { 132 ResourceBundle mime = ResourceBundle.getBundle("org/eclipse/jetty/http/mime"); 133 Enumeration i = mime.getKeys(); 134 while(i.hasMoreElements()) 135 { 136 String ext = (String)i.nextElement(); 137 String m = mime.getString(ext); StringUtil.asciiToLowerCase(ext)138 __dftMimeMap.put(StringUtil.asciiToLowerCase(ext),normalizeMimeType(m)); 139 } 140 } 141 catch(MissingResourceException e) 142 { 143 LOG.warn(e.toString()); 144 LOG.debug(e); 145 } 146 147 try 148 { 149 ResourceBundle encoding = ResourceBundle.getBundle("org/eclipse/jetty/http/encoding"); 150 Enumeration i = encoding.getKeys(); 151 while(i.hasMoreElements()) 152 { 153 Buffer type = normalizeMimeType((String)i.nextElement()); __encodings.put(type,encoding.getString(type.toString()))154 __encodings.put(type,encoding.getString(type.toString())); 155 } 156 } 157 catch(MissingResourceException e) 158 { 159 LOG.warn(e.toString()); 160 LOG.debug(e); 161 } 162 163 164 TEXT_HTML_BUFFER.setAssociate("ISO-8859-1",TEXT_HTML_8859_1_BUFFER); 165 TEXT_HTML_BUFFER.setAssociate("ISO_8859_1",TEXT_HTML_8859_1_BUFFER); 166 TEXT_HTML_BUFFER.setAssociate("iso-8859-1",TEXT_HTML_8859_1_BUFFER); 167 TEXT_PLAIN_BUFFER.setAssociate("ISO-8859-1",TEXT_PLAIN_8859_1_BUFFER); 168 TEXT_PLAIN_BUFFER.setAssociate("ISO_8859_1",TEXT_PLAIN_8859_1_BUFFER); 169 TEXT_PLAIN_BUFFER.setAssociate("iso-8859-1",TEXT_PLAIN_8859_1_BUFFER); 170 TEXT_XML_BUFFER.setAssociate("ISO-8859-1",TEXT_XML_8859_1_BUFFER); 171 TEXT_XML_BUFFER.setAssociate("ISO_8859_1",TEXT_XML_8859_1_BUFFER); 172 TEXT_XML_BUFFER.setAssociate("iso-8859-1",TEXT_XML_8859_1_BUFFER); 173 174 TEXT_HTML_BUFFER.setAssociate("UTF-8",TEXT_HTML_UTF_8_BUFFER); 175 TEXT_HTML_BUFFER.setAssociate("UTF8",TEXT_HTML_UTF_8_BUFFER); 176 TEXT_HTML_BUFFER.setAssociate("utf8",TEXT_HTML_UTF_8_BUFFER); 177 TEXT_HTML_BUFFER.setAssociate("utf-8",TEXT_HTML_UTF_8_BUFFER); 178 TEXT_PLAIN_BUFFER.setAssociate("UTF-8",TEXT_PLAIN_UTF_8_BUFFER); 179 TEXT_PLAIN_BUFFER.setAssociate("UTF8",TEXT_PLAIN_UTF_8_BUFFER); 180 TEXT_PLAIN_BUFFER.setAssociate("utf8",TEXT_PLAIN_UTF_8_BUFFER); 181 TEXT_PLAIN_BUFFER.setAssociate("utf-8",TEXT_PLAIN_UTF_8_BUFFER); 182 TEXT_XML_BUFFER.setAssociate("UTF-8",TEXT_XML_UTF_8_BUFFER); 183 TEXT_XML_BUFFER.setAssociate("UTF8",TEXT_XML_UTF_8_BUFFER); 184 TEXT_XML_BUFFER.setAssociate("utf8",TEXT_XML_UTF_8_BUFFER); 185 TEXT_XML_BUFFER.setAssociate("utf-8",TEXT_XML_UTF_8_BUFFER); 186 TEXT_JSON_BUFFER.setAssociate("UTF-8",TEXT_JSON_UTF_8_BUFFER); 187 TEXT_JSON_BUFFER.setAssociate("UTF8",TEXT_JSON_UTF_8_BUFFER); 188 TEXT_JSON_BUFFER.setAssociate("utf8",TEXT_JSON_UTF_8_BUFFER); 189 TEXT_JSON_BUFFER.setAssociate("utf-8",TEXT_JSON_UTF_8_BUFFER); 190 } 191 192 193 /* ------------------------------------------------------------ */ 194 private Map _mimeMap; 195 196 /* ------------------------------------------------------------ */ 197 /** Constructor. 198 */ MimeTypes()199 public MimeTypes() 200 { 201 } 202 203 /* ------------------------------------------------------------ */ getMimeMap()204 public synchronized Map getMimeMap() 205 { 206 return _mimeMap; 207 } 208 209 /* ------------------------------------------------------------ */ 210 /** 211 * @param mimeMap A Map of file extension to mime-type. 212 */ setMimeMap(Map mimeMap)213 public void setMimeMap(Map mimeMap) 214 { 215 if (mimeMap==null) 216 { 217 _mimeMap=null; 218 return; 219 } 220 221 Map m=new HashMap(); 222 Iterator i=mimeMap.entrySet().iterator(); 223 while (i.hasNext()) 224 { 225 Map.Entry entry = (Map.Entry)i.next(); 226 m.put(entry.getKey(),normalizeMimeType(entry.getValue().toString())); 227 } 228 _mimeMap=m; 229 } 230 231 /* ------------------------------------------------------------ */ 232 /** Get the MIME type by filename extension. 233 * @param filename A file name 234 * @return MIME type matching the longest dot extension of the 235 * file name. 236 */ getMimeByExtension(String filename)237 public Buffer getMimeByExtension(String filename) 238 { 239 Buffer type=null; 240 241 if (filename!=null) 242 { 243 int i=-1; 244 while(type==null) 245 { 246 i=filename.indexOf(".",i+1); 247 248 if (i<0 || i>=filename.length()) 249 break; 250 251 String ext=StringUtil.asciiToLowerCase(filename.substring(i+1)); 252 if (_mimeMap!=null) 253 type = (Buffer)_mimeMap.get(ext); 254 if (type==null) 255 type=(Buffer)__dftMimeMap.get(ext); 256 } 257 } 258 259 if (type==null) 260 { 261 if (_mimeMap!=null) 262 type=(Buffer)_mimeMap.get("*"); 263 if (type==null) 264 type=(Buffer)__dftMimeMap.get("*"); 265 } 266 267 return type; 268 } 269 270 /* ------------------------------------------------------------ */ 271 /** Set a mime mapping 272 * @param extension 273 * @param type 274 */ addMimeMapping(String extension,String type)275 public void addMimeMapping(String extension,String type) 276 { 277 if (_mimeMap==null) 278 _mimeMap=new HashMap(); 279 280 _mimeMap.put(StringUtil.asciiToLowerCase(extension),normalizeMimeType(type)); 281 } 282 283 /* ------------------------------------------------------------ */ normalizeMimeType(String type)284 private static synchronized Buffer normalizeMimeType(String type) 285 { 286 Buffer b =CACHE.get(type); 287 if (b==null) 288 b=CACHE.add(type,__index++); 289 return b; 290 } 291 292 /* ------------------------------------------------------------ */ getCharsetFromContentType(Buffer value)293 public static String getCharsetFromContentType(Buffer value) 294 { 295 if (value instanceof CachedBuffer) 296 { 297 switch(((CachedBuffer)value).getOrdinal()) 298 { 299 case TEXT_HTML_8859_1_ORDINAL: 300 case TEXT_PLAIN_8859_1_ORDINAL: 301 case TEXT_XML_8859_1_ORDINAL: 302 return StringUtil.__ISO_8859_1; 303 304 case TEXT_HTML_UTF_8_ORDINAL: 305 case TEXT_PLAIN_UTF_8_ORDINAL: 306 case TEXT_XML_UTF_8_ORDINAL: 307 case TEXT_JSON_UTF_8_ORDINAL: 308 return StringUtil.__UTF8; 309 } 310 } 311 312 int i=value.getIndex(); 313 int end=value.putIndex(); 314 int state=0; 315 int start=0; 316 boolean quote=false; 317 for (;i<end;i++) 318 { 319 byte b = value.peek(i); 320 321 if (quote && state!=10) 322 { 323 if ('"'==b) 324 quote=false; 325 continue; 326 } 327 328 switch(state) 329 { 330 case 0: 331 if ('"'==b) 332 { 333 quote=true; 334 break; 335 } 336 if (';'==b) 337 state=1; 338 break; 339 340 case 1: if ('c'==b) state=2; else if (' '!=b) state=0; break; 341 case 2: if ('h'==b) state=3; else state=0;break; 342 case 3: if ('a'==b) state=4; else state=0;break; 343 case 4: if ('r'==b) state=5; else state=0;break; 344 case 5: if ('s'==b) state=6; else state=0;break; 345 case 6: if ('e'==b) state=7; else state=0;break; 346 case 7: if ('t'==b) state=8; else state=0;break; 347 348 case 8: if ('='==b) state=9; else if (' '!=b) state=0; break; 349 350 case 9: 351 if (' '==b) 352 break; 353 if ('"'==b) 354 { 355 quote=true; 356 start=i+1; 357 state=10; 358 break; 359 } 360 start=i; 361 state=10; 362 break; 363 364 case 10: 365 if (!quote && (';'==b || ' '==b )|| 366 (quote && '"'==b )) 367 return CACHE.lookup(value.peek(start,i-start)).toString(StringUtil.__UTF8); 368 } 369 } 370 371 if (state==10) 372 return CACHE.lookup(value.peek(start,i-start)).toString(StringUtil.__UTF8); 373 374 return (String)__encodings.get(value); 375 } 376 } 377