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