1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.servlets.gzip;
20
21 import java.io.IOException;
22 import java.io.OutputStream;
23 import java.io.OutputStreamWriter;
24 import java.io.PrintWriter;
25 import java.io.UnsupportedEncodingException;
26 import java.util.HashSet;
27 import java.util.Set;
28 import java.util.StringTokenizer;
29 import java.util.zip.DeflaterOutputStream;
30 import java.util.zip.GZIPOutputStream;
31
32 import javax.servlet.AsyncEvent;
33 import javax.servlet.AsyncListener;
34 import javax.servlet.ServletException;
35 import javax.servlet.http.HttpServletRequest;
36 import javax.servlet.http.HttpServletResponse;
37
38 import org.eclipse.jetty.http.HttpMethod;
39 import org.eclipse.jetty.http.MimeTypes;
40 import org.eclipse.jetty.server.Request;
41 import org.eclipse.jetty.server.handler.HandlerWrapper;
42 import org.eclipse.jetty.util.log.Log;
43 import org.eclipse.jetty.util.log.Logger;
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 public class GzipHandler extends HandlerWrapper
63 {
64 private static final Logger LOG = Log.getLogger(GzipHandler.class);
65
66 final protected Set<String> _mimeTypes=new HashSet<>();
67 protected boolean _excludeMimeTypes=false;
68 protected Set<String> _excludedUA;
69 protected int _bufferSize = 8192;
70 protected int _minGzipSize = 256;
71 protected String _vary = "Accept-Encoding, User-Agent";
72
73
74
75
76
77 public GzipHandler()
78 {
79 }
80
81
82
83
84
85
86
87 public Set<String> getMimeTypes()
88 {
89 return _mimeTypes;
90 }
91
92
93
94
95
96
97
98
99 public void setMimeTypes(Set<String> mimeTypes)
100 {
101 _excludeMimeTypes=false;
102 _mimeTypes.clear();
103 _mimeTypes.addAll(mimeTypes);
104 }
105
106
107
108
109
110
111
112
113 public void setMimeTypes(String mimeTypes)
114 {
115 if (mimeTypes != null)
116 {
117 _excludeMimeTypes=false;
118 _mimeTypes.clear();
119 StringTokenizer tok = new StringTokenizer(mimeTypes,",",false);
120 while (tok.hasMoreTokens())
121 {
122 _mimeTypes.add(tok.nextToken());
123 }
124 }
125 }
126
127
128
129
130
131 public void setExcludeMimeTypes(boolean exclude)
132 {
133 _excludeMimeTypes=exclude;
134 }
135
136
137
138
139
140
141
142 public Set<String> getExcluded()
143 {
144 return _excludedUA;
145 }
146
147
148
149
150
151
152
153
154 public void setExcluded(Set<String> excluded)
155 {
156 _excludedUA = excluded;
157 }
158
159
160
161
162
163
164
165
166 public void setExcluded(String excluded)
167 {
168 if (excluded != null)
169 {
170 _excludedUA = new HashSet<String>();
171 StringTokenizer tok = new StringTokenizer(excluded,",",false);
172 while (tok.hasMoreTokens())
173 _excludedUA.add(tok.nextToken());
174 }
175 }
176
177
178
179
180
181 public String getVary()
182 {
183 return _vary;
184 }
185
186
187
188
189
190
191
192
193
194
195
196
197 public void setVary(String vary)
198 {
199 _vary = vary;
200 }
201
202
203
204
205
206
207
208 public int getBufferSize()
209 {
210 return _bufferSize;
211 }
212
213
214
215
216
217
218
219
220 public void setBufferSize(int bufferSize)
221 {
222 _bufferSize = bufferSize;
223 }
224
225
226
227
228
229
230
231 public int getMinGzipSize()
232 {
233 return _minGzipSize;
234 }
235
236
237
238
239
240
241
242
243 public void setMinGzipSize(int minGzipSize)
244 {
245 _minGzipSize = minGzipSize;
246 }
247
248
249 @Override
250 protected void doStart() throws Exception
251 {
252 if (_mimeTypes.size()==0)
253 {
254 for (String type:MimeTypes.getKnownMimeTypes())
255 {
256 if (type.startsWith("image/")||
257 type.startsWith("audio/")||
258 type.startsWith("video/"))
259 _mimeTypes.add(type);
260 _mimeTypes.add("application/compress");
261 _mimeTypes.add("application/zip");
262 _mimeTypes.add("application/gzip");
263 }
264 }
265 super.doStart();
266 }
267
268
269
270
271
272 @Override
273 public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
274 {
275 if (_handler!=null && isStarted())
276 {
277 String ae = request.getHeader("accept-encoding");
278 if (ae != null && ae.indexOf("gzip")>=0 && !response.containsHeader("Content-Encoding")
279 && !HttpMethod.HEAD.is(request.getMethod()))
280 {
281 if (_excludedUA!=null)
282 {
283 String ua = request.getHeader("User-Agent");
284 if (_excludedUA.contains(ua))
285 {
286 _handler.handle(target,baseRequest, request, response);
287 return;
288 }
289 }
290
291 final CompressedResponseWrapper wrappedResponse = newGzipResponseWrapper(request,response);
292
293 boolean exceptional=true;
294 try
295 {
296 _handler.handle(target, baseRequest, request, wrappedResponse);
297 exceptional=false;
298 }
299 finally
300 {
301 if (request.isAsyncStarted())
302 {
303 request.getAsyncContext().addListener(new AsyncListener()
304 {
305
306 @Override
307 public void onTimeout(AsyncEvent event) throws IOException
308 {
309 }
310
311 @Override
312 public void onStartAsync(AsyncEvent event) throws IOException
313 {
314 }
315
316 @Override
317 public void onError(AsyncEvent event) throws IOException
318 {
319 }
320
321 @Override
322 public void onComplete(AsyncEvent event) throws IOException
323 {
324 try
325 {
326 wrappedResponse.finish();
327 }
328 catch(IOException e)
329 {
330 LOG.warn(e);
331 }
332 }
333 });
334 }
335 else if (exceptional && !response.isCommitted())
336 {
337 wrappedResponse.resetBuffer();
338 wrappedResponse.noCompression();
339 }
340 else
341 wrappedResponse.finish();
342 }
343 }
344 else
345 {
346 _handler.handle(target,baseRequest, request, response);
347 }
348 }
349 }
350
351
352
353
354
355
356
357
358 protected CompressedResponseWrapper newGzipResponseWrapper(HttpServletRequest request, HttpServletResponse response)
359 {
360 return new CompressedResponseWrapper(request,response)
361 {
362 {
363 super.setMimeTypes(GzipHandler.this._mimeTypes,GzipHandler.this._excludeMimeTypes);
364 super.setBufferSize(GzipHandler.this._bufferSize);
365 super.setMinCompressSize(GzipHandler.this._minGzipSize);
366 }
367
368 @Override
369 protected AbstractCompressedStream newCompressedStream(HttpServletRequest request,HttpServletResponse response) throws IOException
370 {
371 return new AbstractCompressedStream("gzip",request,this,_vary)
372 {
373 @Override
374 protected DeflaterOutputStream createStream() throws IOException
375 {
376 return new GZIPOutputStream(_response.getOutputStream(),_bufferSize);
377 }
378 };
379 }
380
381 @Override
382 protected PrintWriter newWriter(OutputStream out,String encoding) throws UnsupportedEncodingException
383 {
384 return GzipHandler.this.newWriter(out,encoding);
385 }
386 };
387 }
388
389
390
391
392
393
394
395
396
397 protected PrintWriter newWriter(OutputStream out,String encoding) throws UnsupportedEncodingException
398 {
399 return encoding==null?new PrintWriter(out):new PrintWriter(new OutputStreamWriter(out,encoding));
400 }
401 }