// ======================================================================== // Copyright (c) 2000-2009 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.http; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.BufferCache; import org.eclipse.jetty.io.BufferCache.CachedBuffer; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /* ------------------------------------------------------------ */ /** * */ public class MimeTypes { private static final Logger LOG = Log.getLogger(MimeTypes.class); public final static String FORM_ENCODED="application/x-www-form-urlencoded", MESSAGE_HTTP="message/http", MULTIPART_BYTERANGES="multipart/byteranges", TEXT_HTML="text/html", TEXT_PLAIN="text/plain", TEXT_XML="text/xml", TEXT_JSON="text/json", TEXT_HTML_8859_1="text/html;charset=ISO-8859-1", TEXT_PLAIN_8859_1="text/plain;charset=ISO-8859-1", TEXT_XML_8859_1="text/xml;charset=ISO-8859-1", TEXT_HTML_UTF_8="text/html;charset=UTF-8", TEXT_PLAIN_UTF_8="text/plain;charset=UTF-8", TEXT_XML_UTF_8="text/xml;charset=UTF-8", TEXT_JSON_UTF_8="text/json;charset=UTF-8"; private final static String TEXT_HTML__8859_1="text/html; charset=ISO-8859-1", TEXT_PLAIN__8859_1="text/plain; charset=ISO-8859-1", TEXT_XML__8859_1="text/xml; charset=ISO-8859-1", TEXT_HTML__UTF_8="text/html; charset=UTF-8", TEXT_PLAIN__UTF_8="text/plain; charset=UTF-8", TEXT_XML__UTF_8="text/xml; charset=UTF-8", TEXT_JSON__UTF_8="text/json; charset=UTF-8"; private final static int FORM_ENCODED_ORDINAL=1, MESSAGE_HTTP_ORDINAL=2, MULTIPART_BYTERANGES_ORDINAL=3, TEXT_HTML_ORDINAL=4, TEXT_PLAIN_ORDINAL=5, TEXT_XML_ORDINAL=6, TEXT_JSON_ORDINAL=7, TEXT_HTML_8859_1_ORDINAL=8, TEXT_PLAIN_8859_1_ORDINAL=9, TEXT_XML_8859_1_ORDINAL=10, TEXT_HTML_UTF_8_ORDINAL=11, TEXT_PLAIN_UTF_8_ORDINAL=12, TEXT_XML_UTF_8_ORDINAL=13, TEXT_JSON_UTF_8_ORDINAL=14; private static int __index=15; public final static BufferCache CACHE = new BufferCache(); public final static CachedBuffer FORM_ENCODED_BUFFER=CACHE.add(FORM_ENCODED,FORM_ENCODED_ORDINAL), MESSAGE_HTTP_BUFFER=CACHE.add(MESSAGE_HTTP, MESSAGE_HTTP_ORDINAL), MULTIPART_BYTERANGES_BUFFER=CACHE.add(MULTIPART_BYTERANGES,MULTIPART_BYTERANGES_ORDINAL), TEXT_HTML_BUFFER=CACHE.add(TEXT_HTML,TEXT_HTML_ORDINAL), TEXT_PLAIN_BUFFER=CACHE.add(TEXT_PLAIN,TEXT_PLAIN_ORDINAL), TEXT_XML_BUFFER=CACHE.add(TEXT_XML,TEXT_XML_ORDINAL), TEXT_JSON_BUFFER=CACHE.add(TEXT_JSON,TEXT_JSON_ORDINAL), TEXT_HTML_8859_1_BUFFER=CACHE.add(TEXT_HTML_8859_1,TEXT_HTML_8859_1_ORDINAL), TEXT_PLAIN_8859_1_BUFFER=CACHE.add(TEXT_PLAIN_8859_1,TEXT_PLAIN_8859_1_ORDINAL), TEXT_XML_8859_1_BUFFER=CACHE.add(TEXT_XML_8859_1,TEXT_XML_8859_1_ORDINAL), TEXT_HTML_UTF_8_BUFFER=CACHE.add(TEXT_HTML_UTF_8,TEXT_HTML_UTF_8_ORDINAL), TEXT_PLAIN_UTF_8_BUFFER=CACHE.add(TEXT_PLAIN_UTF_8,TEXT_PLAIN_UTF_8_ORDINAL), TEXT_XML_UTF_8_BUFFER=CACHE.add(TEXT_XML_UTF_8,TEXT_XML_UTF_8_ORDINAL), TEXT_JSON_UTF_8_BUFFER=CACHE.add(TEXT_JSON_UTF_8,TEXT_JSON_UTF_8_ORDINAL), TEXT_HTML__8859_1_BUFFER=CACHE.add(TEXT_HTML__8859_1,TEXT_HTML_8859_1_ORDINAL), TEXT_PLAIN__8859_1_BUFFER=CACHE.add(TEXT_PLAIN__8859_1,TEXT_PLAIN_8859_1_ORDINAL), TEXT_XML__8859_1_BUFFER=CACHE.add(TEXT_XML__8859_1,TEXT_XML_8859_1_ORDINAL), TEXT_HTML__UTF_8_BUFFER=CACHE.add(TEXT_HTML__UTF_8,TEXT_HTML_UTF_8_ORDINAL), TEXT_PLAIN__UTF_8_BUFFER=CACHE.add(TEXT_PLAIN__UTF_8,TEXT_PLAIN_UTF_8_ORDINAL), TEXT_XML__UTF_8_BUFFER=CACHE.add(TEXT_XML__UTF_8,TEXT_XML_UTF_8_ORDINAL), TEXT_JSON__UTF_8_BUFFER=CACHE.add(TEXT_JSON__UTF_8,TEXT_JSON_UTF_8_ORDINAL); /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ private final static Map __dftMimeMap = new HashMap(); private final static Map __encodings = new HashMap(); static { try { ResourceBundle mime = ResourceBundle.getBundle("org/eclipse/jetty/http/mime"); Enumeration i = mime.getKeys(); while(i.hasMoreElements()) { String ext = (String)i.nextElement(); String m = mime.getString(ext); __dftMimeMap.put(StringUtil.asciiToLowerCase(ext),normalizeMimeType(m)); } } catch(MissingResourceException e) { LOG.warn(e.toString()); LOG.debug(e); } try { ResourceBundle encoding = ResourceBundle.getBundle("org/eclipse/jetty/http/encoding"); Enumeration i = encoding.getKeys(); while(i.hasMoreElements()) { Buffer type = normalizeMimeType((String)i.nextElement()); __encodings.put(type,encoding.getString(type.toString())); } } catch(MissingResourceException e) { LOG.warn(e.toString()); LOG.debug(e); } TEXT_HTML_BUFFER.setAssociate("ISO-8859-1",TEXT_HTML_8859_1_BUFFER); TEXT_HTML_BUFFER.setAssociate("ISO_8859_1",TEXT_HTML_8859_1_BUFFER); TEXT_HTML_BUFFER.setAssociate("iso-8859-1",TEXT_HTML_8859_1_BUFFER); TEXT_PLAIN_BUFFER.setAssociate("ISO-8859-1",TEXT_PLAIN_8859_1_BUFFER); TEXT_PLAIN_BUFFER.setAssociate("ISO_8859_1",TEXT_PLAIN_8859_1_BUFFER); TEXT_PLAIN_BUFFER.setAssociate("iso-8859-1",TEXT_PLAIN_8859_1_BUFFER); TEXT_XML_BUFFER.setAssociate("ISO-8859-1",TEXT_XML_8859_1_BUFFER); TEXT_XML_BUFFER.setAssociate("ISO_8859_1",TEXT_XML_8859_1_BUFFER); TEXT_XML_BUFFER.setAssociate("iso-8859-1",TEXT_XML_8859_1_BUFFER); TEXT_HTML_BUFFER.setAssociate("UTF-8",TEXT_HTML_UTF_8_BUFFER); TEXT_HTML_BUFFER.setAssociate("UTF8",TEXT_HTML_UTF_8_BUFFER); TEXT_HTML_BUFFER.setAssociate("utf8",TEXT_HTML_UTF_8_BUFFER); TEXT_HTML_BUFFER.setAssociate("utf-8",TEXT_HTML_UTF_8_BUFFER); TEXT_PLAIN_BUFFER.setAssociate("UTF-8",TEXT_PLAIN_UTF_8_BUFFER); TEXT_PLAIN_BUFFER.setAssociate("UTF8",TEXT_PLAIN_UTF_8_BUFFER); TEXT_PLAIN_BUFFER.setAssociate("utf8",TEXT_PLAIN_UTF_8_BUFFER); TEXT_PLAIN_BUFFER.setAssociate("utf-8",TEXT_PLAIN_UTF_8_BUFFER); TEXT_XML_BUFFER.setAssociate("UTF-8",TEXT_XML_UTF_8_BUFFER); TEXT_XML_BUFFER.setAssociate("UTF8",TEXT_XML_UTF_8_BUFFER); TEXT_XML_BUFFER.setAssociate("utf8",TEXT_XML_UTF_8_BUFFER); TEXT_XML_BUFFER.setAssociate("utf-8",TEXT_XML_UTF_8_BUFFER); TEXT_JSON_BUFFER.setAssociate("UTF-8",TEXT_JSON_UTF_8_BUFFER); TEXT_JSON_BUFFER.setAssociate("UTF8",TEXT_JSON_UTF_8_BUFFER); TEXT_JSON_BUFFER.setAssociate("utf8",TEXT_JSON_UTF_8_BUFFER); TEXT_JSON_BUFFER.setAssociate("utf-8",TEXT_JSON_UTF_8_BUFFER); } /* ------------------------------------------------------------ */ private Map _mimeMap; /* ------------------------------------------------------------ */ /** Constructor. */ public MimeTypes() { } /* ------------------------------------------------------------ */ public synchronized Map getMimeMap() { return _mimeMap; } /* ------------------------------------------------------------ */ /** * @param mimeMap A Map of file extension to mime-type. */ public void setMimeMap(Map mimeMap) { if (mimeMap==null) { _mimeMap=null; return; } Map m=new HashMap(); Iterator i=mimeMap.entrySet().iterator(); while (i.hasNext()) { Map.Entry entry = (Map.Entry)i.next(); m.put(entry.getKey(),normalizeMimeType(entry.getValue().toString())); } _mimeMap=m; } /* ------------------------------------------------------------ */ /** Get the MIME type by filename extension. * @param filename A file name * @return MIME type matching the longest dot extension of the * file name. */ public Buffer getMimeByExtension(String filename) { Buffer type=null; if (filename!=null) { int i=-1; while(type==null) { i=filename.indexOf(".",i+1); if (i<0 || i>=filename.length()) break; String ext=StringUtil.asciiToLowerCase(filename.substring(i+1)); if (_mimeMap!=null) type = (Buffer)_mimeMap.get(ext); if (type==null) type=(Buffer)__dftMimeMap.get(ext); } } if (type==null) { if (_mimeMap!=null) type=(Buffer)_mimeMap.get("*"); if (type==null) type=(Buffer)__dftMimeMap.get("*"); } return type; } /* ------------------------------------------------------------ */ /** Set a mime mapping * @param extension * @param type */ public void addMimeMapping(String extension,String type) { if (_mimeMap==null) _mimeMap=new HashMap(); _mimeMap.put(StringUtil.asciiToLowerCase(extension),normalizeMimeType(type)); } /* ------------------------------------------------------------ */ private static synchronized Buffer normalizeMimeType(String type) { Buffer b =CACHE.get(type); if (b==null) b=CACHE.add(type,__index++); return b; } /* ------------------------------------------------------------ */ public static String getCharsetFromContentType(Buffer value) { if (value instanceof CachedBuffer) { switch(((CachedBuffer)value).getOrdinal()) { case TEXT_HTML_8859_1_ORDINAL: case TEXT_PLAIN_8859_1_ORDINAL: case TEXT_XML_8859_1_ORDINAL: return StringUtil.__ISO_8859_1; case TEXT_HTML_UTF_8_ORDINAL: case TEXT_PLAIN_UTF_8_ORDINAL: case TEXT_XML_UTF_8_ORDINAL: case TEXT_JSON_UTF_8_ORDINAL: return StringUtil.__UTF8; } } int i=value.getIndex(); int end=value.putIndex(); int state=0; int start=0; boolean quote=false; for (;i<end;i++) { byte b = value.peek(i); if (quote && state!=10) { if ('"'==b) quote=false; continue; } switch(state) { case 0: if ('"'==b) { quote=true; break; } if (';'==b) state=1; break; case 1: if ('c'==b) state=2; else if (' '!=b) state=0; break; case 2: if ('h'==b) state=3; else state=0;break; case 3: if ('a'==b) state=4; else state=0;break; case 4: if ('r'==b) state=5; else state=0;break; case 5: if ('s'==b) state=6; else state=0;break; case 6: if ('e'==b) state=7; else state=0;break; case 7: if ('t'==b) state=8; else state=0;break; case 8: if ('='==b) state=9; else if (' '!=b) state=0; break; case 9: if (' '==b) break; if ('"'==b) { quote=true; start=i+1; state=10; break; } start=i; state=10; break; case 10: if (!quote && (';'==b || ' '==b )|| (quote && '"'==b )) return CACHE.lookup(value.peek(start,i-start)).toString(); } } if (state==10) return CACHE.lookup(value.peek(start,i-start)).toString(); return (String)__encodings.get(value); } }