From aa4b8a2a6cee441712cdf0f9710179d5d03bbf14 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Tue, 23 Feb 2010 07:18:58 +0000 Subject: [PATCH] * Changed the type of WriteCompletionEvent.amount from int to long * Added back FileRegion since mapped buffer does not outperform transferTo() * Refactored SocketSendBufferPool to support FileRegion --- .../org/jboss/netty/channel/Channels.java | 25 ++- .../netty/channel/DefaultFileRegion.java | 43 +++++ .../channel/DefaultWriteCompletionEvent.java | 14 +- .../org/jboss/netty/channel/FileRegion.java | 47 +++++ .../netty/channel/WriteCompletionEvent.java | 2 +- .../socket/nio/NioDatagramChannel.java | 12 +- .../channel/socket/nio/NioDatagramWorker.java | 17 +- .../channel/socket/nio/NioSocketChannel.java | 12 +- .../netty/channel/socket/nio/NioWorker.java | 27 ++- .../socket/nio/SocketSendBufferPool.java | 165 +++++++++++++++--- .../file/HttpStaticFileServerHandler.java | 7 +- 11 files changed, 310 insertions(+), 61 deletions(-) create mode 100644 src/main/java/org/jboss/netty/channel/DefaultFileRegion.java create mode 100644 src/main/java/org/jboss/netty/channel/FileRegion.java diff --git a/src/main/java/org/jboss/netty/channel/Channels.java b/src/main/java/org/jboss/netty/channel/Channels.java index 84d7f9a801..23c5ec90ab 100644 --- a/src/main/java/org/jboss/netty/channel/Channels.java +++ b/src/main/java/org/jboss/netty/channel/Channels.java @@ -313,6 +313,20 @@ public class Channels { return; } + channel.getPipeline().sendUpstream( + new DefaultWriteCompletionEvent(channel, (long) amount)); + } + + /** + * Sends a {@code "writeComplete"} event to the first + * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of + * the specified {@link Channel}. + */ + public static void fireWriteComplete(Channel channel, long amount) { + if (amount == 0) { + return; + } + channel.getPipeline().sendUpstream( new DefaultWriteCompletionEvent(channel, amount)); } @@ -324,9 +338,18 @@ public class Channels { * {@link ChannelHandlerContext}. */ public static void fireWriteComplete(ChannelHandlerContext ctx, int amount) { - ctx.sendUpstream(new DefaultWriteCompletionEvent(ctx.getChannel(), amount)); + ctx.sendUpstream(new DefaultWriteCompletionEvent(ctx.getChannel(), (long) amount)); } + /** + * Sends a {@code "writeComplete"} event to the + * {@link ChannelUpstreamHandler} which is placed in the closest upstream + * from the handler associated with the specified + * {@link ChannelHandlerContext}. + */ + public static void fireWriteComplete(ChannelHandlerContext ctx, long amount) { + ctx.sendUpstream(new DefaultWriteCompletionEvent(ctx.getChannel(), amount)); + } /** * Sends a {@code "channelInterestChanged"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of diff --git a/src/main/java/org/jboss/netty/channel/DefaultFileRegion.java b/src/main/java/org/jboss/netty/channel/DefaultFileRegion.java new file mode 100644 index 0000000000..02e1605599 --- /dev/null +++ b/src/main/java/org/jboss/netty/channel/DefaultFileRegion.java @@ -0,0 +1,43 @@ +package org.jboss.netty.channel; + +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.channels.WritableByteChannel; + +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; + +public class DefaultFileRegion implements FileRegion { + + private static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultFileRegion.class); + + private final FileChannel file; + private final long position; + private final long count; + + public DefaultFileRegion(FileChannel file, long position, long count) { + this.file = file; + this.position = position; + this.count = count; + } + + public long getPosition() { + return position; + } + + public long getCount() { + return count; + } + + public long transferTo(WritableByteChannel target) throws IOException { + return file.transferTo(position, count, target); + } + + public void releaseExternalResources() { + try { + file.close(); + } catch (IOException e) { + logger.warn("Failed to close a file.", e); + } + } +} diff --git a/src/main/java/org/jboss/netty/channel/DefaultWriteCompletionEvent.java b/src/main/java/org/jboss/netty/channel/DefaultWriteCompletionEvent.java index 797d3f0524..96d948e1a1 100644 --- a/src/main/java/org/jboss/netty/channel/DefaultWriteCompletionEvent.java +++ b/src/main/java/org/jboss/netty/channel/DefaultWriteCompletionEvent.java @@ -28,12 +28,20 @@ import static org.jboss.netty.channel.Channels.*; public class DefaultWriteCompletionEvent implements WriteCompletionEvent { private final Channel channel; - private final int writtenAmount; + private final long writtenAmount; + + /** + * @deprecated Use {@link #DefaultWriteCompletionEvent(Channel, long)} instead. + */ + @Deprecated + public DefaultWriteCompletionEvent(Channel channel, int writtenAmount) { + this(channel, (long) writtenAmount); + } /** * Creates a new instance. */ - public DefaultWriteCompletionEvent(Channel channel, int writtenAmount) { + public DefaultWriteCompletionEvent(Channel channel, long writtenAmount) { if (channel == null) { throw new NullPointerException("channel"); } @@ -54,7 +62,7 @@ public class DefaultWriteCompletionEvent implements WriteCompletionEvent { return succeededFuture(getChannel()); } - public int getWrittenAmount() { + public long getWrittenAmount() { return writtenAmount; } diff --git a/src/main/java/org/jboss/netty/channel/FileRegion.java b/src/main/java/org/jboss/netty/channel/FileRegion.java new file mode 100644 index 0000000000..a2979e806c --- /dev/null +++ b/src/main/java/org/jboss/netty/channel/FileRegion.java @@ -0,0 +1,47 @@ +/* + * Copyright 2010 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.jboss.netty.channel; + +import java.io.IOException; +import java.nio.channels.WritableByteChannel; + +import org.jboss.netty.util.ExternalResourceReleasable; + +/** + * A region of a file that is sent via a {@link Channel} which supports + * zero-copy file transfer. + * + * @author The Netty Project + * @author Trustin Lee + * @version $Rev: 2080 $, $Date: 2010-01-26 18:04:19 +0900 (Tue, 26 Jan 2010) $ + */ +public interface FileRegion extends ExternalResourceReleasable { + + /** + * Returns the offset in the file where the transfer began. + */ + long getPosition(); + + /** + * Returns the number of bytes to transfer. + */ + long getCount(); + + /** + * Transfers the content of this file region to the specified channel. + */ + long transferTo(WritableByteChannel target) throws IOException; +} \ No newline at end of file diff --git a/src/main/java/org/jboss/netty/channel/WriteCompletionEvent.java b/src/main/java/org/jboss/netty/channel/WriteCompletionEvent.java index 41574d2c54..6a80d54d5f 100644 --- a/src/main/java/org/jboss/netty/channel/WriteCompletionEvent.java +++ b/src/main/java/org/jboss/netty/channel/WriteCompletionEvent.java @@ -34,5 +34,5 @@ public interface WriteCompletionEvent extends ChannelEvent { * @return the number of written bytes or messages, depending on the * type of the transport */ - int getWrittenAmount(); + long getWrittenAmount(); } diff --git a/src/main/java/org/jboss/netty/channel/socket/nio/NioDatagramChannel.java b/src/main/java/org/jboss/netty/channel/socket/nio/NioDatagramChannel.java index 7e37829fb6..0b4c0fdc9b 100644 --- a/src/main/java/org/jboss/netty/channel/socket/nio/NioDatagramChannel.java +++ b/src/main/java/org/jboss/netty/channel/socket/nio/NioDatagramChannel.java @@ -261,7 +261,7 @@ class NioDatagramChannel extends AbstractChannel boolean success = super.offer(e); assert success; - int messageSize = ((ChannelBuffer) e.getMessage()).readableBytes(); + int messageSize = getMessageSize(e); int newWriteBufferSize = writeBufferSize.addAndGet(messageSize); int highWaterMark = getConfig().getWriteBufferHighWaterMark(); @@ -286,7 +286,7 @@ class NioDatagramChannel extends AbstractChannel public MessageEvent poll() { MessageEvent e = super.poll(); if (e != null) { - int messageSize = ((ChannelBuffer) e.getMessage()).readableBytes(); + int messageSize = getMessageSize(e); int newWriteBufferSize = writeBufferSize.addAndGet(-messageSize); int lowWaterMark = getConfig().getWriteBufferLowWaterMark(); @@ -303,6 +303,14 @@ class NioDatagramChannel extends AbstractChannel } return e; } + + private int getMessageSize(MessageEvent e) { + Object m = e.getMessage(); + if (m instanceof ChannelBuffer) { + return ((ChannelBuffer) m).readableBytes(); + } + return 0; + } } /** diff --git a/src/main/java/org/jboss/netty/channel/socket/nio/NioDatagramWorker.java b/src/main/java/org/jboss/netty/channel/socket/nio/NioDatagramWorker.java index e850de92c8..89dceb5523 100644 --- a/src/main/java/org/jboss/netty/channel/socket/nio/NioDatagramWorker.java +++ b/src/main/java/org/jboss/netty/channel/socket/nio/NioDatagramWorker.java @@ -36,7 +36,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBufferFactory; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelException; @@ -500,7 +499,7 @@ class NioDatagramWorker implements Runnable { boolean addOpWrite = false; boolean removeOpWrite = false; - int writtenBytes = 0; + long writtenBytes = 0; final SocketSendBufferPool sendBufferPool = this.sendBufferPool; final DatagramChannel ch = channel.getDatagramChannel(); @@ -514,7 +513,6 @@ class NioDatagramWorker implements Runnable { for (;;) { MessageEvent evt = channel.currentWriteEvent; SendBuffer buf; - ByteBuffer bb; if (evt == null) { if ((channel.currentWriteEvent = evt = writeBuffer.poll()) == null) { removeOpWrite = true; @@ -522,20 +520,17 @@ class NioDatagramWorker implements Runnable { break; } - ChannelBuffer origBuf = (ChannelBuffer) evt.getMessage(); - channel.currentWriteBuffer = buf = sendBufferPool.acquire(origBuf); - bb = buf.buffer; + channel.currentWriteBuffer = buf = sendBufferPool.acquire(evt.getMessage()); } else { buf = channel.currentWriteBuffer; - bb = buf.buffer; } try { - int localWrittenBytes = 0; + long localWrittenBytes = 0; SocketAddress raddr = evt.getRemoteAddress(); if (raddr == null) { for (int i = writeSpinCount; i > 0; i --) { - localWrittenBytes = ch.write(bb); + localWrittenBytes = buf.transferTo(ch); if (localWrittenBytes != 0) { writtenBytes += localWrittenBytes; break; @@ -543,7 +538,7 @@ class NioDatagramWorker implements Runnable { } } else { for (int i = writeSpinCount; i > 0; i --) { - localWrittenBytes = ch.send(bb, raddr); + localWrittenBytes = buf.transferTo(ch, raddr); if (localWrittenBytes != 0) { writtenBytes += localWrittenBytes; break; @@ -559,7 +554,6 @@ class NioDatagramWorker implements Runnable { channel.currentWriteBuffer = null; evt = null; buf = null; - bb = null; future.setSuccess(); } else { // Not written at all - perhaps the kernel buffer is full. @@ -576,7 +570,6 @@ class NioDatagramWorker implements Runnable { channel.currentWriteBuffer = null; buf = null; evt = null; - bb = null; future.setFailure(t); fireExceptionCaught(channel, t); } diff --git a/src/main/java/org/jboss/netty/channel/socket/nio/NioSocketChannel.java b/src/main/java/org/jboss/netty/channel/socket/nio/NioSocketChannel.java index 6c1fbf92fd..d4ff2b0114 100644 --- a/src/main/java/org/jboss/netty/channel/socket/nio/NioSocketChannel.java +++ b/src/main/java/org/jboss/netty/channel/socket/nio/NioSocketChannel.java @@ -209,7 +209,7 @@ class NioSocketChannel extends AbstractChannel boolean success = super.offer(e); assert success; - int messageSize = ((ChannelBuffer) e.getMessage()).readableBytes(); + int messageSize = getMessageSize(e); int newWriteBufferSize = writeBufferSize.addAndGet(messageSize); int highWaterMark = getConfig().getWriteBufferHighWaterMark(); @@ -230,7 +230,7 @@ class NioSocketChannel extends AbstractChannel public MessageEvent poll() { MessageEvent e = super.poll(); if (e != null) { - int messageSize = ((ChannelBuffer) e.getMessage()).readableBytes(); + int messageSize = getMessageSize(e); int newWriteBufferSize = writeBufferSize.addAndGet(-messageSize); int lowWaterMark = getConfig().getWriteBufferLowWaterMark(); @@ -247,6 +247,14 @@ class NioSocketChannel extends AbstractChannel } return e; } + + private int getMessageSize(MessageEvent e) { + Object m = e.getMessage(); + if (m instanceof ChannelBuffer) { + return ((ChannelBuffer) m).readableBytes(); + } + return 0; + } } private final class WriteTask implements Runnable { diff --git a/src/main/java/org/jboss/netty/channel/socket/nio/NioWorker.java b/src/main/java/org/jboss/netty/channel/socket/nio/NioWorker.java index 6e2f1f0f4e..0b1c8137f2 100644 --- a/src/main/java/org/jboss/netty/channel/socket/nio/NioWorker.java +++ b/src/main/java/org/jboss/netty/channel/socket/nio/NioWorker.java @@ -439,7 +439,7 @@ class NioWorker implements Runnable { boolean addOpWrite = false; boolean removeOpWrite = false; - int writtenBytes = 0; + long writtenBytes = 0; final SocketSendBufferPool sendBufferPool = this.sendBufferPool; final SocketChannel ch = channel.socket; @@ -450,7 +450,6 @@ class NioWorker implements Runnable { for (;;) { MessageEvent evt = channel.currentWriteEvent; SendBuffer buf; - ByteBuffer bb; if (evt == null) { if ((channel.currentWriteEvent = evt = writeBuffer.poll()) == null) { removeOpWrite = true; @@ -458,44 +457,41 @@ class NioWorker implements Runnable { break; } - ChannelBuffer origBuf = (ChannelBuffer) evt.getMessage(); - channel.currentWriteBuffer = buf = sendBufferPool.acquire(origBuf); - bb = buf.buffer; + channel.currentWriteBuffer = buf = sendBufferPool.acquire(evt.getMessage()); } else { buf = channel.currentWriteBuffer; - bb = buf.buffer; } ChannelFuture future = evt.getFuture(); try { - int oldWrittenBytes = writtenBytes; + long localWrittenBytes = 0; for (int i = writeSpinCount; i > 0; i --) { - int localWrittenBytes = ch.write(bb); + localWrittenBytes = buf.transferTo(ch); if (localWrittenBytes != 0) { writtenBytes += localWrittenBytes; break; } } - if (!bb.hasRemaining()) { + if (buf.finished()) { // Successful write - proceed to the next message. buf.release(); channel.currentWriteEvent = null; channel.currentWriteBuffer = null; evt = null; buf = null; - bb = null; future.setSuccess(); } else { // Not written fully - perhaps the kernel buffer is full. addOpWrite = true; channel.writeSuspended = true; - // Notify progress listeners if necessary. - future.setProgress( - writtenBytes - oldWrittenBytes, - bb.position() - buf.initialPos, - bb.limit() - buf.initialPos); + if (localWrittenBytes > 0) { + // Notify progress listeners if necessary. + future.setProgress( + localWrittenBytes, + buf.writtenBytes(), buf.totalBytes()); + } break; } } catch (AsynchronousCloseException e) { @@ -506,7 +502,6 @@ class NioWorker implements Runnable { channel.currentWriteBuffer = null; buf = null; evt = null; - bb = null; future.setFailure(t); fireExceptionCaught(channel, t); if (t instanceof IOException) { diff --git a/src/main/java/org/jboss/netty/channel/socket/nio/SocketSendBufferPool.java b/src/main/java/org/jboss/netty/channel/socket/nio/SocketSendBufferPool.java index d00b8f2e88..5620cb9c2d 100644 --- a/src/main/java/org/jboss/netty/channel/socket/nio/SocketSendBufferPool.java +++ b/src/main/java/org/jboss/netty/channel/socket/nio/SocketSendBufferPool.java @@ -15,10 +15,15 @@ */ package org.jboss.netty.channel.socket.nio; +import java.io.IOException; import java.lang.ref.SoftReference; +import java.net.SocketAddress; import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; +import java.nio.channels.WritableByteChannel; import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.FileRegion; /** * @author The Netty Project @@ -42,27 +47,34 @@ final class SocketSendBufferPool { super(); } - final SendBuffer acquire(ChannelBuffer src) { - if (src.isDirect()) { - return new SendBuffer(null, src.toByteBuffer()); - } - if (src.readableBytes() > DEFAULT_PREALLOCATION_SIZE) { - return new SendBuffer(null, src.toByteBuffer()); + final SendBuffer acquire(Object message) { + if (message instanceof ChannelBuffer) { + return acquire((ChannelBuffer) message); + } else if (message instanceof FileRegion) { + return acquire((FileRegion) message); } - SendBuffer dst = acquire(src.readableBytes()); - ByteBuffer dstbuf = dst.buffer; - dstbuf.mark(); - src.getBytes(src.readerIndex(), dstbuf); - dstbuf.reset(); - return dst; + throw new IllegalArgumentException( + "unsupported message type: " + message.getClass()); } - private final SendBuffer acquire(int size) { - assert size <= DEFAULT_PREALLOCATION_SIZE; + private final SendBuffer acquire(FileRegion src) { + return new FileSendBuffer(src); + } + + private final SendBuffer acquire(ChannelBuffer src) { + if (src.isDirect()) { + return new UnpooledSendBuffer(src.toByteBuffer()); + } + if (src.readableBytes() > DEFAULT_PREALLOCATION_SIZE) { + return new UnpooledSendBuffer(src.toByteBuffer()); + } + + final int size = src.readableBytes(); Preallocation current = this.current; ByteBuffer buffer = current.buffer; int remaining = buffer.remaining(); + PooledSendBuffer dst; if (size < remaining) { int nextPos = buffer.position() + size; @@ -70,7 +82,7 @@ final class SocketSendBufferPool { buffer.position(align(nextPos)); slice.limit(nextPos); current.refCnt ++; - return new SendBuffer(current, slice); + dst = new PooledSendBuffer(current, slice); } else if (size > remaining) { this.current = current = getPreallocation(); buffer = current.buffer; @@ -78,12 +90,18 @@ final class SocketSendBufferPool { buffer.position(align(size)); slice.limit(size); current.refCnt ++; - return new SendBuffer(current, slice); + dst = new PooledSendBuffer(current, slice); } else { // size == remaining current.refCnt ++; this.current = getPreallocation0(); - return new SendBuffer(current, current.buffer); + dst = new PooledSendBuffer(current, current.buffer); } + + ByteBuffer dstbuf = dst.buffer; + dstbuf.mark(); + src.getBytes(src.readerIndex(), dstbuf); + dstbuf.reset(); + return dst; } private final Preallocation getPreallocation() { @@ -142,20 +160,85 @@ final class SocketSendBufferPool { } } - final class SendBuffer { + interface SendBuffer { + boolean finished(); + long writtenBytes(); + long totalBytes(); + + long transferTo(WritableByteChannel ch) throws IOException; + long transferTo(DatagramChannel ch, SocketAddress raddr) throws IOException; + + void release(); + } + + class UnpooledSendBuffer implements SendBuffer { + final ByteBuffer buffer; + final int initialPos; + + UnpooledSendBuffer(ByteBuffer buffer) { + this.buffer = buffer; + initialPos = buffer.position(); + } + + public final boolean finished() { + return !buffer.hasRemaining(); + } + + public final long writtenBytes() { + return buffer.position() - initialPos; + } + + public final long totalBytes() { + return buffer.limit() - initialPos; + } + + public final long transferTo(WritableByteChannel ch) throws IOException { + return ch.write(buffer); + } + + public final long transferTo(DatagramChannel ch, SocketAddress raddr) throws IOException { + return ch.send(buffer, raddr); + } + + public void release() { + // Unpooled. + } + } + + final class PooledSendBuffer implements SendBuffer { private final Preallocation parent; final ByteBuffer buffer; final int initialPos; - SendBuffer(Preallocation parent, ByteBuffer buffer) { + PooledSendBuffer(Preallocation parent, ByteBuffer buffer) { this.parent = parent; this.buffer = buffer; initialPos = buffer.position(); } - void release() { + public boolean finished() { + return !buffer.hasRemaining(); + } + + public long writtenBytes() { + return buffer.position() - initialPos; + } + + public long totalBytes() { + return buffer.limit() - initialPos; + } + + public long transferTo(WritableByteChannel ch) throws IOException { + return ch.write(buffer); + } + + public long transferTo(DatagramChannel ch, SocketAddress raddr) throws IOException { + return ch.send(buffer, raddr); + } + + public void release() { final Preallocation parent = this.parent; - if (parent != null && -- parent.refCnt == 0) { + if (-- parent.refCnt == 0) { parent.buffer.clear(); if (parent != current) { poolHead = new PreallocationRef(parent, poolHead); @@ -163,4 +246,42 @@ final class SocketSendBufferPool { } } } -} \ No newline at end of file + + final class FileSendBuffer implements SendBuffer { + + private final FileRegion file; + private long writtenBytes; + + + FileSendBuffer(FileRegion file) { + this.file = file; + } + + public boolean finished() { + return writtenBytes >= file.getCount(); + } + + public long writtenBytes() { + return writtenBytes; + } + + public long totalBytes() { + return file.getCount(); + } + + public long transferTo(WritableByteChannel ch) throws IOException { + long localWrittenBytes = file.transferTo(ch); + writtenBytes += localWrittenBytes; + return localWrittenBytes; + } + + public long transferTo(DatagramChannel ch, SocketAddress raddr) + throws IOException { + throw new UnsupportedOperationException(); + } + + public void release() { + // Unpooled. + } + } +} diff --git a/src/main/java/org/jboss/netty/example/http/file/HttpStaticFileServerHandler.java b/src/main/java/org/jboss/netty/example/http/file/HttpStaticFileServerHandler.java index 0b587df5d2..0e0f4d59e8 100644 --- a/src/main/java/org/jboss/netty/example/http/file/HttpStaticFileServerHandler.java +++ b/src/main/java/org/jboss/netty/example/http/file/HttpStaticFileServerHandler.java @@ -32,6 +32,7 @@ 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.DefaultFileRegion; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.SimpleChannelUpstreamHandler; @@ -40,7 +41,6 @@ import org.jboss.netty.handler.codec.http.DefaultHttpResponse; 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.stream.ChunkedFile; import org.jboss.netty.util.CharsetUtil; /** @@ -91,7 +91,8 @@ public class HttpStaticFileServerHandler extends SimpleChannelUpstreamHandler { ch.write(response); // Write the content. - ChannelFuture writeFuture = ch.write(new ChunkedFile(raf, 0, fileLength, 8192)); + //ChannelFuture writeFuture = ch.write(new ChunkedFile(raf, 0, fileLength, 8192)); + ChannelFuture writeFuture = ch.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength)); // Decide whether to close the connection or not. if (!isKeepAlive(request)) { @@ -105,6 +106,8 @@ public class HttpStaticFileServerHandler extends SimpleChannelUpstreamHandler { throws Exception { Channel ch = e.getChannel(); Throwable cause = e.getCause(); + cause.printStackTrace(); + System.exit(1); if (cause instanceof TooLongFrameException) { sendError(ctx, BAD_REQUEST); return;