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