1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jboss.netty.example.http.file;
17
18 import static org.jboss.netty.handler.codec.http.HttpHeaders.*;
19 import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.*;
20 import static org.jboss.netty.handler.codec.http.HttpMethod.*;
21 import static org.jboss.netty.handler.codec.http.HttpResponseStatus.*;
22 import static org.jboss.netty.handler.codec.http.HttpVersion.*;
23
24 import java.io.File;
25 import java.io.FileNotFoundException;
26 import java.io.RandomAccessFile;
27 import java.io.UnsupportedEncodingException;
28 import java.net.URLDecoder;
29
30 import org.jboss.netty.buffer.ChannelBuffers;
31 import org.jboss.netty.channel.Channel;
32 import org.jboss.netty.channel.ChannelFuture;
33 import org.jboss.netty.channel.ChannelFutureListener;
34 import org.jboss.netty.channel.ChannelFutureProgressListener;
35 import org.jboss.netty.channel.ChannelHandlerContext;
36 import org.jboss.netty.channel.DefaultFileRegion;
37 import org.jboss.netty.channel.ExceptionEvent;
38 import org.jboss.netty.channel.FileRegion;
39 import org.jboss.netty.channel.MessageEvent;
40 import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
41 import org.jboss.netty.handler.codec.frame.TooLongFrameException;
42 import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
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.ssl.SslHandler;
47 import org.jboss.netty.handler.stream.ChunkedFile;
48 import org.jboss.netty.util.CharsetUtil;
49
50
51
52
53
54 public class HttpStaticFileServerHandler extends SimpleChannelUpstreamHandler {
55
56 @Override
57 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
58 HttpRequest request = (HttpRequest) e.getMessage();
59 if (request.getMethod() != GET) {
60 sendError(ctx, METHOD_NOT_ALLOWED);
61 return;
62 }
63
64 final String path = sanitizeUri(request.getUri());
65 if (path == null) {
66 sendError(ctx, FORBIDDEN);
67 return;
68 }
69
70 File file = new File(path);
71 if (file.isHidden() || !file.exists()) {
72 sendError(ctx, NOT_FOUND);
73 return;
74 }
75 if (!file.isFile()) {
76 sendError(ctx, FORBIDDEN);
77 return;
78 }
79
80 RandomAccessFile raf;
81 try {
82 raf = new RandomAccessFile(file, "r");
83 } catch (FileNotFoundException fnfe) {
84 sendError(ctx, NOT_FOUND);
85 return;
86 }
87 long fileLength = raf.length();
88
89 HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
90 setContentLength(response, fileLength);
91
92 Channel ch = e.getChannel();
93
94
95 ch.write(response);
96
97
98 ChannelFuture writeFuture;
99 if (ch.getPipeline().get(SslHandler.class) != null) {
100
101 writeFuture = ch.write(new ChunkedFile(raf, 0, fileLength, 8192));
102 } else {
103
104 final FileRegion region =
105 new DefaultFileRegion(raf.getChannel(), 0, fileLength);
106 writeFuture = ch.write(region);
107 writeFuture.addListener(new ChannelFutureProgressListener() {
108 public void operationComplete(ChannelFuture future) {
109 region.releaseExternalResources();
110 }
111
112 public void operationProgressed(
113 ChannelFuture future, long amount, long current, long total) {
114 System.out.printf("%s: %d / %d (+%d)%n", path, current, total, amount);
115 }
116 });
117 }
118
119
120 if (!isKeepAlive(request)) {
121
122 writeFuture.addListener(ChannelFutureListener.CLOSE);
123 }
124 }
125
126 @Override
127 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
128 throws Exception {
129 Channel ch = e.getChannel();
130 Throwable cause = e.getCause();
131 if (cause instanceof TooLongFrameException) {
132 sendError(ctx, BAD_REQUEST);
133 return;
134 }
135
136 cause.printStackTrace();
137 if (ch.isConnected()) {
138 sendError(ctx, INTERNAL_SERVER_ERROR);
139 }
140 }
141
142 private String sanitizeUri(String uri) {
143
144 try {
145 uri = URLDecoder.decode(uri, "UTF-8");
146 } catch (UnsupportedEncodingException e) {
147 try {
148 uri = URLDecoder.decode(uri, "ISO-8859-1");
149 } catch (UnsupportedEncodingException e1) {
150 throw new Error();
151 }
152 }
153
154
155 uri = uri.replace('/', File.separatorChar);
156
157
158
159 if (uri.contains(File.separator + ".") ||
160 uri.contains("." + File.separator) ||
161 uri.startsWith(".") || uri.endsWith(".")) {
162 return null;
163 }
164
165
166 return System.getProperty("user.dir") + File.separator + uri;
167 }
168
169 private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
170 HttpResponse response = new DefaultHttpResponse(HTTP_1_1, status);
171 response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
172 response.setContent(ChannelBuffers.copiedBuffer(
173 "Failure: " + status.toString() + "\r\n",
174 CharsetUtil.UTF_8));
175
176
177 ctx.getChannel().write(response).addListener(ChannelFutureListener.CLOSE);
178 }
179 }