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();
206
207 if (chunk.isLast()) {
208 readHttpDataAllReceive(e.getChannel());
209 writeResponse(e.getChannel());
210 readingChunks = false;
211 }
212 }
213 }
214
215
216
217
218 private void readHttpDataAllReceive(Channel channel) {
219 List<InterfaceHttpData> datas;
220 try {
221 datas = decoder.getBodyHttpDatas();
222 } catch (NotEnoughDataDecoderException e1) {
223
224 e1.printStackTrace();
225 responseContent.append(e1.getMessage());
226 writeResponse(channel);
227 Channels.close(channel);
228 return;
229 }
230 for (InterfaceHttpData data: datas) {
231 writeHttpData(data);
232 }
233 responseContent.append("\r\n\r\nEND OF CONTENT AT FINAL END\r\n");
234 }
235
236
237
238
239
240 private void readHttpDataChunkByChunk() {
241 try {
242 while (decoder.hasNext()) {
243 InterfaceHttpData data = decoder.next();
244 if (data != null) {
245
246 writeHttpData(data);
247 }
248 }
249 } catch (EndOfDataDecoderException e1) {
250
251 responseContent
252 .append("\r\n\r\nEND OF CONTENT CHUNK BY CHUNK\r\n\r\n");
253 }
254 }
255
256 private void writeHttpData(InterfaceHttpData data) {
257 if (data.getHttpDataType() == HttpDataType.Attribute) {
258 Attribute attribute = (Attribute) data;
259 String value;
260 try {
261 value = attribute.getValue();
262 } catch (IOException e1) {
263
264 e1.printStackTrace();
265 responseContent.append("\r\nBODY Attribute: " +
266 attribute.getHttpDataType().name() + ": " +
267 attribute.getName() + " Error while reading value: " +
268 e1.getMessage() + "\r\n");
269 return;
270 }
271 if (value.length() > 100) {
272 responseContent.append("\r\nBODY Attribute: " +
273 attribute.getHttpDataType().name() + ": " +
274 attribute.getName() + " data too long\r\n");
275 } else {
276 responseContent.append("\r\nBODY Attribute: " +
277 attribute.getHttpDataType().name() + ": " +
278 attribute.toString() + "\r\n");
279 }
280 } else {
281 responseContent.append("\r\nBODY FileUpload: " +
282 data.getHttpDataType().name() + ": " + data.toString() +
283 "\r\n");
284 if (data.getHttpDataType() == HttpDataType.FileUpload) {
285 FileUpload fileUpload = (FileUpload) data;
286 if (fileUpload.isCompleted()) {
287 if (fileUpload.length() < 10000) {
288 responseContent.append("\tContent of file\r\n");
289 try {
290 responseContent.append(fileUpload.getString(fileUpload.getCharset()));
291 } catch (IOException e1) {
292
293 e1.printStackTrace();
294 }
295 responseContent.append("\r\n");
296 } else {
297 responseContent
298 .append("\tFile too long to be printed out:" +
299 fileUpload.length() + "\r\n");
300 }
301
302
303
304
305
306
307 } else {
308 responseContent
309 .append("\tFile to be continued but should not!\r\n");
310 }
311 }
312 }
313 }
314
315 private void writeResponse(Channel channel) {
316
317 ChannelBuffer buf = ChannelBuffers.copiedBuffer(responseContent
318 .toString(), CharsetUtil.UTF_8);
319 responseContent.setLength(0);
320
321
322 boolean close = HttpHeaders.Values.CLOSE.equalsIgnoreCase(request
323 .getHeader(HttpHeaders.Names.CONNECTION)) ||
324 request.getProtocolVersion().equals(HttpVersion.HTTP_1_0) &&
325 !HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(request
326 .getHeader(HttpHeaders.Names.CONNECTION));
327
328
329 HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
330 HttpResponseStatus.OK);
331 response.setContent(buf);
332 response.setHeader(HttpHeaders.Names.CONTENT_TYPE,
333 "text/plain; charset=UTF-8");
334
335 if (!close) {
336
337
338 response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String
339 .valueOf(buf.readableBytes()));
340 }
341
342 Set<Cookie> cookies;
343 String value = request.getHeader(HttpHeaders.Names.COOKIE);
344 if (value == null) {
345 cookies = Collections.emptySet();
346 } else {
347 CookieDecoder decoder = new CookieDecoder();
348 cookies = decoder.decode(value);
349 }
350 if (!cookies.isEmpty()) {
351
352 CookieEncoder cookieEncoder = new CookieEncoder(true);
353 for (Cookie cookie: cookies) {
354 cookieEncoder.addCookie(cookie);
355 response.addHeader(HttpHeaders.Names.SET_COOKIE, cookieEncoder
356 .encode());
357 cookieEncoder = new CookieEncoder(true);
358 }
359 }
360
361 ChannelFuture future = channel.write(response);
362
363 if (close) {
364 future.addListener(ChannelFutureListener.CLOSE);
365 }
366 }
367
368 private void writeMenu(MessageEvent e) {
369
370
371 responseContent.setLength(0);
372
373
374 responseContent.append("<html>");
375 responseContent.append("<head>");
376 responseContent.append("<title>Netty Test Form</title>\r\n");
377 responseContent.append("</head>\r\n");
378 responseContent
379 .append("<body bgcolor=white><style>td{font-size: 12pt;}</style>");
380
381 responseContent.append("<table border=\"0\">");
382 responseContent.append("<tr>");
383 responseContent.append("<td>");
384 responseContent.append("<h1>Netty Test Form</h1>");
385 responseContent.append("Choose one FORM");
386 responseContent.append("</td>");
387 responseContent.append("</tr>");
388 responseContent.append("</table>\r\n");
389
390
391 responseContent
392 .append("<CENTER>GET FORM<HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>");
393 responseContent.append("<FORM ACTION=\"/formget\" METHOD=\"GET\">");
394 responseContent
395 .append("<input type=hidden name=getform value=\"GET\">");
396 responseContent.append("<table border=\"0\">");
397 responseContent
398 .append("<tr><td>Fill with value: <br> <input type=text name=\"info\" size=10></td></tr>");
399 responseContent
400 .append("<tr><td>Fill with value: <br> <input type=text name=\"secondinfo\" size=20>");
401 responseContent
402 .append("<tr><td>Fill with value: <br> <textarea name=\"thirdinfo\" cols=40 rows=10></textarea>");
403 responseContent.append("</td></tr>");
404 responseContent
405 .append("<tr><td><INPUT TYPE=\"submit\" NAME=\"Send\" VALUE=\"Send\"></INPUT></td>");
406 responseContent
407 .append("<td><INPUT TYPE=\"reset\" NAME=\"Clear\" VALUE=\"Clear\" ></INPUT></td></tr>");
408 responseContent.append("</table></FORM>\r\n");
409 responseContent
410 .append("<CENTER><HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>");
411
412
413 responseContent
414 .append("<CENTER>POST FORM<HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>");
415 responseContent.append("<FORM ACTION=\"/formpost\" METHOD=\"POST\">");
416 responseContent
417 .append("<input type=hidden name=getform value=\"POST\">");
418 responseContent.append("<table border=\"0\">");
419 responseContent
420 .append("<tr><td>Fill with value: <br> <input type=text name=\"info\" size=10></td></tr>");
421 responseContent
422 .append("<tr><td>Fill with value: <br> <input type=text name=\"secondinfo\" size=20>");
423 responseContent
424 .append("<tr><td>Fill with value: <br> <textarea name=\"thirdinfo\" cols=40 rows=10></textarea>");
425 responseContent
426 .append("<tr><td>Fill with file (only file name will be transmitted): <br> " +
427 "<input type=file name=\"myfile\">");
428 responseContent.append("</td></tr>");
429 responseContent
430 .append("<tr><td><INPUT TYPE=\"submit\" NAME=\"Send\" VALUE=\"Send\"></INPUT></td>");
431 responseContent
432 .append("<td><INPUT TYPE=\"reset\" NAME=\"Clear\" VALUE=\"Clear\" ></INPUT></td></tr>");
433 responseContent.append("</table></FORM>\r\n");
434 responseContent
435 .append("<CENTER><HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>");
436
437
438 responseContent
439 .append("<CENTER>POST MULTIPART FORM<HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>");
440 responseContent
441 .append("<FORM ACTION=\"/formpostmultipart\" ENCTYPE=\"multipart/form-data\" METHOD=\"POST\">");
442 responseContent
443 .append("<input type=hidden name=getform value=\"POST\">");
444 responseContent.append("<table border=\"0\">");
445 responseContent
446 .append("<tr><td>Fill with value: <br> <input type=text name=\"info\" size=10></td></tr>");
447 responseContent
448 .append("<tr><td>Fill with value: <br> <input type=text name=\"secondinfo\" size=20>");
449 responseContent
450 .append("<tr><td>Fill with value: <br> <textarea name=\"thirdinfo\" cols=40 rows=10></textarea>");
451 responseContent
452 .append("<tr><td>Fill with file: <br> <input type=file name=\"myfile\">");
453 responseContent.append("</td></tr>");
454 responseContent
455 .append("<tr><td><INPUT TYPE=\"submit\" NAME=\"Send\" VALUE=\"Send\"></INPUT></td>");
456 responseContent
457 .append("<td><INPUT TYPE=\"reset\" NAME=\"Clear\" VALUE=\"Clear\" ></INPUT></td></tr>");
458 responseContent.append("</table></FORM>\r\n");
459 responseContent
460 .append("<CENTER><HR WIDTH=\"75%\" NOSHADE color=\"blue\"></CENTER>");
461
462 responseContent.append("</body>");
463 responseContent.append("</html>");
464
465 ChannelBuffer buf = ChannelBuffers.copiedBuffer(responseContent
466 .toString(), CharsetUtil.UTF_8);
467
468 HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
469 HttpResponseStatus.OK);
470 response.setContent(buf);
471 response.setHeader(HttpHeaders.Names.CONTENT_TYPE,
472 "text/html; charset=UTF-8");
473 response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(buf
474 .readableBytes()));
475
476 e.getChannel().write(response);
477 }
478
479 @Override
480 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
481 throws Exception {
482 logger.error(responseContent.toString(), e.getCause());
483 e.getChannel().close();
484 }
485 }