1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jboss.netty.example.http.upload;
17
18 import java.io.IOException;
19 import java.net.URI;
20 import java.util.Collections;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Map.Entry;
24 import java.util.Set;
25
26 import org.jboss.netty.buffer.ChannelBuffer;
27 import org.jboss.netty.buffer.ChannelBuffers;
28 import org.jboss.netty.channel.Channel;
29 import org.jboss.netty.channel.ChannelFuture;
30 import org.jboss.netty.channel.ChannelFutureListener;
31 import org.jboss.netty.channel.ChannelHandlerContext;
32 import org.jboss.netty.channel.ChannelStateEvent;
33 import org.jboss.netty.channel.Channels;
34 import org.jboss.netty.channel.ExceptionEvent;
35 import org.jboss.netty.channel.MessageEvent;
36 import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
37 import org.jboss.netty.handler.codec.http.Cookie;
38 import org.jboss.netty.handler.codec.http.CookieDecoder;
39 import org.jboss.netty.handler.codec.http.CookieEncoder;
40 import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
41 import org.jboss.netty.handler.codec.http.HttpChunk;
42 import org.jboss.netty.handler.codec.http.HttpHeaders;
43 import org.jboss.netty.handler.codec.http.HttpRequest;
44 import org.jboss.netty.handler.codec.http.HttpResponse;
45 import org.jboss.netty.handler.codec.http.HttpResponseStatus;
46 import org.jboss.netty.handler.codec.http.HttpVersion;
47 import org.jboss.netty.handler.codec.http.QueryStringDecoder;
48 import org.jboss.netty.handler.codec.http.multipart.Attribute;
49 import org.jboss.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
50 import org.jboss.netty.handler.codec.http.multipart.DiskAttribute;
51 import org.jboss.netty.handler.codec.http.multipart.DiskFileUpload;
52 import org.jboss.netty.handler.codec.http.multipart.FileUpload;
53 import org.jboss.netty.handler.codec.http.multipart.HttpDataFactory;
54 import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
55 import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException;
56 import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException;
57 import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.IncompatibleDataDecoderException;
58 import org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.NotEnoughDataDecoderException;
59 import org.jboss.netty.handler.codec.http.multipart.InterfaceHttpData;
60 import org.jboss.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType;
61 import org.jboss.netty.logging.InternalLogger;
62 import org.jboss.netty.logging.InternalLoggerFactory;
63 import org.jboss.netty.util.CharsetUtil;
64
65 public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler {
66
67 private static final InternalLogger logger =
68 InternalLoggerFactory.getInstance(HttpUploadServerHandler.class);
69
70 private HttpRequest request;
71
72 private boolean readingChunks;
73
74 private final StringBuilder responseContent = new StringBuilder();
75
76 private static final HttpDataFactory factory = new DefaultHttpDataFactory(
77 DefaultHttpDataFactory.MINSIZE);
78
79 private HttpPostRequestDecoder decoder;
80 static {
81 DiskFileUpload.deleteOnExitTemporaryFile = true;
82
83
84 DiskFileUpload.baseDirectory = null;
85 DiskAttribute.deleteOnExitTemporaryFile = true;
86
87 DiskAttribute.baseDirectory = null;
88 }
89
90 @Override
91 public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
92 throws Exception {
93 if (decoder != null) {
94 decoder.cleanFiles();
95 }
96 }
97
98 @Override
99 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
100 if (!readingChunks) {
101
102 if (decoder != null) {
103 decoder.cleanFiles();
104 decoder = null;
105 }
106
107 HttpRequest request = this.request = (HttpRequest) e.getMessage();
108 URI uri = new URI(request.getUri());
109 if (!uri.getPath().startsWith("/form")) {
110
111 writeMenu(e);
112 return;
113 }
114 responseContent.setLength(0);
115 responseContent.append("WELCOME TO THE WILD WILD WEB SERVER\r\n");
116 responseContent.append("===================================\r\n");
117
118 responseContent.append("VERSION: " +
119 request.getProtocolVersion().getText() + "\r\n");
120
121 responseContent.append("REQUEST_URI: " + request.getUri() +
122 "\r\n\r\n");
123 responseContent.append("\r\n\r\n");
124
125
126 List<Entry<String, String>> headers = request.getHeaders();
127 for (Entry<String, String> entry: headers) {
128 responseContent.append("HEADER: " + entry.getKey() + "=" +
129 entry.getValue() + "\r\n");
130 }
131 responseContent.append("\r\n\r\n");
132
133
134 Set<Cookie> cookies;
135 String value = request.getHeader(HttpHeaders.Names.COOKIE);
136 if (value == null) {
137 cookies = Collections.emptySet();
138 } else {
139 CookieDecoder decoder = new CookieDecoder();
140 cookies = decoder.decode(value);
141 }
142 for (Cookie cookie: cookies) {
143 responseContent.append("COOKIE: " + cookie.toString() + "\r\n");
144 }
145 responseContent.append("\r\n\r\n");
146
147 QueryStringDecoder decoderQuery = new QueryStringDecoder(request
148 .getUri());
149 Map<String, List<String>> uriAttributes = decoderQuery
150 .getParameters();
151 for (String key: uriAttributes.keySet()) {
152 for (String valuen: uriAttributes.get(key)) {
153 responseContent.append("URI: " + key + "=" + valuen +
154 "\r\n");
155 }
156 }
157 responseContent.append("\r\n\r\n");
158
159
160 try {
161 decoder = new HttpPostRequestDecoder(factory, request);
162 } catch (ErrorDataDecoderException e1) {
163 e1.printStackTrace();
164 responseContent.append(e1.getMessage());
165 writeResponse(e.getChannel());
166 Channels.close(e.getChannel());
167 return;
168 } catch (IncompatibleDataDecoderException e1) {
169
170
171 responseContent.append(e1.getMessage());
172 responseContent.append("\r\n\r\nEND OF GET CONTENT\r\n");
173 writeResponse(e.getChannel());
174 return;
175 }
176
177 responseContent.append("Is Chunked: " + request.isChunked() +
178 "\r\n");
179 responseContent.append("IsMultipart: " + decoder.isMultipart() +
180 "\r\n");
181 if (request.isChunked()) {
182
183 responseContent.append("Chunks: ");
184 readingChunks = true;
185 } else {
186
187 readHttpDataAllReceive(e.getChannel());
188 responseContent
189 .append("\r\n\r\nEND OF NOT CHUNKED CONTENT\r\n");
190 writeResponse(e.getChannel());
191 }
192 } else {
193
194 HttpChunk chunk = (HttpChunk) e.getMessage();
195 try {
196 decoder.offer(chunk);
197 } catch (ErrorDataDecoderException e1) {
198 e1.printStackTrace();
199 responseContent.append(e1.getMessage());
200 writeResponse(e.getChannel());
201 Channels.close(e.getChannel());
202 return;
203 }
204 responseContent.append("o");
205
206 readHttpDataChunkByChunk(e.getChannel());
207
208 if (chunk.isLast()) {
209 readHttpDataAllReceive(e.getChannel());
210 writeResponse(e.getChannel());
211 readingChunks = false;
212 }
213 }
214 }
215
216
217
218
219
220
221 private void readHttpDataAllReceive(Channel channel) {
222 List<InterfaceHttpData> datas = null;
223 try {
224 datas = decoder.getBodyHttpDatas();
225 } catch (NotEnoughDataDecoderException e1) {
226
227 e1.printStackTrace();
228 responseContent.append(e1.getMessage());
229 writeResponse(channel);
230 Channels.close(channel);
231 return;
232 }
233 for (InterfaceHttpData data: datas) {
234 writeHttpData(data);
235 }
236 responseContent.append("\r\n\r\nEND OF CONTENT AT FINAL END\r\n");
237 }
238
239
240
241
242
243
244
245 private void readHttpDataChunkByChunk(Channel channel) {
246 try {
247 while (decoder.hasNext()) {
248 InterfaceHttpData data = decoder.next();
249 if (data != null) {
250
251 writeHttpData(data);
252 }
253 }
254 } catch (EndOfDataDecoderException e1) {
255
256 responseContent
257 .append("\r\n\r\nEND OF CONTENT CHUNK BY CHUNK\r\n\r\n");
258 }
259 }
260
261 private void writeHttpData(InterfaceHttpData data) {
262 if (data.getHttpDataType() == HttpDataType.Attribute) {
263 Attribute attribute = (Attribute) data;
264 String value;
265 try {
266 value = attribute.getValue();
267 } catch (IOException e1) {
268
269 e1.printStackTrace();
270 responseContent.append("\r\nBODY Attribute: " +
271 attribute.getHttpDataType().name() + ": " +
272 attribute.getName() + " Error while reading value: " +
273 e1.getMessage() + "\r\n");
274 return;
275 }
276 if (value.length() > 100) {
277 responseContent.append("\r\nBODY Attribute: " +
278 attribute.getHttpDataType().name() + ": " +
279 attribute.getName() + " data too long\r\n");
280 } else {
281 responseContent.append("\r\nBODY Attribute: " +
282 attribute.getHttpDataType().name() + ": " +
283 attribute.toString() + "\r\n");
284 }
285 } else {
286 responseContent.append("\r\nBODY FileUpload: " +
287 data.getHttpDataType().name() + ": " + data.toString() +
288 "\r\n");
289 if (data.getHttpDataType() == HttpDataType.FileUpload) {
290 FileUpload fileUpload = (FileUpload) data;
291 if (fileUpload.isCompleted()) {
292 if (fileUpload.length() < 10000) {
293 responseContent.append("\tContent of file\r\n");
294 try {
295 responseContent
296 .append(((FileUpload) data)
297 .getString(((FileUpload) data)
298 .getCharset()));
299 } catch (IOException e1) {
300
301 e1.printStackTrace();
302 }
303 responseContent.append("\r\n");
304 } else {
305 responseContent
306 .append("\tFile too long to be printed out:" +
307 fileUpload.length() + "\r\n");
308 }
309
310
311
312
313
314
315 } else {
316 responseContent
317 .append("\tFile to be continued but should not!\r\n");
318 }
319 }
320 }
321 }
322
323 private void writeResponse(Channel channel) {
324
325 ChannelBuffer buf = ChannelBuffers.copiedBuffer(responseContent
326 .toString(), CharsetUtil.UTF_8);
327 responseContent.setLength(0);
328
329
330 boolean close = HttpHeaders.Values.CLOSE.equalsIgnoreCase(request
331 .getHeader(HttpHeaders.Names.CONNECTION)) ||
332 request.getProtocolVersion().equals(HttpVersion.HTTP_1_0) &&
333 !HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(request
334 .getHeader(HttpHeaders.Names.CONNECTION));
335
336
337 HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
338 HttpResponseStatus.OK);
339 response.setContent(buf);
340 response.setHeader(HttpHeaders.Names.CONTENT_TYPE,
341 "text/plain; charset=UTF-8");
342
343 if (!close) {
344
345
346 response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String
347 .valueOf(buf.readableBytes()));
348 }
349
350 Set<Cookie> cookies;
351 String value = request.getHeader(HttpHeaders.Names.COOKIE);
352 if (value == null) {
353 cookies = Collections.emptySet();
354 } else {
355 CookieDecoder decoder = new CookieDecoder();
356 cookies = decoder.decode(value);
357 }
358 if (!cookies.isEmpty()) {
359
360 CookieEncoder cookieEncoder = new CookieEncoder(true);
361 for (Cookie cookie: cookies) {
362 cookieEncoder.addCookie(cookie);
363 response.addHeader(HttpHeaders.Names.SET_COOKIE, cookieEncoder
364 .encode());
365 cookieEncoder = new CookieEncoder(true);
366 }
367 }
368
369 ChannelFuture future = channel.write(response);
370
371 if (close) {
372 future.addListener(ChannelFutureListener.CLOSE);
373 }
374 }
375
376 private void writeMenu(MessageEvent e) {
377
378
379 responseContent.setLength(0);
380
381
382 responseContent.append("<html>");
383 responseContent.append("<head>");
384 responseContent.append("<title>Netty Test Form</title>\r\n");
385 responseContent.append("</head>\r\n");
386 responseContent
387 .append("<body bgcolor=white><style>td{font-size: 12pt;}</style>");
388
389 responseContent.append("<table border=\"0\">");
390 responseContent.append("<tr>");
391 responseContent.append("<td>");
392 responseContent.append("<h1>Netty Test Form</h1>");
393 responseContent.append("Choose one FORM");
394 responseContent.append("</td>");
395 responseContent.append("</tr>");
396 responseContent.append("</table>\r\n");
397
398
399 responseContent
400 .append("<CENTER>GET FORM<HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>");
401 responseContent.append("<FORM ACTION=\"/formget\" METHOD=\"GET\">");
402 responseContent
403 .append("<input type=hidden name=getform value=\"GET\">");
404 responseContent.append("<table border=\"0\">");
405 responseContent
406 .append("<tr><td>Fill with value: <br> <input type=text name=\"info\" size=10></td></tr>");
407 responseContent
408 .append("<tr><td>Fill with value: <br> <input type=text name=\"secondinfo\" size=20>");
409 responseContent
410 .append("<tr><td>Fill with value: <br> <textarea name=\"thirdinfo\" cols=40 rows=10></textarea>");
411 responseContent.append("</td></tr>");
412 responseContent
413 .append("<tr><td><INPUT TYPE=\"submit\" NAME=\"Send\" VALUE=\"Send\"></INPUT></td>");
414 responseContent
415 .append("<td><INPUT TYPE=\"reset\" NAME=\"Clear\" VALUE=\"Clear\" ></INPUT></td></tr>");
416 responseContent.append("</table></FORM>\r\n");
417 responseContent
418 .append("<CENTER><HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>");
419
420
421 responseContent
422 .append("<CENTER>POST FORM<HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>");
423 responseContent.append("<FORM ACTION=\"/formpost\" METHOD=\"POST\">");
424 responseContent
425 .append("<input type=hidden name=getform value=\"POST\">");
426 responseContent.append("<table border=\"0\">");
427 responseContent
428 .append("<tr><td>Fill with value: <br> <input type=text name=\"info\" size=10></td></tr>");
429 responseContent
430 .append("<tr><td>Fill with value: <br> <input type=text name=\"secondinfo\" size=20>");
431 responseContent
432 .append("<tr><td>Fill with value: <br> <textarea name=\"thirdinfo\" cols=40 rows=10></textarea>");
433 responseContent
434 .append("<tr><td>Fill with file (only file name will be transmitted): <br> " +
435 "<input type=file name=\"myfile\">");
436 responseContent.append("</td></tr>");
437 responseContent
438 .append("<tr><td><INPUT TYPE=\"submit\" NAME=\"Send\" VALUE=\"Send\"></INPUT></td>");
439 responseContent
440 .append("<td><INPUT TYPE=\"reset\" NAME=\"Clear\" VALUE=\"Clear\" ></INPUT></td></tr>");
441 responseContent.append("</table></FORM>\r\n");
442 responseContent
443 .append("<CENTER><HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>");
444
445
446 responseContent
447 .append("<CENTER>POST MULTIPART FORM<HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>");
448 responseContent
449 .append("<FORM ACTION=\"/formpostmultipart\" ENCTYPE=\"multipart/form-data\" METHOD=\"POST\">");
450 responseContent
451 .append("<input type=hidden name=getform value=\"POST\">");
452 responseContent.append("<table border=\"0\">");
453 responseContent
454 .append("<tr><td>Fill with value: <br> <input type=text name=\"info\" size=10></td></tr>");
455 responseContent
456 .append("<tr><td>Fill with value: <br> <input type=text name=\"secondinfo\" size=20>");
457 responseContent
458 .append("<tr><td>Fill with value: <br> <textarea name=\"thirdinfo\" cols=40 rows=10></textarea>");
459 responseContent
460 .append("<tr><td>Fill with file: <br> <input type=file name=\"myfile\">");
461 responseContent.append("</td></tr>");
462 responseContent
463 .append("<tr><td><INPUT TYPE=\"submit\" NAME=\"Send\" VALUE=\"Send\"></INPUT></td>");
464 responseContent
465 .append("<td><INPUT TYPE=\"reset\" NAME=\"Clear\" VALUE=\"Clear\" ></INPUT></td></tr>");
466 responseContent.append("</table></FORM>\r\n");
467 responseContent
468 .append("<CENTER><HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>");
469
470 responseContent.append("</body>");
471 responseContent.append("</html>");
472
473 ChannelBuffer buf = ChannelBuffers.copiedBuffer(responseContent
474 .toString(), CharsetUtil.UTF_8);
475
476 HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
477 HttpResponseStatus.OK);
478 response.setContent(buf);
479 response.setHeader(HttpHeaders.Names.CONTENT_TYPE,
480 "text/html; charset=UTF-8");
481 response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(buf
482 .readableBytes()));
483
484 e.getChannel().write(response);
485 }
486
487 @Override
488 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
489 throws Exception {
490 logger.error(responseContent.toString(), e.getCause());
491 e.getChannel().close();
492 }
493 }