import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetAddress;
-import java.nio.channels.SocketChannel;
+import java.nio.channels.SelectionKey;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.FastHttpDateFormat;
import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.net.NioChannel;
import org.apache.tomcat.util.net.NioEndpoint;
import org.apache.tomcat.util.net.NioEndpoint.Handler;
import org.apache.tomcat.util.net.NioEndpoint.Handler.SocketState;
+import org.apache.tomcat.util.net.SSLSupport;
import org.apache.tomcat.util.res.StringManager;
-import java.nio.channels.SelectionKey;
/**
protected static StringManager sm =
StringManager.getManager(Constants.Package);
+ /**
+ * SSL information.
+ */
+ protected SSLSupport sslSupport;
// ----------------------------------------------------------- Constructors
response.setOutputBuffer(outputBuffer);
request.setResponse(response);
- ssl = !"off".equalsIgnoreCase(endpoint.getSSLEngine());
+ ssl = endpoint.getSecure();
initializeFilters();
/**
* Socket associated with the current connection.
*/
- protected SocketChannel socket = null;
+ protected NioChannel socket = null;
/**
if (request.getAttribute("org.apache.tomcat.comet") == null) {
comet = false;
}
- SelectionKey key = socket.keyFor(endpoint.getPoller().getSelector());
+ SelectionKey key = socket.getIOChannel().keyFor(endpoint.getPoller().getSelector());
if ( key != null ) {
NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment) key.attachment();
if ( attach!=null ) {
*
* @throws IOException error during an I/O operation
*/
- public SocketState process(SocketChannel socket)
+ public SocketState process(NioChannel socket)
throws IOException {
RequestInfo rp = request.getRequestProcessor();
rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
// Parsing the request header
try {
if( !disableUploadTimeout && keptAlive && soTimeout > 0 ) {
- socket.socket().setSoTimeout((int)soTimeout);
+ socket.getIOChannel().socket().setSoTimeout((int)soTimeout);
inputBuffer.readTimeout = soTimeout;
}
if (!inputBuffer.parseRequestLine
request.setStartTime(System.currentTimeMillis());
keptAlive = true;
if (!disableUploadTimeout) {
- socket.socket().setSoTimeout((int)timeout);
+ socket.getIOChannel().socket().setSoTimeout((int)timeout);
inputBuffer.readTimeout = soTimeout;
}
inputBuffer.parseHeaders();
if (request.getAttribute("org.apache.tomcat.comet") != null) {
comet = true;
}
- SelectionKey key = socket.keyFor(endpoint.getPoller().getSelector());
+ SelectionKey key = socket.getIOChannel().keyFor(endpoint.getPoller().getSelector());
if (key != null) {
NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment) key.attachment();
if (attach != null) {
comet = false;
cometClose = true;
- SelectionKey key = socket.keyFor(endpoint.getPoller().getSelector());
+ SelectionKey key = socket.getIOChannel().keyFor(endpoint.getPoller().getSelector());
if ( key != null ) {
NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment) key.attachment();
if ( attach!=null && attach.getComet()) {
// Get remote host address
if ((remoteAddr == null) && (socket != null)) {
- InetAddress inetAddr = socket.socket().getInetAddress();
+ InetAddress inetAddr = socket.getIOChannel().socket().getInetAddress();
if (inetAddr != null) {
remoteAddr = inetAddr.getHostAddress();
}
// Get local host name
if ((localName == null) && (socket != null)) {
- InetAddress inetAddr = socket.socket().getLocalAddress();
+ InetAddress inetAddr = socket.getIOChannel().socket().getLocalAddress();
if (inetAddr != null) {
localName = inetAddr.getHostName();
}
// Get remote host name
if ((remoteHost == null) && (socket != null)) {
- InetAddress inetAddr = socket.socket().getInetAddress();
+ InetAddress inetAddr = socket.getIOChannel().socket().getInetAddress();
if (inetAddr != null) {
remoteHost = inetAddr.getHostName();
}
} else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
if (localAddr == null)
- localAddr = socket.socket().getLocalAddress().getHostAddress();
+ localAddr = socket.getIOChannel().socket().getLocalAddress().getHostAddress();
request.localAddr().setString(localAddr);
} else if (actionCode == ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE) {
if ((remotePort == -1 ) && (socket !=null)) {
- remotePort = socket.socket().getPort();
+ remotePort = socket.getIOChannel().socket().getPort();
}
request.setRemotePort(remotePort);
} else if (actionCode == ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE) {
if ((localPort == -1 ) && (socket !=null)) {
- localPort = socket.socket().getLocalPort();
+ localPort = socket.getIOChannel().socket().getLocalPort();
}
request.setLocalPort(localPort);
} else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
-// if (ssl && (socket != 0)) {
-// try {
-// // Cipher suite
-// Object sslO = SSLSocket.getInfoS(socket, SSL.SSL_INFO_CIPHER);
-// if (sslO != null) {
-// request.setAttribute
-// (NioEndpoint.CIPHER_SUITE_KEY, sslO);
-// }
-// // Client certificate chain if present
-// int certLength = SSLSocket.getInfoI(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN);
-// X509Certificate[] certs = null;
-// if (certLength > 0) {
-// certs = new X509Certificate[certLength];
-// for (int i = 0; i < certLength; i++) {
-// byte[] data = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
-// CertificateFactory cf =
-// CertificateFactory.getInstance("X.509");
-// ByteArrayInputStream stream = new ByteArrayInputStream(data);
-// certs[i] = (X509Certificate) cf.generateCertificate(stream);
-// }
-// }
-// if (certs != null) {
-// request.setAttribute
-// (NioEndpoint.CERTIFICATE_KEY, certs);
-// }
-// // User key size
-// sslO = new Integer(SSLSocket.getInfoI(socket, SSL.SSL_INFO_CIPHER_USEKEYSIZE));
-// if (sslO != null) {
-// request.setAttribute
-// (NioEndpoint.KEY_SIZE_KEY, sslO);
-// }
-// // SSL session ID
-// sslO = SSLSocket.getInfoS(socket, SSL.SSL_INFO_SESSION_ID);
-// if (sslO != null) {
-// request.setAttribute
-// (NioEndpoint.SESSION_ID_KEY, sslO);
-// }
-// } catch (Exception e) {
-// log.warn(sm.getString("http11processor.socket.ssl"), e);
-// }
-// }
+ try {
+ if (sslSupport != null) {
+ Object sslO = sslSupport.getCipherSuite();
+ if (sslO != null)
+ request.setAttribute
+ (SSLSupport.CIPHER_SUITE_KEY, sslO);
+ sslO = sslSupport.getPeerCertificateChain(false);
+ if (sslO != null)
+ request.setAttribute
+ (SSLSupport.CERTIFICATE_KEY, sslO);
+ sslO = sslSupport.getKeySize();
+ if (sslO != null)
+ request.setAttribute
+ (SSLSupport.KEY_SIZE_KEY, sslO);
+ sslO = sslSupport.getSessionId();
+ if (sslO != null)
+ request.setAttribute
+ (SSLSupport.SESSION_ID_KEY, sslO);
+ }
+ } catch (Exception e) {
+ log.warn(sm.getString("http11processor.socket.ssl"), e);
+ }
} else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE) {
-// if (ssl && (socket != 0)) {
-// // Consume and buffer the request body, so that it does not
-// // interfere with the client's handshake messages
-// InputFilter[] inputFilters = inputBuffer.getFilters();
-// ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER])
-// .setLimit(maxSavePostSize);
-// inputBuffer.addActiveFilter
-// (inputFilters[Constants.BUFFERED_FILTER]);
-// try {
-// // Renegociate certificates
-// SSLSocket.renegotiate(socket);
-// // Client certificate chain if present
-// int certLength = SSLSocket.getInfoI(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN);
-// X509Certificate[] certs = null;
-// if (certLength > 0) {
-// certs = new X509Certificate[certLength];
-// for (int i = 0; i < certLength; i++) {
-// byte[] data = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
-// CertificateFactory cf =
-// CertificateFactory.getInstance("X.509");
-// ByteArrayInputStream stream = new ByteArrayInputStream(data);
-// certs[i] = (X509Certificate) cf.generateCertificate(stream);
-// }
-// }
-// if (certs != null) {
-// request.setAttribute
-// (NioEndpoint.CERTIFICATE_KEY, certs);
-// }
-// } catch (Exception e) {
-// log.warn(sm.getString("http11processor.socket.ssl"), e);
-// }
-// }
+ if( sslSupport != null) {
+ /*
+ * Consume and buffer the request body, so that it does not
+ * interfere with the client's handshake messages
+ */
+ InputFilter[] inputFilters = inputBuffer.getFilters();
+ ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER])
+ .setLimit(maxSavePostSize);
+ inputBuffer.addActiveFilter
+ (inputFilters[Constants.BUFFERED_FILTER]);
+ try {
+ Object sslO = sslSupport.getPeerCertificateChain(true);
+ if( sslO != null) {
+ request.setAttribute
+ (SSLSupport.CERTIFICATE_KEY, sslO);
+ }
+ } catch (Exception e) {
+ log.warn(sm.getString("http11processor.socket.ssl"), e);
+ }
+ }
} else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
ByteChunk body = (ByteChunk) param;
this.adapter = adapter;
}
+ public void setSslSupport(SSLSupport sslSupport) {
+ this.sslSupport = sslSupport;
+ }
/**
* Get the associated adapter.
return adapter;
}
+ public SSLSupport getSslSupport() {
+ return sslSupport;
+ }
// ------------------------------------------------------ Protected Methods
import java.net.InetAddress;
import java.net.URLEncoder;
-import java.nio.channels.SocketChannel;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.tomcat.util.net.NioEndpoint;
import org.apache.tomcat.util.net.NioEndpoint.Handler;
import org.apache.tomcat.util.res.StringManager;
+import org.apache.tomcat.util.net.NioChannel;
+import org.apache.tomcat.util.net.SSLImplementation;
+import org.apache.tomcat.util.net.SecureNioChannel;
/**
*/
public class Http11NioProtocol implements ProtocolHandler, MBeanRegistration
{
+ protected SSLImplementation sslImplementation = null;
+
public Http11NioProtocol() {
cHandler = new Http11ConnectionHandler( this );
setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
public void init() throws Exception {
ep.setName(getName());
ep.setHandler(cHandler);
-
+ ep.setReadBufSize(getMaxHttpHeaderSize());
+ ep.setWriteBufSize(getMaxHttpHeaderSize());
try {
ep.init();
+
+ sslImplementation = SSLImplementation.getInstance("org.apache.tomcat.util.net.jsse.JSSEImplementation");
+
} catch (Exception ex) {
log.error(sm.getString("http11protocol.endpoint.initerror"), ex);
throw ex;
// -------------------- Properties--------------------
protected NioEndpoint ep=new NioEndpoint();
- protected boolean secure;
+ protected boolean secure = false;
protected Hashtable attributes = new Hashtable();
}
public void setSecure( boolean b ) {
+ ep.setSecure(b);
secure=b;
setAttribute("secure", "" + b);
}
// -------------------- SSL related properties --------------------
- /**
- * SSL engine.
- */
- public String getSSLEngine() { return ep.getSSLEngine(); }
- public void setSSLEngine(String SSLEngine) { ep.setSSLEngine(SSLEngine); }
-
-
- /**
- * SSL protocol.
- */
- public String getSSLProtocol() { return ep.getSSLProtocol(); }
- public void setSSLProtocol(String SSLProtocol) { ep.setSSLProtocol(SSLProtocol); }
-
-
- /**
- * SSL password (if a cert is encrypted, and no password has been provided, a callback
- * will ask for a password).
- */
- public String getSSLPassword() { return ep.getSSLPassword(); }
- public void setSSLPassword(String SSLPassword) { ep.setSSLPassword(SSLPassword); }
-
-
- /**
- * SSL cipher suite.
- */
- public String getSSLCipherSuite() { return ep.getSSLCipherSuite(); }
- public void setSSLCipherSuite(String SSLCipherSuite) { ep.setSSLCipherSuite(SSLCipherSuite); }
-
-
- /**
- * SSL certificate file.
- */
- public String getSSLCertificateFile() { return ep.getSSLCertificateFile(); }
- public void setSSLCertificateFile(String SSLCertificateFile) { ep.setSSLCertificateFile(SSLCertificateFile); }
-
-
- /**
- * SSL certificate key file.
- */
- public String getSSLCertificateKeyFile() { return ep.getSSLCertificateKeyFile(); }
- public void setSSLCertificateKeyFile(String SSLCertificateKeyFile) { ep.setSSLCertificateKeyFile(SSLCertificateKeyFile); }
-
-
- /**
- * SSL certificate chain file.
- */
- public String getSSLCertificateChainFile() { return ep.getSSLCertificateChainFile(); }
- public void setSSLCertificateChainFile(String SSLCertificateChainFile) { ep.setSSLCertificateChainFile(SSLCertificateChainFile); }
-
-
- /**
- * SSL CA certificate path.
- */
- public String getSSLCACertificatePath() { return ep.getSSLCACertificatePath(); }
- public void setSSLCACertificatePath(String SSLCACertificatePath) { ep.setSSLCACertificatePath(SSLCACertificatePath); }
-
-
- /**
- * SSL CA certificate file.
- */
- public String getSSLCACertificateFile() { return ep.getSSLCACertificateFile(); }
- public void setSSLCACertificateFile(String SSLCACertificateFile) { ep.setSSLCACertificateFile(SSLCACertificateFile); }
-
-
- /**
- * SSL CA revocation path.
- */
- public String getSSLCARevocationPath() { return ep.getSSLCARevocationPath(); }
- public void setSSLCARevocationPath(String SSLCARevocationPath) { ep.setSSLCARevocationPath(SSLCARevocationPath); }
-
-
- /**
- * SSL CA revocation file.
- */
- public String getSSLCARevocationFile() { return ep.getSSLCARevocationFile(); }
- public void setSSLCARevocationFile(String SSLCARevocationFile) { ep.setSSLCARevocationFile(SSLCARevocationFile); }
-
-
- /**
- * SSL verify client.
- */
- public String getSSLVerifyClient() { return ep.getSSLVerifyClient(); }
- public void setSSLVerifyClient(String SSLVerifyClient) { ep.setSSLVerifyClient(SSLVerifyClient); }
-
-
- /**
- * SSL verify depth.
- */
- public int getSSLVerifyDepth() { return ep.getSSLVerifyDepth(); }
- public void setSSLVerifyDepth(int SSLVerifyDepth) { ep.setSSLVerifyDepth(SSLVerifyDepth); }
+ public String getKeystoreFile() { return ep.getKeystoreFile();}
+ public void setKeystoreFile(String s ) { ep.setKeystoreFile(s);}
+
+ public String getAlgorithm() { return ep.getAlgorithm();}
+ public void setAlgorithm(String s ) { ep.setAlgorithm(s);}
+
+ public boolean getClientAuth() { return ep.getClientAuth();}
+ public void setClientAuth(boolean b ) { ep.setClientAuth(b);}
+
+ public String getKeystorePass() { return ep.getKeystorePass();}
+ public void setKeystorePass(String s ) { ep.setKeystorePass(s);}
+
+ public String getKeystoreType() { return ep.getKeystoreType();}
+ public void setKeystoreType(String s ) { ep.setKeystoreType(s);}
+
+ public String getSslProtocol() { return ep.getSslProtocol();}
+ public void setSslProtocol(String s) { ep.setSslProtocol(s);}
+
+ public String getCiphers() { return ep.getCiphers();}
+ public void setCiphers(String s) { ep.setCiphers(s);}
+
// -------------------- Connection handler --------------------
protected ThreadLocal<Http11NioProcessor> localProcessor =
new ThreadLocal<Http11NioProcessor>();
- protected ConcurrentHashMap<SocketChannel, Http11NioProcessor> connections =
- new ConcurrentHashMap<SocketChannel, Http11NioProcessor>();
+ protected ConcurrentHashMap<NioChannel, Http11NioProcessor> connections =
+ new ConcurrentHashMap<NioChannel, Http11NioProcessor>();
protected java.util.Stack<Http11NioProcessor> recycledProcessors =
new java.util.Stack<Http11NioProcessor>();
this.proto = proto;
}
- public SocketState event(SocketChannel socket, boolean error) {
+ public SocketState event(NioChannel socket, boolean error) {
Http11NioProcessor result = connections.get(socket);
SocketState state = SocketState.CLOSED;
return state;
}
- public SocketState process(SocketChannel socket) {
+ public SocketState process(NioChannel socket) {
Http11NioProcessor processor = null;
try {
processor = (Http11NioProcessor) localProcessor.get();
}
}
if (processor == null) {
- processor =
- new Http11NioProcessor(proto.maxHttpHeaderSize, proto.ep);
- processor.setAdapter(proto.adapter);
- processor.setMaxKeepAliveRequests(proto.maxKeepAliveRequests);
- processor.setTimeout(proto.timeout);
- processor.setDisableUploadTimeout(proto.disableUploadTimeout);
- processor.setCompression(proto.compression);
- processor.setCompressionMinSize(proto.compressionMinSize);
- processor.setNoCompressionUserAgents(proto.noCompressionUserAgents);
- processor.setCompressableMimeTypes(proto.compressableMimeTypes);
- processor.setRestrictedUserAgents(proto.restrictedUserAgents);
- processor.setSocketBuffer(proto.socketBuffer);
- processor.setMaxSavePostSize(proto.maxSavePostSize);
- processor.setServer(proto.server);
- localProcessor.set(processor);
- if (proto.getDomain() != null) {
- synchronized (this) {
- try {
- RequestInfo rp = processor.getRequest().getRequestProcessor();
- rp.setGlobalProcessor(global);
- ObjectName rpName = new ObjectName
- (proto.getDomain() + ":type=RequestProcessor,worker="
- + proto.getName() + ",name=HttpRequest" + count++);
- Registry.getRegistry(null, null).registerComponent(rp, rpName, null);
- } catch (Exception e) {
- log.warn("Error registering request");
- }
- }
- }
+ processor = createProcessor();
}
if (processor instanceof ActionHook) {
((ActionHook) processor).action(ActionCode.ACTION_START, null);
}
+
+
+ if (proto.ep.getSecure() && (proto.sslImplementation != null)) {
+ if (socket instanceof SecureNioChannel) {
+ SecureNioChannel ch = (SecureNioChannel)socket;
+ processor.setSslSupport(proto.sslImplementation.getSSLSupport(ch.getSslEngine().getSession()));
+ }else processor.setSslSupport(null);
+ } else {
+ processor.setSslSupport(null);
+ }
+
SocketState state = processor.process(socket);
if (state == SocketState.LONG) {
}
return SocketState.CLOSED;
}
+
+ public Http11NioProcessor createProcessor() {
+ Http11NioProcessor processor = new Http11NioProcessor(proto.maxHttpHeaderSize, proto.ep);
+ processor.setAdapter(proto.adapter);
+ processor.setMaxKeepAliveRequests(proto.maxKeepAliveRequests);
+ processor.setTimeout(proto.timeout);
+ processor.setDisableUploadTimeout(proto.disableUploadTimeout);
+ processor.setCompression(proto.compression);
+ processor.setCompressionMinSize(proto.compressionMinSize);
+ processor.setNoCompressionUserAgents(proto.noCompressionUserAgents);
+ processor.setCompressableMimeTypes(proto.compressableMimeTypes);
+ processor.setRestrictedUserAgents(proto.restrictedUserAgents);
+ processor.setSocketBuffer(proto.socketBuffer);
+ processor.setMaxSavePostSize(proto.maxSavePostSize);
+ processor.setServer(proto.server);
+ localProcessor.set(processor);
+ if (proto.getDomain() != null) {
+ synchronized (this) {
+ try {
+ RequestInfo rp = processor.getRequest().getRequestProcessor();
+ rp.setGlobalProcessor(global);
+ ObjectName rpName = new ObjectName
+ (proto.getDomain() + ":type=RequestProcessor,worker="
+ + proto.getName() + ",name=HttpRequest" + count++);
+ Registry.getRegistry(null, null).registerComponent(rp, rpName, null);
+ } catch (Exception e) {
+ log.warn("Error registering request");
+ }
+ }
+ }
+ return processor;
+ }
}
protected static org.apache.commons.logging.Log log
import java.nio.ByteBuffer;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.SelectionKey;
-import java.nio.channels.SocketChannel;
import org.apache.coyote.InputBuffer;
import org.apache.coyote.Request;
import org.apache.tomcat.util.net.NioEndpoint.KeyAttachment;
import org.apache.tomcat.util.net.NioEndpoint.Poller;
import org.apache.tomcat.util.res.StringManager;
+import org.apache.tomcat.util.net.NioChannel;
/**
* Implementation of InputBuffer which provides HTTP request header parsing as
headers = request.getMimeHeaders();
buf = new byte[headerBufferSize];
- if (headerBufferSize < (8 * 1024)) {
- bbuf = ByteBuffer.allocateDirect(6 * 1500);
- } else {
- bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500);
- }
+// if (headerBufferSize < (8 * 1024)) {
+// bbuf = ByteBuffer.allocateDirect(6 * 1500);
+// } else {
+// bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500);
+// }
inputStreamInputBuffer = new SocketInputBuffer();
protected int end;
- /**
- * Direct byte buffer used to perform actual reading.
- */
- protected ByteBuffer bbuf;
-
/**
* Underlying socket.
*/
- protected SocketChannel socket;
+ protected NioChannel socket;
/**
/**
* Set the underlying socket.
*/
- public void setSocket(SocketChannel socket) {
+ public void setSocket(NioChannel socket) {
this.socket = socket;
}
/**
* Get the underlying socket input stream.
*/
- public SocketChannel getSocket() {
+ public NioChannel getSocket() {
return socket;
}
long start = System.currentTimeMillis();
boolean timedOut = false;
do {
- bbuf.clear();
- nRead = socket.read(bbuf);
+
+ socket.getBufHandler().getReadBuffer().clear();
+ nRead = socket.read(socket.getBufHandler().getReadBuffer());
if (nRead > 0) {
- bbuf.flip();
- bbuf.limit(nRead);
+ socket.getBufHandler().getReadBuffer().flip();
+ socket.getBufHandler().getReadBuffer().limit(nRead);
expand(nRead + pos);
- bbuf.get(buf, pos, nRead);
+ socket.getBufHandler().getReadBuffer().get(buf, pos, nRead);
lastValid = pos + nRead;
return true;
} else if (nRead == -1) {
timedOut = (readTimeout != -1) && ((System.currentTimeMillis()-start)>this.readTimeout);
if ( !timedOut && nRead == 0 )
try {
- final SelectionKey key = socket.keyFor(poller.getSelector());
+ final SelectionKey key = socket.getIOChannel().keyFor(poller.getSelector());
final KeyAttachment att = (KeyAttachment)key.attachment();
//to do, add in a check, we might have just timed out on the wait,
//so there is no need to register us again.
KeyAttachment ka = (KeyAttachment)key.attachment();
ka.setError(true); //set to collect this socket immediately
}
- socket.socket().close();
+ socket.getIOChannel().socket().close();
socket.close();
att.setWakeUp(false);
} catch (Exception ignore) {}
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.nio.channels.SocketChannel;
import org.apache.coyote.ActionCode;
import org.apache.coyote.OutputBuffer;
import java.nio.channels.SelectionKey;
import org.apache.tomcat.util.net.NioEndpoint;
import java.nio.channels.Selector;
+import org.apache.tomcat.util.net.NioChannel;
/**
* Output buffer.
} else {
bbufLimit = (headerBufferSize / 1500 + 1) * 1500;
}
- bbuf = ByteBuffer.allocateDirect(bbufLimit);
+ //bbuf = ByteBuffer.allocateDirect(bbufLimit);
outputStreamOutputBuffer = new SocketOutputBuffer();
/**
* Underlying socket.
*/
- protected SocketChannel socket;
+ protected NioChannel socket;
/**
protected int lastActiveFilter;
- /**
- * Direct byte buffer used for writing.
- */
- protected ByteBuffer bbuf = null;
-
// ------------------------------------------------------------- Properties
/**
* Set the underlying socket.
*/
- public void setSocket(SocketChannel socket) {
+ public void setSocket(NioChannel socket) {
this.socket = socket;
}
/**
* Get the underlying socket input stream.
*/
- public SocketChannel getSocket() {
+ public NioChannel getSocket() {
return socket;
}
/**
// Recycle Request object
response.recycle();
- bbuf.clear();
+ socket.getBufHandler().getWriteBuffer().clear();
socket = null;
pos = 0;
while ( bytebuffer.hasRemaining() ) {
int written = socket.write(bytebuffer);
}
- bbuf.clear();
+ socket.getBufHandler().getWriteBuffer().clear();
this.total = 0;
}
int total = 0;
private synchronized void addToBB(byte[] buf, int offset, int length) throws IOException {
- if (bbuf.capacity() <= (offset + length)) {
+ if (socket.getBufHandler().getWriteBuffer().capacity() <= (offset + length)) {
flushBuffer();
}
- bbuf.put(buf, offset, length);
+ socket.getBufHandler().getWriteBuffer().put(buf, offset, length);
total += length;
}
throws IOException {
//prevent timeout for async,
- SelectionKey key = socket.keyFor(selector);
+ SelectionKey key = socket.getIOChannel().keyFor(selector);
if (key != null) {
NioEndpoint.KeyAttachment attach = (NioEndpoint.KeyAttachment) key.attachment();
attach.access();
}
//write to the socket, if there is anything to write
- if (bbuf.position() > 0) {
- writeToSocket(bbuf,true);
+ if (socket.getBufHandler().getWriteBuffer().position() > 0) {
+ writeToSocket(socket.getBufHandler().getWriteBuffer(),true);
}
}
byte[] b = chunk.getBuffer();
while (len > 0) {
int thisTime = len;
- if (bbuf.position() == bbuf.capacity()) {
+ if (socket.getBufHandler().getWriteBuffer().position() == socket.getBufHandler().getWriteBuffer().capacity()) {
flushBuffer();
}
- if (thisTime > bbuf.capacity() - bbuf.position()) {
- thisTime = bbuf.capacity() - bbuf.position();
+ if (thisTime > socket.getBufHandler().getWriteBuffer().capacity() - socket.getBufHandler().getWriteBuffer().position()) {
+ thisTime = socket.getBufHandler().getWriteBuffer().capacity() - socket.getBufHandler().getWriteBuffer().position();
}
addToBB(b,start,thisTime);
len = len - thisTime;
--- /dev/null
+/*
+ * Copyright 2005-2006 The Apache Software Foundation
+ *
+ * Licensed 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.apache.tomcat.util.net;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ByteChannel;
+import java.nio.channels.SocketChannel;
+
+import org.apache.tomcat.util.net.SecureNioChannel.ApplicationBufferHandler;
+/**
+ *
+ * Base class for a SocketChannel wrapper used by the endpoint.
+ * This way, logic for a SSL socket channel remains the same as for
+ * a non SSL, making sure we don't need to code for any exception cases.
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class NioChannel implements ByteChannel{
+
+ protected static ByteBuffer emptyBuf = ByteBuffer.allocate(0);
+
+ protected SocketChannel sc = null;
+
+ protected ApplicationBufferHandler bufHandler;
+
+ public NioChannel(SocketChannel channel, ApplicationBufferHandler bufHandler) throws IOException {
+ this.sc = channel;
+ this.bufHandler = bufHandler;
+ }
+
+
+ /**
+ * Closes this channel.
+ *
+ * @throws IOException If an I/O error occurs
+ * @todo Implement this java.nio.channels.Channel method
+ */
+ public void close() throws IOException {
+ sc.close();
+ }
+
+ public void close(boolean force) throws IOException {
+ close();
+ }
+ /**
+ * Tells whether or not this channel is open.
+ *
+ * @return <tt>true</tt> if, and only if, this channel is open
+ * @todo Implement this java.nio.channels.Channel method
+ */
+ public boolean isOpen() {
+ return sc.isOpen();
+ }
+
+ /**
+ * Writes a sequence of bytes to this channel from the given buffer.
+ *
+ * @param src The buffer from which bytes are to be retrieved
+ * @return The number of bytes written, possibly zero
+ * @throws IOException If some other I/O error occurs
+ * @todo Implement this java.nio.channels.WritableByteChannel method
+ */
+ public int write(ByteBuffer src) throws IOException {
+ return sc.write(src);
+ }
+
+ /**
+ * Reads a sequence of bytes from this channel into the given buffer.
+ *
+ * @param dst The buffer into which bytes are to be transferred
+ * @return The number of bytes read, possibly zero, or <tt>-1</tt> if the channel has reached end-of-stream
+ * @throws IOException If some other I/O error occurs
+ * @todo Implement this java.nio.channels.ReadableByteChannel method
+ */
+ public int read(ByteBuffer dst) throws IOException {
+ return sc.read(dst);
+ }
+
+
+ /**
+ * getBufHandler
+ *
+ * @return ApplicationBufferHandler
+ * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method
+ */
+ public ApplicationBufferHandler getBufHandler() {
+ return bufHandler;
+ }
+
+ /**
+ * getIOChannel
+ *
+ * @return SocketChannel
+ * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method
+ */
+ public SocketChannel getIOChannel() {
+ return sc;
+ }
+
+ /**
+ * isClosing
+ *
+ * @return boolean
+ * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method
+ */
+ public boolean isClosing() {
+ return false;
+ }
+
+ /**
+ * isInitHandshakeComplete
+ *
+ * @return boolean
+ * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method
+ */
+ public boolean isInitHandshakeComplete() {
+ return true;
+ }
+
+ public int handshake(boolean read, boolean write) throws IOException {
+ return 0;
+ }
+
+
+}
\ No newline at end of file
package org.apache.tomcat.util.net;
+import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
import java.nio.channels.CancelledKeyException;
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.Iterator;
import java.util.LinkedList;
import java.util.Set;
import java.util.concurrent.Executor;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.TrustManagerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.apache.tomcat.jni.SSL;
+import org.apache.tomcat.util.net.SecureNioChannel.ApplicationBufferHandler;
import org.apache.tomcat.util.res.StringManager;
/**
protected int sequence = 0;
- /**
- * Root APR memory pool.
- */
- protected long rootPool = 0;
-
-
+ protected int readBufSize = 8192;
+ protected int writeBufSize = 8192;
+
/**
* Server socket "pointer".
*/
protected long serverSockPool = 0;
- /**
- * SSL context.
- */
- protected long sslContext = 0;
-
+
// ------------------------------------------------------------- Properties
*/
public int getMinSpareThreads() { return 0; }
+ // -------------------- SSL related properties --------------------
+ protected String keystoreFile = System.getProperty("user.home")+"/.keystore";
+ public String getKeystoreFile() { return keystoreFile;}
+ public void setKeystoreFile(String s ) { this.keystoreFile = s;}
- /**
- * SSL engine.
- */
- protected String SSLEngine = "off";
- public String getSSLEngine() { return SSLEngine; }
- public void setSSLEngine(String SSLEngine) { this.SSLEngine = SSLEngine; }
-
-
- /**
- * SSL protocols.
- */
- protected String SSLProtocol = "all";
- public String getSSLProtocol() { return SSLProtocol; }
- public void setSSLProtocol(String SSLProtocol) { this.SSLProtocol = SSLProtocol; }
-
-
- /**
- * SSL password (if a cert is encrypted, and no password has been provided, a callback
- * will ask for a password).
- */
- protected String SSLPassword = null;
- public String getSSLPassword() { return SSLPassword; }
- public void setSSLPassword(String SSLPassword) { this.SSLPassword = SSLPassword; }
-
-
- /**
- * SSL cipher suite.
- */
- protected String SSLCipherSuite = "ALL";
- public String getSSLCipherSuite() { return SSLCipherSuite; }
- public void setSSLCipherSuite(String SSLCipherSuite) { this.SSLCipherSuite = SSLCipherSuite; }
-
-
- /**
- * SSL certificate file.
- */
- protected String SSLCertificateFile = null;
- public String getSSLCertificateFile() { return SSLCertificateFile; }
- public void setSSLCertificateFile(String SSLCertificateFile) { this.SSLCertificateFile = SSLCertificateFile; }
-
-
- /**
- * SSL certificate key file.
- */
- protected String SSLCertificateKeyFile = null;
- public String getSSLCertificateKeyFile() { return SSLCertificateKeyFile; }
- public void setSSLCertificateKeyFile(String SSLCertificateKeyFile) { this.SSLCertificateKeyFile = SSLCertificateKeyFile; }
-
-
- /**
- * SSL certificate chain file.
- */
- protected String SSLCertificateChainFile = null;
- public String getSSLCertificateChainFile() { return SSLCertificateChainFile; }
- public void setSSLCertificateChainFile(String SSLCertificateChainFile) { this.SSLCertificateChainFile = SSLCertificateChainFile; }
-
-
- /**
- * SSL CA certificate path.
- */
- protected String SSLCACertificatePath = null;
- public String getSSLCACertificatePath() { return SSLCACertificatePath; }
- public void setSSLCACertificatePath(String SSLCACertificatePath) { this.SSLCACertificatePath = SSLCACertificatePath; }
-
-
- /**
- * SSL CA certificate file.
- */
- protected String SSLCACertificateFile = null;
- public String getSSLCACertificateFile() { return SSLCACertificateFile; }
- public void setSSLCACertificateFile(String SSLCACertificateFile) { this.SSLCACertificateFile = SSLCACertificateFile; }
-
-
- /**
- * SSL CA revocation path.
- */
- protected String SSLCARevocationPath = null;
- public String getSSLCARevocationPath() { return SSLCARevocationPath; }
- public void setSSLCARevocationPath(String SSLCARevocationPath) { this.SSLCARevocationPath = SSLCARevocationPath; }
-
-
- /**
- * SSL CA revocation file.
- */
- protected String SSLCARevocationFile = null;
- public String getSSLCARevocationFile() { return SSLCARevocationFile; }
- public void setSSLCARevocationFile(String SSLCARevocationFile) { this.SSLCARevocationFile = SSLCARevocationFile; }
-
+ protected String algorithm = "SunX509";
+ public String getAlgorithm() { return algorithm;}
+ public void setAlgorithm(String s ) { this.algorithm = s;}
- /**
- * SSL verify client.
- */
- protected String SSLVerifyClient = "none";
- public String getSSLVerifyClient() { return SSLVerifyClient; }
- public void setSSLVerifyClient(String SSLVerifyClient) { this.SSLVerifyClient = SSLVerifyClient; }
+ protected boolean clientAuth = false;
+ public boolean getClientAuth() { return clientAuth;}
+ public void setClientAuth(boolean b ) { this.clientAuth = b;}
+
+ protected String keystorePass = "changeit";
+ public String getKeystorePass() { return keystorePass;}
+ public void setKeystorePass(String s ) { this.keystorePass = s;}
+
+ protected String keystoreType = "JKS";
+ public String getKeystoreType() { return keystoreType;}
+ public void setKeystoreType(String s ) { this.keystoreType = s;}
+ protected String sslProtocol = "TLS";
+ public String getSslProtocol() { return sslProtocol;}
+ public void setSslProtocol(String s) { sslProtocol = s;}
+
+ protected String ciphers = null;
+ public String getCiphers() { return ciphers;}
+ public void setCiphers(String s) { ciphers = s;}
+
+ protected boolean secure = false;
+ public boolean getSecure() { return secure;}
+ public void setSecure(boolean b) { secure = b;}
- /**
- * SSL verify depth.
- */
- protected int SSLVerifyDepth = 10;
- public int getSSLVerifyDepth() { return SSLVerifyDepth; }
- public void setSSLVerifyDepth(int SSLVerifyDepth) { this.SSLVerifyDepth = SSLVerifyDepth; }
+ public void setWriteBufSize(int writeBufSize) {
+ this.writeBufSize = writeBufSize;
+ }
+ public void setReadBufSize(int readBufSize) {
+ this.readBufSize = readBufSize;
+ }
+ protected SSLContext sslContext = null;
+ public SSLContext getSSLContext() { return sslContext;}
+ public void setSSLContext(SSLContext c) { sslContext = c;}
+
// --------------------------------------------------------- Public Methods
}
// Initialize SSL if needed
- if (!"off".equalsIgnoreCase(SSLEngine)) {
+ if (secure) {
// Initialize SSL
- // FIXME: one per VM call ?
- if ("on".equalsIgnoreCase(SSLEngine)) {
- //SSL.initialize(null);
- } else {
- //SSL.initialize(SSLEngine);
- }
- // SSL protocol
- int value = SSL.SSL_PROTOCOL_ALL;
- if ("SSLv2".equalsIgnoreCase(SSLProtocol)) {
- value = SSL.SSL_PROTOCOL_SSLV2;
- } else if ("SSLv3".equalsIgnoreCase(SSLProtocol)) {
- value = SSL.SSL_PROTOCOL_SSLV3;
- } else if ("TLSv1".equalsIgnoreCase(SSLProtocol)) {
- value = SSL.SSL_PROTOCOL_TLSV1;
- } else if ("SSLv2+SSLv3".equalsIgnoreCase(SSLProtocol)) {
- value = SSL.SSL_PROTOCOL_SSLV2 | SSL.SSL_PROTOCOL_SSLV3;
- }
-// // Create SSL Context
-// sslContext = SSLContext.make(rootPool, value, SSL.SSL_MODE_SERVER);
-// // List the ciphers that the client is permitted to negotiate
-// SSLContext.setCipherSuite(sslContext, SSLCipherSuite);
-// // Load Server key and certificate
-// SSLContext.setCertificate(sslContext, SSLCertificateFile, SSLCertificateKeyFile, SSLPassword, SSL.SSL_AIDX_RSA);
-// // Set certificate chain file
-// SSLContext.setCertificateChainFile(sslContext, SSLCertificateChainFile, false);
-// // Support Client Certificates
-// SSLContext.setCACertificate(sslContext, SSLCACertificateFile, SSLCACertificatePath);
-// // Set revocation
-// SSLContext.setCARevocation(sslContext, SSLCARevocationFile, SSLCARevocationPath);
-// // Client certificate verification
-// value = SSL.SSL_CVERIFY_NONE;
-// if ("optional".equalsIgnoreCase(SSLVerifyClient)) {
-// value = SSL.SSL_CVERIFY_OPTIONAL;
-// } else if ("require".equalsIgnoreCase(SSLVerifyClient)) {
-// value = SSL.SSL_CVERIFY_REQUIRE;
-// } else if ("optionalNoCA".equalsIgnoreCase(SSLVerifyClient)) {
-// value = SSL.SSL_CVERIFY_OPTIONAL_NO_CA;
-// }
-// SSLContext.setVerify(sslContext, value, SSLVerifyDepth);
+ char[] passphrase = getKeystorePass().toCharArray();
+
+ KeyStore ks = KeyStore.getInstance(getKeystoreType());
+ ks.load(new FileInputStream(getKeystoreFile()), passphrase);
+ KeyStore ts = KeyStore.getInstance(getKeystoreType());
+ ts.load(new FileInputStream(getKeystoreFile()), passphrase);
+
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(getAlgorithm());
+ kmf.init(ks, passphrase);
+
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(getAlgorithm());
+ tmf.init(ts);
+
+ sslContext = SSLContext.getInstance(getSslProtocol());
+ sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+
}
initialized = true;
serverSock.socket().close();
serverSock.close();
serverSock = null;
- sslContext = 0;
+ sslContext = null;
initialized = false;
}
return sequence++;
}
+ public int getWriteBufSize() {
+ return writeBufSize;
+ }
+
+ public int getReadBufSize() {
+ return readBufSize;
+ }
/**
* Unlock the server socket accept using a bugus connection.
if (soTimeout > 0)
socket.socket().setSoTimeout(soTimeout);
-
- // 2: SSL handshake
+ NioChannel channel = null;
+ // 2: SSL setup
step = 2;
- if (sslContext != 0) {
-// SSLSocket.attach(sslContext, socket);
-// if (SSLSocket.handshake(socket) != 0) {
-// if (log.isDebugEnabled()) {
-// log.debug(sm.getString("endpoint.err.handshake") + ": " + SSL.getLastError());
-// }
-// return false;
-// }
+ if (sslContext != null) {
+ SSLEngine engine = sslContext.createSSLEngine();
+ engine.setNeedClientAuth(getClientAuth());
+ engine.setUseClientMode(false);
+ int appbufsize = engine.getSession().getApplicationBufferSize();
+ int bufsize = Math.max(Math.max(getReadBufSize(),getWriteBufSize()),appbufsize);
+ NioBufferHandler bufhandler = new NioBufferHandler(bufsize,bufsize);
+ channel = new SecureNioChannel(socket,engine,bufhandler);
+
+ } else {
+ NioBufferHandler bufhandler = new NioBufferHandler(getReadBufSize(),getWriteBufSize());
+ channel = new NioChannel(socket,bufhandler);
}
- getPoller().register(socket);
+ getPoller().register(channel);
} catch (Throwable t) {
if (log.isDebugEnabled()) {
/**
* Process given socket.
*/
- protected boolean processSocket(SocketChannel socket) {
+ protected boolean processSocket(NioChannel socket) {
try {
if (executor == null) {
getWorkerThread().assign(socket);
/**
* Process given socket for an event.
*/
- protected boolean processSocket(SocketChannel socket, boolean error) {
+ protected boolean processSocket(NioChannel socket, boolean error) {
try {
if (executor == null) {
getWorkerThread().assign(socket, error);
*
* @param socket to add to the poller
*/
- public void add(final SocketChannel socket) {
- final SelectionKey key = socket.keyFor(selector);
+ public void add(final NioChannel socket) {
+ final SelectionKey key = socket.getIOChannel().keyFor(selector);
KeyAttachment att = (KeyAttachment)key.attachment();
if ( att != null ) att.setWakeUp(false);
Runnable r = new Runnable() {
KeyAttachment ka = (KeyAttachment)key.attachment();
ka.setError(true); //set to collect this socket immediately
}
- socket.socket().close();
+ socket.getIOChannel().socket().close();
socket.close();
} catch ( Exception ignore ) {}
}
return result;
}
- public void register(final SocketChannel socket)
+ public void register(final NioChannel socket)
{
- SelectionKey key = socket.keyFor(selector);
+ SelectionKey key = socket.getIOChannel().keyFor(selector);
Runnable r = new Runnable() {
public void run() {
try {
- socket.register(selector, SelectionKey.OP_READ, new KeyAttachment());
+ KeyAttachment ka = new KeyAttachment();
+ ka.setChannel(socket);
+ socket.getIOChannel().register(selector, SelectionKey.OP_READ, ka);
} catch (Exception x) {
log.error("", x);
}
try {
KeyAttachment ka = (KeyAttachment) key.attachment();
key.cancel();
- if (ka != null && ka.getComet()) processSocket( (SocketChannel) key.channel(), true);
+ if (ka != null && ka.getComet()) processSocket( ka.getChannel(), true);
key.channel().close();
} catch (IOException e) {
if ( log.isDebugEnabled() ) log.debug("",e);
sk.attach(attachment);
int readyOps = sk.readyOps();
sk.interestOps(sk.interestOps() & ~readyOps);
- SocketChannel channel = (SocketChannel)sk.channel();
- boolean read = sk.isReadable();
- if (read) {
+ NioChannel channel = attachment.getChannel();
+ if (sk.isReadable() || sk.isWritable() ) {
if ( attachment.getWakeUp() ) {
attachment.setWakeUp(false);
synchronized (attachment.getMutex()) {attachment.getMutex().notifyAll();}
} else {
boolean close = (!processSocket(channel));
if ( close ) {
- channel.socket().close();
+ channel.getIOChannel().socket().close();
channel.close();
}
}
- }
+ }
} else {
//invalid key
cancelledKey(sk);
}
} catch ( CancelledKeyException ckx ) {
- if (attachment!=null && attachment.getComet()) processSocket( (SocketChannel) sk.channel(), true);
+ if (attachment!=null && attachment.getComet()) processSocket( attachment.getChannel(), true);
try {
sk.channel().close();
}catch ( Exception ignore){}
public long getTimeout() {return this.timeout;}
public boolean getError() { return error; }
public void setError(boolean error) { this.error = error; }
+ public NioChannel getChannel() { return channel;}
+ public void setChannel(NioChannel channel) { this.channel = channel;}
protected Object mutex = new Object();
protected boolean wakeUp = false;
protected long lastAccess = System.currentTimeMillis();
protected boolean comet = false;
protected long timeout = -1;
protected boolean error = false;
+ protected NioChannel channel = null;
}
protected Thread thread = null;
protected boolean available = false;
- protected SocketChannel socket = null;
+ protected NioChannel socket = null;
protected boolean event = false;
protected boolean error = false;
*
* @param socket TCP socket to process
*/
- protected synchronized void assign(SocketChannel socket) {
+ protected synchronized void assign(NioChannel socket) {
// Wait for the Processor to get the previous Socket
while (available) {
}
- protected synchronized void assign(SocketChannel socket, boolean error) {
+ protected synchronized void assign(NioChannel socket, boolean error) {
// Wait for the Processor to get the previous Socket
while (available) {
* Await a newly assigned Socket from our Connector, or <code>null</code>
* if we are supposed to shut down.
*/
- protected synchronized SocketChannel await() {
+ protected synchronized NioChannel await() {
// Wait for the Connector to provide a new Socket
while (!available) {
}
// Notify the Connector that we have received this Socket
- SocketChannel socket = this.socket;
+ NioChannel socket = this.socket;
available = false;
notifyAll();
// Process requests until we receive a shutdown signal
while (running) {
// Wait for the next socket to be assigned
- SocketChannel socket = await();
+ NioChannel socket = await();
if (socket == null)
continue;
-
- // Process the request from this socket
- if ((event) && (handler.event(socket, error) == Handler.SocketState.CLOSED)) {
- // Close socket and pool
- try {
- socket.socket().close();
- socket.close();
- }catch ( Exception x ) {
- log.error("",x);
- }
- } else if ((!event) && (handler.process(socket) == Handler.SocketState.CLOSED)) {
- // Close socket and pool
- try {
- socket.socket().close();
- socket.close();
- }catch ( Exception x ) {
- log.error("",x);
+ SelectionKey key = socket.getIOChannel().keyFor(getPoller().getSelector());
+ int handshake = -1;
+ try {
+ handshake = socket.handshake(key.isReadable(), key.isWritable());
+ }catch ( IOException x ) {
+ handshake = -1;
+ log.error("Error during SSL handshake",x);
+ }
+ if ( handshake == 0 ) {
+ // Process the request from this socket
+ if ((event) && (handler.event(socket, error) == Handler.SocketState.CLOSED)) {
+ // Close socket and pool
+ try {
+ socket.getIOChannel().socket().close();
+ socket.close();
+ }catch ( Exception x ) {
+ log.error("",x);
+ }
+ } else if ((!event) && (handler.process(socket) == Handler.SocketState.CLOSED)) {
+ // Close socket and pool
+ try {
+ socket.getIOChannel().socket().close();
+ socket.close();
+ }catch ( Exception x ) {
+ log.error("",x);
+ }
}
+ } else if (handshake == -1 ) {
+ key.cancel();
+ try {socket.close(true);}catch (IOException ignore){}
+ } else {
+ final SelectionKey fk = key;
+ final int intops = handshake;
+ //register for handshake ops
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ fk.interestOps(intops);
+ } catch (CancelledKeyException ckx) {
+ try {
+ if ( fk != null && fk.attachment() != null ) {
+ KeyAttachment ka = (KeyAttachment)fk.attachment();
+ ka.setError(true); //set to collect this socket immediately
+ try {ka.getChannel().getIOChannel().socket().close();}catch(Exception ignore){}
+ try {ka.getChannel().close();}catch(Exception ignore){}
+ ka.setWakeUp(false);
+ }
+ } catch (Exception ignore) {}
+ }
+
+ }
+ };
+ getPoller().addEvent(r);
}
//dereference socket to let GC do its job
socket = null;
}
+ // ------------------------------------------------ Application Buffer Handler
+ public class NioBufferHandler implements ApplicationBufferHandler {
+ protected ByteBuffer readbuf = null;
+ protected ByteBuffer writebuf = null;
+
+ public NioBufferHandler(int readsize, int writesize) {
+ readbuf = ByteBuffer.allocateDirect(readsize);
+ writebuf = ByteBuffer.allocateDirect(writesize);
+ }
+
+ public ByteBuffer expand(ByteBuffer buffer, int remaining) {return buffer;}
+ public ByteBuffer getReadBuffer() {return readbuf;}
+ public ByteBuffer getWriteBuffer() {return writebuf;}
+ }
// ------------------------------------------------ Handler Inner Interface
public enum SocketState {
OPEN, CLOSED, LONG
}
- public SocketState process(SocketChannel socket);
- public SocketState event(SocketChannel socket, boolean error);
+ public SocketState process(NioChannel socket);
+ public SocketState event(NioChannel socket, boolean error);
}
*/
protected class SocketProcessor implements Runnable {
- protected SocketChannel socket = null;
+ protected NioChannel socket = null;
- public SocketProcessor(SocketChannel socket) {
+ public SocketProcessor(NioChannel socket) {
this.socket = socket;
}
if (handler.process(socket) == Handler.SocketState.CLOSED) {
// Close socket and pool
try {
- socket.socket().close();
+ socket.getIOChannel().socket().close();
socket.close();
} catch ( Exception x ) {
log.error("",x);
*/
protected class SocketEventProcessor implements Runnable {
- protected SocketChannel socket = null;
+ protected NioChannel socket = null;
protected boolean error = false;
- public SocketEventProcessor(SocketChannel socket, boolean error) {
+ public SocketEventProcessor(NioChannel socket, boolean error) {
this.socket = socket;
this.error = error;
}
if (handler.event(socket, error) == Handler.SocketState.CLOSED) {
// Close socket and pool
try {
- socket.socket().close();
+ socket.getIOChannel().socket().close();
socket.close();
} catch ( Exception x ) {
log.error("",x);
package org.apache.tomcat.util.net;
import java.net.Socket;
+import javax.net.ssl.SSLSession;
/* SSLImplementation:
abstract public String getImplementationName();
abstract public ServerSocketFactory getServerSocketFactory();
abstract public SSLSupport getSSLSupport(Socket sock);
+ abstract public SSLSupport getSSLSupport(SSLSession session);
}
--- /dev/null
+package org.apache.tomcat.util.net;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLEngineResult.Status;
+
+/**
+ *
+ * Implementation of a secure socket channel
+ * @author Filip Hanik
+ * @version 1.0
+ */
+
+public class SecureNioChannel extends NioChannel {
+
+ protected ByteBuffer netInBuffer;
+ protected ByteBuffer netOutBuffer;
+
+ protected SSLEngine sslEngine;
+
+ protected boolean initHandshakeComplete = false;
+ protected HandshakeStatus initHandshakeStatus; //gets set by begin handshake
+
+ protected boolean closed = false;
+ protected boolean closing = false;
+
+ public SecureNioChannel(SocketChannel channel, SSLEngine engine, ApplicationBufferHandler bufHandler) throws IOException {
+ super(channel,bufHandler);
+
+ this.sslEngine = engine;
+
+
+ int appBufSize = sslEngine.getSession().getApplicationBufferSize();
+ int netBufSize = sslEngine.getSession().getPacketBufferSize();
+
+ //ensure that the application has a large enough read/write buffers
+ //by doing this, we should not encounter any buffer overflow errors
+ bufHandler.expand(bufHandler.getReadBuffer(), appBufSize);
+ bufHandler.expand(bufHandler.getWriteBuffer(), appBufSize);
+ //allocate network buffers - TODO, add in optional direct buffers
+ this.netInBuffer = ByteBuffer.allocate(netBufSize);
+ this.netOutBuffer = ByteBuffer.allocate(netBufSize);
+ this.netOutBuffer.position(0);
+ this.netOutBuffer.limit(0);
+ this.netInBuffer.position(0);
+ this.netInBuffer.limit(0);
+
+ //initiate handshake
+ sslEngine.beginHandshake();
+ initHandshakeStatus = sslEngine.getHandshakeStatus();
+ }
+
+//===========================================================================================
+// NIO SSL METHODS
+//===========================================================================================
+
+ /**
+ * Flushes the buffer to the network
+ * @param buf ByteBuffer
+ * @return boolean true if the buffer has been emptied out, false otherwise
+ * @throws IOException
+ */
+ protected boolean flush(ByteBuffer buf) throws IOException {
+ int remaining = buf.remaining();
+ if ( remaining > 0 ) {
+ int written = sc.write(buf);
+ return written >= remaining;
+ }else {
+ return true;
+ }
+ }
+
+ /**
+ * Performs SSL handshake, non blocking, but performs NEED_TASK on the same thread.<br>
+ * Hence, you should never call this method using your Acceptor thread, as you would slow down
+ * your system significantly.<br>
+ * The return for this operation is 0 if the handshake is complete and a positive value if it is not complete.
+ * In the event of a positive value coming back, reregister the selection key for the return values interestOps.
+ * @param read boolean - true if the underlying channel is readable
+ * @param write boolean - true if the underlying channel is writable
+ * @return int - 0 if hand shake is complete, otherwise it returns a SelectionKey interestOps value
+ * @throws IOException
+ */
+ public int handshake(boolean read, boolean write) throws IOException {
+ if ( initHandshakeComplete ) return 0; //we have done our initial handshake
+
+ if (!flush(netOutBuffer)) return SelectionKey.OP_WRITE; //we still have data to write
+
+ SSLEngineResult handshake = null;
+
+ while (!initHandshakeComplete) {
+ switch ( initHandshakeStatus ) {
+ case NOT_HANDSHAKING: {
+ //should never happen
+ throw new IOException("NOT_HANDSHAKING during handshake");
+ }
+ case FINISHED: {
+ //we are complete if we have delivered the last package
+ initHandshakeComplete = !netOutBuffer.hasRemaining();
+ //return 0 if we are complete, otherwise we still have data to write
+ return initHandshakeComplete?0:SelectionKey.OP_WRITE;
+ }
+ case NEED_WRAP: {
+ //perform the wrap function
+ handshake = handshakeWrap(write);
+ if ( handshake.getStatus() == Status.OK ){
+ if (initHandshakeStatus == HandshakeStatus.NEED_TASK)
+ initHandshakeStatus = tasks();
+ } else {
+ //wrap should always work with our buffers
+ throw new IOException("Unexpected status:" + handshake.getStatus() + " during handshake WRAP.");
+ }
+ if ( initHandshakeStatus != HandshakeStatus.NEED_UNWRAP || (!flush(netOutBuffer)) ) {
+ //should actually return OP_READ if we have NEED_UNWRAP
+ return SelectionKey.OP_WRITE;
+ }
+ //fall down to NEED_UNWRAP on the same call, will result in a
+ //BUFFER_UNDERFLOW if it needs data
+ }
+ case NEED_UNWRAP: {
+ //perform the unwrap function
+ handshake = handshakeUnwrap(read);
+ if ( handshake.getStatus() == Status.OK ) {
+ if (initHandshakeStatus == HandshakeStatus.NEED_TASK)
+ initHandshakeStatus = tasks();
+ } else if ( handshake.getStatus() == Status.BUFFER_UNDERFLOW ){
+ //read more data, reregister for OP_READ
+ return SelectionKey.OP_READ;
+ } else {
+ throw new IOException("Invalid handshake status:"+initHandshakeStatus+" during handshake UNWRAP.");
+ }//switch
+ break;
+ }
+ case NEED_TASK: {
+ initHandshakeStatus = tasks();
+ break;
+ }
+ default: throw new IllegalStateException("Invalid handshake status:"+initHandshakeStatus);
+ }//switch
+ }//while
+ //return 0 if we are complete, otherwise reregister for any activity that
+ //would cause this method to be called again.
+ return initHandshakeComplete?0:(SelectionKey.OP_WRITE|SelectionKey.OP_READ);
+ }
+
+ /**
+ * Executes all the tasks needed on the same thread.
+ * @return HandshakeStatus
+ */
+ protected SSLEngineResult.HandshakeStatus tasks() {
+ Runnable r = null;
+ while ( (r = sslEngine.getDelegatedTask()) != null) {
+ r.run();
+ }
+ return sslEngine.getHandshakeStatus();
+ }
+
+ /**
+ * Performs the WRAP function
+ * @param doWrite boolean
+ * @return SSLEngineResult
+ * @throws IOException
+ */
+ protected SSLEngineResult handshakeWrap(boolean doWrite) throws IOException {
+ //this should never be called with a network buffer that contains data
+ //so we can clear it here.
+ netOutBuffer.clear();
+ //perform the wrap
+ SSLEngineResult result = sslEngine.wrap(bufHandler.getWriteBuffer(), netOutBuffer);
+ //prepare the results to be written
+ netOutBuffer.flip();
+ //set the status
+ initHandshakeStatus = result.getHandshakeStatus();
+ //optimization, if we do have a writable channel, write it now
+ if ( doWrite ) flush(netOutBuffer);
+ return result;
+ }
+
+ /**
+ * Perform handshake unwrap
+ * @param doread boolean
+ * @return SSLEngineResult
+ * @throws IOException
+ */
+ protected SSLEngineResult handshakeUnwrap(boolean doread) throws IOException {
+
+ if (netInBuffer.position() == netInBuffer.limit()) {
+ //clear the buffer if we have emptied it out on data
+ netInBuffer.clear();
+ }
+ if ( doread ) {
+ //if we have data to read, read it
+ int read = sc.read(netInBuffer);
+ if (read == -1) throw new IOException("EOF encountered during handshake.");
+ }
+ SSLEngineResult result;
+ boolean cont = false;
+ //loop while we can perform pure SSLEngine data
+ do {
+ //prepare the buffer with the incoming data
+ netInBuffer.flip();
+ //call unwrap
+ result = sslEngine.unwrap(netInBuffer, bufHandler.getReadBuffer());
+ //compact the buffer, this is an optional method, wonder what would happen if we didn't
+ netInBuffer.compact();
+ //read in the status
+ initHandshakeStatus = result.getHandshakeStatus();
+ if ( result.getStatus() == SSLEngineResult.Status.OK &&
+ result.getHandshakeStatus() == HandshakeStatus.NEED_TASK ) {
+ //execute tasks if we need to
+ initHandshakeStatus = tasks();
+ }
+ //perform another unwrap?
+ cont = result.getStatus() == SSLEngineResult.Status.OK &&
+ initHandshakeStatus == HandshakeStatus.NEED_UNWRAP;
+ }while ( cont );
+ return result;
+ }
+
+ /**
+ * Sends a SSL close message, will not physically close the connection here.<br>
+ * To close the connection, you could do something like
+ * <pre><code>
+ * close();
+ * while (isOpen() && !myTimeoutFunction()) Thread.sleep(25);
+ * if ( isOpen() ) close(true); //forces a close if you timed out
+ * </code></pre>
+ * @throws IOException if an I/O error occurs
+ * @throws IOException if there is data on the outgoing network buffer and we are unable to flush it
+ * @todo Implement this java.io.Closeable method
+ */
+ public void close() throws IOException {
+ if (closing) return;
+ closing = true;
+ sslEngine.closeOutbound();
+
+ if (!flush(netOutBuffer)) {
+ throw new IOException("Remaining data in the network buffer, can't send SSL close message, force a close with close(true) instead");
+ }
+ //prep the buffer for the close message
+ netOutBuffer.clear();
+ //perform the close, since we called sslEngine.closeOutbound
+ SSLEngineResult handshake = sslEngine.wrap(getEmptyBuf(), netOutBuffer);
+ //we should be in a close state
+ if (handshake.getStatus() != SSLEngineResult.Status.CLOSED) {
+ throw new IOException("Invalid close state, will not send network data.");
+ }
+ //prepare the buffer for writing
+ netOutBuffer.flip();
+ //if there is data to be written
+ flush(netOutBuffer);
+
+ //is the channel closed?
+ closed = (!netOutBuffer.hasRemaining() && (handshake.getHandshakeStatus() != HandshakeStatus.NEED_WRAP));
+ }
+
+ /**
+ * Force a close, can throw an IOException
+ * @param force boolean
+ * @throws IOException
+ */
+ public void close(boolean force) throws IOException {
+ try {
+ close();
+ }finally {
+ closed = true;
+ sc.close();
+ }
+ }
+
+ /**
+ * Reads a sequence of bytes from this channel into the given buffer.
+ *
+ * @param dst The buffer into which bytes are to be transferred
+ * @return The number of bytes read, possibly zero, or <tt>-1</tt> if the channel has reached end-of-stream
+ * @throws IOException If some other I/O error occurs
+ * @throws IllegalArgumentException if the destination buffer is different than bufHandler.getReadBuffer()
+ * @todo Implement this java.nio.channels.ReadableByteChannel method
+ */
+ public int read(ByteBuffer dst) throws IOException {
+ //if we want to take advantage of the expand function, make sure we only use the ApplicationBufferHandler's buffers
+ if ( dst != bufHandler.getReadBuffer() ) throw new IllegalArgumentException("You can only read using the application read buffer provided by the handler.");
+ //are we in the middle of closing or closed?
+ if ( closing || closed) return -1;
+ //did we finish our handshake?
+ if (!initHandshakeComplete) throw new IllegalStateException("Handshake incomplete, you must complete handshake before reading data.");
+
+ //read from the network
+ int netread = sc.read(netInBuffer);
+ //did we reach EOF? if so send EOF up one layer.
+ if (netread == -1) return -1;
+
+ //the data read
+ int read = 0;
+ //the SSL engine result
+ SSLEngineResult unwrap;
+ do {
+ //prepare the buffer
+ netInBuffer.flip();
+ //unwrap the data
+ unwrap = sslEngine.unwrap(netInBuffer, dst);
+ //compact the buffer
+ netInBuffer.compact();
+
+ if ( unwrap.getStatus()==Status.OK || unwrap.getStatus()==Status.BUFFER_UNDERFLOW ) {
+ //we did receive some data, add it to our total
+ read += unwrap.bytesProduced();
+ //perform any tasks if needed
+ if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) tasks();
+ //if we need more network data, then bail out for now.
+ if ( unwrap.getStatus() == Status.BUFFER_UNDERFLOW ) break;
+ }else {
+ //here we should trap BUFFER_OVERFLOW and call expand on the buffer
+ //for now, throw an exception, as we initialized the buffers
+ //in the constructor
+ throw new IOException("Unable to unwrap data, invalid status: " + unwrap.getStatus());
+ }
+ } while ( (netInBuffer.position() != 0));
+ return (read);
+ }
+
+ /**
+ * Writes a sequence of bytes to this channel from the given buffer.
+ *
+ * @param src The buffer from which bytes are to be retrieved
+ * @return The number of bytes written, possibly zero
+ * @throws IOException If some other I/O error occurs
+ * @todo Implement this java.nio.channels.WritableByteChannel method
+ */
+ public int write(ByteBuffer src) throws IOException {
+ //make sure we can handle expand, and that we only use on buffer
+ if ( src != bufHandler.getWriteBuffer() ) throw new IllegalArgumentException("You can only write using the application write buffer provided by the handler.");
+ //are we closing or closed?
+ if ( closing || closed) throw new IOException("Channel is in closing state.");
+
+ //the number of bytes written
+ int written = 0;
+
+ if (!flush(netOutBuffer)) {
+ //we haven't emptied out the buffer yet
+ return written;
+ }
+
+ /*
+ * The data buffer is empty, we can reuse the entire buffer.
+ */
+ netOutBuffer.clear();
+
+ SSLEngineResult result = sslEngine.wrap(src, netOutBuffer);
+ written = result.bytesConsumed();
+ netOutBuffer.flip();
+
+ if (result.getStatus() == Status.OK) {
+ if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) tasks();
+ } else {
+ throw new IOException("Unable to wrap data, invalid engine state: " +result.getStatus());
+ }
+
+ //force a flush
+ flush(netOutBuffer);
+
+ return written;
+ }
+
+ /**
+ * Callback interface to be able to expand buffers
+ * when buffer overflow exceptions happen
+ */
+ public static interface ApplicationBufferHandler {
+ public ByteBuffer expand(ByteBuffer buffer, int remaining);
+ public ByteBuffer getReadBuffer();
+ public ByteBuffer getWriteBuffer();
+ }
+
+ public ApplicationBufferHandler getBufHandler() {
+ return bufHandler;
+ }
+
+ public boolean isInitHandshakeComplete() {
+ return initHandshakeComplete;
+ }
+
+ public boolean isClosing() {
+ return closing;
+ }
+
+ public SSLEngine getSslEngine() {
+ return sslEngine;
+ }
+
+ public ByteBuffer getEmptyBuf() {
+ return emptyBuf;
+ }
+
+ public void setBufHandler(ApplicationBufferHandler bufHandler) {
+ this.bufHandler = bufHandler;
+ }
+
+ public SocketChannel getIOChannel() {
+ return sc;
+ }
+
+}
\ No newline at end of file
import org.apache.tomcat.util.net.SSLSupport;
import org.apache.tomcat.util.net.ServerSocketFactory;
+import javax.net.ssl.SSLSession;
/**
* Factory interface to construct components based on the JSSE version
* in use.
*
* @author Bill Barker
+ * @author Filip Hanik
*/
public class JSSEFactory {
public SSLSupport getSSLSupport(Socket socket) {
return new JSSESupport((SSLSocket)socket);
}
+
+ public SSLSupport getSSLSupport(SSLSession session) {
+ return new JSSESupport(session);
+ }
};
import org.apache.tomcat.util.net.SSLImplementation;
import org.apache.tomcat.util.net.SSLSupport;
import org.apache.tomcat.util.net.ServerSocketFactory;
+import javax.net.ssl.SSLSession;
/* JSSEImplementation:
SSLSupport ssls = factory.getSSLSupport(s);
return ssls;
}
+
+ public SSLSupport getSSLSupport(SSLSession session) {
+ SSLSupport ssls = factory.getSSLSupport(session);
+ return ssls;
+ }
+
}
import org.apache.tomcat.util.net.SSLSupport;
-/* JSSESupport
+/** JSSESupport
Concrete implementation class for JSSE
Support classes.
@author EKR
@author Craig R. McClanahan
+ @author Filip Hanik
Parts cribbed from JSSECertCompat
Parts cribbed from CertificatesValve
*/
org.apache.commons.logging.LogFactory.getLog(JSSESupport.class);
protected SSLSocket ssl;
+ protected SSLSession session;
Listener listener = new Listener();
JSSESupport(SSLSocket sock){
ssl=sock;
+ session = sock.getSession();
sock.addHandshakeCompletedListener(listener);
}
+
+ JSSESupport(SSLSession session) {
+ this.session = session;
+ }
public String getCipherSuite() throws IOException {
// Look up the current SSLSession
- SSLSession session = ssl.getSession();
if (session == null)
return null;
return session.getCipherSuite();
public Object[] getPeerCertificateChain(boolean force)
throws IOException {
// Look up the current SSLSession
- SSLSession session = ssl.getSession();
if (session == null)
return null;
public Integer getKeySize()
throws IOException {
// Look up the current SSLSession
- SSLSession session = ssl.getSession();
SSLSupport.CipherData c_aux[]=ciphers;
if (session == null)
return null;
public String getSessionId()
throws IOException {
// Look up the current SSLSession
- SSLSession session = ssl.getSession();
if (session == null)
return null;
// Expose ssl_session (getId)