import org.apache.tomcat.util.net.SocketStatus;
import org.apache.tomcat.util.net.NioEndpoint.Handler.SocketState;
import org.apache.tomcat.util.res.StringManager;
+import org.apache.tomcat.util.net.NioEndpoint.KeyAttachment;
/**
*/
protected boolean http09 = false;
-
+ /**
+ * Sendfile data.
+ */
+ protected NioEndpoint.SendfileData sendfileData = null;
/**
* Comet used.
response.setStatus(500);
}
request.updateCounters();
+
+ // Do sendfile as needed: add socket to sendfile and end
+ if (sendfileData != null && !error) {
+ KeyAttachment ka = (KeyAttachment)socket.getAttachment(false);
+ ka.setSendfileData(sendfileData);
+ sendfileData.keepAlive = keepAlive;
+ endpoint.getPoller0().add(socket,SelectionKey.OP_WRITE);
+ openSocket = true;
+ break;
+ }
+
rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
http09 = false;
contentDelimitation = false;
expectation = false;
+ sendfileData = null;
if (ssl) {
request.scheme().setString("https");
}
contentDelimitation = true;
}
+ // Advertise sendfile support through a request attribute
+ if (endpoint.getUseSendfile())
+ request.setAttribute("org.apache.tomcat.sendfile.support", Boolean.TRUE);
// Advertise comet support through a request attribute
request.setAttribute("org.apache.tomcat.comet.support", Boolean.TRUE);
// Advertise comet timeout support
(outputFilters[Constants.VOID_FILTER]);
contentDelimitation = true;
}
+
+ // Sendfile support
+ if (this.endpoint.getUseSendfile()) {
+ String fileName = (String) request.getAttribute("org.apache.tomcat.sendfile.filename");
+ if (fileName != null) {
+ // No entity body sent here
+ outputBuffer.addActiveFilter(outputFilters[Constants.VOID_FILTER]);
+ contentDelimitation = true;
+ sendfileData = new NioEndpoint.SendfileData();
+ sendfileData.fileName = fileName;
+ sendfileData.pos = ((Long) request.getAttribute("org.apache.tomcat.sendfile.start")).longValue();
+ sendfileData.length = ((Long) request.getAttribute("org.apache.tomcat.sendfile.end")).longValue() - sendfileData.pos;
+ }
+ }
+
// Check for compression
boolean useCompression = false;
- if (entityBody && (compressionLevel > 0)) {
+ if (entityBody && (compressionLevel > 0) && (sendfileData == null)) {
useCompression = isCompressable();
// Change content-length to -1 to force chunking
if (useCompression) {
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.CancelledKeyException;
+import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.security.KeyStore;
+import java.util.Collection;
+import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import org.apache.tomcat.util.IntrospectionUtils;
import org.apache.tomcat.util.net.SecureNioChannel.ApplicationBufferHandler;
import org.apache.tomcat.util.res.StringManager;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.CountDownLatch;
-import java.util.Comparator;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.Collection;
-import java.util.concurrent.ThreadFactory;
+import java.io.File;
/**
* NIO tailored thread pool, providing the following services:
* Server socket "pointer".
*/
protected ServerSocketChannel serverSock = null;
-
+
+ /**
+ * use send file
+ */
+ private boolean useSendfile = true;
+
+
/**
* Cache for SocketProcessor objects
*/
this.socketProperties = socketProperties;
}
+ public void setUseSendfile(boolean useSendfile) {
+
+ this.useSendfile = useSendfile;
+ }
+
protected SSLContext sslContext = null;
public SSLContext getSSLContext() { return sslContext;}
public void setSSLContext(SSLContext c) { sslContext = c;}
serverSock.socket().bind(addr,backlog);
serverSock.configureBlocking(true); //mimic APR behavior
- // Initialize thread count defaults for acceptor, poller and sendfile
+ // Initialize thread count defaults for acceptor, poller
if (acceptorThreadCount == 0) {
// FIXME: Doesn't seem to work that well with multiple accept threads
acceptorThreadCount = 1;
return socketProperties;
}
+ public boolean getUseSendfile() {
+
+ return useSendfile;
+ }
+
/**
* Unlock the server socket accept using a bogus connection.
*/
sk.attach(attachment);//cant remember why this is here
NioChannel channel = attachment.getChannel();
if (sk.isReadable() || sk.isWritable() ) {
- if ( attachment.getComet() ) {
+ if ( attachment.getSendfileData() != null ) {
+ processSendfile(sk,attachment);
+ } else if ( attachment.getComet() ) {
//check if thread is available
if ( isWorkerAvailable() ) {
unreg(sk, attachment);
}
return result;
}
+
+ protected void processSendfile(SelectionKey sk, KeyAttachment attachment) {
+ try {
+ //unreg(sk,attachment);//only do this if we do process send file on a separate thread
+ SendfileData sd = attachment.getSendfileData();
+ if ( sd.fchannel == null ) {
+ File f = new File(sd.fileName);
+ if ( !f.exists() ) {
+ cancelledKey(sk,SocketStatus.ERROR,false);
+ return;
+ }
+ sd.fchannel = new FileInputStream(f).getChannel();
+ }
+ SocketChannel sc = attachment.getChannel().getIOChannel();
+ long written = sd.fchannel.transferTo(sd.pos,sd.length,sc);
+ if ( written > 0 ) {
+ sd.pos += written;
+ sd.length -= written;
+ }
+ if ( sd.length <= 0 ) {
+ attachment.setSendfileData(null);
+ if ( sd.keepAlive )
+ reg(sk,attachment,SelectionKey.OP_READ);
+ else
+ cancelledKey(sk,SocketStatus.STOP,false);
+ }
+ }catch ( Throwable t ) {
+ log.error("",t);
+ cancelledKey(sk, SocketStatus.ERROR);
+ }
+ }
protected void unreg(SelectionKey sk, KeyAttachment attachment) {
- sk.interestOps(0); //this is a must, so that we don't have multiple threads messing with the socket
- attachment.interestOps(0);//fast access interestp ops
+ //this is a must, so that we don't have multiple threads messing with the socket
+ reg(sk,attachment,0);
}
+ protected void reg(SelectionKey sk, KeyAttachment attachment, int intops) {
+ sk.interestOps(intops);
+ attachment.interestOps(intops);
+ }
+
protected void timeout(int keyCount, boolean hasEvents) {
long now = System.currentTimeMillis();
//don't process timeouts too frequently, but if the selector simply timed out
public long getLastRegistered() { return lastRegistered; };
public void setLastRegistered(long reg) { lastRegistered = reg; }
+ public void setSendfileData(SendfileData sf) { this.sendfileData = sf;}
+ public SendfileData getSendfileData() { return this.sendfileData;}
+
protected Object mutex = new Object();
protected long lastAccess = -1;
protected boolean currentAccess = false;
protected CountDownLatch latch = null;
protected int fairness = 0;
protected long lastRegistered = 0;
+ protected SendfileData sendfileData = null;
}
// ----------------------------------------------------- Key Fairness Comparator
public static class KeyFairnessComparator implements Comparator<SelectionKey> {
return t;
}
}
+
+ // ----------------------------------------------- SendfileData Inner Class
+
+
+ /**
+ * SendfileData class.
+ */
+ public static class SendfileData {
+ // File
+ public String fileName;
+ public FileChannel fchannel;
+ public long pos;
+ public long length;
+ // KeepAlive flag
+ public boolean keepAlive;
+ }
+
}