Skip to content
This repository has been archived by the owner on May 6, 2020. It is now read-only.

memory mapped files #9

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/main/java/se/cgbystrom/netty/http/CachableHttpResponse.java
@@ -1,12 +1,15 @@
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;

public class CachableHttpResponse extends DefaultHttpResponse {
private String requestUri;
private int cacheMaxAge;
private FileChannel fileChannel;

public CachableHttpResponse(HttpVersion version, HttpResponseStatus status) {
super(version, status);
Expand All @@ -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();
}
}
}
}
23 changes: 18 additions & 5 deletions 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;
Expand Down Expand Up @@ -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;
}
}

Expand Down
88 changes: 56 additions & 32 deletions src/main/java/se/cgbystrom/netty/http/FileServerHandler.java
Expand Up @@ -6,18 +6,24 @@
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;
import org.jboss.netty.handler.codec.http.HttpRequest;
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;
Expand All @@ -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;
Expand Down Expand Up @@ -85,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;
}
Expand All @@ -97,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.
Expand All @@ -109,37 +117,53 @@ public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Ex
}
}

private ChannelBuffer getFileContent(String path) {
InputStream is;
try {
if (fromClasspath) {
is = this.getClass().getResourceAsStream(rootPath + path);
} else {
is = new FileInputStream(rootPath + path);
}
private class FileContentInfo
{
public ChannelBuffer content;
public FileChannel fileChannel;
public FileContentInfo(FileChannel fileChannel, ChannelBuffer content)
{
this.fileChannel = fileChannel;
this.content = content;
}
}

if (is == null) {
return null;
}

final int maxSize = 512 * 1024;
ByteArrayOutputStream out = new ByteArrayOutputStream(maxSize);
byte[] bytes = new byte[maxSize];
private FileContentInfo getFileContent(String path) {
FileChannel fc = null;
FileContentInfo result = null;

while (true) {
int r = is.read(bytes);
if (r == -1) break;
try {
File file;

out.write(bytes, 0, r);
if (fromClasspath) {
file = new File(this.getClass().getResource(rootPath + path).getFile());
} else {
file = new File(rootPath + path);
}

ChannelBuffer cb = ChannelBuffers.copiedBuffer(out.toByteArray());
out.close();
is.close();
return cb;
fc = new RandomAccessFile(file, "r").getChannel();
ByteBuffer roBuf = fc.map(FileChannel.MapMode.READ_ONLY, 0, (int)fc.size());
result = new FileContentInfo(fc, ChannelBuffers.wrappedBuffer(roBuf));
} catch (IOException e) {
return null;
e.printStackTrace();
} finally {
if (result == null)
{
if (fc != null)
{
try
{
fc.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}

return result;
}

@Override
Expand Down