From 2660e040f5dd5e77e613ffe2caa22c7c22bed20b Mon Sep 17 00:00:00 2001 From: Brian Guarraci Date: Sun, 14 Aug 2011 14:46:21 -0700 Subject: [PATCH 1/3] use mapped bytebuffer for file reads --- .../netty/http/FileServerHandler.java | 47 ++++++++----------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/src/main/java/se/cgbystrom/netty/http/FileServerHandler.java b/src/main/java/se/cgbystrom/netty/http/FileServerHandler.java index a528a5d..a85a1f4 100644 --- a/src/main/java/se/cgbystrom/netty/http/FileServerHandler.java +++ b/src/main/java/se/cgbystrom/netty/http/FileServerHandler.java @@ -6,9 +6,17 @@ import static org.jboss.netty.handler.codec.http.HttpResponseStatus.*; import static org.jboss.netty.handler.codec.http.HttpVersion.*; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.buffer.ChannelBuffer; -import org.jboss.netty.channel.*; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelFutureListener; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ExceptionEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; import org.jboss.netty.handler.codec.frame.TooLongFrameException; import org.jboss.netty.handler.codec.http.DefaultHttpResponse; import org.jboss.netty.handler.codec.http.HttpHeaders; @@ -16,8 +24,6 @@ import org.jboss.netty.handler.codec.http.HttpResponse; import org.jboss.netty.handler.codec.http.HttpResponseStatus; import org.jboss.netty.handler.codec.http.QueryStringDecoder; -import org.jboss.netty.handler.ssl.SslHandler; -import org.jboss.netty.handler.stream.ChunkedFile; import org.jboss.netty.util.CharsetUtil; import javax.activation.MimetypesFileTypeMap; @@ -30,7 +36,8 @@ * If you wish to customize the error message, please sub-class and override sendError(). * Based on Trustin Lee's original file serving example */ -public class FileServerHandler extends SimpleChannelUpstreamHandler { +public class FileServerHandler extends SimpleChannelUpstreamHandler +{ private String rootPath; private String stripFromUri; private int cacheMaxAge = -1; @@ -110,36 +117,22 @@ public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Ex } private ChannelBuffer getFileContent(String path) { - InputStream is; try { + File file; + if (fromClasspath) { - is = this.getClass().getResourceAsStream(rootPath + path); + file = new File(this.getClass().getResource(rootPath + path).getFile()); } else { - is = new FileInputStream(rootPath + path); - } - - if (is == null) { - return null; + file = new File(rootPath + path); } - - final int maxSize = 512 * 1024; - ByteArrayOutputStream out = new ByteArrayOutputStream(maxSize); - byte[] bytes = new byte[maxSize]; - - while (true) { - int r = is.read(bytes); - if (r == -1) break; - out.write(bytes, 0, r); - } - - ChannelBuffer cb = ChannelBuffers.copiedBuffer(out.toByteArray()); - out.close(); - is.close(); - return cb; + FileChannel fc = new RandomAccessFile(file, "r").getChannel(); + ByteBuffer roBuf = fc.map(FileChannel.MapMode.READ_ONLY, 0, (int)fc.size()); + return ChannelBuffers.copiedBuffer(roBuf); } catch (IOException e) { - return null; + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } + return null; } @Override From f06ddbfb6961b8e6c090e2763328f6eac9e5df25 Mon Sep 17 00:00:00 2001 From: Brian Guarraci Date: Sun, 14 Aug 2011 14:49:09 -0700 Subject: [PATCH 2/3] remove comment --- src/main/java/se/cgbystrom/netty/http/FileServerHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/se/cgbystrom/netty/http/FileServerHandler.java b/src/main/java/se/cgbystrom/netty/http/FileServerHandler.java index a85a1f4..231ef23 100644 --- a/src/main/java/se/cgbystrom/netty/http/FileServerHandler.java +++ b/src/main/java/se/cgbystrom/netty/http/FileServerHandler.java @@ -130,7 +130,7 @@ private ChannelBuffer getFileContent(String path) { ByteBuffer roBuf = fc.map(FileChannel.MapMode.READ_ONLY, 0, (int)fc.size()); return ChannelBuffers.copiedBuffer(roBuf); } catch (IOException e) { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + e.printStackTrace(); } return null; } From 70dd7ad629de24fd60957a3c70b526df3767b327 Mon Sep 17 00:00:00 2001 From: Brian Guarraci Date: Mon, 15 Aug 2011 12:15:58 -0700 Subject: [PATCH 3/3] rework CachableHttpResponse to allow for disposing of underlying resources --- .../netty/http/CachableHttpResponse.java | 27 +++++++++++ .../se/cgbystrom/netty/http/CacheHandler.java | 23 +++++++-- .../netty/http/FileServerHandler.java | 47 +++++++++++++++---- 3 files changed, 84 insertions(+), 13 deletions(-) diff --git a/src/main/java/se/cgbystrom/netty/http/CachableHttpResponse.java b/src/main/java/se/cgbystrom/netty/http/CachableHttpResponse.java index 6997b00..5341b65 100644 --- a/src/main/java/se/cgbystrom/netty/http/CachableHttpResponse.java +++ b/src/main/java/se/cgbystrom/netty/http/CachableHttpResponse.java @@ -1,5 +1,7 @@ package se.cgbystrom.netty.http; +import java.io.IOException; +import java.nio.channels.FileChannel; import org.jboss.netty.handler.codec.http.DefaultHttpResponse; import org.jboss.netty.handler.codec.http.HttpResponseStatus; import org.jboss.netty.handler.codec.http.HttpVersion; @@ -7,6 +9,7 @@ public class CachableHttpResponse extends DefaultHttpResponse { private String requestUri; private int cacheMaxAge; + private FileChannel fileChannel; public CachableHttpResponse(HttpVersion version, HttpResponseStatus status) { super(version, status); @@ -27,4 +30,28 @@ public int getCacheMaxAge() { public void setCacheMaxAge(int cacheMaxAge) { this.cacheMaxAge = cacheMaxAge; } + + public void setBackingFileChannel(FileChannel fileChannel) { + this.fileChannel = fileChannel; + } + + public FileChannel getFileChannel() + { + return fileChannel; + } + + public void dispose() + { + if (fileChannel != null) + { + try + { + fileChannel.close(); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + } } diff --git a/src/main/java/se/cgbystrom/netty/http/CacheHandler.java b/src/main/java/se/cgbystrom/netty/http/CacheHandler.java index 7d0a8e5..f481c62 100644 --- a/src/main/java/se/cgbystrom/netty/http/CacheHandler.java +++ b/src/main/java/se/cgbystrom/netty/http/CacheHandler.java @@ -1,5 +1,6 @@ package se.cgbystrom.netty.http; +import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.*; import org.jboss.netty.handler.codec.http.DefaultHttpResponse; @@ -28,13 +29,25 @@ public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exc HttpRequest request = (HttpRequest)((MessageEvent)e).getMessage(); CacheEntry ce = cache.get(request.getUri()); - if (ce != null && ce.expires > System.currentTimeMillis()) { - ChannelFuture f = e.getChannel().write(ce.content); - f.addListener(ChannelFutureListener.CLOSE); - if (!HttpHeaders.isKeepAlive(request)) { + if (ce != null) { + if (ce.expires > System.currentTimeMillis()) + { + ChannelFuture f = e.getChannel().write(ce.content); f.addListener(ChannelFutureListener.CLOSE); + if (!HttpHeaders.isKeepAlive(request)) { + f.addListener(ChannelFutureListener.CLOSE); + } + return; + } + else + { + if (ce.content instanceof CachableHttpResponse) + { + CachableHttpResponse r = (CachableHttpResponse)ce.content; + r.dispose(); + } + cache.remove(ce); } - return; } } diff --git a/src/main/java/se/cgbystrom/netty/http/FileServerHandler.java b/src/main/java/se/cgbystrom/netty/http/FileServerHandler.java index 231ef23..228df80 100644 --- a/src/main/java/se/cgbystrom/netty/http/FileServerHandler.java +++ b/src/main/java/se/cgbystrom/netty/http/FileServerHandler.java @@ -92,8 +92,8 @@ public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Ex } - ChannelBuffer content = getFileContent(path); - if (content == null) { + FileContentInfo contentInfo = getFileContent(path); + if (contentInfo == null) { sendError(ctx, NOT_FOUND); return; } @@ -104,9 +104,10 @@ public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Ex response.setRequestUri(request.getUri()); response.setCacheMaxAge(cacheMaxAge); response.setHeader(HttpHeaders.Names.CONTENT_TYPE, contentType); - setContentLength(response, content.readableBytes()); + setContentLength(response, contentInfo.content.readableBytes()); - response.setContent(content); + response.setBackingFileChannel(contentInfo.fileChannel); + response.setContent(contentInfo.content); ChannelFuture writeFuture = e.getChannel().write(response); // Decide whether to close the connection or not. @@ -116,7 +117,21 @@ public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Ex } } - private ChannelBuffer getFileContent(String path) { + private class FileContentInfo + { + public ChannelBuffer content; + public FileChannel fileChannel; + public FileContentInfo(FileChannel fileChannel, ChannelBuffer content) + { + this.fileChannel = fileChannel; + this.content = content; + } + } + + private FileContentInfo getFileContent(String path) { + FileChannel fc = null; + FileContentInfo result = null; + try { File file; @@ -126,13 +141,29 @@ private ChannelBuffer getFileContent(String path) { file = new File(rootPath + path); } - FileChannel fc = new RandomAccessFile(file, "r").getChannel(); + fc = new RandomAccessFile(file, "r").getChannel(); ByteBuffer roBuf = fc.map(FileChannel.MapMode.READ_ONLY, 0, (int)fc.size()); - return ChannelBuffers.copiedBuffer(roBuf); + result = new FileContentInfo(fc, ChannelBuffers.wrappedBuffer(roBuf)); } catch (IOException e) { e.printStackTrace(); + } finally { + if (result == null) + { + if (fc != null) + { + try + { + fc.close(); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + } } - return null; + + return result; } @Override