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