View Javadoc

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