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