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