Browse Source

WebSocket transport - errors occurs under heavy load bug. Issue #87

master
Nikita 12 years ago
parent
commit
5191dd2645
  1. 129
      src/main/java/com/corundumstudio/socketio/handler/ResourceHandler.java

129
src/main/java/com/corundumstudio/socketio/handler/ResourceHandler.java

@ -25,6 +25,7 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
@ -35,8 +36,6 @@ import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.stream.ChunkedStream;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import java.io.InputStream;
import java.net.URL;
@ -56,10 +55,12 @@ import javax.activation.MimetypesFileTypeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.corundumstudio.socketio.SocketIOChannelInitializer;
@Sharable
public class ResourceHandler extends ChunkedWriteHandler {
private final Logger log = LoggerFactory.getLogger(getClass());
public class ResourceHandler extends ChannelInboundHandlerAdapter {
private final Logger log = LoggerFactory.getLogger(getClass());
public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
public static final String HTTP_DATE_GMT_TIMEZONE = "GMT";
@ -73,68 +74,60 @@ public class ResourceHandler extends ChunkedWriteHandler {
}
public void addResource(String pathPart, String resourcePath) {
URL resUrl = getClass().getResource(resourcePath);
if (resUrl == null) {
log.error("The specified resource was not found: " + resourcePath);
return;
}
resources.put(pathPart, resUrl);
URL resUrl = getClass().getResource(resourcePath);
if (resUrl == null) {
log.error("The specified resource was not found: " + resourcePath);
return;
}
resources.put(pathPart, resUrl);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
FullHttpRequest req = (FullHttpRequest) msg;
QueryStringDecoder queryDecoder = new QueryStringDecoder(req.getUri());
URL resUrl = resources.get(queryDecoder.path());
if (resUrl != null) {
URLConnection fileUrl = resUrl.openConnection();
long lastModified = fileUrl.getLastModified();
// check if file has been modified since last request
if (isNotModified(req, lastModified)) {
sendNotModified(ctx);
req.release();
return;
}
// create resource input-stream and check existence
final InputStream is = fileUrl.getInputStream();
if (is == null) {
sendError(ctx, NOT_FOUND);
return;
}
// create ok response
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.OK);
// set Content-Length header
setContentLength(res, fileUrl.getContentLengthLong());
// set Content-Type header
setContentTypeHeader(res, fileUrl);
// set Date, Expires, Cache-Control and Last-Modified headers
setDateAndCacheHeaders(res, lastModified);
// write initial response header
ctx.write(res);
// write the content stream
ctx.pipeline().addBefore(SocketIOChannelInitializer.RESOURCE_HANDLER, "chunkedWriter", new ChunkedWriteHandler());
ChannelFuture writeFuture = ctx.channel().write(new ChunkedStream(is, fileUrl.getContentLength()));
// add operation complete listener so we can close the channel and the input stream
writeFuture.addListener(ChannelFutureListener.CLOSE);
return;
}
}
ctx.fireChannelRead(msg);
}
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
FullHttpRequest req = (FullHttpRequest) msg;
QueryStringDecoder queryDecoder = new QueryStringDecoder(req.getUri());
URL resUrl = resources.get(queryDecoder.path());
if (resUrl != null) {
URLConnection fileUrl = resUrl.openConnection();
long lastModified = fileUrl.getLastModified();
// check if file has been modified since last request
if (isNotModified(req, lastModified)) {
sendNotModified(ctx);
req.release();
return;
}
// create resource input-stream and check existence
final InputStream is = fileUrl.getInputStream();
if (is == null) {
sendError(ctx, NOT_FOUND);
return;
}
// create ok response
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.OK);
// set Content-Length header
setContentLength(res, fileUrl.getContentLengthLong());
// set Content-Type header
setContentTypeHeader(res, fileUrl);
// set Date, Expires, Cache-Control and Last-Modified headers
setDateAndCacheHeaders(res, lastModified);
// write initial response header
ctx.write(res);
// write the content stream
ChannelFuture writeFuture = ctx.channel().write(new ChunkedStream(is, fileUrl.getContentLength()));
// add operation complete listener so we can close the channel and the input stream
writeFuture.addListener(new GenericFutureListener<Future<? super Void>>() {
@Override
public void operationComplete(Future<? super Void> future) throws Exception {
// close the channel and insput stream on finish
ctx.channel().close();
is.close();
}
});
return;
}
}
ctx.fireChannelRead(msg);
}
/*
* Checks if the content has been modified sicne the date provided by the IF_MODIFIED_SINCE http header
* */
/*
* Checks if the content has been modified sicne the date provided by the IF_MODIFIED_SINCE http header
* */
private boolean isNotModified(HttpRequest request, long lastModified) throws ParseException {
String ifModifiedSince = request.headers().get(HttpHeaders.Names.IF_MODIFIED_SINCE);
if (ifModifiedSince != null && !ifModifiedSince.equals("")) {
@ -152,7 +145,7 @@ public class ResourceHandler extends ChunkedWriteHandler {
/*
* Sends a Not Modified response to the client
*
*
* */
private void sendNotModified(ChannelHandlerContext ctx) {
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.NOT_MODIFIED);
@ -179,8 +172,8 @@ public class ResourceHandler extends ChunkedWriteHandler {
/**
* Sends an Error response with status message
*
* @param ctx
*
* @param ctx
* @param status
*/
private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {

Loading…
Cancel
Save