1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.http;
20
21 import java.nio.ByteBuffer;
22 import java.nio.charset.Charset;
23 import java.util.Enumeration;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.Locale;
27 import java.util.Map;
28 import java.util.Map.Entry;
29 import java.util.MissingResourceException;
30 import java.util.ResourceBundle;
31 import java.util.Set;
32
33 import org.eclipse.jetty.util.ArrayTrie;
34 import org.eclipse.jetty.util.BufferUtil;
35 import org.eclipse.jetty.util.StringUtil;
36 import org.eclipse.jetty.util.Trie;
37 import org.eclipse.jetty.util.log.Log;
38 import org.eclipse.jetty.util.log.Logger;
39
40
41
42
43
44
45 public class MimeTypes
46 {
47 public enum Type
48 {
49 FORM_ENCODED("application/x-www-form-urlencoded"),
50 MESSAGE_HTTP("message/http"),
51 MULTIPART_BYTERANGES("multipart/byteranges"),
52
53 TEXT_HTML("text/html"),
54 TEXT_PLAIN("text/plain"),
55 TEXT_XML("text/xml"),
56 TEXT_JSON("text/json"),
57
58 TEXT_HTML_8859_1("text/html;charset=ISO-8859-1"),
59 TEXT_PLAIN_8859_1("text/plain;charset=ISO-8859-1"),
60 TEXT_XML_8859_1("text/xml;charset=ISO-8859-1"),
61 TEXT_HTML_UTF_8("text/html;charset=UTF-8"),
62 TEXT_PLAIN_UTF_8("text/plain;charset=UTF-8"),
63 TEXT_XML_UTF_8("text/xml;charset=UTF-8"),
64 TEXT_JSON_UTF_8("text/json;charset=UTF-8");
65
66
67
68 private final String _string;
69 private final ByteBuffer _buffer;
70 private final Charset _charset;
71
72
73 Type(String s)
74 {
75 _string=s;
76 _buffer=BufferUtil.toBuffer(s);
77
78 int i=s.toLowerCase(Locale.ENGLISH).indexOf("charset=");
79 _charset=(i>0)?Charset.forName(s.substring(i+8)):null;
80 }
81
82
83 public ByteBuffer asBuffer()
84 {
85 return _buffer.asReadOnlyBuffer();
86 }
87
88
89 public Charset getCharset()
90 {
91 return _charset;
92 }
93
94
95 public boolean is(String s)
96 {
97 return _string.equalsIgnoreCase(s);
98 }
99
100
101 public String asString()
102 {
103 return _string;
104 }
105
106
107 @Override
108 public String toString()
109 {
110 return _string;
111 }
112 }
113
114
115 private static final Logger LOG = Log.getLogger(MimeTypes.class);
116 public final static Trie<MimeTypes.Type> CACHE= new ArrayTrie<>(512);
117 private final static Trie<ByteBuffer> TYPES= new ArrayTrie<ByteBuffer>(512);
118 private final static Map<String,String> __dftMimeMap = new HashMap<String,String>();
119 private final static Map<String,String> __encodings = new HashMap<String,String>();
120
121 static
122 {
123 for (MimeTypes.Type type : MimeTypes.Type.values())
124 {
125 CACHE.put(type.toString(),type);
126 TYPES.put(type.toString(),type.asBuffer());
127
128 int charset=type.toString().indexOf(";charset=");
129 if (charset>0)
130 {
131 CACHE.put(type.toString().replace(";charset=","; charset="),type);
132 TYPES.put(type.toString().replace(";charset=","; charset="),type.asBuffer());
133 }
134 }
135
136 try
137 {
138 ResourceBundle mime = ResourceBundle.getBundle("org/eclipse/jetty/http/mime");
139 Enumeration i = mime.getKeys();
140 while(i.hasMoreElements())
141 {
142 String ext = (String)i.nextElement();
143 String m = mime.getString(ext);
144 __dftMimeMap.put(StringUtil.asciiToLowerCase(ext),normalizeMimeType(m));
145 }
146 }
147 catch(MissingResourceException e)
148 {
149 LOG.warn(e.toString());
150 LOG.debug(e);
151 }
152
153 try
154 {
155 ResourceBundle encoding = ResourceBundle.getBundle("org/eclipse/jetty/http/encoding");
156 Enumeration<String> i = encoding.getKeys();
157 while(i.hasMoreElements())
158 {
159 String type = i.nextElement();
160 __encodings.put(type,encoding.getString(type));
161 }
162 }
163 catch(MissingResourceException e)
164 {
165 LOG.warn(e.toString());
166 LOG.debug(e);
167 }
168 }
169
170
171
172 private final Map<String,String> _mimeMap=new HashMap<String,String>();
173
174
175
176
177 public MimeTypes()
178 {
179 }
180
181
182 public synchronized Map<String,String> getMimeMap()
183 {
184 return _mimeMap;
185 }
186
187
188
189
190
191 public void setMimeMap(Map<String,String> mimeMap)
192 {
193 _mimeMap.clear();
194 if (mimeMap!=null)
195 {
196 for (Entry<String, String> ext : mimeMap.entrySet())
197 _mimeMap.put(StringUtil.asciiToLowerCase(ext.getKey()),normalizeMimeType(ext.getValue()));
198 }
199 }
200
201
202
203
204
205
206
207 public String getMimeByExtension(String filename)
208 {
209 String type=null;
210
211 if (filename!=null)
212 {
213 int i=-1;
214 while(type==null)
215 {
216 i=filename.indexOf(".",i+1);
217
218 if (i<0 || i>=filename.length())
219 break;
220
221 String ext=StringUtil.asciiToLowerCase(filename.substring(i+1));
222 if (_mimeMap!=null)
223 type=_mimeMap.get(ext);
224 if (type==null)
225 type=__dftMimeMap.get(ext);
226 }
227 }
228
229 if (type==null)
230 {
231 if (_mimeMap!=null)
232 type=_mimeMap.get("*");
233 if (type==null)
234 type=__dftMimeMap.get("*");
235 }
236
237 return type;
238 }
239
240
241
242
243
244
245 public void addMimeMapping(String extension,String type)
246 {
247 _mimeMap.put(StringUtil.asciiToLowerCase(extension),normalizeMimeType(type));
248 }
249
250
251 public static Set<String> getKnownMimeTypes()
252 {
253 return new HashSet<>(__dftMimeMap.values());
254 }
255
256
257 private static String normalizeMimeType(String type)
258 {
259 MimeTypes.Type t =CACHE.get(type);
260 if (t!=null)
261 return t.asString();
262
263 return StringUtil.asciiToLowerCase(type);
264 }
265
266
267 public static String getCharsetFromContentType(String value)
268 {
269 if (value==null)
270 return null;
271 int end=value.length();
272 int state=0;
273 int start=0;
274 boolean quote=false;
275 int i=0;
276 for (;i<end;i++)
277 {
278 char b = value.charAt(i);
279
280 if (quote && state!=10)
281 {
282 if ('"'==b)
283 quote=false;
284 continue;
285 }
286
287 switch(state)
288 {
289 case 0:
290 if ('"'==b)
291 {
292 quote=true;
293 break;
294 }
295 if (';'==b)
296 state=1;
297 break;
298
299 case 1: if ('c'==b) state=2; else if (' '!=b) state=0; break;
300 case 2: if ('h'==b) state=3; else state=0;break;
301 case 3: if ('a'==b) state=4; else state=0;break;
302 case 4: if ('r'==b) state=5; else state=0;break;
303 case 5: if ('s'==b) state=6; else state=0;break;
304 case 6: if ('e'==b) state=7; else state=0;break;
305 case 7: if ('t'==b) state=8; else state=0;break;
306
307 case 8: if ('='==b) state=9; else if (' '!=b) state=0; break;
308
309 case 9:
310 if (' '==b)
311 break;
312 if ('"'==b)
313 {
314 quote=true;
315 start=i+1;
316 state=10;
317 break;
318 }
319 start=i;
320 state=10;
321 break;
322
323 case 10:
324 if (!quote && (';'==b || ' '==b )||
325 (quote && '"'==b ))
326 return StringUtil.normalizeCharset(value,start,i-start);
327 }
328 }
329
330 if (state==10)
331 return StringUtil.normalizeCharset(value,start,i-start);
332
333 return null;
334 }
335
336 public static String inferCharsetFromContentType(String value)
337 {
338 return __encodings.get(value);
339 }
340
341 public static String getContentTypeWithoutCharset(String value)
342 {
343 int end=value.length();
344 int state=0;
345 int start=0;
346 boolean quote=false;
347 int i=0;
348 StringBuilder builder=null;
349 for (;i<end;i++)
350 {
351 char b = value.charAt(i);
352
353 if ('"'==b)
354 {
355 if (quote)
356 {
357 quote=false;
358 }
359 else
360 {
361 quote=true;
362 }
363
364 switch(state)
365 {
366 case 11:
367 builder.append(b);break;
368 case 10:
369 break;
370 case 9:
371 builder=new StringBuilder();
372 builder.append(value,0,start+1);
373 state=10;
374 break;
375 default:
376 start=i;
377 state=0;
378 }
379 continue;
380 }
381
382 if (quote)
383 {
384 if (builder!=null && state!=10)
385 builder.append(b);
386 continue;
387 }
388
389 switch(state)
390 {
391 case 0:
392 if (';'==b)
393 state=1;
394 else if (' '!=b)
395 start=i;
396 break;
397
398 case 1: if ('c'==b) state=2; else if (' '!=b) state=0; break;
399 case 2: if ('h'==b) state=3; else state=0;break;
400 case 3: if ('a'==b) state=4; else state=0;break;
401 case 4: if ('r'==b) state=5; else state=0;break;
402 case 5: if ('s'==b) state=6; else state=0;break;
403 case 6: if ('e'==b) state=7; else state=0;break;
404 case 7: if ('t'==b) state=8; else state=0;break;
405 case 8: if ('='==b) state=9; else if (' '!=b) state=0; break;
406
407 case 9:
408 if (' '==b)
409 break;
410 builder=new StringBuilder();
411 builder.append(value,0,start+1);
412 state=10;
413 break;
414
415 case 10:
416 if (';'==b)
417 {
418 builder.append(b);
419 state=11;
420 }
421 break;
422 case 11:
423 if (' '!=b)
424 builder.append(b);
425 }
426 }
427 if (builder==null)
428 return value;
429 return builder.toString();
430
431 }
432 }