Author: remy.maucherat(a)jboss.com
Date: 2012-06-13 04:50:17 -0400 (Wed, 13 Jun 2012)
New Revision: 2041
Added:
trunk/java/org/apache/coyote/http11/AbstractInternalInputBuffer.java
trunk/java/org/apache/coyote/http11/AbstractInternalOutputBuffer.java
trunk/java/org/apache/coyote/http11/Http11AbstractProcessor.java
trunk/java/org/apache/coyote/http11/Http11AbstractProtocol.java
trunk/java/org/apache/coyote/http11/Http11NioProcessor.java
trunk/java/org/apache/coyote/http11/Http11NioProtocol.java
trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java
trunk/java/org/apache/coyote/http11/InternalNioOutputBuffer.java
trunk/java/org/apache/tomcat/util/net/AbstractEndpoint.java
trunk/java/org/apache/tomcat/util/net/CompletionHandlerAdapter.java
trunk/java/org/apache/tomcat/util/net/DefaultNioServerSocketChannelFactory.java
trunk/java/org/apache/tomcat/util/net/NioChannel.java
trunk/java/org/apache/tomcat/util/net/NioEndpoint.java
trunk/java/org/apache/tomcat/util/net/NioServerSocketChannelFactory.java
trunk/java/org/apache/tomcat/util/net/jsse/NioJSSEFactory.java
trunk/java/org/apache/tomcat/util/net/jsse/NioJSSEImplementation.java
trunk/java/org/apache/tomcat/util/net/jsse/NioJSSESocketChannelFactory.java
trunk/java/org/apache/tomcat/util/net/jsse/NioJSSESupport.java
trunk/java/org/apache/tomcat/util/net/jsse/SecureNioChannel.java
Modified:
trunk/.classpath
trunk/build.properties.default
trunk/build.xml
trunk/java/org/apache/tomcat/util/net/SSLImplementation.java
trunk/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java
trunk/webapps/docs/changelog.xml
Log:
- Merge the NIO 2 HTTP connector.
- Now requires Java 7 to build.
- It builds, but not tested yet.
Modified: trunk/.classpath
===================================================================
--- trunk/.classpath 2012-06-07 13:48:42 UTC (rev 2040)
+++ trunk/.classpath 2012-06-13 08:50:17 UTC (rev 2041)
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry excluding="**/.svn/**" kind="src"
path="java"/>
- <classpathentry kind="con"
path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con"
path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="lib"
path="output/jars/jasper-jdt.jar"/>
<classpathentry kind="output" path=".settings/output"/>
</classpath>
Modified: trunk/build.properties.default
===================================================================
--- trunk/build.properties.default 2012-06-07 13:48:42 UTC (rev 2040)
+++ trunk/build.properties.default 2012-06-13 08:50:17 UTC (rev 2041)
@@ -24,8 +24,8 @@
#base.path=C:/path/to/the/repository
#base.path=/usr/local
-compile.source=1.6
-compile.target=1.6
+compile.source=1.7
+compile.target=1.7
compile.debug=true
base-commons.loc=http://archive.apache.org/dist/commons
Modified: trunk/build.xml
===================================================================
--- trunk/build.xml 2012-06-07 13:48:42 UTC (rev 2040)
+++ trunk/build.xml 2012-06-13 08:50:17 UTC (rev 2041)
@@ -35,7 +35,7 @@
<property name="test.runner"
value="junit.textui.TestRunner"/>
<!-- Can't be lower - jsp uses templates -->
- <property name="compile.source" value="1.6"/>
+ <property name="compile.source" value="1.7"/>
<property name="jasper-jdt.home"
value="${base.path}/jbossweb-deps/jdt" />
<property name="jasper-jdt.jar"
value="${jasper-jdt.home}/jasper-jdt.jar"/>
Added: trunk/java/org/apache/coyote/http11/AbstractInternalInputBuffer.java
===================================================================
--- trunk/java/org/apache/coyote/http11/AbstractInternalInputBuffer.java
(rev 0)
+++ trunk/java/org/apache/coyote/http11/AbstractInternalInputBuffer.java 2012-06-13
08:50:17 UTC (rev 2041)
@@ -0,0 +1,632 @@
+/**
+ * JBoss, Home of Professional Open Source. Copyright 2012, Red Hat, Inc., and
+ * individual contributors as indicated by the @author tags. See the
+ * copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
+ * site:
http://www.fsf.org.
+ */
+package org.apache.coyote.http11;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.coyote.InputBuffer;
+import org.apache.coyote.Request;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.res.StringManager;
+import org.jboss.logging.Logger;
+
+/**
+ * {@code AbstractInternalInputBuffer}
+ *
+ * Created on Jan 10, 2012 at 10:31:06 AM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public abstract class AbstractInternalInputBuffer implements InputBuffer {
+
+ /**
+ *
+ */
+ protected static final Logger log =
Logger.getLogger(AbstractInternalInputBuffer.class);
+
+ /**
+ * The string manager for this package.
+ */
+ protected static StringManager sm = StringManager.getManager(Constants.Package);
+
+ /**
+ * Associated Coyote request.
+ */
+ protected Request request;
+
+ /**
+ * Headers of the associated request.
+ */
+ protected MimeHeaders headers;
+
+ /**
+ * State.
+ */
+ protected boolean parsingHeader;
+
+ /**
+ * Swallow input ? (in the case of an expectation)
+ */
+ protected boolean swallowInput;
+
+ /**
+ * Pointer to the current read buffer.
+ */
+ protected byte[] buf;
+
+ /**
+ * Direct byte buffer used to perform actual reading.
+ */
+ protected ByteBuffer bbuf;
+
+ /**
+ * Last valid byte.
+ */
+ protected int lastValid;
+
+ /**
+ * Position in the buffer.
+ */
+ protected int pos;
+
+ /**
+ * Position of the end of the header in the buffer, which is also the start
+ * of the body.
+ */
+ protected int end;
+
+ /**
+ * Filter library. Note: Filter[0] is always the "chunked" filter.
+ */
+ protected InputFilter[] filterLibrary;
+
+ /**
+ * Active filters (in order).
+ */
+ protected InputFilter[] activeFilters;
+
+ /**
+ * Index of the last active filter.
+ */
+ protected int lastActiveFilter;
+
+ /**
+ * Underlying input buffer.
+ */
+ protected InputBuffer inputBuffer;
+
+ /**
+ * Read timeout
+ */
+ protected int readTimeout = -1;
+
+ /**
+ * The default time unit
+ */
+ protected static final TimeUnit unit = TimeUnit.MILLISECONDS;
+
+ /**
+ * Create a new instance of {@code AbstractInternalInputBuffer}
+ *
+ * @param request
+ */
+ public AbstractInternalInputBuffer(Request request) {
+ this(request, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
+ }
+
+ /**
+ * Create a new instance of {@code AbstractInternalInputBuffer}
+ *
+ * @param request
+ * @param headerBufferSize
+ */
+ public AbstractInternalInputBuffer(Request request, int headerBufferSize) {
+ this.request = request;
+ headers = request.getMimeHeaders();
+ buf = new byte[headerBufferSize];
+ filterLibrary = new InputFilter[0];
+ activeFilters = new InputFilter[0];
+ lastActiveFilter = -1;
+ parsingHeader = true;
+ swallowInput = true;
+
+ if (headerBufferSize < (8 * 1024)) {
+ bbuf = ByteBuffer.allocateDirect(6 * 1500);
+ } else {
+ bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500);
+ }
+ }
+
+ /**
+ * Get filters.
+ *
+ * @return the list of filters
+ */
+ public InputFilter[] getFilters() {
+ return filterLibrary;
+ }
+
+ /**
+ * Set the swallow input flag.
+ *
+ * @param swallowInput
+ */
+ public void setSwallowInput(boolean swallowInput) {
+ this.swallowInput = swallowInput;
+ }
+
+ /**
+ * Add an input filter to the filter library.
+ *
+ * @param filter
+ */
+ public void addFilter(InputFilter filter) {
+ if (filter == null) {
+ return;
+ }
+
+ InputFilter[] newFilterLibrary = new InputFilter[filterLibrary.length + 1];
+ for (int i = 0; i < filterLibrary.length; i++) {
+ newFilterLibrary[i] = filterLibrary[i];
+ }
+ newFilterLibrary[filterLibrary.length] = filter;
+ filterLibrary = newFilterLibrary;
+ activeFilters = new InputFilter[filterLibrary.length];
+ }
+
+ /**
+ * Clear filters.
+ */
+ public void clearFilters() {
+ filterLibrary = new InputFilter[0];
+ lastActiveFilter = -1;
+ }
+
+ /**
+ * Add an input filter to the filter library.
+ *
+ * @param filter
+ */
+ public void addActiveFilter(InputFilter filter) {
+
+ if (lastActiveFilter == -1) {
+ filter.setBuffer(inputBuffer);
+ } else {
+ for (int i = 0; i <= lastActiveFilter; i++) {
+ if (activeFilters[i] == filter)
+ return;
+ }
+ filter.setBuffer(activeFilters[lastActiveFilter]);
+ }
+
+ activeFilters[++lastActiveFilter] = filter;
+ filter.setRequest(request);
+ }
+
+ public void removeActiveFilters() {
+ // Recycle filters
+ for (int i = 0; i <= lastActiveFilter; i++) {
+ activeFilters[i].recycle();
+ }
+ lastActiveFilter = -1;
+ }
+
+ /**
+ * Recycle the input buffer. This should be called when closing the
+ * connection.
+ */
+ public void recycle() {
+ // Recycle Request object
+ request.recycle();
+ lastValid = 0;
+ reset();
+ }
+
+ /**
+ *
+ */
+ protected void reset() {
+ pos = 0;
+ lastActiveFilter = -1;
+ parsingHeader = true;
+ swallowInput = true;
+ }
+
+ /**
+ * End processing of current HTTP request. Note: All bytes of the current
+ * request should have been already consumed. This method only resets all
+ * the pointers so that we are ready to parse the next HTTP request.
+ *
+ * @return <tt>true</tt> if the position of the last valid char is > 0,
else
+ * <tt>false</tt>
+ */
+ public boolean nextRequest() {
+
+ // Recycle Request object
+ request.recycle();
+
+ // Copy leftover bytes to the beginning of the buffer
+ if (lastValid - pos > 0) {
+ int npos = 0;
+ int opos = pos;
+ while (lastValid - opos > opos - npos) {
+ System.arraycopy(buf, opos, buf, npos, opos - npos);
+ npos += pos;
+ opos += pos;
+ }
+ System.arraycopy(buf, opos, buf, npos, lastValid - opos);
+ }
+
+ // Recycle filters
+ for (int i = 0; i <= lastActiveFilter; i++) {
+ activeFilters[i].recycle();
+ }
+
+ // Reset pointers
+ lastValid = lastValid - pos;
+ reset();
+
+ return (lastValid > 0);
+ }
+
+ /**
+ * End request (consumes leftover bytes).
+ *
+ * @throws IOException
+ * an undelying I/O error occured
+ */
+ public void endRequest() throws IOException {
+
+ if (swallowInput && (lastActiveFilter != -1)) {
+ int extraBytes = (int) activeFilters[lastActiveFilter].end();
+ pos = pos - extraBytes;
+ }
+ }
+
+ /**
+ * Read the request line. This function is meant to be used during the HTTP
+ * request header parsing. Do NOT attempt to read the request body using it.
+ *
+ * @throws IOException
+ * If an exception occurs during the underlying socket read
+ * operations, or if the given buffer is not big enough to
+ * accomodate the whole line.
+ */
+ public void parseRequestLine() throws IOException {
+
+ int start = 0;
+ // Skipping blank lines
+
+ byte chr = 0;
+ do {
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+ if (!fill())
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+
+ chr = buf[pos++];
+ } while ((chr == Constants.CR) || (chr == Constants.LF));
+
+ pos--;
+ // Mark the current buffer position
+ start = pos;
+
+ // Reading the method name
+ // Method name is always US-ASCII
+
+ boolean space = false;
+
+ while (!space) {
+
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+ if (!fill()) {
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+ }
+
+ // Spec says single SP but it also says be tolerant of HT
+ if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+ space = true;
+ request.method().setBytes(buf, start, pos - start);
+ }
+
+ pos++;
+ }
+
+ // Spec says single SP but also says be tolerant of multiple and/or HT
+ while (space) {
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+ if (!fill()) {
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+ }
+ if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+ pos++;
+ } else {
+ space = false;
+ }
+ }
+
+ // Mark the current buffer position
+ start = pos;
+ int end = 0;
+ int questionPos = -1;
+
+ // Reading the URI
+
+ boolean eol = false;
+
+ while (!space) {
+
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+ if (!fill()) {
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+ }
+
+ // Spec says single SP but it also says be tolerant of HT
+ if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+ space = true;
+ end = pos;
+ } else if ((buf[pos] == Constants.CR) || (buf[pos] == Constants.LF)) {
+ // HTTP/0.9 style request
+ eol = true;
+ space = true;
+ end = pos;
+ } else if ((buf[pos] == Constants.QUESTION) && (questionPos == -1)) {
+ questionPos = pos;
+ }
+
+ pos++;
+ }
+
+ request.unparsedURI().setBytes(buf, start, end - start);
+ if (questionPos >= 0) {
+ request.queryString().setBytes(buf, questionPos + 1, end - questionPos - 1);
+ request.requestURI().setBytes(buf, start, questionPos - start);
+ } else {
+ request.requestURI().setBytes(buf, start, end - start);
+ }
+
+ // Spec says single SP but also says be tolerant of multiple and/or HT
+ while (space) {
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+ if (!fill()) {
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+ }
+ if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+ pos++;
+ } else {
+ space = false;
+ }
+ }
+
+ // Mark the current buffer position
+ start = pos;
+ end = 0;
+
+ // Reading the protocol
+ // Protocol is always US-ASCII
+
+ while (!eol) {
+
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+ if (!fill()) {
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+ }
+
+ if (buf[pos] == Constants.CR) {
+ end = pos;
+ } else if (buf[pos] == Constants.LF) {
+ if (end == 0)
+ end = pos;
+ eol = true;
+ }
+
+ pos++;
+ }
+
+ if ((end - start) > 0) {
+ request.protocol().setBytes(buf, start, end - start);
+ } else {
+ request.protocol().setString("");
+ }
+ }
+
+ /**
+ * Parse the HTTP headers.
+ *
+ * @throws IOException
+ */
+ public void parseHeaders() throws IOException {
+ while (parseHeader()) {
+ }
+
+ parsingHeader = false;
+ end = pos;
+ }
+
+ /**
+ * Parse an HTTP header.
+ *
+ * @return false after reading a blank line (which indicates that the HTTP
+ * header parsing is done
+ * @throws IOException
+ */
+ public boolean parseHeader() throws IOException {
+ // Check for blank line
+ byte chr = 0;
+ while (true) {
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+ if (!fill())
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+
+ chr = buf[pos];
+
+ if ((chr == Constants.CR) || (chr == Constants.LF)) {
+ if (chr == Constants.LF) {
+ pos++;
+ return false;
+ }
+ } else {
+ break;
+ }
+
+ pos++;
+ }
+
+ // Mark the current buffer position
+ int start = pos;
+
+ // Reading the header name
+ // Header name is always US-ASCII
+ boolean colon = false;
+ MessageBytes headerValue = null;
+
+ while (!colon) {
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+ if (!fill()) {
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+ }
+
+ if (buf[pos] == Constants.COLON) {
+ colon = true;
+ headerValue = headers.addValue(buf, start, pos - start);
+ }
+ chr = buf[pos];
+ if ((chr >= Constants.A) && (chr <= Constants.Z)) {
+ buf[pos] = (byte) (chr - Constants.LC_OFFSET);
+ }
+
+ pos++;
+ }
+
+ // Mark the current buffer position
+ start = pos;
+ int realPos = pos;
+
+ // Reading the header value (which can be spanned over multiple lines)
+
+ boolean eol = false;
+ boolean validLine = true;
+
+ while (validLine) {
+
+ boolean space = true;
+
+ // Skipping spaces
+ while (space) {
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+ if (!fill()) {
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+ }
+
+ if ((buf[pos] == Constants.SP) || (buf[pos] == Constants.HT)) {
+ pos++;
+ } else {
+ space = false;
+ }
+ }
+
+ int lastSignificantChar = realPos;
+
+ // Reading bytes until the end of the line
+ while (!eol) {
+
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+ if (!fill()) {
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+ }
+
+ if (buf[pos] == Constants.CR) {
+ } else if (buf[pos] == Constants.LF) {
+ eol = true;
+ } else if (buf[pos] == Constants.SP) {
+ buf[realPos] = buf[pos];
+ realPos++;
+ } else {
+ buf[realPos] = buf[pos];
+ realPos++;
+ lastSignificantChar = realPos;
+ }
+
+ pos++;
+ }
+
+ realPos = lastSignificantChar;
+
+ // Checking the first character of the new line. If the character
+ // is a LWS, then it's a multiline header
+
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+ if (!fill()) {
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+ }
+
+ chr = buf[pos];
+ if ((chr != Constants.SP) && (chr != Constants.HT)) {
+ validLine = false;
+ } else {
+ eol = false;
+ // Copying one extra space in the buffer (since there must
+ // be at least one space inserted between the lines)
+ buf[realPos] = chr;
+ realPos++;
+ }
+ }
+
+ // Set the header value
+ headerValue.setBytes(buf, start, realPos - start);
+
+ return true;
+ }
+
+ /**
+ * Fill the internal buffer using data from the undelying input stream.
+ *
+ * @return false if at end of stream
+ */
+ protected abstract boolean fill() throws IOException;
+
+}
Added: trunk/java/org/apache/coyote/http11/AbstractInternalOutputBuffer.java
===================================================================
--- trunk/java/org/apache/coyote/http11/AbstractInternalOutputBuffer.java
(rev 0)
+++ trunk/java/org/apache/coyote/http11/AbstractInternalOutputBuffer.java 2012-06-13
08:50:17 UTC (rev 2041)
@@ -0,0 +1,686 @@
+/**
+ * JBoss, Home of Professional Open Source. Copyright 2012, Red Hat, Inc., and
+ * individual contributors as indicated by the @author tags. See the
+ * copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
+ * site:
http://www.fsf.org.
+ */
+package org.apache.coyote.http11;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.Response;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.HttpMessages;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.res.StringManager;
+import org.jboss.logging.Logger;
+
+/**
+ * {@code AbstractInternalOutputBuffer}
+ *
+ * Created on Jan 10, 2012 at 12:08:51 PM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public abstract class AbstractInternalOutputBuffer implements OutputBuffer {
+
+ /**
+ *
+ */
+ protected static final Logger log =
Logger.getLogger(AbstractInternalOutputBuffer.class);
+
+ /**
+ * The string manager for this package.
+ */
+ protected static StringManager sm = StringManager.getManager(Constants.Package);
+
+ /**
+ * Associated Coyote response.
+ */
+ protected Response response;
+
+ /**
+ * Headers of the associated request.
+ */
+ protected MimeHeaders headers;
+
+ /**
+ * Committed flag.
+ */
+ protected boolean committed;
+
+ /**
+ * Finished flag.
+ */
+ protected boolean finished;
+
+ /**
+ * Pointer to the current write buffer.
+ */
+ protected byte[] buf;
+
+ /**
+ * Position in the buffer.
+ */
+ protected int pos;
+
+ /**
+ * Underlying output buffer.
+ */
+ protected OutputBuffer outputBuffer;
+
+ /**
+ * Filter library. Note: Filter[0] is always the "chunked" filter.
+ */
+ protected OutputFilter[] filterLibrary;
+
+ /**
+ * Active filter (which is actually the top of the pipeline).
+ */
+ protected OutputFilter[] activeFilters;
+
+ /**
+ * Index of the last active filter.
+ */
+ protected int lastActiveFilter;
+
+ /**
+ * Direct byte buffer used for writing.
+ */
+ protected ByteBuffer bbuf = null;
+
+ /**
+ * Leftover bytes which could not be written during a non blocking write.
+ */
+ protected ByteChunk leftover = null;
+
+ /**
+ * Non blocking mode.
+ */
+ protected boolean nonBlocking = false;
+
+ /**
+ * Write timeout
+ */
+ protected int writeTimeout = -1;
+
+ /**
+ * Create a new instance of {@code AbstractInternalOutputBuffer}
+ *
+ * @param response
+ * @param headerBufferSize
+ */
+ public AbstractInternalOutputBuffer(Response response, int headerBufferSize) {
+
+ this.response = response;
+ this.headers = response.getMimeHeaders();
+ buf = new byte[headerBufferSize];
+ if (headerBufferSize < Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE) {
+ bbuf = ByteBuffer.allocateDirect(6 * 1500);
+ } else {
+ bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500);
+ }
+
+ outputBuffer = new OutputBufferImpl();
+ filterLibrary = new OutputFilter[0];
+ activeFilters = new OutputFilter[0];
+ lastActiveFilter = -1;
+
+ committed = false;
+ finished = false;
+
+ leftover = new ByteChunk();
+ nonBlocking = false;
+
+ // Cause loading of HttpMessages
+ HttpMessages.getMessage(200);
+ }
+
+ /**
+ * Initialize the internal output buffer
+ */
+ protected abstract void init();
+
+ /**
+ * Set the non blocking flag.
+ *
+ * @param nonBlocking
+ */
+ public void setNonBlocking(boolean nonBlocking) {
+ this.nonBlocking = nonBlocking;
+ }
+
+ /**
+ * Get the non blocking flag value.
+ *
+ * @return non blocking
+ */
+ public boolean getNonBlocking() {
+ return nonBlocking;
+ }
+
+ /**
+ * Add an output filter to the filter library.
+ *
+ * @param filter
+ */
+ public void addFilter(OutputFilter filter) {
+
+ OutputFilter[] newFilterLibrary = new OutputFilter[filterLibrary.length + 1];
+ for (int i = 0; i < filterLibrary.length; i++) {
+ newFilterLibrary[i] = filterLibrary[i];
+ }
+ newFilterLibrary[filterLibrary.length] = filter;
+ filterLibrary = newFilterLibrary;
+ activeFilters = new OutputFilter[filterLibrary.length];
+ }
+
+ /**
+ * Get filters.
+ *
+ * @return the list of filters
+ */
+ public OutputFilter[] getFilters() {
+ return filterLibrary;
+ }
+
+ /**
+ * Clear filters.
+ */
+ public void clearFilters() {
+ filterLibrary = new OutputFilter[0];
+ lastActiveFilter = -1;
+ }
+
+ /**
+ * Perform a write operation. The operation may be blocking or non-blocking
+ * depending on the value of {@code nonBlocking} flag.
+ *
+ * @param timeout
+ * a timeout for the operation
+ * @param unit
+ * The time unit of the timeout
+ * @return
+ */
+ protected abstract int write(final long timeout, final TimeUnit unit);
+
+ /**
+ * Add an output filter to the filter library.
+ *
+ * @param filter
+ */
+ public void addActiveFilter(OutputFilter filter) {
+
+ if (lastActiveFilter == -1) {
+ filter.setBuffer(outputBuffer);
+ } else {
+ for (int i = 0; i <= lastActiveFilter; i++) {
+ if (activeFilters[i] == filter)
+ return;
+ }
+ filter.setBuffer(activeFilters[lastActiveFilter]);
+ }
+
+ activeFilters[++lastActiveFilter] = filter;
+ filter.setResponse(response);
+ }
+
+ public void removeActiveFilters() {
+ // Recycle filters
+ for (int i = 0; i <= lastActiveFilter; i++) {
+ activeFilters[i].recycle();
+ }
+ lastActiveFilter = -1;
+ }
+
+ /**
+ * Flush the response.
+ *
+ * @throws IOException
+ * an undelying I/O error occured
+ */
+ public void flush() throws IOException {
+ if (!committed) {
+
+ // Send the connector a request for commit. The connector should
+ // then validate the headers, send them (using sendHeader) and
+ // set the filters accordingly.
+ response.action(ActionCode.ACTION_COMMIT, null);
+ }
+
+ // Flush the current buffer
+ flushBuffer();
+ }
+
+ /**
+ *
+ */
+ protected void clearBuffer() {
+ synchronized (this.bbuf) {
+ this.bbuf.clear();
+ }
+ }
+
+ /**
+ * Recycle this object
+ */
+ public void recycle() {
+ // Recycle Request object
+ response.recycle();
+ this.clearBuffer();
+ pos = 0;
+ lastActiveFilter = -1;
+ committed = false;
+ finished = false;
+ }
+
+ /**
+ * End processing of current HTTP request. Note: All bytes of the current
+ * request should have been already consumed. This method only resets all
+ * the pointers so that we are ready to parse the next HTTP request.
+ */
+ public void nextRequest() {
+ // Recycle Request object
+ response.recycle();
+
+ // Recycle filters
+ for (int i = 0; i <= lastActiveFilter; i++) {
+ activeFilters[i].recycle();
+ }
+
+ // Reset pointers
+ leftover.recycle();
+ pos = 0;
+ lastActiveFilter = -1;
+ committed = false;
+ finished = false;
+ nonBlocking = false;
+ }
+
+ /**
+ * End request.
+ *
+ * @throws IOException
+ * an undelying I/O error occured
+ */
+ public void endRequest() throws IOException {
+
+ if (!committed) {
+ // Send the connector a request for commit. The connector should
+ // then validate the headers, send them (using sendHeader) and
+ // set the filters accordingly.
+ response.action(ActionCode.ACTION_COMMIT, null);
+ }
+
+ if (finished) {
+ return;
+ }
+
+ if (lastActiveFilter != -1) {
+ activeFilters[lastActiveFilter].end();
+ }
+
+ flushBuffer();
+ finished = true;
+ }
+
+ // ------------------------------------------------ HTTP/1.1 Output Methods
+
+ /**
+ * Send an acknowledgment.
+ *
+ * @throws Exception
+ */
+ public abstract void sendAck() throws Exception;
+
+ /**
+ * Send the response status line.
+ */
+ public void sendStatus() {
+
+ // Write protocol name
+ write(Constants.HTTP_11_BYTES);
+ buf[pos++] = Constants.SP;
+
+ // Write status code
+ int status = response.getStatus();
+ switch (status) {
+ case 200:
+ write(Constants._200_BYTES);
+ break;
+ case 400:
+ write(Constants._400_BYTES);
+ break;
+ case 404:
+ write(Constants._404_BYTES);
+ break;
+ default:
+ write(status);
+ }
+
+ buf[pos++] = Constants.SP;
+
+ // Write message
+ String message = null;
+ if (org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER) {
+ message = response.getMessage();
+ }
+ if (message == null) {
+ write(HttpMessages.getMessage(status));
+ } else {
+ write(message.replace('\n', ' ').replace('\r', ' '));
+ }
+
+ // End the response status line
+ buf[pos++] = Constants.CR;
+ buf[pos++] = Constants.LF;
+ }
+
+ /**
+ * Send a header.
+ *
+ * @param name
+ * Header name
+ * @param value
+ * Header value
+ */
+ public void sendHeader(MessageBytes name, MessageBytes value) {
+ if (name.getLength() > 0 && !value.isNull()) {
+ write(name);
+ buf[pos++] = Constants.COLON;
+ buf[pos++] = Constants.SP;
+ write(value);
+ buf[pos++] = Constants.CR;
+ buf[pos++] = Constants.LF;
+ }
+ }
+
+ /**
+ * Send a header.
+ *
+ * @param name
+ * Header name
+ * @param value
+ * Header value
+ */
+ public void sendHeader(ByteChunk name, ByteChunk value) {
+ write(name);
+ buf[pos++] = Constants.COLON;
+ buf[pos++] = Constants.SP;
+ write(value);
+ buf[pos++] = Constants.CR;
+ buf[pos++] = Constants.LF;
+ }
+
+ /**
+ * Send a header.
+ *
+ * @param name
+ * Header name
+ * @param value
+ * Header value
+ */
+ public void sendHeader(String name, String value) {
+ write(name);
+ buf[pos++] = Constants.COLON;
+ buf[pos++] = Constants.SP;
+ write(value);
+ buf[pos++] = Constants.CR;
+ buf[pos++] = Constants.LF;
+ }
+
+ /**
+ * End the header block.
+ */
+ public void endHeaders() {
+ buf[pos++] = Constants.CR;
+ buf[pos++] = Constants.LF;
+ }
+
+ /**
+ * Write the contents of a byte chunk.
+ *
+ * @param chunk
+ * byte chunk
+ * @return number of bytes written
+ * @throws IOException
+ * an undelying I/O error occured
+ */
+ public abstract int doWrite(ByteChunk chunk, Response res) throws IOException;
+
+ /**
+ * Commit the response.
+ *
+ * @throws IOException
+ * an undelying I/O error occured
+ */
+ protected void commit() throws IOException {
+
+ // The response is now committed
+ committed = true;
+ response.setCommitted(true);
+
+ if (pos > 0) {
+ // Sending the response header buffer
+ bbuf.put(buf, 0, pos);
+ }
+ }
+
+ /**
+ * This method will write the contents of the specyfied message bytes buffer
+ * to the output stream, without filtering. This method is meant to be used
+ * to write the response header.
+ *
+ * @param mb
+ * data to be written
+ */
+ protected void write(MessageBytes mb) {
+ if (mb == null) {
+ return;
+ }
+
+ switch (mb.getType()) {
+ case MessageBytes.T_BYTES:
+ write(mb.getByteChunk());
+ break;
+ case MessageBytes.T_CHARS:
+ write(mb.getCharChunk());
+ break;
+ default:
+ write(mb.toString());
+ break;
+ }
+ }
+
+ /**
+ * This method will write the contents of the specyfied message bytes buffer
+ * to the output stream, without filtering. This method is meant to be used
+ * to write the response header.
+ *
+ * @param bc
+ * data to be written
+ */
+ protected void write(ByteChunk bc) {
+ // Writing the byte chunk to the output buffer
+ int length = bc.getLength();
+ System.arraycopy(bc.getBytes(), bc.getStart(), buf, pos, length);
+ pos = pos + length;
+ }
+
+ /**
+ * This method will write the contents of the specyfied char buffer to the
+ * output stream, without filtering. This method is meant to be used to
+ * write the response header.
+ *
+ * @param cc
+ * data to be written
+ */
+ protected void write(CharChunk cc) {
+ int start = cc.getStart();
+ int end = cc.getEnd();
+ char[] cbuf = cc.getBuffer();
+ for (int i = start; i < end; i++) {
+ char c = cbuf[i];
+ // Note: This is clearly incorrect for many strings,
+ // but is the only consistent approach within the current
+ // servlet framework. It must suffice until servlet output
+ // streams properly encode their output.
+ if (((c <= 31) && (c != 9)) || c == 127 || c > 255) {
+ c = ' ';
+ }
+ buf[pos++] = (byte) c;
+ }
+
+ }
+
+ /**
+ * This method will write the contents of the specyfied byte buffer to the
+ * output stream, without filtering. This method is meant to be used to
+ * write the response header.
+ *
+ * @param b
+ * data to be written
+ */
+ public void write(byte[] b) {
+ // Writing the byte chunk to the output buffer
+ System.arraycopy(b, 0, buf, pos, b.length);
+ pos = pos + b.length;
+ }
+
+ /**
+ * This method will write the contents of the specyfied String to the output
+ * stream, without filtering. This method is meant to be used to write the
+ * response header.
+ *
+ * @param s
+ * data to be written
+ */
+ protected void write(String s) {
+ if (s == null) {
+ return;
+ }
+
+ // From the Tomcat 3.3 HTTP/1.0 connector
+ int len = s.length();
+ for (int i = 0; i < len; i++) {
+ char c = s.charAt(i);
+ // Note: This is clearly incorrect for many strings,
+ // but is the only consistent approach within the current
+ // servlet framework. It must suffice until servlet output
+ // streams properly encode their output.
+
+ if ((c <= 31 && c != 9) || c == 127) {
+ c = ' ';
+ }
+
+ buf[pos++] = (byte) c;
+ }
+ }
+
+ /**
+ * This method will print the specified integer to the output stream,
+ * without filtering. This method is meant to be used to write the response
+ * header.
+ *
+ * @param i
+ * data to be written
+ */
+ protected void write(int i) {
+ write(String.valueOf(i));
+ }
+
+ /**
+ * Callback to write data from the buffer.
+ */
+ protected abstract void flushBuffer() throws IOException;
+
+ /**
+ * Flush leftover bytes.
+ *
+ * @return true if all leftover bytes have been flushed
+ * @throws IOException
+ */
+ public abstract boolean flushLeftover() throws IOException;
+
+ // ----------------------------------- OutputBufferImpl Inner Class
+
+ /**
+ * {@code OutputBufferImpl} This class is an output buffer which will write
+ * data to an output stream/channel.
+ *
+ * Created on Jan 10, 2012 at 12:20:15 PM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+ class OutputBufferImpl implements OutputBuffer {
+
+ /**
+ * Write chunk.
+ */
+ public int doWrite(ByteChunk chunk, Response res) throws IOException {
+ // If non blocking (event) and there are leftover bytes,
+ // put all remaining bytes in the leftover buffer (they are
+ // part of the same write operation)
+ if (leftover.getLength() > 0) {
+ leftover.append(chunk);
+ return chunk.getLength();
+ }
+
+ int len = chunk.getLength();
+ int start = chunk.getStart();
+ byte[] b = chunk.getBuffer();
+
+ while (len > 0) {
+ int thisTime = len;
+ // if (bbuf.position() == bbuf.capacity()) {
+ if (!bbuf.hasRemaining()) {
+ flushBuffer();
+ if (leftover.getLength() > 0) {
+ // If non blocking (event) and there are leftover bytes,
+ // put all remaining bytes in the leftover buffer (they
+ // are
+ // part of the same write operation)
+ int oldStart = chunk.getOffset();
+ chunk.setOffset(start);
+ leftover.append(chunk);
+ chunk.setOffset(oldStart);
+ // After that, all content has been "written"
+ return chunk.getLength();
+ }
+ }
+ // if (thisTime > bbuf.capacity() - bbuf.position()) {
+ if (thisTime > bbuf.remaining()) {
+ // thisTime = bbuf.capacity() - bbuf.position();
+ thisTime = bbuf.remaining();
+ }
+
+ bbuf.put(b, start, thisTime);
+ len = len - thisTime;
+ start = start + thisTime;
+ }
+ return chunk.getLength();
+ }
+ }
+
+}
Added: trunk/java/org/apache/coyote/http11/Http11AbstractProcessor.java
===================================================================
--- trunk/java/org/apache/coyote/http11/Http11AbstractProcessor.java
(rev 0)
+++ trunk/java/org/apache/coyote/http11/Http11AbstractProcessor.java 2012-06-13 08:50:17
UTC (rev 2041)
@@ -0,0 +1,1268 @@
+/**
+ * JBoss, Home of Professional Open Source. Copyright 2011, Red Hat, Inc., and
+ * individual contributors as indicated by the @author tags. See the
+ * copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
+ * site:
http://www.fsf.org.
+ */
+package org.apache.coyote.http11;
+
+import java.io.IOException;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.apache.coyote.ActionHook;
+import org.apache.coyote.Adapter;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+import org.apache.tomcat.util.buf.Ascii;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.SocketStatus;
+import org.apache.tomcat.util.net.NioEndpoint.Handler.SocketState;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * {@code Http11AbstractProcessor}
+ *
+ * Created on Dec 19, 2011 at 2:35:14 PM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public abstract class Http11AbstractProcessor implements ActionHook {
+
+ /**
+ * Logger.
+ */
+ protected static org.jboss.logging.Logger log = org.jboss.logging.Logger
+ .getLogger(Http11Processor.class);
+
+ /**
+ * The string manager for this package.
+ */
+ protected static StringManager sm = StringManager.getManager(Constants.Package);
+
+ protected static final boolean CHUNK_ON_CLOSE = Boolean.valueOf(
+ System.getProperty("org.apache.coyote.http11.Http11Processor.CHUNK_ON_CLOSE",
"false"))
+ .booleanValue();
+
+ /**
+ * Thread local marker.
+ */
+ public static ThreadLocal<Boolean> containerThread = new
ThreadLocal<Boolean>();
+
+ /**
+ * Associated adapter.
+ */
+ protected Adapter adapter = null;
+
+ /**
+ * Request object.
+ */
+ protected Request request = null;
+
+ /**
+ * Response object.
+ */
+ protected Response response = null;
+ /**
+ * Error flag.
+ */
+ protected boolean error = false;
+
+ /**
+ * Keep-alive.
+ */
+ protected boolean keepAlive = true;
+
+ /**
+ * HTTP/1.1 flag.
+ */
+ protected boolean http11 = true;
+
+ /**
+ * HTTP/0.9 flag.
+ */
+ protected boolean http09 = false;
+
+ /**
+ * Content delimitator for the request (if false, the connection will be
+ * closed at the end of the request).
+ */
+ protected boolean contentDelimitation = true;
+
+ /**
+ * Is there an expectation ?
+ */
+ protected boolean expectation = false;
+
+ /**
+ * List of restricted user agents.
+ */
+ protected Pattern[] restrictedUserAgents = null;
+
+ /**
+ * Maximum number of Keep-Alive requests to honor.
+ */
+ protected int maxKeepAliveRequests = -1;
+
+ /**
+ * The number of seconds Tomcat will wait for a subsequent request before
+ * closing the connection.
+ */
+ protected int keepAliveTimeout = -1;
+
+ /**
+ * SSL information.
+ */
+ protected SSLSupport sslSupport;
+
+ /**
+ * Remote Address associated with the current connection.
+ */
+ protected String remoteAddr = null;
+
+ /**
+ * Remote Host associated with the current connection.
+ */
+ protected String remoteHost = null;
+
+ /**
+ * Local Host associated with the current connection.
+ */
+ protected String localName = null;
+
+ /**
+ * Local port to which the socket is connected
+ */
+ protected int localPort = -1;
+
+ /**
+ * Remote port to which the socket is connected
+ */
+ protected int remotePort = -1;
+
+ /**
+ * The local Host address.
+ */
+ protected String localAddr = null;
+
+ /**
+ * Flag to disable setting a different time-out on uploads.
+ */
+ protected boolean disableUploadTimeout = false;
+
+ /**
+ * Allowed compression level.
+ */
+ protected int compressionLevel = 0;
+
+ /**
+ * Minimum contentsize to make compression.
+ */
+ protected int compressionMinSize = 2048;
+
+ /**
+ * Max saved post size.
+ */
+ protected int maxSavePostSize = 4 * 1024;
+
+ /**
+ * List of user agents to not use gzip with
+ */
+ protected Pattern noCompressionUserAgents[] = null;
+
+ /**
+ * List of MIMES which could be gzipped
+ */
+ protected String[] compressableMimeTypes = { "text/html",
"text/xml", "text/plain" };
+
+ /**
+ * Host name (used to avoid useless B2C conversion on the host name).
+ */
+ protected char[] hostNameC = new char[0];
+
+ /**
+ * Allow a customized the server header for the tin-foil hat folks.
+ */
+ protected String server = null;
+
+ protected boolean sslEnabled;
+
+ /**
+ * Event used.
+ */
+ protected boolean event = false;
+
+ /**
+ * True if a resume has been requested.
+ */
+ protected boolean resumeNotification = false;
+ /**
+ * True if a read has been requested.
+ */
+ protected boolean readNotifications = true;
+ /**
+ * True if a write has been requested.
+ */
+ protected boolean writeNotification = false;
+
+ /**
+ * Event processing.
+ */
+ protected boolean eventProcessing = true;
+
+ /**
+ * Timeout.
+ */
+ protected int timeout = -1;
+
+ /**
+ * @return compression level.
+ */
+ public String getCompression() {
+ switch (compressionLevel) {
+ case 0:
+ return "off";
+ case 1:
+ return "on";
+ case 2:
+ return "force";
+ }
+ return "off";
+ }
+
+ /**
+ * Set compression level.
+ *
+ * @param compression
+ */
+ public void setCompression(String compression) {
+ if (compression.equals("on")) {
+ this.compressionLevel = 1;
+ } else if (compression.equals("force")) {
+ this.compressionLevel = 2;
+ } else if (compression.equals("off")) {
+ this.compressionLevel = 0;
+ } else {
+ try {
+ // Try to parse compression as an int, which would give the
+ // minimum compression size
+ compressionMinSize = Integer.parseInt(compression);
+ this.compressionLevel = 1;
+ } catch (Exception e) {
+ this.compressionLevel = 0;
+ }
+ }
+ }
+
+ /**
+ * Add user-agent for which gzip compression didn't works The user agent
+ * String given will be exactly matched to the user-agent header submitted
+ * by the client.
+ *
+ * @param userAgent
+ * user-agent string
+ */
+ public void addNoCompressionUserAgent(String userAgent) {
+ try {
+ Pattern nRule = Pattern.compile(userAgent);
+ noCompressionUserAgents = addREArray(noCompressionUserAgents, nRule);
+ } catch (PatternSyntaxException pse) {
+ log.error(sm.getString("http11processor.regexp.error", userAgent), pse);
+ }
+ }
+
+ /**
+ * Set no compression user agent list. List contains users agents separated
+ * by ',' :
+ *
+ * ie: "gorilla,desesplorer,tigrus"
+ *
+ * @param noCompressionUserAgents
+ */
+ public void setNoCompressionUserAgents(String noCompressionUserAgents) {
+ if (noCompressionUserAgents != null) {
+ StringTokenizer st = new StringTokenizer(noCompressionUserAgents, ",");
+
+ while (st.hasMoreTokens()) {
+ addNoCompressionUserAgent(st.nextToken().trim());
+ }
+ }
+ }
+
+ /**
+ * Add a mime-type which will be compressable The mime-type String will be
+ * exactly matched in the response mime-type header .
+ *
+ * @param mimeType
+ * mime-type string
+ */
+ public void addCompressableMimeType(String mimeType) {
+ compressableMimeTypes = addStringArray(compressableMimeTypes, mimeType);
+ }
+
+ /**
+ * Set compressable mime-type list (this method is best when used with a
+ * large number of connectors, where it would be better to have all of them
+ * referenced a single array).
+ *
+ * @param compressableMimeTypes
+ */
+ public void setCompressableMimeTypes(String[] compressableMimeTypes) {
+ this.compressableMimeTypes = compressableMimeTypes;
+ }
+
+ /**
+ * Set compressable mime-type list List contains users agents separated by
+ * ',' :
+ *
+ * ie: "text/html,text/xml,text/plain"
+ *
+ * @param compressableMimeTypes
+ */
+ public void setCompressableMimeTypes(String compressableMimeTypes) {
+ if (compressableMimeTypes != null) {
+ this.compressableMimeTypes = null;
+ StringTokenizer st = new StringTokenizer(compressableMimeTypes, ",");
+ while (st.hasMoreTokens()) {
+ addCompressableMimeType(st.nextToken().trim());
+ }
+ }
+ }
+
+ /**
+ * @return the list of restricted user agents.
+ */
+ public String[] findCompressableMimeTypes() {
+ return (compressableMimeTypes);
+ }
+
+ /**
+ * Add input or output filter.
+ *
+ * @param className
+ * class name of the filter
+ */
+ protected abstract void addFilter(String className);
+
+ /**
+ * General use method
+ *
+ * @param sArray
+ * the StringArray
+ * @param value
+ * string
+ */
+ protected String[] addStringArray(String sArray[], String value) {
+ String[] result = null;
+ if (sArray == null) {
+ result = new String[1];
+ result[0] = value;
+ } else {
+ result = new String[sArray.length + 1];
+ for (int i = 0; i < sArray.length; i++)
+ result[i] = sArray[i];
+ result[sArray.length] = value;
+ }
+ return result;
+ }
+
+ /**
+ * General use method
+ *
+ * @param rArray
+ * the REArray
+ * @param value
+ * Obj
+ */
+ protected Pattern[] addREArray(Pattern rArray[], Pattern value) {
+ Pattern[] result = null;
+ if (rArray == null) {
+ result = new Pattern[1];
+ result[0] = value;
+ } else {
+ result = new Pattern[rArray.length + 1];
+ for (int i = 0; i < rArray.length; i++)
+ result[i] = rArray[i];
+ result[rArray.length] = value;
+ }
+ return result;
+ }
+
+ /**
+ * Checks if any entry in the string array starts with the specified value
+ *
+ * @param sArray
+ * the StringArray
+ * @param value
+ * string
+ */
+ protected boolean startsWithStringArray(String sArray[], String value) {
+ if (value == null)
+ return false;
+ for (int i = 0; i < sArray.length; i++) {
+ if (value.startsWith(sArray[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Add restricted user-agent (which will downgrade the connector to HTTP/1.0
+ * mode). The user agent String given will be matched via regexp to the
+ * user-agent header submitted by the client.
+ *
+ * @param userAgent
+ * user-agent string
+ */
+ public void addRestrictedUserAgent(String userAgent) {
+ try {
+ Pattern nRule = Pattern.compile(userAgent);
+ restrictedUserAgents = addREArray(restrictedUserAgents, nRule);
+ } catch (PatternSyntaxException pse) {
+ log.error(sm.getString("http11processor.regexp.error", userAgent), pse);
+ }
+ }
+
+ /**
+ * Set restricted user agent list (which will downgrade the connector to
+ * HTTP/1.0 mode). List contains users agents separated by ',' :
+ *
+ * ie: "gorilla,desesplorer,tigrus"
+ *
+ * @param restrictedUserAgents
+ */
+ public void setRestrictedUserAgents(String restrictedUserAgents) {
+ if (restrictedUserAgents != null) {
+ StringTokenizer st = new StringTokenizer(restrictedUserAgents, ",");
+ while (st.hasMoreTokens()) {
+ addRestrictedUserAgent(st.nextToken().trim());
+ }
+ }
+ }
+
+ /**
+ * @return the list of restricted user agents.
+ */
+ public String[] findRestrictedUserAgents() {
+ String[] sarr = new String[restrictedUserAgents.length];
+
+ for (int i = 0; i < restrictedUserAgents.length; i++)
+ sarr[i] = restrictedUserAgents[i].toString();
+
+ return (sarr);
+ }
+
+ /**
+ * Set the server header name.
+ *
+ * @param server
+ */
+ public void setServer(String server) {
+ if (server == null || server.equals("")) {
+ this.server = null;
+ } else {
+ this.server = server;
+ }
+ }
+
+ /**
+ *
+ */
+ public abstract void endRequest();
+
+ /**
+ *
+ */
+ public void recycle() {
+ timeout = -1;
+ readNotifications = true;
+ writeNotification = false;
+ resumeNotification = false;
+ eventProcessing = true;
+ }
+
+ /**
+ * Check for compression
+ */
+ protected boolean isCompressable() {
+
+ // Nope Compression could works in HTTP 1.0 also
+ // cf: mod_deflate
+
+ // Compression only since HTTP 1.1
+ // if (! http11)
+ // return false;
+
+ // Check if browser support gzip encoding
+ MessageBytes acceptEncodingMB =
request.getMimeHeaders().getValue("accept-encoding");
+
+ if ((acceptEncodingMB == null) || (acceptEncodingMB.indexOf("gzip") == -1))
+ return false;
+
+ // Check if content is not allready gzipped
+ MessageBytes contentEncodingMB =
response.getMimeHeaders().getValue("Content-Encoding");
+
+ if ((contentEncodingMB != null) && (contentEncodingMB.indexOf("gzip")
!= -1))
+ return false;
+
+ // If force mode, allways compress (test purposes only)
+ if (compressionLevel == 2)
+ return true;
+
+ // Check for incompatible Browser
+ if (noCompressionUserAgents != null) {
+ MessageBytes userAgentValueMB =
request.getMimeHeaders().getValue("user-agent");
+ if (userAgentValueMB != null) {
+ String userAgentValue = userAgentValueMB.toString();
+
+ // If one Regexp rule match, disable compression
+ for (int i = 0; i < noCompressionUserAgents.length; i++)
+ if (noCompressionUserAgents[i].matcher(userAgentValue).matches())
+ return false;
+ }
+ }
+
+ // Check if suffisant len to trig the compression
+ long contentLength = response.getContentLengthLong();
+ if ((contentLength == -1) || (contentLength > compressionMinSize)) {
+ // Check for compatible MIME-TYPE
+ if (compressableMimeTypes != null) {
+ return (startsWithStringArray(compressableMimeTypes, response.getContentType()));
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * After reading the request headers, we have to setup the request filters.
+ */
+ protected abstract void prepareRequest();
+
+ /**
+ * When committing the response, we have to validate the set of headers, as
+ * well as setup the response filters.
+ */
+ protected abstract void prepareResponse();
+
+ /**
+ *
+ */
+ protected abstract void initializeFilters();
+
+ /**
+ * Process pipelined HTTP requests using the specified input and output
+ * streams.
+ *
+ * @param status
+ * @return a <tt>SocketState</tt>
+ *
+ * @throws IOException
+ * error during an I/O operation
+ */
+ public abstract SocketState event(SocketStatus status) throws IOException;
+
+ /**
+ * Reset flags of the Processor
+ */
+ protected void reset() {
+ // Set the remote address
+ remoteAddr = null;
+ remoteHost = null;
+ localAddr = null;
+ localName = null;
+ remotePort = -1;
+ localPort = -1;
+
+ // Error flag
+ error = false;
+ event = false;
+ keepAlive = true;
+ }
+
+ /**
+ * Determine if we must drop the connection because of the HTTP status code.
+ * Use the same list of codes as Apache/httpd.
+ */
+ protected boolean statusDropsConnection(int status) {
+ return status == 400 /* SC_BAD_REQUEST */|| status == 408 /* SC_REQUEST_TIMEOUT */
+ || status == 411 /* SC_LENGTH_REQUIRED */|| status == 413 /*
SC_REQUEST_ENTITY_TOO_LARGE */
+ || status == 414 /* SC_REQUEST_URI_TOO_LARGE */|| status == 500 /*
SC_INTERNAL_SERVER_ERROR */
+ || status == 503 /* SC_SERVICE_UNAVAILABLE */|| status == 501 /* SC_NOT_IMPLEMENTED
*/;
+ }
+
+ /**
+ * Specialized utility method: find a sequence of lower case bytes inside a
+ * ByteChunk.
+ */
+ protected int findBytes(ByteChunk bc, byte[] b) {
+
+ byte first = b[0];
+ byte[] buff = bc.getBuffer();
+ int start = bc.getStart();
+ int end = bc.getEnd();
+
+ // Look for first char
+ int srcEnd = b.length;
+
+ for (int i = start; i <= (end - srcEnd); i++) {
+ if (Ascii.toLower(buff[i]) != first)
+ continue;
+ // found first char, now look for a match
+ int myPos = i + 1;
+ for (int srcPos = 1; srcPos < srcEnd;) {
+ if (Ascii.toLower(buff[myPos++]) != b[srcPos++])
+ break;
+ if (srcPos == srcEnd)
+ return i - start; // found it
+ }
+ }
+ return -1;
+
+ }
+
+ /**
+ * Getter for adapter
+ *
+ * @return the adapter
+ */
+ public Adapter getAdapter() {
+ return this.adapter;
+ }
+
+ /**
+ * Setter for the adapter
+ *
+ * @param adapter
+ * the adapter to set
+ */
+ public void setAdapter(Adapter adapter) {
+ this.adapter = adapter;
+ }
+
+ /**
+ * Getter for request
+ *
+ * @return the request
+ */
+ public Request getRequest() {
+ return this.request;
+ }
+
+ /**
+ * Setter for the request
+ *
+ * @param request
+ * the request to set
+ */
+ public void setRequest(Request request) {
+ this.request = request;
+ }
+
+ /**
+ * Getter for response
+ *
+ * @return the response
+ */
+ public Response getResponse() {
+ return this.response;
+ }
+
+ /**
+ * Setter for the response
+ *
+ * @param response
+ * the response to set
+ */
+ public void setResponse(Response response) {
+ this.response = response;
+ }
+
+ /**
+ * Getter for error
+ *
+ * @return the error
+ */
+ public boolean isError() {
+ return this.error;
+ }
+
+ /**
+ * Setter for the error
+ *
+ * @param error
+ * the error to set
+ */
+ public void setError(boolean error) {
+ this.error = error;
+ }
+
+ /**
+ * Getter for keepAlive
+ *
+ * @return the keepAlive
+ */
+ public boolean isKeepAlive() {
+ return this.keepAlive;
+ }
+
+ /**
+ * Setter for the keepAlive
+ *
+ * @param keepAlive
+ * the keepAlive to set
+ */
+ public void setKeepAlive(boolean keepAlive) {
+ this.keepAlive = keepAlive;
+ }
+
+ /**
+ * Getter for http11
+ *
+ * @return the http11
+ */
+ public boolean isHttp11() {
+ return this.http11;
+ }
+
+ /**
+ * Setter for the http11
+ *
+ * @param http11
+ * the http11 to set
+ */
+ public void setHttp11(boolean http11) {
+ this.http11 = http11;
+ }
+
+ /**
+ * Getter for http09
+ *
+ * @return the http09
+ */
+ public boolean isHttp09() {
+ return this.http09;
+ }
+
+ /**
+ * Setter for the http09
+ *
+ * @param http09
+ * the http09 to set
+ */
+ public void setHttp09(boolean http09) {
+ this.http09 = http09;
+ }
+
+ /**
+ * Getter for contentDelimitation
+ *
+ * @return the contentDelimitation
+ */
+ public boolean isContentDelimitation() {
+ return this.contentDelimitation;
+ }
+
+ /**
+ * Setter for the contentDelimitation
+ *
+ * @param contentDelimitation
+ * the contentDelimitation to set
+ */
+ public void setContentDelimitation(boolean contentDelimitation) {
+ this.contentDelimitation = contentDelimitation;
+ }
+
+ /**
+ * Getter for expectation
+ *
+ * @return the expectation
+ */
+ public boolean isExpectation() {
+ return this.expectation;
+ }
+
+ /**
+ * Setter for the expectation
+ *
+ * @param expectation
+ * the expectation to set
+ */
+ public void setExpectation(boolean expectation) {
+ this.expectation = expectation;
+ }
+
+ /**
+ * Getter for restrictedUserAgents
+ *
+ * @return the restrictedUserAgents
+ */
+ public Pattern[] getRestrictedUserAgents() {
+ return this.restrictedUserAgents;
+ }
+
+ /**
+ * Setter for the restrictedUserAgents
+ *
+ * @param restrictedUserAgents
+ * the restrictedUserAgents to set
+ */
+ public void setRestrictedUserAgents(Pattern[] restrictedUserAgents) {
+ this.restrictedUserAgents = restrictedUserAgents;
+ }
+
+ /**
+ * Getter for maxKeepAliveRequests
+ *
+ * @return the maxKeepAliveRequests
+ */
+ public int getMaxKeepAliveRequests() {
+ return this.maxKeepAliveRequests;
+ }
+
+ /**
+ * Setter for the maxKeepAliveRequests
+ *
+ * @param maxKeepAliveRequests
+ * the maxKeepAliveRequests to set
+ */
+ public void setMaxKeepAliveRequests(int maxKeepAliveRequests) {
+ this.maxKeepAliveRequests = maxKeepAliveRequests;
+ }
+
+ /**
+ * Getter for keepAliveTimeout
+ *
+ * @return the keepAliveTimeout
+ */
+ public int getKeepAliveTimeout() {
+ return this.keepAliveTimeout;
+ }
+
+ /**
+ * Setter for the keepAliveTimeout
+ *
+ * @param keepAliveTimeout
+ * the keepAliveTimeout to set
+ */
+ public void setKeepAliveTimeout(int keepAliveTimeout) {
+ this.keepAliveTimeout = keepAliveTimeout;
+ }
+
+ /**
+ * Getter for sslSupport
+ *
+ * @return the sslSupport
+ */
+ public SSLSupport getSSLSupport() {
+ return this.sslSupport;
+ }
+
+ /**
+ * Setter for the sslSupport
+ *
+ * @param sslSupport
+ * the sslSupport to set
+ */
+ public void setSSLSupport(SSLSupport sslSupport) {
+ this.sslSupport = sslSupport;
+ }
+
+ /**
+ * Getter for remoteAddr
+ *
+ * @return the remoteAddr
+ */
+ public String getRemoteAddr() {
+ return this.remoteAddr;
+ }
+
+ /**
+ * Setter for the remoteAddr
+ *
+ * @param remoteAddr
+ * the remoteAddr to set
+ */
+ public void setRemoteAddr(String remoteAddr) {
+ this.remoteAddr = remoteAddr;
+ }
+
+ /**
+ * Getter for remoteHost
+ *
+ * @return the remoteHost
+ */
+ public String getRemoteHost() {
+ return this.remoteHost;
+ }
+
+ /**
+ * Setter for the remoteHost
+ *
+ * @param remoteHost
+ * the remoteHost to set
+ */
+ public void setRemoteHost(String remoteHost) {
+ this.remoteHost = remoteHost;
+ }
+
+ /**
+ * Getter for localName
+ *
+ * @return the localName
+ */
+ public String getLocalName() {
+ return this.localName;
+ }
+
+ /**
+ * Setter for the localName
+ *
+ * @param localName
+ * the localName to set
+ */
+ public void setLocalName(String localName) {
+ this.localName = localName;
+ }
+
+ /**
+ * Getter for localPort
+ *
+ * @return the localPort
+ */
+ public int getLocalPort() {
+ return this.localPort;
+ }
+
+ /**
+ * Setter for the localPort
+ *
+ * @param localPort
+ * the localPort to set
+ */
+ public void setLocalPort(int localPort) {
+ this.localPort = localPort;
+ }
+
+ /**
+ * Getter for remotePort
+ *
+ * @return the remotePort
+ */
+ public int getRemotePort() {
+ return this.remotePort;
+ }
+
+ /**
+ * Setter for the remotePort
+ *
+ * @param remotePort
+ * the remotePort to set
+ */
+ public void setRemotePort(int remotePort) {
+ this.remotePort = remotePort;
+ }
+
+ /**
+ * Getter for localAddr
+ *
+ * @return the localAddr
+ */
+ public String getLocalAddr() {
+ return this.localAddr;
+ }
+
+ /**
+ * Setter for the localAddr
+ *
+ * @param localAddr
+ * the localAddr to set
+ */
+ public void setLocalAddr(String localAddr) {
+ this.localAddr = localAddr;
+ }
+
+ /**
+ * Getter for disableUploadTimeout
+ *
+ * @return the disableUploadTimeout
+ */
+ public boolean getDisableUploadTimeout() {
+ return this.disableUploadTimeout;
+ }
+
+ /**
+ * Setter for the disableUploadTimeout
+ *
+ * @param disableUploadTimeout
+ * the disableUploadTimeout to set
+ */
+ public void setDisableUploadTimeout(boolean disableUploadTimeout) {
+ this.disableUploadTimeout = disableUploadTimeout;
+ }
+
+ /**
+ * Getter for compressionLevel
+ *
+ * @return the compressionLevel
+ */
+ public int getCompressionLevel() {
+ return this.compressionLevel;
+ }
+
+ /**
+ * Setter for the compressionLevel
+ *
+ * @param compressionLevel
+ * the compressionLevel to set
+ */
+ public void setCompressionLevel(int compressionLevel) {
+ this.compressionLevel = compressionLevel;
+ }
+
+ /**
+ * Getter for compressionMinSize
+ *
+ * @return the compressionMinSize
+ */
+ public int getCompressionMinSize() {
+ return this.compressionMinSize;
+ }
+
+ /**
+ * Setter for the compressionMinSize
+ *
+ * @param compressionMinSize
+ * the compressionMinSize to set
+ */
+ public void setCompressionMinSize(int compressionMinSize) {
+ this.compressionMinSize = compressionMinSize;
+ }
+
+ /**
+ * Getter for maxSavePostSize
+ *
+ * @return the maxSavePostSize
+ */
+ public int getMaxSavePostSize() {
+ return this.maxSavePostSize;
+ }
+
+ /**
+ * Setter for the maxSavePostSize
+ *
+ * @param maxSavePostSize
+ * the maxSavePostSize to set
+ */
+ public void setMaxSavePostSize(int maxSavePostSize) {
+ this.maxSavePostSize = maxSavePostSize;
+ }
+
+ /**
+ * Getter for noCompressionUserAgents
+ *
+ * @return the noCompressionUserAgents
+ */
+ public Pattern[] getNoCompressionUserAgents() {
+ return this.noCompressionUserAgents;
+ }
+
+ /**
+ * Setter for the noCompressionUserAgents
+ *
+ * @param noCompressionUserAgents
+ * the noCompressionUserAgents to set
+ */
+ public void setNoCompressionUserAgents(Pattern[] noCompressionUserAgents) {
+ this.noCompressionUserAgents = noCompressionUserAgents;
+ }
+
+ /**
+ * Getter for hostNameC
+ *
+ * @return the hostNameC
+ */
+ public char[] getHostNameC() {
+ return this.hostNameC;
+ }
+
+ /**
+ * Setter for the hostNameC
+ *
+ * @param hostNameC
+ * the hostNameC to set
+ */
+ public void setHostNameC(char[] hostNameC) {
+ this.hostNameC = hostNameC;
+ }
+
+ /**
+ * Getter for event
+ *
+ * @return the event
+ */
+ public boolean isEvent() {
+ return this.event;
+ }
+
+ /**
+ * Setter for the event
+ *
+ * @param event
+ * the event to set
+ */
+ public void setEvent(boolean event) {
+ this.event = event;
+ }
+
+ /**
+ * Getter for resumeNotification
+ *
+ * @return the resumeNotification
+ */
+ public boolean getResumeNotification() {
+ return this.resumeNotification;
+ }
+
+ /**
+ * Setter for the resumeNotification
+ *
+ * @param resumeNotification
+ * the resumeNotification to set
+ */
+ public void setResumeNotification(boolean resumeNotification) {
+ this.resumeNotification = resumeNotification;
+ }
+
+ /**
+ * Getter for eventProcessing
+ *
+ * @return the eventProcessing
+ */
+ public boolean getEventProcessing() {
+ return this.eventProcessing;
+ }
+
+ /**
+ * Setter for the eventProcessing
+ *
+ * @param eventProcessing
+ * the eventProcessing to set
+ */
+ public void setEventProcessing(boolean eventProcessing) {
+ this.eventProcessing = eventProcessing;
+ }
+
+ /**
+ * Getter for timeout
+ *
+ * @return the timeout
+ */
+ public int getTimeout() {
+ return this.timeout;
+ }
+
+ /**
+ * Setter for the timeout
+ *
+ * @param timeout
+ * the timeout to set
+ */
+ public void setTimeout(int timeout) {
+ this.timeout = timeout;
+ }
+
+ /**
+ * Getter for compressableMimeTypes
+ *
+ * @return the compressableMimeTypes
+ */
+ public String[] getCompressableMimeTypes() {
+ return this.compressableMimeTypes;
+ }
+
+ /**
+ * Getter for server
+ *
+ * @return the server
+ */
+ public String getServer() {
+ return this.server;
+ }
+
+ /**
+ * Getter for readNotifications
+ *
+ * @return the readNotifications
+ */
+ public boolean getReadNotifications() {
+ return this.readNotifications;
+ }
+
+ /**
+ * Setter for the readNotifications
+ *
+ * @param readNotifications
+ * the readNotifications to set
+ */
+ public void setReadNotifications(boolean readNotifications) {
+ this.readNotifications = readNotifications;
+ }
+
+ /**
+ * Getter for writeNotification
+ *
+ * @return the writeNotification
+ */
+ public boolean getWriteNotification() {
+ return this.writeNotification;
+ }
+
+ /**
+ * Setter for the writeNotification
+ *
+ * @param writeNotification
+ * the writeNotification to set
+ */
+ public void setWriteNotification(boolean writeNotification) {
+ this.writeNotification = writeNotification;
+ }
+
+ /**
+ * Getter for sslEnabled
+ *
+ * @return the sslEnabled
+ */
+ public boolean getSSLEnabled() {
+ return this.sslEnabled;
+ }
+
+ /**
+ * Setter for the sslEnabled
+ *
+ * @param sslEnabled
+ * the sslEnabled to set
+ */
+ public void setSSLEnabled(boolean sslEnabled) {
+ this.sslEnabled = sslEnabled;
+ }
+}
Added: trunk/java/org/apache/coyote/http11/Http11AbstractProtocol.java
===================================================================
--- trunk/java/org/apache/coyote/http11/Http11AbstractProtocol.java
(rev 0)
+++ trunk/java/org/apache/coyote/http11/Http11AbstractProtocol.java 2012-06-13 08:50:17
UTC (rev 2041)
@@ -0,0 +1,802 @@
+/**
+ * JBoss, Home of Professional Open Source. Copyright 2011, Red Hat, Inc., and
+ * individual contributors as indicated by the @author tags. See the
+ * copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
+ * site:
http://www.fsf.org.
+ */
+package org.apache.coyote.http11;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.net.ssl.SSLContext;
+
+import org.apache.coyote.Adapter;
+import org.apache.coyote.ProtocolHandler;
+import org.apache.tomcat.util.net.SSLImplementation;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * {@code Http11AbstractProtocol}
+ * <p>
+ * Abstract the protocol implementation, including threading, etc. Processor is
+ * single threaded and specific to stream-based protocols, will not fit Jk
+ * protocols like JNI.
+ * </p>
+ * Created on Dec 19, 2011 at 11:58:19 AM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public abstract class Http11AbstractProtocol implements ProtocolHandler,
MBeanRegistration {
+
+ protected static org.jboss.logging.Logger log = org.jboss.logging.Logger
+ .getLogger(Http11NioProtocol.class);
+
+ /**
+ * The string manager for this package.
+ */
+ protected static StringManager sm = StringManager.getManager(Constants.Package);
+
+ protected HashMap<String, Object> attributes = new HashMap<String,
Object>();
+ // *
+ protected ObjectName tpOname = null;
+ // *
+ protected ObjectName rgOname = null;
+
+ protected SSLImplementation sslImplementation = null;
+
+
+ /**
+ * The adapter, used to call the connector.
+ */
+ protected Adapter adapter;
+
+ /**
+ * Processor cache.
+ */
+ protected int processorCache = -1;
+ protected boolean canDestroy = false;
+ protected int socketBuffer = 9000;
+ /**
+ * Maximum size of the post which will be saved when processing certain
+ * requests, such as a POST.
+ */
+ protected int maxSavePostSize = 4 * 1024;
+ // --------------- HTTP ---------------
+ /**
+ * Maximum size of the HTTP message header.
+ */
+ protected int maxHttpHeaderSize = 8 * 1024;
+ /**
+ * If true, the regular socket timeout will be used for the full duration of
+ * the connection.
+ */
+ protected boolean disableUploadTimeout = true;
+ /**
+ * Integrated compression support.
+ */
+ protected String compression = "off";
+ protected String noCompressionUserAgents = null;
+ protected String compressableMimeTypes = "text/html,text/xml,text/plain";
+ protected int compressionMinSize = 2048;
+ protected String protocol = null;
+ /**
+ * User agents regular expressions which should be restricted to HTTP/1.0
+ * support.
+ */
+ protected String restrictedUserAgents = null;
+ /**
+ * Maximum number of requests which can be performed over a keepalive
+ * connection. The default is the same as for Apache HTTP Server.
+ */
+ protected int maxKeepAliveRequests = Integer.valueOf(
+ System.getProperty("org.apache.coyote.http11.Http11Protocol.MAX_KEEP_ALIVE_REQUESTS",
+ "100")).intValue();
+
+ protected String domain;
+ protected ObjectName oname;
+ protected MBeanServer mserver;
+
+ /**
+ * Server header.
+ */
+ protected String server;
+ /**
+ * This timeout represents the socket timeout which will be used while the
+ * adapter execution is in progress, unless disableUploadTimeout is set to
+ * true. The default is the same as for Apache HTTP Server (300 000
+ * milliseconds).
+ */
+ protected int timeout = 300000;
+ /**
+ * This field indicates if the protocol is secure from the perspective of
+ * the client (= https is used).
+ */
+ protected boolean secure;
+
+ /**
+ * Create a new instance of {@code Http11AbstractProtocol}
+ */
+ public Http11AbstractProtocol() {
+ super();
+ }
+
+ /**
+ * @return the name of the protocol
+ */
+ public abstract String getName();
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.ProtocolHandler#setAttribute(java.lang.String,
+ * java.lang.Object)
+ */
+ @Override
+ public void setAttribute(String name, Object value) {
+ if (log.isTraceEnabled())
+ log.trace(sm.getString("http11protocol.setattribute", name, value));
+
+ attributes.put(name, value);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.ProtocolHandler#getAttribute(java.lang.String)
+ */
+ @Override
+ public Object getAttribute(String key) {
+ if (log.isTraceEnabled())
+ log.trace(sm.getString("http11protocol.getattribute", key));
+ return attributes.get(key);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.ProtocolHandler#getAttributeNames()
+ */
+ @Override
+ public Iterator<String> getAttributeNames() {
+ return attributes.keySet().iterator();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.coyote.ProtocolHandler#setAdapter(org.apache.coyote.Adapter)
+ */
+ @Override
+ public void setAdapter(Adapter adapter) {
+ this.adapter = adapter;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.ProtocolHandler#getAdapter()
+ */
+ @Override
+ public Adapter getAdapter() {
+ return adapter;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.ProtocolHandler#hasIoEvents()
+ */
+ @Override
+ public boolean hasIoEvents() {
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * javax.management.MBeanRegistration#preRegister(javax.management.MBeanServer
+ * , javax.management.ObjectName)
+ */
+ @Override
+ public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {
+ oname = name;
+ mserver = server;
+ domain = name.getDomain();
+ return name;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.management.MBeanRegistration#postRegister(java.lang.Boolean)
+ */
+ @Override
+ public void postRegister(Boolean registrationDone) {
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.management.MBeanRegistration#preDeregister()
+ */
+ @Override
+ public void preDeregister() throws Exception {
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.management.MBeanRegistration#postDeregister()
+ */
+ @Override
+ public void postDeregister() {
+ }
+
+ /**
+ * Getter for attributes
+ *
+ * @return the attributes
+ */
+ public HashMap<String, Object> getAttributes() {
+ return this.attributes;
+ }
+
+ /**
+ * Setter for the attributes
+ *
+ * @param attributes
+ * the attributes to set
+ */
+ public void setAttributes(HashMap<String, Object> attributes) {
+ this.attributes = attributes;
+ }
+
+ /**
+ * Getter for tpOname
+ *
+ * @return the tpOname
+ */
+ public ObjectName getTpOname() {
+ return this.tpOname;
+ }
+
+ /**
+ * Setter for the tpOname
+ *
+ * @param tpOname
+ * the tpOname to set
+ */
+ public void setTpOname(ObjectName tpOname) {
+ this.tpOname = tpOname;
+ }
+
+ /**
+ * Getter for rgOname
+ *
+ * @return the rgOname
+ */
+ public ObjectName getRgOname() {
+ return this.rgOname;
+ }
+
+ /**
+ * Setter for the rgOname
+ *
+ * @param rgOname
+ * the rgOname to set
+ */
+ public void setRgOname(ObjectName rgOname) {
+ this.rgOname = rgOname;
+ }
+
+ /**
+ * Getter for processorCache
+ *
+ * @return the processorCache
+ */
+ public int getProcessorCache() {
+ return this.processorCache;
+ }
+
+ /**
+ * Setter for the processorCache
+ *
+ * @param processorCache
+ * the processorCache to set
+ */
+ public void setProcessorCache(int processorCache) {
+ this.processorCache = processorCache;
+ }
+
+ /**
+ * Getter for canDestroy
+ *
+ * @return the canDestroy
+ */
+ public boolean getCanDestroy() {
+ return this.canDestroy;
+ }
+
+ /**
+ * Setter for the canDestroy
+ *
+ * @param canDestroy
+ * the canDestroy to set
+ */
+ public void setCanDestroy(boolean canDestroy) {
+ this.canDestroy = canDestroy;
+ }
+
+ /**
+ * Getter for socketBuffer
+ *
+ * @return the socketBuffer
+ */
+ public int getSocketBuffer() {
+ return this.socketBuffer;
+ }
+
+ /**
+ * Setter for the socketBuffer
+ *
+ * @param socketBuffer
+ * the socketBuffer to set
+ */
+ public void setSocketBuffer(int socketBuffer) {
+ this.socketBuffer = socketBuffer;
+ }
+
+ /**
+ * Getter for maxSavePostSize
+ *
+ * @return the maxSavePostSize
+ */
+ public int getMaxSavePostSize() {
+ return this.maxSavePostSize;
+ }
+
+ /**
+ * Setter for the maxSavePostSize
+ *
+ * @param maxSavePostSize
+ * the maxSavePostSize to set
+ */
+ public void setMaxSavePostSize(int maxSavePostSize) {
+ this.maxSavePostSize = maxSavePostSize;
+ }
+
+ /**
+ * Getter for maxHttpHeaderSize
+ *
+ * @return the maxHttpHeaderSize
+ */
+ public int getMaxHttpHeaderSize() {
+ return this.maxHttpHeaderSize;
+ }
+
+ /**
+ * Setter for the maxHttpHeaderSize
+ *
+ * @param maxHttpHeaderSize
+ * the maxHttpHeaderSize to set
+ */
+ public void setMaxHttpHeaderSize(int maxHttpHeaderSize) {
+ this.maxHttpHeaderSize = maxHttpHeaderSize;
+ }
+
+ /**
+ * Getter for disableUploadTimeout
+ *
+ * @return the disableUploadTimeout
+ */
+ public boolean getDisableUploadTimeout() {
+ return this.disableUploadTimeout;
+ }
+
+ /**
+ * Setter for the disableUploadTimeout
+ *
+ * @param disableUploadTimeout
+ * the disableUploadTimeout to set
+ */
+ public void setDisableUploadTimeout(boolean disableUploadTimeout) {
+ this.disableUploadTimeout = disableUploadTimeout;
+ }
+
+ /**
+ * Getter for compression
+ *
+ * @return the compression
+ */
+ public String getCompression() {
+ return this.compression;
+ }
+
+ /**
+ * Setter for the compression
+ *
+ * @param compression
+ * the compression to set
+ */
+ public void setCompression(String compression) {
+ this.compression = compression;
+ }
+
+ /**
+ * Getter for noCompressionUserAgents
+ *
+ * @return the noCompressionUserAgents
+ */
+ public String getNoCompressionUserAgents() {
+ return this.noCompressionUserAgents;
+ }
+
+ /**
+ * Setter for the noCompressionUserAgents
+ *
+ * @param noCompressionUserAgents
+ * the noCompressionUserAgents to set
+ */
+ public void setNoCompressionUserAgents(String noCompressionUserAgents) {
+ this.noCompressionUserAgents = noCompressionUserAgents;
+ }
+
+ /**
+ * Getter for compressableMimeTypes
+ *
+ * @return the compressableMimeTypes
+ */
+ public String getCompressableMimeType() {
+ return this.compressableMimeTypes;
+ }
+
+ /**
+ * Setter for the compressableMimeTypes
+ *
+ * @param compressableMimeTypes
+ * the compressableMimeTypes to set
+ */
+ public void setCompressableMimeType(String compressableMimeTypes) {
+ this.compressableMimeTypes = compressableMimeTypes;
+ }
+
+ /**
+ * Getter for compressionMinSize
+ *
+ * @return the compressionMinSize
+ */
+ public int getCompressionMinSize() {
+ return this.compressionMinSize;
+ }
+
+ /**
+ * Setter for the compressionMinSize
+ *
+ * @param compressionMinSize
+ * the compressionMinSize to set
+ */
+ public void setCompressionMinSize(int compressionMinSize) {
+ this.compressionMinSize = compressionMinSize;
+ }
+
+ /**
+ * Getter for protocol
+ *
+ * @return the protocol
+ */
+ public String getProtocol() {
+ return this.protocol;
+ }
+
+ /**
+ * Setter for the protocol
+ *
+ * @param protocol
+ * the protocol to set
+ */
+ public void setProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+
+ /**
+ * Getter for restrictedUserAgents
+ *
+ * @return the restrictedUserAgents
+ */
+ public String getRestrictedUserAgents() {
+ return this.restrictedUserAgents;
+ }
+
+ /**
+ * Setter for the restrictedUserAgents
+ *
+ * @param restrictedUserAgents
+ * the restrictedUserAgents to set
+ */
+ public void setRestrictedUserAgents(String restrictedUserAgents) {
+ this.restrictedUserAgents = restrictedUserAgents;
+ }
+
+ /**
+ * Getter for maxKeepAliveRequests
+ *
+ * @return the maxKeepAliveRequests
+ */
+ public int getMaxKeepAliveRequests() {
+ return this.maxKeepAliveRequests;
+ }
+
+ /**
+ * Setter for the maxKeepAliveRequests
+ *
+ * @param maxKeepAliveRequests
+ * the maxKeepAliveRequests to set
+ */
+ public void setMaxKeepAliveRequests(int maxKeepAliveRequests) {
+ this.maxKeepAliveRequests = maxKeepAliveRequests;
+ }
+
+ /**
+ * Getter for domain
+ *
+ * @return the domain
+ */
+ public String getDomain() {
+ return this.domain;
+ }
+
+ /**
+ * Setter for the domain
+ *
+ * @param domain
+ * the domain to set
+ */
+ public void setDomain(String domain) {
+ this.domain = domain;
+ }
+
+ /**
+ * Getter for oname
+ *
+ * @return the oname
+ */
+ public ObjectName getObjectName() {
+ return this.oname;
+ }
+
+ /**
+ * Setter for the oname
+ *
+ * @param oname
+ * the oname to set
+ */
+ public void setObjectName(ObjectName oname) {
+ this.oname = oname;
+ }
+
+ /**
+ * Getter for mserver
+ *
+ * @return the mserver
+ */
+ public MBeanServer getMserver() {
+ return this.mserver;
+ }
+
+ /**
+ * Setter for the mserver
+ *
+ * @param mserver
+ * the mserver to set
+ */
+ public void setMserver(MBeanServer mserver) {
+ this.mserver = mserver;
+ }
+
+ /**
+ * Getter for server
+ *
+ * @return the server
+ */
+ public String getServer() {
+ return this.server;
+ }
+
+ /**
+ * Setter for the server
+ *
+ * @param server
+ * the server to set
+ */
+ public void setServer(String server) {
+ this.server = server;
+ }
+
+ /**
+ * Getter for timeout
+ *
+ * @return the timeout
+ */
+ public int getTimeout() {
+ return this.timeout;
+ }
+
+ /**
+ * Setter for the timeout
+ *
+ * @param timeout
+ * the timeout to set
+ */
+ public void setTimeout(int timeout) {
+ this.timeout = timeout;
+ }
+
+ /**
+ * Getter for secure
+ *
+ * @return the secure
+ */
+ public boolean isSecure() {
+ return this.secure;
+ }
+
+ /**
+ * Setter for the secure
+ *
+ * @param secure
+ * the secure to set
+ */
+ public void setSecure(boolean secure) {
+ this.secure = secure;
+ }
+
+ /**
+ * @return the key store
+ */
+ public String getKeystore() {
+ return (String) getAttribute("keystore");
+ }
+
+ /**
+ * @param keystore
+ */
+ public void setKeystore(String keystore) {
+ setAttribute("keystore", keystore);
+ }
+
+ /**
+ * @return the key pass
+ */
+ public String getKeypass() {
+ return (String) getAttribute("keypass");
+ }
+
+ /**
+ * @param keypass
+ */
+ public void setKeypass(String keypass) {
+ attributes.put("keypass", keypass);
+ }
+
+ /**
+ * @return the key store type
+ */
+ public String getKeytype() {
+ return (String) getAttribute("keystoreType");
+ }
+
+ /**
+ * @param keyType
+ */
+ public void setKeytype(String keyType) {
+ setAttribute("keystoreType", keyType);
+ }
+
+ /**
+ * @return the client authentication
+ */
+ public String getClientauth() {
+ return (String) getAttribute("clientauth");
+ }
+
+ /**
+ * @param k
+ */
+ public void setClientauth(String k) {
+ setAttribute("clientauth", k);
+ }
+
+ /**
+ * @return the protocols
+ */
+ public String getProtocols() {
+ return (String) getAttribute("protocols");
+ }
+
+ /**
+ * @param protocols
+ * the protocols to set
+ */
+ public void setProtocols(String protocols) {
+ setAttribute("protocols", protocols);
+ }
+
+ /**
+ * @return the algorithm
+ */
+ public String getAlgorithm() {
+ return (String) getAttribute("algorithm");
+ }
+
+ /**
+ * @param k
+ */
+ public void setAlgorithm(String k) {
+ setAttribute("algorithm", k);
+ }
+
+ /**
+ * @return the ciphers
+ */
+ public String getCiphers() {
+ return (String) getAttribute("ciphers");
+ }
+
+ /**
+ *
+ * @param ciphers
+ */
+ public void setCiphers(String ciphers) {
+ setAttribute("ciphers", ciphers);
+ }
+
+ /**
+ * @return the ke alias
+ */
+ public String getKeyAlias() {
+ return (String) getAttribute("keyAlias");
+ }
+
+ /**
+ *
+ * @param keyAlias
+ */
+ public void setKeyAlias(String keyAlias) {
+ setAttribute("keyAlias", keyAlias);
+ }
+
+ /**
+ * @return the SSL context
+ */
+ public SSLContext getSSLContext() {
+ return (SSLContext) getAttribute("SSLContext");
+ }
+
+ /**
+ * @param sslContext
+ */
+ public void setSSLContext(SSLContext sslContext) {
+ setAttribute("SSLContext", sslContext);
+ }
+}
Added: trunk/java/org/apache/coyote/http11/Http11NioProcessor.java
===================================================================
--- trunk/java/org/apache/coyote/http11/Http11NioProcessor.java
(rev 0)
+++ trunk/java/org/apache/coyote/http11/Http11NioProcessor.java 2012-06-13 08:50:17 UTC
(rev 2041)
@@ -0,0 +1,1284 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * 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.apache.coyote.http11;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetSocketAddress;
+import java.nio.channels.CompletionHandler;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.Request;
+import org.apache.coyote.RequestInfo;
+import org.apache.coyote.Response;
+import org.apache.coyote.http11.filters.BufferedInputFilter;
+import org.apache.coyote.http11.filters.ChunkedInputFilter;
+import org.apache.coyote.http11.filters.ChunkedOutputFilter;
+import org.apache.coyote.http11.filters.GzipOutputFilter;
+import org.apache.coyote.http11.filters.IdentityInputFilter;
+import org.apache.coyote.http11.filters.IdentityOutputFilter;
+import org.apache.coyote.http11.filters.SavedRequestInputFilter;
+import org.apache.coyote.http11.filters.VoidInputFilter;
+import org.apache.coyote.http11.filters.VoidOutputFilter;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.HexUtils;
+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.SocketState;
+import org.apache.tomcat.util.net.SocketStatus;
+
+/**
+ * {@code Http11NioProcessor}
+ * <p>
+ * Processes HTTP requests.
+ * </p>
+ *
+ * Created on Feb 22, 2012 at 3:00:29 PM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public class Http11NioProcessor extends Http11AbstractProcessor {
+
+ /**
+ * Input.
+ */
+ protected InternalNioInputBuffer inputBuffer = null;
+
+ /**
+ * Output.
+ */
+ protected InternalNioOutputBuffer outputBuffer = null;
+
+ /**
+ * Sendfile data.
+ */
+ protected NioEndpoint.SendfileData sendfileData = null;
+
+ /**
+ * Channel associated with the current connection.
+ */
+ protected NioChannel channel;
+
+ /**
+ * Associated endpoint.
+ */
+ protected NioEndpoint endpoint;
+
+ /**
+ * Create a new instance of {@code Http11NioProcessor}
+ *
+ * @param headerBufferSize
+ * @param endpoint
+ */
+ public Http11NioProcessor(int headerBufferSize, NioEndpoint endpoint) {
+
+ this.endpoint = endpoint;
+ request = new Request();
+ inputBuffer = new InternalNioInputBuffer(request, headerBufferSize, endpoint);
+ request.setInputBuffer(inputBuffer);
+ if (endpoint.getUseSendfile()) {
+ request.setSendfile(true);
+ }
+
+ response = new Response();
+ response.setHook(this);
+ outputBuffer = new InternalNioOutputBuffer(response, headerBufferSize, endpoint);
+ response.setOutputBuffer(outputBuffer);
+ request.setResponse(response);
+ sslEnabled = endpoint.getSSLEnabled();
+ initializeFilters();
+
+ // Cause loading of HexUtils
+ int foo = HexUtils.DEC[0];
+
+ // Cause loading of FastHttpDateFormat
+ FastHttpDateFormat.getCurrentDate();
+ }
+
+ /**
+ * Mark the start of processing
+ */
+ public void startProcessing() {
+ eventProcessing = true;
+ }
+
+ /**
+ * Mark the end of processing
+ */
+ public void endProcessing() {
+ eventProcessing = false;
+ }
+
+ /**
+ * @return true if the input buffer is available
+ */
+ public boolean isAvailable() {
+ return inputBuffer.available();
+ }
+
+ /**
+ * Add input or output filter.
+ *
+ * @param className
+ * class name of the filter
+ */
+ protected void addFilter(String className) {
+ try {
+ Class<?> clazz = Class.forName(className);
+ Object obj = clazz.newInstance();
+ if (obj instanceof InputFilter) {
+ inputBuffer.addFilter((InputFilter) obj);
+ } else if (obj instanceof OutputFilter) {
+ outputBuffer.addFilter((OutputFilter) obj);
+ } else {
+ log.warn(sm.getString("http11processor.filter.unknown", className));
+ }
+ } catch (Exception e) {
+ log.error(sm.getString("http11processor.filter.error", className), e);
+ }
+ }
+
+ /**
+ * General use method
+ *
+ * @param sArray
+ * the StringArray
+ * @param value
+ * string
+ */
+ protected String[] addStringArray(String sArray[], String value) {
+ String[] result = null;
+ if (sArray == null) {
+ result = new String[1];
+ result[0] = value;
+ } else {
+ result = new String[sArray.length + 1];
+ System.arraycopy(sArray, 0, result, 0, sArray.length);
+ // for (int i = 0; i < sArray.length; i++)
+ // result[i] = sArray[i];
+ result[sArray.length] = value;
+ }
+ return result;
+ }
+
+ /**
+ *
+ * @param channel
+ */
+ private void setChannel(NioChannel channel) {
+ // Setting up the channel
+ this.channel = channel;
+ this.inputBuffer.setChannel(channel);
+ this.outputBuffer.setChannel(channel);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.coyote.http11.Http11AbstractProcessor#event(org.apache.tomcat
+ * .util.net.SocketStatus)
+ */
+ public SocketState event(SocketStatus status) throws IOException {
+
+ RequestInfo rp = request.getRequestProcessor();
+ try {
+ // If processing a write event, must flush any leftover bytes first
+ if (status == SocketStatus.OPEN_WRITE) {
+ // If the flush does not manage to flush all leftover bytes, the
+ // socket should
+ // go back to the poller.
+ if (!outputBuffer.flushLeftover()) {
+ return SocketState.LONG;
+ }
+ // The write notification is now done
+ writeNotification = false;
+ // Allow convenient synchronous blocking writes
+ response.setFlushLeftovers(true);
+ } else if (status == SocketStatus.OPEN_CALLBACK) {
+ // The resume notification is now done
+ resumeNotification = false;
+ } else if (status == SocketStatus.ERROR) {
+ // Set error flag right away
+ error = true;
+ }
+ containerThread.set(Boolean.TRUE);
+ rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
+ error = !adapter.event(request, response, status);
+ } catch (InterruptedIOException e) {
+ error = true;
+ } catch (Throwable t) {
+ log.error(sm.getString("http11processor.request.process"), t);
+ // 500 - Internal Server Error
+ response.setStatus(500);
+ error = true;
+ }
+
+ rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
+
+ if (error) {
+ inputBuffer.nextRequest();
+ outputBuffer.nextRequest();
+ recycle();
+ return SocketState.CLOSED;
+ } else if (!event) {
+ endRequest();
+ boolean pipelined = inputBuffer.nextRequest();
+ outputBuffer.nextRequest();
+ recycle();
+ return (pipelined || !keepAlive) ? SocketState.CLOSED : SocketState.OPEN;
+ } else {
+ return SocketState.LONG;
+ }
+ }
+
+ /**
+ * Process pipelined HTTP requests using the specified input and output
+ * streams.
+ *
+ * @param channel
+ * @return the process state
+ *
+ * @throws IOException
+ * error during an I/O operation
+ */
+ public SocketState process(NioChannel channel) throws IOException {
+ RequestInfo rp = request.getRequestProcessor();
+ rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
+
+ this.reset();
+ // Setting up the channel
+ this.setChannel(channel);
+
+ int keepAliveLeft = maxKeepAliveRequests;
+ int soTimeout = endpoint.getSoTimeout();
+ boolean keptAlive = false;
+ boolean openChannel = false;
+
+ while (!error && keepAlive && !event) {
+ // Parsing the request header
+ try {
+ if (!disableUploadTimeout && keptAlive && soTimeout > 0) {
+ endpoint.setSoTimeout(soTimeout * 1000);
+ }
+
+ if (!inputBuffer.parseRequestLine(keptAlive)) {
+ // This means that no data is available right now
+ // (long keep-alive), so that the processor should be
+ // recycled and the method should return true
+ final NioChannel ch = channel;
+ // Perform an asynchronous read operation to wait for
+ // incoming data
+ ch.awaitRead(soTimeout, TimeUnit.MILLISECONDS, ch,
+ new CompletionHandler<Integer, NioChannel>() {
+
+ @Override
+ public void completed(Integer nBytes, NioChannel attachment) {
+ if (nBytes < 0) {
+ // Reach the end of the stream
+ failed(null, attachment);
+ } else {
+ endpoint.processChannel(ch, null);
+ }
+ }
+
+ @Override
+ public void failed(Throwable exc, NioChannel attachment) {
+ closeChannel(attachment);
+ }
+ });
+ openChannel = true;
+ break;
+ }
+
+ request.setStartTime(System.currentTimeMillis());
+ keptAlive = true;
+ if (!disableUploadTimeout) {
+ endpoint.setSoTimeout(timeout * 1000);
+ }
+ inputBuffer.parseHeaders();
+ } catch (IOException e) {
+ error = true;
+ break;
+ } catch (Throwable t) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("http11processor.header.parse"), t);
+ }
+ // 400 - Bad Request
+ response.setStatus(400);
+ error = true;
+ }
+ // Setting up filters, and parse some request headers
+ rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
+ try {
+ prepareRequest();
+ } catch (Throwable t) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("http11processor.request.prepare"), t);
+ }
+ // 500 - Internal Server Error
+ response.setStatus(500);
+ error = true;
+ }
+
+ if (maxKeepAliveRequests > 0 && --keepAliveLeft == 0) {
+ keepAlive = false;
+ }
+
+ // Process the request in the adapter
+ if (!error) {
+ try {
+ rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
+ adapter.service(request, response);
+ // Handle when the response was committed before a serious
+ // error occurred. Throwing a ServletException should both
+ // set the status to 500 and set the errorException.
+ // If we fail here, then the response is likely already
+ // committed, so we can't try and set headers.
+ if (keepAlive && !error) { // Avoid checking twice.
+ error = response.getErrorException() != null
+ || statusDropsConnection(response.getStatus());
+ }
+ } catch (InterruptedIOException e) {
+ error = true;
+ } catch (Throwable t) {
+ log.error(sm.getString("http11processor.request.process"), t);
+ // 500 - Internal Server Error
+ response.setStatus(500);
+ error = true;
+ }
+ }
+
+ // Finish the handling of the request
+ if (error) {
+ // If there is an unspecified error, the connection will be
+ // closed
+ inputBuffer.setSwallowInput(false);
+ }
+ if (!event) {
+ endRequest();
+ }
+
+ // If there was an error, make sure the request is counted as
+ // and error, and update the statistics counter
+ if (error) {
+ response.setStatus(500);
+ }
+ request.updateCounters();
+ boolean pipelined = false;
+ if (!event) {
+ // Next request
+ pipelined = inputBuffer.nextRequest();
+ outputBuffer.nextRequest();
+ }
+
+ // Do sendfile as needed: add socket to sendfile and end
+ if (sendfileData != null && !error) {
+ sendfileData.setChannel(channel);
+ sendfileData.setKeepAlive(keepAlive && !pipelined);
+ if (!endpoint.addSendfileData(sendfileData)) {
+ if (sendfileData.getChannel() == null) {
+ error = true;
+ } else {
+ openChannel = true;
+ }
+ break;
+ }
+ }
+
+ rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
+ }
+ rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
+
+ if (event) {
+ if (error) {
+ inputBuffer.nextRequest();
+ outputBuffer.nextRequest();
+ recycle();
+ return SocketState.CLOSED;
+ } else {
+ eventProcessing = false;
+ return SocketState.LONG;
+ }
+ } else {
+ recycle();
+ return (openChannel) ? SocketState.OPEN : SocketState.CLOSED;
+ }
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.http11.Http11AbstractProcessor#endRequest()
+ */
+ public void endRequest() {
+
+ // Finish the handling of the request
+ try {
+ inputBuffer.endRequest();
+ } catch (IOException e) {
+ error = true;
+ } catch (Throwable t) {
+ log.error(sm.getString("http11processor.request.finish"), t);
+ // 500 - Internal Server Error
+ response.setStatus(500);
+ error = true;
+ }
+ try {
+ outputBuffer.endRequest();
+ } catch (IOException e) {
+ error = true;
+ } catch (Throwable t) {
+ log.error(sm.getString("http11processor.response.finish"), t);
+ error = true;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.http11.Http11AbstractProcessor#recycle()
+ */
+ public void recycle() {
+ inputBuffer.recycle();
+ outputBuffer.recycle();
+ this.channel = null;
+ super.recycle();
+ }
+
+ /**
+ * Commit the action
+ *
+ * @param param
+ */
+ private void commit(Object param) {
+ if (!response.isCommitted()) {
+ // Validate and write response headers
+ prepareResponse();
+ try {
+ outputBuffer.commit();
+ } catch (IOException e) {
+ // Set error flag
+ error = true;
+ }
+ }
+ }
+
+ /**
+ * Send a 100 status back if it makes sense (response not committed yet, and
+ * client specified an expectation for 100-continue)
+ *
+ * @param param
+ */
+ private void sendAck(Object param) {
+
+ if ((response.isCommitted()) || !expectation) {
+ return;
+ }
+
+ inputBuffer.setSwallowInput(true);
+ try {
+ outputBuffer.sendAck();
+ } catch (Exception e) {
+ // Set error flag
+ error = true;
+ }
+ }
+
+ /**
+ * Flush the output buffer
+ */
+ private void flush() {
+ try {
+ outputBuffer.flush();
+ } catch (IOException e) {
+ // Set error flag
+ error = true;
+ response.setErrorException(e);
+ }
+ }
+
+ /**
+ * End the processing of the current request, and stop any further
+ * transactions with the client
+ */
+ private void close() {
+ event = false;
+ try {
+ outputBuffer.endRequest();
+ } catch (IOException e) {
+ // Set error flag
+ error = true;
+ }
+ }
+
+ /**
+ * Close the specified channel without handling of possible exception
+ *
+ * @param ch
+ * the channel to be closed
+ */
+ private void closeChannel(NioChannel ch) {
+ endpoint.closeChannel(ch);
+ }
+
+ /**
+ * Get the remote host address
+ */
+ private void requestHostAddressAttr() {
+ if (remoteAddr == null && (channel != null)) {
+ try {
+ remoteAddr = ((InetSocketAddress) this.channel.getRemoteAddress()).getHostName();
+ } catch (Exception e) {
+ log.warn(sm.getString("http11processor.socket.info"), e);
+ }
+ }
+ request.remoteAddr().setString(remoteAddr);
+ }
+
+ /**
+ * Request the local name attribute
+ */
+ private void requestLocalNameAttr() {
+ if (localName == null && (channel != null)) {
+ try {
+ localName = ((InetSocketAddress) this.channel.getLocalAddress()).getHostName();
+ } catch (Exception e) {
+ log.warn(sm.getString("http11processor.socket.info"), e);
+ }
+ }
+ request.localName().setString(localName);
+ }
+
+ /**
+ * Get remote host name
+ */
+ private void requestHostAttribute() {
+ if (remoteHost == null && (channel != null)) {
+ try {
+ remoteHost = ((InetSocketAddress) this.channel.getRemoteAddress()).getHostName();
+ if (remoteHost == null) {
+ remoteAddr = ((InetSocketAddress) this.channel.getRemoteAddress()).getAddress()
+ .getHostAddress();
+ remoteHost = remoteAddr;
+ }
+ } catch (Exception e) {
+ log.warn(sm.getString("http11processor.socket.info"), e);
+ }
+ }
+ request.remoteHost().setString(remoteHost);
+ }
+
+ /**
+ * Get local host address
+ */
+ private void requestLocalHostAddressAttr() {
+ if (localAddr == null && (channel != null)) {
+ try {
+ localAddr = ((InetSocketAddress) this.channel.getLocalAddress()).getAddress()
+ .getHostAddress();
+ } catch (Exception e) {
+ log.warn(sm.getString("http11processor.socket.info"), e);
+ }
+ }
+
+ request.localAddr().setString(localAddr);
+ }
+
+ /**
+ * Get remote port
+ */
+ private void requestRemotePortAttr() {
+ if (remotePort == -1 && (channel != null)) {
+ try {
+ remotePort = ((InetSocketAddress) this.channel.getRemoteAddress()).getPort();
+ } catch (Exception e) {
+ log.warn(sm.getString("http11processor.socket.info"), e);
+ }
+ }
+ request.setRemotePort(remotePort);
+ }
+
+ /**
+ * Get local port
+ */
+ private void requestLocalPortAttr() {
+ if (localPort == -1 && (channel != null)) {
+ try {
+ localPort = ((InetSocketAddress) this.channel.getLocalAddress()).getPort();
+ } catch (Exception e) {
+ log.warn(sm.getString("http11processor.socket.info"), e);
+ }
+ }
+ request.setLocalPort(localPort);
+ }
+
+ /**
+ * Get the SSL attribute
+ */
+ private void requestSSLAttr() {
+ try {
+ if (sslSupport != null) {
+ Object sslO = sslSupport.getCipherSuite();
+ if (sslO != null)
+ request.setAttribute(org.apache.tomcat.util.net.Constants.CIPHER_SUITE_KEY, sslO);
+ sslO = sslSupport.getPeerCertificateChain(false);
+ if (sslO != null)
+ request.setAttribute(org.apache.tomcat.util.net.Constants.CERTIFICATE_KEY, sslO);
+ sslO = sslSupport.getKeySize();
+ if (sslO != null)
+ request.setAttribute(org.apache.tomcat.util.net.Constants.KEY_SIZE_KEY, sslO);
+ sslO = sslSupport.getSessionId();
+ if (sslO != null)
+ request.setAttribute(org.apache.tomcat.util.net.Constants.SESSION_ID_KEY, sslO);
+ }
+ } catch (Exception e) {
+ log.warn(sm.getString("http11processor.socket.ssl"), e);
+ }
+ }
+
+ /**
+ * Get the SSL certificate
+ */
+ private void requestSSLCertificate() {
+ if (sslSupport != null) {
+ // Consume and buffer the request body, so that it does not
+ // interfere with the client's handshake messages
+ if (maxSavePostSize != 0) {
+ BufferedInputFilter buffredInputFilter = new BufferedInputFilter();
+ buffredInputFilter.setLimit(maxSavePostSize);
+ inputBuffer.addActiveFilter(buffredInputFilter);
+ }
+ try {
+ Object sslO = sslSupport.getPeerCertificateChain(true);
+ if (sslO != null) {
+ request.setAttribute(org.apache.tomcat.util.net.Constants.CERTIFICATE_KEY, sslO);
+ }
+ } catch (Exception e) {
+ log.warn(sm.getString("http11processor.socket.ssl"), e);
+ }
+ }
+ }
+
+ /**
+ *
+ * @param param
+ */
+ private void requestSetBodyReplay(Object param) {
+ ByteChunk body = (ByteChunk) param;
+
+ InputFilter savedBody = new SavedRequestInputFilter(body);
+ savedBody.setRequest(request);
+
+ InternalNioInputBuffer internalBuffer = (InternalNioInputBuffer)
request.getInputBuffer();
+ internalBuffer.addActiveFilter(savedBody);
+ }
+
+ /**
+ * Begin an event
+ *
+ * @param param
+ * the vent parameter
+ */
+ private void beginEvent(Object param) {
+ event = true;
+ // Set channel to non blocking mode
+ if (param == Boolean.TRUE) {
+ outputBuffer.setNonBlocking(true);
+ inputBuffer.setNonBlocking(true);
+ }
+ }
+
+ /**
+ * End the event
+ *
+ * @param param
+ * the event parameter
+ */
+ private void endEvent(Object param) {
+ event = false;
+ // End non blocking mode
+ if (outputBuffer.getNonBlocking()) {
+ outputBuffer.setNonBlocking(false);
+ inputBuffer.setNonBlocking(false);
+ }
+ }
+
+ /**
+ * Resume the event
+ *
+ * @param param
+ * the vent parameter
+ */
+ private void resumeEvent(Object param) {
+ readNotifications = true;
+ // An event is being processed already: adding for resume will be
+ // done
+ // when the channel gets back to the poller
+ if (!eventProcessing && !resumeNotification) {
+ endpoint.addEventChannel(channel, keepAliveTimeout, false, false, true, true);
+ }
+ resumeNotification = true;
+ }
+
+ /**
+ * Write Event
+ *
+ * @param param
+ */
+ private void writeEvent(Object param) {
+ // An event is being processed already: adding for write will be
+ // done
+ // when the channel gets back to the poller
+ if (!eventProcessing && !writeNotification) {
+ endpoint.addEventChannel(channel, timeout, false, true, false, true);
+ }
+ writeNotification = true;
+ }
+
+ /**
+ * Suspend Event
+ */
+ private void suspendEvent() {
+ readNotifications = false;
+ }
+
+ /**
+ * Timeout event
+ *
+ * @param param
+ * the timeout value
+ */
+ private void timeoutEvent(Object param) {
+ timeout = ((Integer) param).intValue();
+ }
+
+ /**
+ * Make the input buffer available
+ */
+ private void makeAvailable() {
+ inputBuffer.useAvailable();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.ActionHook#action(org.apache.coyote.ActionCode,
+ * java.lang.Object)
+ */
+ public void action(ActionCode actionCode, Object param) {
+
+ if (actionCode == ActionCode.ACTION_COMMIT) {
+ // Commit current response
+ commit(param);
+ } else if (actionCode == ActionCode.ACTION_ACK) {
+ // Acknowledge request
+ sendAck(param);
+ } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
+ // Flush
+ flush();
+ } else if (actionCode == ActionCode.ACTION_CLOSE) {
+ // Close
+ close();
+ } else if (actionCode == ActionCode.ACTION_CUSTOM) {
+ // DO NOTHING
+ } else if (actionCode == ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE) {
+ // Get remote host address
+ requestHostAddressAttr();
+ } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE) {
+ // Get local host name
+ requestLocalNameAttr();
+ } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
+ // Get remote host name
+ requestHostAttribute();
+ } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
+ // Get local host address
+ requestLocalHostAddressAttr();
+ } else if (actionCode == ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE) {
+ // Get remote port
+ requestRemotePortAttr();
+ } else if (actionCode == ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE) {
+ // Get local port
+ requestLocalPortAttr();
+ } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE) {
+ // request for the SSL attribute
+ requestSSLAttr();
+ } else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE) {
+ // Request for the SSL certificate
+ requestSSLCertificate();
+ } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
+ //
+ requestSetBodyReplay(param);
+ } else if (actionCode == ActionCode.ACTION_AVAILABLE) {
+ // make the input buffer available
+ makeAvailable();
+ } else if (actionCode == ActionCode.ACTION_EVENT_BEGIN) {
+ // Begin event
+ beginEvent(param);
+ } else if (actionCode == ActionCode.ACTION_EVENT_END) {
+ // End event
+ endEvent(param);
+ } else if (actionCode == ActionCode.ACTION_EVENT_SUSPEND) {
+ // Suspend event
+ suspendEvent();
+ } else if (actionCode == ActionCode.ACTION_EVENT_RESUME) {
+ // Resume event
+ resumeEvent(param);
+ } else if (actionCode == ActionCode.ACTION_EVENT_WRITE) {
+ // Write event
+ writeEvent(param);
+ } else if (actionCode == ActionCode.ACTION_EVENT_WRITE) {
+ // Timeout event
+ timeoutEvent(param);
+ } else if (actionCode == ActionCode.UPGRADE) {
+ // Switch to raw bytes mode
+ inputBuffer.removeActiveFilters();
+ outputBuffer.removeActiveFilters();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.http11.Http11AbstractProcessor#prepareRequest()
+ */
+ protected void prepareRequest() {
+
+ http11 = true;
+ http09 = false;
+ contentDelimitation = false;
+ expectation = false;
+ sendfileData = null;
+
+ if (sslEnabled) {
+ request.scheme().setString("https");
+ }
+ MessageBytes protocolMB = request.protocol();
+
+ if (protocolMB.equals(Constants.HTTP_11)) {
+ http11 = true;
+ protocolMB.setString(Constants.HTTP_11);
+ } else if (protocolMB.equals(Constants.HTTP_10)) {
+ http11 = false;
+ keepAlive = false;
+ protocolMB.setString(Constants.HTTP_10);
+ } else if (protocolMB.equals("")) {
+ // HTTP/0.9
+ http09 = true;
+ http11 = false;
+ keepAlive = false;
+ } else {
+ // Unsupported protocol
+ http11 = false;
+ error = true;
+ // Send 505; Unsupported HTTP version
+ response.setStatus(505);
+ }
+
+ MessageBytes methodMB = request.method();
+ if (methodMB.equals(Constants.GET)) {
+ methodMB.setString(Constants.GET);
+ } else if (methodMB.equals(Constants.POST)) {
+ methodMB.setString(Constants.POST);
+ }
+
+ MimeHeaders headers = request.getMimeHeaders();
+
+ // Check connection header
+ MessageBytes connectionValueMB = headers.getValue("connection");
+ if (connectionValueMB != null) {
+ ByteChunk connectionValueBC = connectionValueMB.getByteChunk();
+ if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {
+ keepAlive = false;
+ } else if (findBytes(connectionValueBC, Constants.KEEPALIVE_BYTES) != -1) {
+ keepAlive = true;
+ }
+ }
+
+ MessageBytes expectMB = null;
+ if (http11)
+ expectMB = headers.getValue("expect");
+ if ((expectMB != null) && (expectMB.indexOfIgnoreCase("100-continue",
0) != -1)) {
+ inputBuffer.setSwallowInput(false);
+ expectation = true;
+ }
+
+ // Check user-agent header
+ if ((restrictedUserAgents != null) && ((http11) || (keepAlive))) {
+ MessageBytes userAgentValueMB = headers.getValue("user-agent");
+ // Check in the restricted list, and adjust the http11
+ // and keepAlive flags accordingly
+ if (userAgentValueMB != null) {
+ String userAgentValue = userAgentValueMB.toString();
+ for (int i = 0; i < restrictedUserAgents.length; i++) {
+ if (restrictedUserAgents[i].matcher(userAgentValue).matches()) {
+ http11 = false;
+ keepAlive = false;
+ break;
+ }
+ }
+ }
+ }
+
+ // Check for a full URI (including protocol://host:port/)
+ ByteChunk uriBC = request.requestURI().getByteChunk();
+ if (uriBC.startsWithIgnoreCase("http", 0)) {
+
+ int pos = uriBC.indexOf("://", 0, 3, 4);
+ int uriBCStart = uriBC.getStart();
+ int slashPos = -1;
+ if (pos != -1) {
+ byte[] uriB = uriBC.getBytes();
+ slashPos = uriBC.indexOf('/', pos + 3);
+ if (slashPos == -1) {
+ slashPos = uriBC.getLength();
+ // Set URI as "/"
+ request.requestURI().setBytes(uriB, uriBCStart + pos + 1, 1);
+ } else {
+ request.requestURI().setBytes(uriB, uriBCStart + slashPos,
+ uriBC.getLength() - slashPos);
+ }
+ MessageBytes hostMB = headers.setValue("host");
+ hostMB.setBytes(uriB, uriBCStart + pos + 3, slashPos - pos - 3);
+ }
+
+ }
+
+ // Input filter setup
+ InputFilter[] inputFilters = inputBuffer.getFilters();
+
+ // Parse transfer-encoding header
+ MessageBytes transferEncodingValueMB = null;
+ if (http11)
+ transferEncodingValueMB = headers.getValue("transfer-encoding");
+ if (transferEncodingValueMB != null) {
+ String transferEncodingValue = transferEncodingValueMB.toString();
+ // Parse the comma separated list. "identity" codings are ignored
+ int startPos = 0;
+ int commaPos = transferEncodingValue.indexOf(',');
+ String encodingName = null;
+ while (commaPos != -1) {
+ encodingName = transferEncodingValue.substring(startPos, commaPos)
+ .toLowerCase(Locale.ENGLISH).trim();
+ if (!addInputFilter(inputFilters, encodingName)) {
+ // Unsupported transfer encoding
+ error = true;
+ // 501 - Unimplemented
+ response.setStatus(501);
+ }
+ startPos = commaPos + 1;
+ commaPos = transferEncodingValue.indexOf(',', startPos);
+ }
+ encodingName = transferEncodingValue.substring(startPos).toLowerCase(Locale.ENGLISH)
+ .trim();
+ if (!addInputFilter(inputFilters, encodingName)) {
+ // Unsupported transfer encoding
+ error = true;
+ // 501 - Unimplemented
+ response.setStatus(501);
+ }
+ }
+
+ // Parse content-length header
+ long contentLength = request.getContentLengthLong();
+ if (contentLength >= 0 && !contentDelimitation) {
+ inputBuffer.addActiveFilter(inputFilters[Constants.IDENTITY_FILTER]);
+ contentDelimitation = true;
+ }
+
+ MessageBytes valueMB = headers.getValue("host");
+
+ // Check host header
+ if (http11 && (valueMB == null)) {
+ error = true;
+ // 400 - Bad request
+ response.setStatus(400);
+ }
+
+ parseHost(valueMB);
+
+ if (!contentDelimitation) {
+ // If there's no content length
+ // (broken HTTP/1.0 or HTTP/1.1), assume
+ // the client is not broken and didn't send a body
+ inputBuffer.addActiveFilter(inputFilters[Constants.VOID_FILTER]);
+ contentDelimitation = true;
+ }
+
+ }
+
+ /**
+ * Parse host.
+ */
+ protected void parseHost(MessageBytes valueMB) {
+
+ if (valueMB == null || valueMB.isNull()) {
+ // HTTP/1.0
+ // Default is what the socket tells us. Overriden if a host is
+ // found/parsed
+ request.setServerPort(endpoint.getPort());
+ return;
+ }
+
+ ByteChunk valueBC = valueMB.getByteChunk();
+ byte[] valueB = valueBC.getBytes();
+ int valueL = valueBC.getLength();
+ int valueS = valueBC.getStart();
+ int colonPos = -1;
+ if (hostNameC.length < valueL) {
+ hostNameC = new char[valueL];
+ }
+
+ boolean ipv6 = (valueB[valueS] == '[');
+ boolean bracketClosed = false;
+ for (int i = 0; i < valueL; i++) {
+ char b = (char) valueB[i + valueS];
+ hostNameC[i] = b;
+ if (b == ']') {
+ bracketClosed = true;
+ } else if (b == ':') {
+ if (!ipv6 || bracketClosed) {
+ colonPos = i;
+ break;
+ }
+ }
+ }
+
+ if (colonPos < 0) {
+ if (!sslEnabled) {
+ // 80 - Default HTTP port
+ request.setServerPort(80);
+ } else {
+ // 443 - Default HTTPS port
+ request.setServerPort(443);
+ }
+ request.serverName().setChars(hostNameC, 0, valueL);
+ } else {
+
+ request.serverName().setChars(hostNameC, 0, colonPos);
+
+ int port = 0;
+ int mult = 1;
+ for (int i = valueL - 1; i > colonPos; i--) {
+ int charValue = HexUtils.DEC[valueB[i + valueS]];
+ if (charValue == -1) {
+ // Invalid character
+ error = true;
+ // 400 - Bad request
+ response.setStatus(400);
+ break;
+ }
+ port = port + (charValue * mult);
+ mult = 10 * mult;
+ }
+ request.setServerPort(port);
+
+ }
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.http11.Http11AbstractProcessor#prepareResponse()
+ */
+ protected void prepareResponse() {
+
+ boolean entityBody = true;
+ contentDelimitation = false;
+
+ OutputFilter[] outputFilters = outputBuffer.getFilters();
+
+ if (http09 == true) {
+ // HTTP/0.9
+ outputBuffer.addActiveFilter(outputFilters[Constants.IDENTITY_FILTER]);
+ return;
+ }
+
+ int statusCode = response.getStatus();
+ if ((statusCode == 204) || (statusCode == 205) || (statusCode == 304)) {
+ // No entity body
+ outputBuffer.addActiveFilter(outputFilters[Constants.VOID_FILTER]);
+ entityBody = false;
+ contentDelimitation = true;
+ }
+ if (statusCode >= 100 && statusCode < 200) {
+ entityBody = false;
+ contentDelimitation = true;
+ }
+
+ MessageBytes methodMB = request.method();
+ if (methodMB.equals("HEAD")) {
+ // No entity body
+ outputBuffer.addActiveFilter(outputFilters[Constants.VOID_FILTER]);
+ contentDelimitation = true;
+ }
+
+ // Sendfile support
+ if (response.getSendfilePath() != null && endpoint.getUseSendfile()) {
+ // No entity body sent here
+ outputBuffer.addActiveFilter(outputFilters[Constants.VOID_FILTER]);
+ contentDelimitation = true;
+ sendfileData = endpoint.getSendfileData();
+ sendfileData.setFileName(response.getSendfilePath());
+ sendfileData.setStart(response.getSendfileStart());
+ sendfileData.setEnd(response.getSendfileEnd());
+ sendfileData.setKeepAlive(keepAlive);
+
+ }
+
+ // Check for compression
+ boolean useCompression = false;
+ if (entityBody && (compressionLevel > 0) && (sendfileData == null))
{
+ useCompression = isCompressable();
+ // Change content-length to -1 to force chunking
+ if (useCompression) {
+ response.setContentLength(-1);
+ }
+ }
+
+ MimeHeaders headers = response.getMimeHeaders();
+ if (!entityBody) {
+ response.setContentLength(-1);
+ } else {
+ String contentType = response.getContentType();
+ if (contentType != null) {
+ headers.setValue("Content-Type").setString(contentType);
+ }
+ String contentLanguage = response.getContentLanguage();
+ if (contentLanguage != null) {
+ headers.setValue("Content-Language").setString(contentLanguage);
+ }
+ }
+
+ long contentLength = response.getContentLengthLong();
+ if (contentLength != -1) {
+ headers.setValue("Content-Length").setLong(contentLength);
+ outputBuffer.addActiveFilter(outputFilters[Constants.IDENTITY_FILTER]);
+ contentDelimitation = true;
+ } else {
+ if (entityBody && http11 && (keepAlive || CHUNK_ON_CLOSE)) {
+ outputBuffer.addActiveFilter(outputFilters[Constants.CHUNKED_FILTER]);
+ contentDelimitation = true;
+ headers.addValue(Constants.TRANSFERENCODING).setString(Constants.CHUNKED);
+ } else {
+ outputBuffer.addActiveFilter(outputFilters[Constants.IDENTITY_FILTER]);
+ }
+ }
+
+ if (useCompression) {
+ outputBuffer.addActiveFilter(outputFilters[Constants.GZIP_FILTER]);
+ headers.setValue("Content-Encoding").setString("gzip");
+ // Make Proxies happy via Vary (from mod_deflate)
+ headers.addValue("Vary").setString("Accept-Encoding");
+ }
+
+ // Add date header
+ headers.setValue("Date").setString(FastHttpDateFormat.getCurrentDate());
+
+ // FIXME: Add transfer encoding header
+
+ if ((entityBody) && (!contentDelimitation)) {
+ // Mark as close the connection after the request, and add the
+ // connection: close header
+ keepAlive = false;
+ }
+
+ // If we know that the request is bad this early, add the
+ // Connection: close header.
+ keepAlive = keepAlive && !statusDropsConnection(statusCode);
+ if (!keepAlive) {
+ headers.addValue(Constants.CONNECTION).setString(Constants.CLOSE);
+ } else if (!http11 && !error) {
+ headers.addValue(Constants.CONNECTION).setString(Constants.KEEPALIVE);
+ }
+
+ // Build the response header
+ outputBuffer.sendStatus();
+
+ // Add server header
+ if (server != null) {
+ headers.setValue("Server").setString(server);
+ } else {
+ outputBuffer.write(Constants.SERVER_BYTES);
+ }
+
+ int size = headers.size();
+ for (int i = 0; i < size; i++) {
+ outputBuffer.sendHeader(headers.getName(i), headers.getValue(i));
+ }
+ outputBuffer.endHeaders();
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.http11.Http11AbstractProcessor#initializeFilters()
+ */
+ protected void initializeFilters() {
+
+ // Create and add the identity filters.
+ inputBuffer.addFilter(new IdentityInputFilter());
+ outputBuffer.addFilter(new IdentityOutputFilter());
+
+ // Create and add the chunked filters.
+ inputBuffer.addFilter(new ChunkedInputFilter());
+ outputBuffer.addFilter(new ChunkedOutputFilter());
+
+ // Create and add the void filters.
+ inputBuffer.addFilter(new VoidInputFilter());
+ outputBuffer.addFilter(new VoidOutputFilter());
+
+ // Create and add the chunked filters.
+ // inputBuffer.addFilter(new GzipInputFilter());
+ outputBuffer.addFilter(new GzipOutputFilter());
+
+ }
+
+ /**
+ * Add an input filter to the current request.
+ *
+ * @return false if the encoding was not found (which would mean it is
+ * unsupported)
+ */
+ protected boolean addInputFilter(InputFilter[] inputFilters, String encodingName) {
+ if (encodingName.equals("identity")) {
+ // Skip
+ } else if (encodingName.equals("chunked")) {
+ inputBuffer.addActiveFilter(inputFilters[Constants.CHUNKED_FILTER]);
+ contentDelimitation = true;
+ } else {
+ for (int i = 2; i < inputFilters.length; i++) {
+ if (inputFilters[i].getEncodingName().toString().equals(encodingName)) {
+ inputBuffer.addActiveFilter(inputFilters[i]);
+ return true;
+ }
+ }
+ return false;
+ }
+ return true;
+ }
+
+}
Added: trunk/java/org/apache/coyote/http11/Http11NioProtocol.java
===================================================================
--- trunk/java/org/apache/coyote/http11/Http11NioProtocol.java
(rev 0)
+++ trunk/java/org/apache/coyote/http11/Http11NioProtocol.java 2012-06-13 08:50:17 UTC
(rev 2041)
@@ -0,0 +1,1026 @@
+/**
+ * JBoss, Home of Professional Open Source. Copyright 2011, Red Hat, Inc., and
+ * individual contributors as indicated by the @author tags. See the
+ * copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
+ * site:
http://www.fsf.org.
+ */
+package org.apache.coyote.http11;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.InetAddress;
+import java.net.URLEncoder;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.CompletionHandler;
+import java.util.Iterator;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.management.ObjectName;
+
+import org.apache.coyote.RequestGroupInfo;
+import org.apache.coyote.RequestInfo;
+import org.apache.tomcat.util.modeler.Registry;
+import org.apache.tomcat.util.net.NioChannel;
+import org.apache.tomcat.util.net.NioEndpoint;
+import org.apache.tomcat.util.net.SSLImplementation;
+import org.apache.tomcat.util.net.SocketStatus;
+import org.apache.tomcat.util.net.jsse.NioJSSEImplementation;
+import org.apache.tomcat.util.net.jsse.NioJSSESocketChannelFactory;
+
+/**
+ * {@code Http11NioProtocol}
+ *
+ * Created on Jan 10, 2012 at 3:14:49 PM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public class Http11NioProtocol extends Http11AbstractProtocol {
+
+ protected NioEndpoint endpoint = new NioEndpoint();
+ private Http11ConnectionHandler cHandler = new Http11ConnectionHandler(this);
+ protected NioJSSESocketChannelFactory socketFactory = null;
+
+ /**
+ * Create a new instance of {@code Http11NioProtocol}
+ */
+ public Http11NioProtocol() {
+ setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
+ setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
+ setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.ProtocolHandler#getRequestGroupInfo()
+ */
+ @Override
+ public RequestGroupInfo getRequestGroupInfo() {
+ return cHandler.global;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.ProtocolHandler#init()
+ */
+ @Override
+ public void init() throws Exception {
+ endpoint.setName(getName());
+ endpoint.setHandler(cHandler);
+
+ // Verify the validity of the configured socket factory
+ try {
+ if (isSSLEnabled()) {
+ sslImplementation = SSLImplementation.getInstance(NioJSSEImplementation.class
+ .getName());
+ socketFactory = sslImplementation.getServerSocketChannelFactory();
+ endpoint.setServerSocketChannelFactory(socketFactory);
+ }
+ } catch (Exception ex) {
+ log.error(sm.getString("http11protocol.socketfactory.initerror"), ex);
+ throw ex;
+ }
+
+ if (socketFactory != null) {
+ Iterator<String> attE = attributes.keySet().iterator();
+ while (attE.hasNext()) {
+ String key = attE.next();
+ Object v = attributes.get(key);
+ socketFactory.setAttribute(key, v);
+ }
+ }
+
+ try {
+ // endpoint.setKeepAliveTimeout(this.timeout);
+ endpoint.init();
+ } catch (Exception ex) {
+ log.error(sm.getString("http11protocol.endpoint.initerror"), ex);
+ throw ex;
+ }
+ //
+ log.info(sm.getString("http11protocol.init", getName()));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.ProtocolHandler#start()
+ */
+ @Override
+ public void start() throws Exception {
+ if (org.apache.tomcat.util.Constants.ENABLE_MODELER) {
+ if (this.domain != null) {
+ try {
+ tpOname = new ObjectName(domain + ":" + "type=ThreadPool,name="
+ getName());
+ Registry.getRegistry(null, null).registerComponent(endpoint, tpOname, null);
+ } catch (Exception e) {
+ log.error("Can't register threadpool");
+ }
+ rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" +
getName());
+ Registry.getRegistry(null, null).registerComponent(cHandler.global, rgOname, null);
+ }
+ }
+ try {
+ endpoint.start();
+ } catch (Exception ex) {
+ log.error(sm.getString("http11protocol.endpoint.starterror"), ex);
+ throw ex;
+ }
+
+ log.info(sm.getString("http11protocol.start", getName()));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.ProtocolHandler#pause()
+ */
+ @Override
+ public void pause() throws Exception {
+ try {
+ endpoint.pause();
+ } catch (Exception ex) {
+ log.error(sm.getString("http11protocol.endpoint.pauseerror"), ex);
+ throw ex;
+ }
+ canDestroy = false;
+ // Wait for a while until all the processors are idle
+ RequestInfo[] states = cHandler.global.getRequestProcessors();
+ int retry = 0;
+ boolean done = false;
+ while (!done && retry < org.apache.coyote.Constants.MAX_PAUSE_WAIT) {
+ retry++;
+ done = true;
+ for (int i = 0; i < states.length; i++) {
+ if (states[i].getStage() == org.apache.coyote.Constants.STAGE_SERVICE) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // NOTHING TO DO
+ }
+ done = false;
+ break;
+ }
+ }
+ if (done) {
+ canDestroy = true;
+ }
+ }
+
+ log.info(sm.getString("http11protocol.pause", getName()));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.ProtocolHandler#resume()
+ */
+ @Override
+ public void resume() throws Exception {
+ try {
+ endpoint.resume();
+ } catch (Exception ex) {
+ log.error(sm.getString("http11protocol.endpoint.resumeerror"), ex);
+ throw ex;
+ }
+
+ log.info(sm.getString("http11protocol.resume", getName()));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.ProtocolHandler#destroy()
+ */
+ @Override
+ public void destroy() throws Exception {
+ log.info(sm.getString("http11protocol.stop", getName()));
+ if (canDestroy) {
+ endpoint.destroy();
+ } else {
+ log.warn(sm.getString("http11protocol.cannotDestroy", getName()));
+ try {
+ RequestInfo[] states = cHandler.global.getRequestProcessors();
+ for (int i = 0; i < states.length; i++) {
+ if (states[i].getStage() == org.apache.coyote.Constants.STAGE_SERVICE) {
+ // FIXME: Log RequestInfo content
+ }
+ }
+ } catch (Exception ex) {
+ log.error(sm.getString("http11protocol.cannotDestroy", getName()), ex);
+ throw ex;
+ }
+ }
+ if (org.apache.tomcat.util.Constants.ENABLE_MODELER) {
+ if (tpOname != null)
+ Registry.getRegistry(null, null).unregisterComponent(tpOname);
+ if (rgOname != null)
+ Registry.getRegistry(null, null).unregisterComponent(rgOname);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.http11.Http11AbstractProtocol#getName()
+ */
+ public String getName() {
+ String encodedAddr = "";
+ if (getAddress() != null) {
+ encodedAddr = "" + getAddress();
+ try {
+ encodedAddr = URLEncoder.encode(encodedAddr.replace('/', '-'),
"UTF-8") + "-";
+ } catch (UnsupportedEncodingException e) {
+ log.warn("UTF-8 encoding is not supported");
+ }
+ }
+ return ("http-" + encodedAddr + endpoint.getPort());
+ }
+
+ /**
+ * @return the executor
+ */
+ public Executor getExecutor() {
+ return endpoint.getExecutor();
+ }
+
+ /**
+ * Setter for the executor
+ *
+ * @param executor
+ * the executor to set
+ */
+ public void setExecutor(Executor executor) {
+ endpoint.setExecutor(executor);
+ }
+
+ /**
+ * @return the maximum number of threads
+ */
+ public int getMaxThreads() {
+ return endpoint.getMaxThreads();
+ }
+
+ /**
+ * Setter for the maximum number of threads
+ *
+ * @param maxThreads
+ * the maximum number of threads to set
+ */
+ public void setMaxThreads(int maxThreads) {
+ endpoint.setMaxThreads(maxThreads);
+ }
+
+ /**
+ * @param size
+ */
+ public void setPollerSize(int size) {
+ this.endpoint.setMaxConnections(size);
+ }
+
+ /**
+ * @return The poller size
+ */
+ public int getPollerSize() {
+ return this.endpoint.getMaxConnections();
+ }
+
+ /**
+ * @return the thread priority
+ */
+ public int getThreadPriority() {
+ return endpoint.getThreadPriority();
+ }
+
+ /**
+ * Setter for the thread priority
+ *
+ * @param threadPriority
+ * the thread priority to set
+ */
+ public void setThreadPriority(int threadPriority) {
+ endpoint.setThreadPriority(threadPriority);
+ }
+
+ /**
+ * @return the backlog
+ */
+ public int getBacklog() {
+ return endpoint.getBacklog();
+ }
+
+ /**
+ * Setter for the backlog
+ *
+ * @param backlog
+ * the backlog to set
+ */
+ public void setBacklog(int backlog) {
+ endpoint.setBacklog(backlog);
+ }
+
+ /**
+ * @return the port number
+ */
+ public int getPort() {
+ return endpoint.getPort();
+ }
+
+ /**
+ * Setter for the port number
+ *
+ * @param port
+ * the port number to set
+ */
+ public void setPort(int port) {
+ endpoint.setPort(port);
+ }
+
+ /**
+ * @return the IP address
+ */
+ public InetAddress getAddress() {
+ return endpoint.getAddress();
+ }
+
+ /**
+ * Setter for the IP address
+ *
+ * @param ia
+ * the IP address to set
+ */
+ public void setAddress(InetAddress ia) {
+ endpoint.setAddress(ia);
+ }
+
+ /**
+ * @return TCP NO DELAY
+ */
+ public boolean getTcpNoDelay() {
+ return endpoint.getTcpNoDelay();
+ }
+
+ /**
+ * @param tcpNoDelay
+ */
+ public void setTcpNoDelay(boolean tcpNoDelay) {
+ endpoint.setTcpNoDelay(tcpNoDelay);
+ }
+
+ /**
+ * @return the soLinger
+ */
+ public int getSoLinger() {
+ return endpoint.getSoLinger();
+ }
+
+ /**
+ * @param soLinger
+ * the soLinger to set
+ */
+ public void setSoLinger(int soLinger) {
+ endpoint.setSoLinger(soLinger);
+ }
+
+ /**
+ * @return the socket timeout
+ */
+ public int getSoTimeout() {
+ return endpoint.getSoTimeout();
+ }
+
+ /**
+ * Setter for the socket timeout
+ *
+ * @param soTimeout
+ */
+ public void setSoTimeout(int soTimeout) {
+ endpoint.setSoTimeout(soTimeout);
+ }
+
+ /**
+ * @return <tt>TRUE</tt> if the reverse connection is enabled, else
+ * <tt>FALSE</tt>
+ */
+ public boolean getReverseConnection() {
+ return endpoint.isReverseConnection();
+ }
+
+ /**
+ * Set the reverse connection
+ *
+ * @param reverseConnection
+ */
+ public void setReverseConnection(boolean reverseConnection) {
+ endpoint.setReverseConnection(reverseConnection);
+ }
+
+ /**
+ * @return <tt>TRUE</tt> if the defer accept is enabled, else
<tt>FALSE</tt>
+ */
+ public boolean getDeferAccept() {
+ return endpoint.getDeferAccept();
+ }
+
+ /**
+ * Set the defer accept
+ *
+ * @param deferAccept
+ */
+ public void setDeferAccept(boolean deferAccept) {
+ endpoint.setDeferAccept(deferAccept);
+ }
+
+ /**
+ * The number of seconds Tomcat will wait for a subsequent request before
+ * closing the connection.
+ *
+ * @return the keep alive timeout value
+ */
+ public int getKeepAliveTimeout() {
+ return endpoint.getKeepAliveTimeout();
+ }
+
+ /**
+ * Set the keep alive timeout value
+ *
+ * @param timeout
+ */
+ public void setKeepAliveTimeout(int timeout) {
+ endpoint.setKeepAliveTimeout(timeout);
+ }
+
+ /**
+ * @return the user send file boolean value
+ */
+ public boolean getUseSendfile() {
+ return endpoint.getUseSendfile();
+ }
+
+ /**
+ * Set the user send file
+ *
+ * @param useSendfile
+ */
+ public void setUseSendfile(boolean useSendfile) {
+ endpoint.setUseSendfile(useSendfile);
+ }
+
+ /**
+ * @return the send file size
+ */
+ public int getSendfileSize() {
+ return endpoint.getSendfileSize();
+ }
+
+ /**
+ * @param sendfileSize
+ */
+ public void setSendfileSize(int sendfileSize) {
+ endpoint.setSendfileSize(sendfileSize);
+ }
+
+ /**
+ * Return the Keep-Alive policy for the connection.
+ *
+ * @return keep-alive
+ */
+ public boolean getKeepAlive() {
+ return ((maxKeepAliveRequests != 0) && (maxKeepAliveRequests != 1));
+ }
+
+ /**
+ * Set the keep-alive policy for this connection.
+ *
+ * @param keepAlive
+ */
+ public void setKeepAlive(boolean keepAlive) {
+ if (!keepAlive) {
+ setMaxKeepAliveRequests(1);
+ }
+ }
+
+ // -------------------- Various implementation classes --------------------
+
+ // -------------------- SSL related properties --------------------
+
+ /**
+ * SSL engine.
+ *
+ * @return <tt>true</tt> if the SSL is enabled, else
<tt>false</tt>
+ */
+ public boolean isSSLEnabled() {
+ return endpoint.getSSLEnabled();
+ }
+
+ /**
+ * @param SSLEnabled
+ */
+ public void setSSLEnabled(boolean SSLEnabled) {
+ endpoint.setSSLEnabled(SSLEnabled);
+ }
+
+ /**
+ * SSL protocol.
+ *
+ * @return the SSL protocol
+ */
+ public String getSSLProtocol() {
+ return endpoint.getSSLProtocol();
+ }
+
+ /**
+ * @param SSLProtocol
+ */
+ public void setSSLProtocol(String SSLProtocol) {
+ endpoint.setSSLProtocol(SSLProtocol);
+ }
+
+ /**
+ * SSL password (if a cert is encrypted, and no password has been provided,
+ * a callback will ask for a password).
+ *
+ * @return the SSL password
+ */
+ public String getSSLPassword() {
+ return endpoint.getSSLPassword();
+ }
+
+ /**
+ * @param SSLPassword
+ */
+ public void setSSLPassword(String SSLPassword) {
+ endpoint.setSSLPassword(SSLPassword);
+ }
+
+ /**
+ * SSL cipher suite.
+ *
+ * @return the SSL cipher suite
+ */
+ public String getSSLCipherSuite() {
+ return endpoint.getSSLCipherSuite();
+ }
+
+ /**
+ * @param SSLCipherSuite
+ */
+ public void setSSLCipherSuite(String SSLCipherSuite) {
+ endpoint.setSSLCipherSuite(SSLCipherSuite);
+ }
+
+ /**
+ * SSL certificate file.
+ *
+ * @return SSL certificate file
+ */
+ public String getSSLCertificateFile() {
+ return endpoint.getSSLCertificateFile();
+ }
+
+ /**
+ * @param SSLCertificateFile
+ */
+ public void setSSLCertificateFile(String SSLCertificateFile) {
+ endpoint.setSSLCertificateFile(SSLCertificateFile);
+ }
+
+ /**
+ * SSL certificate key file.
+ *
+ * @return SSL certificate key file
+ */
+ public String getSSLCertificateKeyFile() {
+ return endpoint.getSSLCertificateKeyFile();
+ }
+
+ /**
+ * @param SSLCertificateKeyFile
+ */
+ public void setSSLCertificateKeyFile(String SSLCertificateKeyFile) {
+ endpoint.setSSLCertificateKeyFile(SSLCertificateKeyFile);
+ }
+
+ /**
+ * SSL certificate chain file.
+ *
+ * @return SSL certificate chain file
+ */
+ public String getSSLCertificateChainFile() {
+ return endpoint.getSSLCertificateChainFile();
+ }
+
+ /**
+ * @param SSLCertificateChainFile
+ */
+ public void setSSLCertificateChainFile(String SSLCertificateChainFile) {
+ endpoint.setSSLCertificateChainFile(SSLCertificateChainFile);
+ }
+
+ /**
+ * SSL CA certificate path.
+ *
+ * @return SSL CA certificate path
+ */
+ public String getSSLCACertificatePath() {
+ return endpoint.getSSLCACertificatePath();
+ }
+
+ /**
+ * @param SSLCACertificatePath
+ */
+ public void setSSLCACertificatePath(String SSLCACertificatePath) {
+ endpoint.setSSLCACertificatePath(SSLCACertificatePath);
+ }
+
+ /**
+ * SSL CA certificate file.
+ *
+ * @return SSL CA certificate file
+ */
+ public String getSSLCACertificateFile() {
+ return endpoint.getSSLCACertificateFile();
+ }
+
+ /**
+ * @param SSLCACertificateFile
+ */
+ public void setSSLCACertificateFile(String SSLCACertificateFile) {
+ endpoint.setSSLCACertificateFile(SSLCACertificateFile);
+ }
+
+ /**
+ * SSL CA revocation path.
+ *
+ * @return SSL CA revocation path
+ */
+ public String getSSLCARevocationPath() {
+ return endpoint.getSSLCARevocationPath();
+ }
+
+ /**
+ * @param SSLCARevocationPath
+ */
+ public void setSSLCARevocationPath(String SSLCARevocationPath) {
+ endpoint.setSSLCARevocationPath(SSLCARevocationPath);
+ }
+
+ /**
+ * SSL CA revocation file.
+ *
+ * @return the SSL CA revocation file
+ */
+ public String getSSLCARevocationFile() {
+ return endpoint.getSSLCARevocationFile();
+ }
+
+ /**
+ * @param SSLCARevocationFile
+ */
+ public void setSSLCARevocationFile(String SSLCARevocationFile) {
+ endpoint.setSSLCARevocationFile(SSLCARevocationFile);
+ }
+
+ /**
+ * SSL verify client.
+ *
+ * @return SSLVerifyClient
+ */
+ public String getSSLVerifyClient() {
+ return endpoint.getSSLVerifyClient();
+ }
+
+ /**
+ * @param SSLVerifyClient
+ */
+ public void setSSLVerifyClient(String SSLVerifyClient) {
+ endpoint.setSSLVerifyClient(SSLVerifyClient);
+ }
+
+ /**
+ * SSL verify depth.
+ *
+ * @return the SSL verify depth
+ */
+ public int getSSLVerifyDepth() {
+ return endpoint.getSSLVerifyDepth();
+ }
+
+ /**
+ * @param SSLVerifyDepth
+ * the SSL verify depth
+ */
+ public void setSSLVerifyDepth(int SSLVerifyDepth) {
+ endpoint.setSSLVerifyDepth(SSLVerifyDepth);
+ }
+
+ // -------------------- Connection handler --------------------
+
+ /**
+ * {@code Http11ConnectionHandler}
+ *
+ * Created on Jan 13, 2012 at 10:45:44 AM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+ static class Http11ConnectionHandler implements NioEndpoint.Handler {
+
+ protected Http11NioProtocol proto;
+ protected AtomicLong registerCount = new AtomicLong(0);
+ protected RequestGroupInfo global = new RequestGroupInfo();
+
+ protected ConcurrentHashMap<Long, Http11NioProcessor> connections = new
ConcurrentHashMap<Long, Http11NioProcessor>();
+ protected ConcurrentLinkedQueue<Http11NioProcessor> recycledProcessors = new
ConcurrentLinkedQueue<Http11NioProcessor>() {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ protected AtomicInteger size = new AtomicInteger(0);
+
+ @Override
+ public boolean offer(Http11NioProcessor processor) {
+ boolean offer = (proto.processorCache == -1) ? true
+ : (size.get() < proto.processorCache);
+ // avoid over growing our cache or add after we have stopped
+ boolean result = false;
+ if (offer) {
+ result = super.offer(processor);
+ if (result) {
+ size.incrementAndGet();
+ }
+ }
+ if (!result)
+ unregister(processor);
+ return result;
+ }
+
+ @Override
+ public Http11NioProcessor poll() {
+ Http11NioProcessor result = super.poll();
+ if (result != null) {
+ size.decrementAndGet();
+ }
+ return result;
+ }
+
+ @Override
+ public void clear() {
+ Http11NioProcessor next = poll();
+ while (next != null) {
+ unregister(next);
+ next = poll();
+ }
+ super.clear();
+ size.set(0);
+ }
+ };
+
+ /**
+ * Create a new instance of {@code Http11ConnectionHandler}
+ *
+ * @param proto
+ */
+ Http11ConnectionHandler(Http11NioProtocol proto) {
+ this.proto = proto;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.NioEndpoint.Handler#event(java.nio.channels
+ * .AsynchronousSocketChannel, org.apache.tomcat.util.net.ChannelStatus)
+ */
+ @Override
+ public SocketState event(NioChannel channel, SocketStatus status) {
+
+ Http11NioProcessor processor = connections.get(channel.getId());
+ SocketState state = SocketState.CLOSED;
+
+ if (processor != null) {
+ processor.startProcessing();
+ // Call the appropriate event
+ try {
+ state = processor.event(status);
+ } catch (java.net.SocketException e) {
+ // SocketExceptions are normal
+ Http11NioProtocol.log.debug(
+ sm.getString("http11protocol.proto.socketexception.debug"), e);
+ } catch (java.io.IOException e) {
+ // IOExceptions are normal
+ Http11NioProtocol.log.debug(
+ sm.getString("http11protocol.proto.ioexception.debug"), e);
+ }
+ // Future developers: if you discover any other
+ // rare-but-nonfatal exceptions, catch them here, and log as
+ // above.
+ catch (Throwable e) {
+ // any other exception or error is odd. Here we log it
+ // with "ERROR" level, so it will show up even on
+ // less-than-verbose logs.
+ Http11NioProtocol.log.error(sm.getString("http11protocol.proto.error"),
e);
+ } finally {
+ if (state != SocketState.LONG) {
+ connections.remove(channel.getId());
+ recycledProcessors.offer(processor);
+ if (proto.endpoint.isRunning() && state == SocketState.OPEN) {
+ final NioChannel ch = channel;
+ proto.endpoint.removeEventChannel(ch);
+ try {
+ ch.awaitRead(proto.getKeepAliveTimeout(), TimeUnit.MILLISECONDS,
+ proto.endpoint,
+ new CompletionHandler<Integer, NioEndpoint>() {
+
+ @Override
+ public void completed(Integer nBytes,
+ NioEndpoint endpoint) {
+ if (nBytes < 0) {
+ failed(new ClosedChannelException(), endpoint);
+ } else {
+ endpoint.processChannel(ch, null);
+ }
+ }
+
+ @Override
+ public void failed(Throwable exc, NioEndpoint endpoint) {
+ endpoint.closeChannel(ch);
+ }
+ });
+ } catch (Exception exp) {
+ // NOPE
+ }
+ }
+ } else {
+ if (proto.endpoint.isRunning()) {
+ proto.endpoint.addEventChannel(channel, processor.getTimeout(),
+ processor.getReadNotifications(),
+ processor.getWriteNotification(),
+ processor.getResumeNotification(), false);
+ }
+ }
+ processor.endProcessing();
+ }
+ }
+
+ return state;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.NioEndpoint.Handler#process(java.nio.channels
+ * .AsynchronousSocketChannel)
+ */
+ @Override
+ public SocketState process(NioChannel channel) {
+ Http11NioProcessor processor = recycledProcessors.poll();
+ try {
+ if (processor == null) {
+ processor = createProcessor();
+ }
+
+ if (proto.secure && (proto.sslImplementation != null)) {
+ processor.setSSLSupport(proto.sslImplementation.getSSLSupport(channel));
+ } else {
+ processor.setSSLSupport(null);
+ }
+
+ SocketState state = processor.process(channel);
+
+ if (state == SocketState.LONG) {
+ // Associate the connection with the processor. The next
+ // request processed by this thread will use either a new or
+ // a recycled processor.
+ connections.put(channel.getId(), processor);
+
+ if ( /* processor.isAvailable() && */processor.getReadNotifications()) {
+ // Call a read event right away
+ processor.inputBuffer.readAsync();
+ } else {
+ proto.endpoint.addEventChannel(channel, processor.getTimeout(),
+ processor.getReadNotifications(), false,
+ processor.getResumeNotification(), false);
+ }
+ } else {
+ recycledProcessors.offer(processor);
+ }
+ return state;
+
+ } catch (IOException e) {
+ if (e instanceof java.net.SocketException) {
+ // SocketExceptions are normal
+ Http11NioProtocol.log.debug(
+ sm.getString("http11protocol.proto.socketexception.debug"), e);
+ } else {
+ // IOExceptions are normal
+ Http11NioProtocol.log.debug(
+ sm.getString("http11protocol.proto.ioexception.debug"), e);
+ }
+ }
+ // Future developers: if you discover any other
+ // rare-but-non-fatal exceptions, catch them here, and log as
+ // above.
+ catch (Throwable e) {
+ // any other exception or error is odd. Here we log it
+ // with "ERROR" level, so it will show up even on
+ // less-than-verbose logs.
+ Http11NioProtocol.log.error(sm.getString("http11protocol.proto.error"),
e);
+ }
+ recycledProcessors.offer(processor);
+ return SocketState.CLOSED;
+ }
+
+ /**
+ * @return
+ */
+ protected Http11NioProcessor createProcessor() {
+ Http11NioProcessor processor = new Http11NioProcessor(proto.maxHttpHeaderSize,
+ proto.endpoint);
+ processor.setAdapter(proto.adapter);
+ processor.setMaxKeepAliveRequests(proto.maxKeepAliveRequests);
+ processor.setTimeout(proto.timeout);
+ processor.setDisableUploadTimeout(proto.disableUploadTimeout);
+ processor.setCompressionMinSize(proto.compressionMinSize);
+ processor.setCompression(proto.compression);
+ processor.setNoCompressionUserAgents(proto.noCompressionUserAgents);
+ processor.setCompressableMimeTypes(proto.compressableMimeTypes);
+ processor.setRestrictedUserAgents(proto.restrictedUserAgents);
+ processor.setMaxSavePostSize(proto.maxSavePostSize);
+ processor.setServer(proto.server);
+ register(processor);
+ return processor;
+ }
+
+ /**
+ * @param processor
+ */
+ protected void register(Http11NioProcessor processor) {
+ RequestInfo rp = processor.getRequest().getRequestProcessor();
+ rp.setGlobalProcessor(global);
+ if (org.apache.tomcat.util.Constants.ENABLE_MODELER && proto.getDomain() !=
null) {
+ synchronized (this) {
+ try {
+ long count = registerCount.incrementAndGet();
+ ObjectName rpName = new ObjectName(proto.getDomain()
+ + ":type=RequestProcessor,worker=" + proto.getName()
+ + ",name=HttpRequest" + count);
+ if (log.isDebugEnabled()) {
+ log.debug("Register " + rpName);
+ }
+ Registry.getRegistry(null, null).registerComponent(rp, rpName, null);
+ rp.setRpName(rpName);
+ } catch (Exception e) {
+ e.printStackTrace();
+ log.warn("Error registering request");
+ }
+ }
+ }
+ }
+
+ /**
+ * @param processor
+ */
+ protected void unregister(Http11NioProcessor processor) {
+ RequestInfo rp = processor.getRequest().getRequestProcessor();
+ rp.setGlobalProcessor(null);
+ if (org.apache.tomcat.util.Constants.ENABLE_MODELER && proto.getDomain() !=
null) {
+ synchronized (this) {
+ try {
+ ObjectName rpName = rp.getRpName();
+ if (log.isDebugEnabled()) {
+ log.debug("Unregister " + rpName);
+ }
+ Registry.getRegistry(null, null).unregisterComponent(rpName);
+ rp.setRpName(null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ log.warn("Error unregistering request", e);
+ }
+ }
+ }
+ }
+ }
+
+}
Added: trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java
===================================================================
--- trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java
(rev 0)
+++ trunk/java/org/apache/coyote/http11/InternalNioInputBuffer.java 2012-06-13 08:50:17
UTC (rev 2041)
@@ -0,0 +1,521 @@
+/**
+ * JBoss, Home of Professional Open Source. Copyright 2012, Red Hat, Inc., and
+ * individual contributors as indicated by the @author tags. See the
+ * copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
+ * site:
http://www.fsf.org.
+ */
+package org.apache.coyote.http11;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.net.SocketTimeoutException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.CompletionHandler;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.coyote.InputBuffer;
+import org.apache.coyote.Request;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.net.NioChannel;
+import org.apache.tomcat.util.net.NioEndpoint;
+import org.apache.tomcat.util.net.SocketStatus;
+
+/**
+ * {@code InternalNioInputBuffer}
+ * <p>
+ * Implementation of InputBuffer which provides HTTP request header parsing as
+ * well as transfer decoding.
+ * </p>
+ *
+ * Created on Dec 14, 2011 at 9:06:18 AM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public class InternalNioInputBuffer extends AbstractInternalInputBuffer {
+
+ /**
+ * Underlying channel.
+ */
+ protected NioChannel channel;
+
+ /**
+ * Non blocking mode.
+ */
+ protected boolean nonBlocking = false;
+
+ /**
+ * Non blocking mode.
+ */
+ protected boolean available = false;
+
+ /**
+ * NIO end point.
+ */
+ protected NioEndpoint endpoint = null;
+
+ /**
+ * The completion handler used for asynchronous read operations
+ */
+ private CompletionHandler<Integer, NioChannel> completionHandler;
+
+ /**
+ * Create a new instance of {@code InternalNioInputBuffer}
+ *
+ * @param request
+ * @param headerBufferSize
+ * @param endpoint
+ */
+ public InternalNioInputBuffer(Request request, int headerBufferSize, NioEndpoint
endpoint) {
+ super(request, headerBufferSize);
+ this.endpoint = endpoint;
+ this.init();
+ }
+
+ /**
+ *
+ */
+ protected void init() {
+ this.inputBuffer = new InputBufferImpl();
+ this.readTimeout = (endpoint.getSoTimeout() > 0 ? endpoint.getSoTimeout()
+ : Integer.MAX_VALUE);
+
+ // Initialize the completion handler
+ this.completionHandler = new CompletionHandler<Integer, NioChannel>() {
+
+ @Override
+ public void completed(Integer nBytes, NioChannel attachment) {
+ if (nBytes < 0) {
+ failed(new ClosedChannelException(), attachment);
+ return;
+ }
+
+ if (nBytes > 0) {
+ bbuf.flip();
+ bbuf.get(buf, pos, nBytes);
+ lastValid = pos + nBytes;
+ endpoint.processChannel(attachment, SocketStatus.OPEN_READ);
+ }
+ }
+
+ @Override
+ public void failed(Throwable exc, NioChannel attachment) {
+ endpoint.removeEventChannel(attachment);
+ endpoint.processChannel(attachment, SocketStatus.ERROR);
+ }
+ };
+ }
+
+ /**
+ * Set the underlying channel.
+ *
+ * @param channel
+ */
+ public void setChannel(NioChannel channel) {
+ this.channel = channel;
+ }
+
+ /**
+ * Get the underlying socket input stream.
+ *
+ * @return the channel
+ */
+ public NioChannel getChannel() {
+ return channel;
+ }
+
+ /**
+ * Set the non blocking flag.
+ *
+ * @param nonBlocking
+ */
+ public void setNonBlocking(boolean nonBlocking) {
+ this.nonBlocking = nonBlocking;
+ }
+
+ /**
+ * Get the non blocking flag value.
+ *
+ * @return true if the buffer is non-blocking else false
+ */
+ public boolean getNonBlocking() {
+ return nonBlocking;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.http11.AbstractInternalInputBuffer#recycle()
+ */
+ public void recycle() {
+ super.recycle();
+ bbuf.clear();
+ channel = null;
+ available = false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.http11.AbstractInternalInputBuffer#nextRequest()
+ */
+ public boolean nextRequest() {
+ boolean result = super.nextRequest();
+ nonBlocking = false;
+ available = false;
+
+ return result;
+ }
+
+ /**
+ * Read the request line. This function is meant to be used during the HTTP
+ * request header parsing. Do NOT attempt to read the request body using it.
+ *
+ * @param useAvailableData
+ *
+ * @throws IOException
+ * If an exception occurs during the underlying socket read
+ * operations, or if the given buffer is not big enough to
+ * accommodate the whole line.
+ * @return true if data is properly fed; false if no data is available
+ * immediately and thread should be freed
+ */
+ public boolean parseRequestLine(boolean useAvailableData) throws IOException {
+
+ int start = 0;
+ // Skipping blank lines
+
+ byte chr = 0;
+ do {
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+ if (useAvailableData) {
+ return false;
+ }
+ if (!fill()) {
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+ }
+
+ chr = buf[pos++];
+ } while ((chr == Constants.CR) || (chr == Constants.LF));
+
+ pos--;
+
+ // Mark the current buffer position
+ start = pos;
+
+ if (pos >= lastValid) {
+ if (useAvailableData) {
+ return false;
+ }
+ if (!fill()) {
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+ }
+
+ // Reading the method name
+ // Method name is always US-ASCII
+
+ boolean space = false;
+
+ while (!space) {
+
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+ if (!fill()) {
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+ }
+
+ // Spec says single SP but it also says be tolerant of HT
+ if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+ space = true;
+ request.method().setBytes(buf, start, pos - start);
+ }
+
+ pos++;
+ }
+
+ // Spec says single SP but also says be tolerant of multiple and/or HT
+ while (space) {
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+ if (!fill()) {
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+ }
+ if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+ pos++;
+ } else {
+ space = false;
+ }
+ }
+
+ // Mark the current buffer position
+ start = pos;
+ int end = 0;
+ int questionPos = -1;
+
+ // Reading the URI
+ boolean eol = false;
+
+ while (!space) {
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+ if (!fill())
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+
+ // Spec says single SP but it also says be tolerant of HT
+ if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+ space = true;
+ end = pos;
+ } else if ((buf[pos] == Constants.CR) || (buf[pos] == Constants.LF)) {
+ // HTTP/0.9 style request
+ eol = true;
+ space = true;
+ end = pos;
+ } else if ((buf[pos] == Constants.QUESTION) && (questionPos == -1)) {
+ questionPos = pos;
+ }
+
+ pos++;
+ }
+
+ request.unparsedURI().setBytes(buf, start, end - start);
+ if (questionPos >= 0) {
+ request.queryString().setBytes(buf, questionPos + 1, end - questionPos - 1);
+ request.requestURI().setBytes(buf, start, questionPos - start);
+ } else {
+ request.requestURI().setBytes(buf, start, end - start);
+ }
+
+ // Spec says single SP but also says be tolerant of multiple and/or HT
+ while (space) {
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+ if (!fill())
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+ if (buf[pos] == Constants.SP || buf[pos] == Constants.HT) {
+ pos++;
+ } else {
+ space = false;
+ }
+ }
+
+ // Mark the current buffer position
+ start = pos;
+ end = 0;
+
+ //
+ // Reading the protocol
+ // Protocol is always US-ASCII
+ //
+ while (!eol) {
+ // Read new bytes if needed
+ if (pos >= lastValid) {
+ if (!fill()) {
+ throw new EOFException(sm.getString("iib.eof.error"));
+ }
+ }
+
+ if (buf[pos] == Constants.CR) {
+ end = pos;
+ } else if (buf[pos] == Constants.LF) {
+ if (end == 0)
+ end = pos;
+ eol = true;
+ }
+
+ pos++;
+ }
+
+ if ((end - start) > 0) {
+ request.protocol().setBytes(buf, start, end - start);
+ } else {
+ request.protocol().setString("");
+ }
+
+ return true;
+ }
+
+ /**
+ * Available bytes (note that due to encoding, this may not correspond )
+ */
+ public void useAvailable() {
+ available = true;
+ }
+
+ /**
+ * Available bytes in the buffer ? (these may not translate to application
+ * readable data)
+ *
+ * @return the number of available bytes in the buffer
+ */
+ public boolean available() {
+ return (lastValid - pos > 0);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.coyote.InputBuffer#doRead(org.apache.tomcat.util.buf.ByteChunk
+ * , org.apache.coyote.Request)
+ */
+ public int doRead(ByteChunk chunk, Request req) throws IOException {
+ return (lastActiveFilter == -1) ? inputBuffer.doRead(chunk, req)
+ : activeFilters[lastActiveFilter].doRead(chunk, req);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.http11.AbstractInternalInputBuffer#fill()
+ */
+ protected boolean fill() throws IOException {
+ int nRead = 0;
+ // Prepare the internal input buffer for reading
+ this.prepare();
+ // Reading from client
+ if (nonBlocking) {
+ nonBlockingRead(bbuf, readTimeout, unit);
+ } else {
+ nRead = blockingRead(bbuf, readTimeout, unit);
+ if (nRead > 0) {
+ bbuf.flip();
+ bbuf.get(buf, pos, nRead);
+ lastValid = pos + nRead;
+ } else if (nRead == NioChannel.OP_STATUS_CLOSED) {
+ throw new IOException(sm.getString("iib.failedread"));
+ } else if (nRead == NioChannel.OP_STATUS_READ_TIMEOUT) {
+ throw new SocketTimeoutException(sm.getString("iib.failedread"));
+ }
+ }
+
+ return (nRead >= 0);
+ }
+
+ /**
+ * Prepare the input buffer for reading
+ */
+ private void prepare() {
+ bbuf.clear();
+
+ if (parsingHeader) {
+ if (lastValid == buf.length) {
+ throw new
IllegalArgumentException(sm.getString("iib.requestheadertoolarge.error"));
+ }
+ } else {
+ if (buf.length - end < 4500) {
+ // In this case, the request header was really large, so we
+ // allocate a
+ // brand new one; the old one will get GCed when subsequent
+ // requests
+ // clear all references
+ buf = new byte[buf.length];
+ end = 0;
+ }
+ pos = end;
+ lastValid = pos;
+ }
+ }
+
+ /**
+ * Close the channel
+ */
+ private void close(NioChannel channel) {
+ endpoint.closeChannel(channel);
+ }
+
+ /**
+ * Read a sequence of bytes in non-blocking mode from he current channel
+ *
+ * @param bb
+ * the byte buffer which will contain the bytes read from the
+ * current channel
+ */
+ private void nonBlockingRead(final ByteBuffer bb, long timeout, TimeUnit unit) {
+ final NioChannel ch = this.channel;
+ try {
+ ch.read(bb, ch, this.completionHandler);
+ } catch (Throwable t) {
+ if (log.isDebugEnabled()) {
+ log.debug("An error occurs when trying a non-blocking read ", t);
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ protected void readAsync() throws IOException {
+ this.prepare();
+ this.nonBlockingRead(bbuf, readTimeout, unit);
+ }
+
+ /**
+ * Read a sequence of bytes in blocking mode from he current channel
+ *
+ * @param bb
+ * @return the number of bytes read or -1 if the end of the stream was
+ * reached
+ */
+ private int blockingRead(ByteBuffer bb, long timeout, TimeUnit unit) {
+ int nr = 0;
+ try {
+ long readTimeout = timeout > 0 ? timeout : Integer.MAX_VALUE;
+ nr = this.channel.readBytes(bb, readTimeout, unit);
+ if (nr < 0) {
+ close(channel);
+ }
+ } catch (Exception e) {
+ if (log.isDebugEnabled()) {
+ log.debug("An error occurs when trying a blocking read " +
e.getMessage());
+ }
+ }
+ return nr;
+ }
+
+ /**
+ * This class is an input buffer which will read its data from an input
+ * stream.
+ */
+ protected class InputBufferImpl implements InputBuffer {
+
+ /**
+ * Read bytes into the specified chunk.
+ */
+ public int doRead(ByteChunk chunk, Request req) throws IOException {
+
+ if (pos >= lastValid) {
+ if (!fill()) {
+ return -1;
+ }
+ }
+
+ int length = lastValid - pos;
+ chunk.setBytes(buf, pos, length);
+ pos = lastValid;
+
+ return (length);
+ }
+ }
+}
Added: trunk/java/org/apache/coyote/http11/InternalNioOutputBuffer.java
===================================================================
--- trunk/java/org/apache/coyote/http11/InternalNioOutputBuffer.java
(rev 0)
+++ trunk/java/org/apache/coyote/http11/InternalNioOutputBuffer.java 2012-06-13 08:50:17
UTC (rev 2041)
@@ -0,0 +1,372 @@
+/**
+ * JBoss, Home of Professional Open Source. Copyright 2012, Red Hat, Inc., and
+ * individual contributors as indicated by the @author tags. See the
+ * copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
+ * site:
http://www.fsf.org.
+ */
+package org.apache.coyote.http11;
+
+import java.io.IOException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.CompletionHandler;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.Response;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.net.NioChannel;
+import org.apache.tomcat.util.net.NioEndpoint;
+
+/**
+ * {@code InternalNioOutputBuffer}
+ *
+ * Created on Dec 16, 2011 at 9:15:05 AM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public class InternalNioOutputBuffer extends AbstractInternalOutputBuffer {
+
+ /**
+ * Underlying channel.
+ */
+ protected NioChannel channel;
+
+ /**
+ * NIO endpoint.
+ */
+ protected NioEndpoint endpoint;
+
+ /**
+ * The completion handler used for asynchronous write operations
+ */
+ private CompletionHandler<Integer, NioChannel> completionHandler;
+
+ /**
+ * Create a new instance of {@code InternalNioOutputBuffer}
+ *
+ * @param response
+ * @param headerBufferSize
+ * @param endpoint
+ */
+ public InternalNioOutputBuffer(Response response, int headerBufferSize, NioEndpoint
endpoint) {
+ super(response, headerBufferSize);
+ this.endpoint = endpoint;
+ // Initialize the input buffer
+ this.init();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.http11.AbstractInternalOutputBuffer#init()
+ */
+ protected void init() {
+
+ this.writeTimeout = (endpoint.getSoTimeout() > 0 ? endpoint.getSoTimeout()
+ : Integer.MAX_VALUE);
+
+ this.completionHandler = new CompletionHandler<Integer, NioChannel>() {
+
+ @Override
+ public void completed(Integer nBytes, NioChannel attachment) {
+ if (nBytes < 0) {
+ failed(new ClosedChannelException(), attachment);
+ return;
+ }
+
+ if (bbuf.hasRemaining()) {
+ attachment.write(bbuf, writeTimeout, TimeUnit.MILLISECONDS, attachment, this);
+ } else {
+ // Clear the buffer when all bytes are written
+ clearBuffer();
+ }
+ }
+
+ @Override
+ public void failed(Throwable exc, NioChannel attachment) {
+ endpoint.removeEventChannel(attachment);
+ // endpoint.processChannel(attachment, SocketStatus.ERROR);
+ }
+ };
+ }
+
+ /**
+ * Set the underlying socket.
+ *
+ * @param channel
+ */
+ public void setChannel(NioChannel channel) {
+ this.channel = channel;
+ }
+
+ /**
+ * Get the underlying socket input stream.
+ *
+ * @return the channel
+ */
+ public NioChannel getChannel() {
+ return channel;
+ }
+
+ /**
+ * Recycle the output buffer. This should be called when closing the
+ * connection.
+ */
+ public void recycle() {
+ super.recycle();
+ channel = null;
+ }
+
+ /**
+ * Close the channel
+ *
+ * @param channel
+ */
+ private void close(NioChannel channel) {
+ endpoint.closeChannel(channel);
+ }
+
+ /**
+ * Perform a blocking write operation
+ *
+ * @param buffer
+ * the buffer containing the data to write
+ * @param timeout
+ * a timeout for the operation
+ * @param unit
+ * The time unit
+ *
+ * @return the number of bytes written, -1 in case of errors
+ */
+ private int blockingWrite(long timeout, TimeUnit unit) {
+ int nw = 0;
+ try {
+ nw = this.channel.writeBytes(this.bbuf, timeout, unit);
+ if (nw < 0) {
+ close(channel);
+ }
+ } catch (Throwable t) {
+ if (log.isDebugEnabled()) {
+ log.debug(t.getMessage(), t);
+ }
+ }
+
+ return nw;
+ }
+
+ /**
+ * Perform a non-blocking write operation
+ *
+ * @param buffer
+ * the buffer containing the data to write
+ * @param timeout
+ * a timeout for the operation
+ * @param unit
+ * The time unit
+ */
+ private void nonBlockingWrite(final long timeout, final TimeUnit unit) {
+ try {
+ // Perform the write operation
+ this.channel.write(this.bbuf, timeout, unit, this.channel, this.completionHandler);
+ } catch (Throwable t) {
+ if (log.isDebugEnabled()) {
+ log.debug(t.getMessage(), t);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.coyote.http11.AbstractInternalOutputBuffer#write(java.nio.
+ * ByteBuffer, long, java.util.concurrent.TimeUnit)
+ */
+ @Override
+ protected int write(final long timeout, final TimeUnit unit) {
+ if (nonBlocking) {
+ nonBlockingWrite(timeout, unit);
+ return 0;
+ }
+
+ return blockingWrite(timeout, unit);
+ }
+
+ /**
+ * Send an acknowledgment.
+ *
+ * @throws Exception
+ */
+ public void sendAck() throws Exception {
+
+ if (!committed) {
+ this.bbuf.clear();
+ this.bbuf.put(Constants.ACK_BYTES).flip();
+ if (this.write(writeTimeout, TimeUnit.MILLISECONDS) < 0) {
+ throw new IOException(sm.getString("oob.failedwrite"));
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.coyote.http11.AbstractInternalOutputBuffer#doWrite(org.apache
+ * .tomcat.util.buf.ByteChunk, org.apache.coyote.Response)
+ */
+ public int doWrite(ByteChunk chunk, Response res) throws IOException {
+
+ if (!committed) {
+ // Send the connector a request for commit. The connector should
+ // then validate the headers, send them (using sendHeaders) and
+ // set the filters accordingly.
+ response.action(ActionCode.ACTION_COMMIT, null);
+ }
+
+ // If non blocking (event) and there are leftover bytes,
+ // and lastWrite was 0 -> error
+ if (leftover.getLength() > 0 && !(Http11NioProcessor.containerThread.get()
== Boolean.TRUE)) {
+ throw new IOException(sm.getString("oob.backlog"));
+ }
+
+ if (lastActiveFilter == -1) {
+ return outputBuffer.doWrite(chunk, res);
+ } else {
+ return activeFilters[lastActiveFilter].doWrite(chunk, res);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.coyote.http11.AbstractInternalOutputBuffer#flushBuffer()
+ */
+ protected void flushBuffer() throws IOException {
+ int res = 0;
+
+ // If there are still leftover bytes here, this means the user did a
+ // direct flush:
+ // - If the call is asynchronous, throw an exception
+ // - If the call is synchronous, make regular blocking writes to flush
+ // the data
+ if (leftover.getLength() > 0) {
+ if (Http11NioProcessor.containerThread.get() == Boolean.TRUE) {
+ // Send leftover bytes
+ while (leftover.getLength() > 0) {
+ // Calculate the maximum number of bytes that can fit in the
+ // buffer
+ int n = Math.min(bbuf.capacity() - bbuf.position(), leftover.getLength());
+ int off = leftover.getOffset();
+ // Put bytes into the buffer
+ bbuf.put(leftover.getBuffer(), off, n).flip();
+ // Update the offset of the leftover ByteChunck
+ leftover.setOffset(off + n);
+ while (bbuf.hasRemaining()) {
+ res = blockingWrite(writeTimeout, TimeUnit.MILLISECONDS);
+ if (res < 0) {
+ break;
+ }
+ }
+ bbuf.clear();
+ if (res < 0) {
+ throw new IOException(sm.getString("oob.failedwrite"));
+ }
+ }
+ leftover.recycle();
+ } else {
+ throw new IOException(sm.getString("oob.backlog"));
+ }
+ }
+
+ if (bbuf.position() > 0) {
+ bbuf.flip();
+
+ if (nonBlocking) {
+ // Perform non blocking writes until all data is written, or the
+ // result of the write is 0
+ nonBlockingWrite(writeTimeout, TimeUnit.MILLISECONDS);
+ } else {
+ while (bbuf.hasRemaining()) {
+ res = blockingWrite(writeTimeout, TimeUnit.MILLISECONDS);
+ if (res <= 0) {
+ break;
+ }
+ }
+ response.setLastWrite(res);
+ // bbuf.clear();
+ clearBuffer();
+ }
+
+ if (res < 0) {
+ throw new IOException(sm.getString("oob.failedwrite"));
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.coyote.http11.AbstractInternalOutputBuffer#flushLeftover()
+ */
+ @Override
+ public boolean flushLeftover() throws IOException {
+ // Calculate the number of bytes that fit in the buffer
+ int n = Math.min(leftover.getLength(), bbuf.capacity() - bbuf.position());
+ // put bytes in the buffer
+ bbuf.put(leftover.getBuffer(), leftover.getOffset(), n).flip();
+ // Update the offset
+ leftover.setOffset(leftover.getOffset() + n);
+ final NioChannel ch = channel;
+
+ ch.write(bbuf, writeTimeout, TimeUnit.MILLISECONDS, null,
+ new CompletionHandler<Integer, Void>() {
+
+ @Override
+ public void completed(Integer result, Void attachment) {
+ if (result < 0) {
+ failed(new IOException(sm.getString("oob.failedwrite")), attachment);
+ return;
+ }
+ response.setLastWrite(result);
+ if (!bbuf.hasRemaining()) {
+ bbuf.clear();
+ if (leftover.getLength() > 0) {
+ int n = Math.min(leftover.getLength(), bbuf.remaining());
+ bbuf.put(leftover.getBuffer(), leftover.getOffset(), n).flip();
+ leftover.setOffset(leftover.getOffset() + n);
+ } else {
+ leftover.recycle();
+ return;
+ }
+ }
+ // Write the remaining bytes
+ ch.write(bbuf, writeTimeout, TimeUnit.MILLISECONDS, null, this);
+ }
+
+ @Override
+ public void failed(Throwable exc, Void attachment) {
+ close(ch);
+ }
+ });
+
+ return true;
+ }
+
+}
Added: trunk/java/org/apache/tomcat/util/net/AbstractEndpoint.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/AbstractEndpoint.java
(rev 0)
+++ trunk/java/org/apache/tomcat/util/net/AbstractEndpoint.java 2012-06-13 08:50:17 UTC
(rev 2041)
@@ -0,0 +1,1087 @@
+/**
+ * JBoss, Home of Professional Open Source. Copyright 2011, Red Hat, Inc., and
+ * individual contributors as indicated by the @author tags. See the
+ * copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
+ * site:
http://www.fsf.org.
+ */
+package
org.apache.tomcat.util.net;
+
+import java.net.InetAddress;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * {@code AbstractEndpoint}
+ *
+ * Created on Dec 14, 2011 at 2:58:58 PM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public abstract class AbstractEndpoint {
+
+ protected static StringManager sm =
StringManager.getManager("org.apache.tomcat.util.net.res");
+
+ /**
+ * The default socket timeout = 60 sec
+ */
+ protected static final int DEFAULT_SO_TIMEOUT = 60 * 1000;
+
+ /**
+ * Running state of the endpoint.
+ */
+ protected volatile boolean running = false;
+
+ /**
+ * Will be set to true whenever the endpoint is paused.
+ */
+ protected volatile boolean paused = false;
+
+ /**
+ * Track the initialization state of the endpoint.
+ */
+ protected boolean initialized = false;
+
+ /**
+ * The number of open connections
+ */
+ protected AtomicInteger counter = new AtomicInteger();
+
+ /**
+ *
+ */
+ protected boolean reuseAddress = false;
+
+ /**
+ * Current worker threads busy count.
+ */
+ protected int curThreadsBusy = 0;
+
+ /**
+ * Current worker threads count.
+ */
+ protected int curThreads = 0;
+
+ /**
+ * Sequence number used to generate thread names.
+ */
+ protected int sequence = 0;
+
+ /**
+ * Acceptor thread count.
+ */
+ protected int acceptorThreadCount = 0;
+
+ /**
+ * External Executor based thread pool.
+ */
+ protected Executor executor = null;
+ /**
+ * Maximum amount of worker threads.
+ */
+ protected int maxThreads = (org.apache.tomcat.util.Constants.LOW_MEMORY) ? 32 : 32 *
Runtime
+ .getRuntime().availableProcessors();
+
+ /**
+ * The maximum number of connections
+ */
+ protected int maxConnections = 1024;
+
+ /**
+ * Priority of the acceptor and poller threads.
+ */
+ protected int threadPriority = Thread.NORM_PRIORITY;
+
+ /**
+ * Size of the sendfile (= concurrent files which can be served).
+ */
+ protected int sendfileSize = -1;
+
+ /**
+ * Server socket port.
+ */
+ protected int port;
+
+ /**
+ * Address for the server socket.
+ */
+ protected InetAddress address;
+
+ /**
+ * The default thread factory
+ */
+ protected ThreadFactory threadFactory;
+
+ /**
+ * Allows the server developer to specify the backlog that should be used
+ * for server sockets. By default, this value is 100.
+ */
+ // protected int backlog = 100;
+ protected int backlog = 511;
+
+ /**
+ * Socket TCP no delay.
+ */
+ protected boolean tcpNoDelay = false;
+
+ /**
+ * Socket linger.
+ */
+ protected int soLinger = 100;
+ /**
+ * Socket timeout.
+ */
+ protected int soTimeout = -1;
+ /**
+ * Defer accept.
+ */
+ protected boolean deferAccept = true;
+
+ /**
+ * Keep-Alive timeout.
+ */
+ protected int keepAliveTimeout = -1;
+
+ /**
+ * The default is true - the created threads will be in daemon mode. If set
+ * to false, the control thread will not be daemon - and will keep the
+ * process alive.
+ */
+ protected boolean daemon = true;
+ /**
+ * Name of the thread pool, which will be used for naming child threads.
+ */
+ protected String name = "TP";
+ /**
+ * Use sendfile for sending static files.
+ */
+ protected boolean useSendfile = true;
+
+ /**
+ * Reverse connection. In this proxied mode, the endpoint will not use a
+ * server socket, but will connect itself to the front end server.
+ */
+ protected boolean reverseConnection = false;
+ /**
+ * SSL engine.
+ */
+ protected boolean SSLEnabled = false;
+
+ /**
+ * SSL protocols.
+ */
+ protected String SSLProtocol = "all";
+
+ /**
+ * SSL password (if a cert is encrypted, and no password has been provided,
+ * a callback will ask for a password).
+ */
+ protected String SSLPassword = null;
+
+ /**
+ * SSL cipher suite.
+ */
+ protected String SSLCipherSuite = "ALL";
+ /**
+ * SSL certificate file.
+ */
+ protected String SSLCertificateFile = null;
+ /**
+ * SSL certificate chain file.
+ */
+ protected String SSLCertificateChainFile = null;
+
+ /**
+ * SSL CA certificate path.
+ */
+ protected String SSLCACertificatePath = null;
+
+ /**
+ * SSL CA certificate file.
+ */
+ protected String SSLCACertificateFile = null;
+ /**
+ * SSL CA revocation path.
+ */
+ protected String SSLCARevocationPath = null;
+ /**
+ * SSL CA revocation file.
+ */
+ protected String SSLCARevocationFile = null;
+ /**
+ * SSL verify client.
+ */
+ protected String SSLVerifyClient = "none";
+ /**
+ * SSL verify depth.
+ */
+ protected int SSLVerifyDepth = 10;
+ /**
+ * SSL allow insecure renegotiation for the the client that does not support
+ * the secure renegotiation.
+ */
+ protected boolean SSLInsecureRenegotiation = false;
+ /**
+ * SSL certificate key file.
+ */
+ protected String SSLCertificateKeyFile = null;
+
+ /**
+ * Initialize the endpoint
+ *
+ * @throws Exception
+ */
+ public abstract void init() throws Exception;
+
+ /**
+ * Start the endpoint, creating acceptor, poller and sendfile threads, etc.
+ *
+ * @throws Exception
+ */
+ public abstract void start() throws Exception;
+
+ /**
+ * Pause the endpoint, which will make it stop accepting new sockets.
+ */
+ public void pause() {
+ if (running && !paused) {
+ paused = true;
+ unlockAccept();
+ }
+ }
+
+ /**
+ * Resume the endpoint, which will make it start accepting new connections
+ * again.
+ */
+ public void resume() {
+ if (running) {
+ paused = false;
+ }
+ }
+
+ /**
+ * Stop the endpoint. This will cause all processing threads to stop.
+ */
+ public abstract void stop();
+
+ /**
+ * Deallocate the memory pools, and close server socket.
+ *
+ * @throws Exception
+ */
+ public abstract void destroy() throws Exception;
+
+ /**
+ * Unlock the server socket accept using a bogus connection.
+ */
+ protected void unlockAccept() {
+ java.net.Socket s = null;
+ try {
+ // Need to create a connection to unlock the accept();
+ if (address == null) {
+ s = new java.net.Socket("localhost", port);
+ } else {
+ s = new java.net.Socket(address, port);
+ // setting soLinger to a small value will help shutdown the
+ // connection quicker
+ s.setSoLinger(true, 0);
+ }
+ // If deferAccept is enabled, send at least one byte
+ if (deferAccept) {
+ s.getOutputStream().write(" ".getBytes());
+ s.getOutputStream().flush();
+ }
+ } catch (Exception e) {
+ // Ignore
+ } finally {
+ if (s != null) {
+ try {
+ s.close();
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a new thread for the specified target
+ *
+ * @param target
+ * @param name
+ * @param daemon
+ * @return an instance of a new thread
+ */
+ protected Thread newThread(Runnable target, String name, boolean daemon) {
+ Thread thread = this.threadFactory != null ? this.threadFactory.newThread(target)
+ : new Thread(target);
+
+ thread.setName(getName() +"-" + name);
+ thread.setPriority(threadPriority);
+ thread.setDaemon(daemon);
+
+ return thread;
+ }
+
+ // --------------------------------------------
+ /**
+ * Getter for running
+ *
+ * @return the running
+ */
+ public boolean isRunning() {
+ return this.running;
+ }
+
+ /**
+ * Setter for the running
+ *
+ * @param running
+ * the running to set
+ */
+ public void setRunning(boolean running) {
+ this.running = running;
+ }
+
+ /**
+ * Getter for paused
+ *
+ * @return the paused
+ */
+ public boolean isPaused() {
+ return this.paused;
+ }
+
+ /**
+ * Setter for the paused
+ *
+ * @param paused
+ * the paused to set
+ */
+ public void setPaused(boolean paused) {
+ this.paused = paused;
+ }
+
+ /**
+ * Getter for initialized
+ *
+ * @return the initialized
+ */
+ public boolean isInitialized() {
+ return this.initialized;
+ }
+
+ /**
+ * Setter for the initialized
+ *
+ * @param initialized
+ * the initialized to set
+ */
+ public void setInitialized(boolean initialized) {
+ this.initialized = initialized;
+ }
+
+ /**
+ * Getter for curThreadsBusy
+ *
+ * @return the curThreadsBusy
+ */
+ public int getCurThreadsBusy() {
+ return this.curThreadsBusy;
+ }
+
+ /**
+ * Setter for the curThreadsBusy
+ *
+ * @param curThreadsBusy
+ * the curThreadsBusy to set
+ */
+ public void setCurThreadsBusy(int curThreadsBusy) {
+ this.curThreadsBusy = curThreadsBusy;
+ }
+
+ /**
+ * Getter for curThreads
+ *
+ * @return the curThreads
+ */
+ public int getCurThreads() {
+ return this.curThreads;
+ }
+
+ /**
+ * Setter for the curThreads
+ *
+ * @param curThreads
+ * the curThreads to set
+ */
+ public void setCurThreads(int curThreads) {
+ this.curThreads = curThreads;
+ }
+
+ /**
+ * Getter for sequence
+ *
+ * @return the sequence
+ */
+ public int getSequence() {
+ return this.sequence++;
+ }
+
+ /**
+ * Setter for the sequence
+ *
+ * @param sequence
+ * the sequence to set
+ */
+ public void setSequence(int sequence) {
+ this.sequence = sequence;
+ }
+
+ /**
+ * Getter for executor
+ *
+ * @return the executor
+ */
+ public Executor getExecutor() {
+ return this.executor;
+ }
+
+ /**
+ * Setter for the executor
+ *
+ * @param executor
+ * the executor to set
+ */
+ public void setExecutor(Executor executor) {
+ this.executor = executor;
+ }
+
+ /**
+ * Getter for maxThreads
+ *
+ * @return the maxThreads
+ */
+ public int getMaxThreads() {
+ return this.maxThreads;
+ }
+
+ /**
+ * Setter for the maxThreads
+ *
+ * @param maxThreads
+ * the maxThreads to set
+ */
+ public void setMaxThreads(int maxThreads) {
+ this.maxThreads = maxThreads;
+ }
+
+ /**
+ * Getter for maxConnections
+ *
+ * @return the maxConnections
+ */
+ public int getMaxConnections() {
+ return this.maxConnections;
+ }
+
+ /**
+ * Setter for the maxConnections
+ *
+ * @param maxConnections
+ * the maxConnections to set
+ */
+ public void setMaxConnections(int maxConnections) {
+ this.maxConnections = maxConnections;
+ }
+
+ /**
+ * Getter for threadPriority
+ *
+ * @return the threadPriority
+ */
+ public int getThreadPriority() {
+ return this.threadPriority;
+ }
+
+ /**
+ * Setter for the threadPriority
+ *
+ * @param threadPriority
+ * the threadPriority to set
+ */
+ public void setThreadPriority(int threadPriority) {
+ this.threadPriority = threadPriority;
+ }
+
+ /**
+ * Getter for sendfileSize
+ *
+ * @return the sendfileSize
+ */
+ public int getSendfileSize() {
+ return this.sendfileSize;
+ }
+
+ /**
+ * Setter for the sendfileSize
+ *
+ * @param sendfileSize
+ * the sendfileSize to set
+ */
+ public void setSendfileSize(int sendfileSize) {
+ this.sendfileSize = sendfileSize;
+ }
+
+ /**
+ * Getter for port
+ *
+ * @return the port
+ */
+ public int getPort() {
+ return this.port;
+ }
+
+ /**
+ * Setter for the port
+ *
+ * @param port
+ * the port to set
+ */
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ /**
+ * Getter for address
+ *
+ * @return the address
+ */
+ public InetAddress getAddress() {
+ return this.address;
+ }
+
+ /**
+ * Setter for the address
+ *
+ * @param address
+ * the address to set
+ */
+ public void setAddress(InetAddress address) {
+ this.address = address;
+ }
+
+ /**
+ * Getter for backlog
+ *
+ * @return the backlog
+ */
+ public int getBacklog() {
+ return this.backlog;
+ }
+
+ /**
+ * Setter for the backlog
+ *
+ * @param backlog
+ * the backlog to set
+ */
+ public void setBacklog(int backlog) {
+ if (backlog > 0)
+ this.backlog = backlog;
+ }
+
+ /**
+ * Getter for tcpNoDelay
+ *
+ * @return the tcpNoDelay
+ */
+ public boolean getTcpNoDelay() {
+ return this.tcpNoDelay;
+ }
+
+ /**
+ * Setter for the tcpNoDelay
+ *
+ * @param tcpNoDelay
+ * the tcpNoDelay to set
+ */
+ public void setTcpNoDelay(boolean tcpNoDelay) {
+ this.tcpNoDelay = tcpNoDelay;
+ }
+
+ /**
+ * Getter for soLinger
+ *
+ * @return the soLinger
+ */
+ public int getSoLinger() {
+ return this.soLinger;
+ }
+
+ /**
+ * Setter for the soLinger
+ *
+ * @param soLinger
+ * the soLinger to set
+ */
+ public void setSoLinger(int soLinger) {
+ this.soLinger = soLinger;
+ }
+
+ /**
+ * Getter for soTimeout
+ *
+ * @return the soTimeout
+ */
+ public int getSoTimeout() {
+ return this.soTimeout;
+ }
+
+ /**
+ * Setter for the soTimeout
+ *
+ * @param soTimeout
+ * the soTimeout to set
+ */
+ public void setSoTimeout(int soTimeout) {
+ this.soTimeout = soTimeout;
+ }
+
+ /**
+ * Getter for deferAccept
+ *
+ * @return the deferAccept
+ */
+ public boolean getDeferAccept() {
+ return this.deferAccept;
+ }
+
+ /**
+ * Setter for the deferAccept
+ *
+ * @param deferAccept
+ * the deferAccept to set
+ */
+ public void setDeferAccept(boolean deferAccept) {
+ this.deferAccept = deferAccept;
+ }
+
+ /**
+ * Getter for keepAliveTimeout
+ *
+ * @return the keepAliveTimeout
+ */
+ public int getKeepAliveTimeout() {
+ return this.keepAliveTimeout;
+ }
+
+ /**
+ * Setter for the keepAliveTimeout
+ *
+ * @param keepAliveTimeout
+ * the keepAliveTimeout to set
+ */
+ public void setKeepAliveTimeout(int keepAliveTimeout) {
+ this.keepAliveTimeout = keepAliveTimeout;
+ }
+
+ /**
+ * Getter for daemon
+ *
+ * @return the daemon
+ */
+ public boolean getDaemon() {
+ return this.daemon;
+ }
+
+ /**
+ * Setter for the daemon
+ *
+ * @param daemon
+ * the daemon to set
+ */
+ public void setDaemon(boolean daemon) {
+ this.daemon = daemon;
+ }
+
+ /**
+ * Getter for name
+ *
+ * @return the name
+ */
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * Setter for the name
+ *
+ * @param name
+ * the name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Getter for useSendfile
+ *
+ * @return the useSendfile
+ */
+ public boolean getUseSendfile() {
+ return this.useSendfile;
+ }
+
+ /**
+ * Setter for the useSendfile
+ *
+ * @param useSendfile
+ * the useSendfile to set
+ */
+ public void setUseSendfile(boolean useSendfile) {
+ this.useSendfile = useSendfile;
+ }
+
+ /**
+ * Getter for reverseConnection
+ *
+ * @return the reverseConnection
+ */
+ public boolean isReverseConnection() {
+ return this.reverseConnection;
+ }
+
+ /**
+ * Setter for the reverseConnection
+ *
+ * @param reverseConnection
+ * the reverseConnection to set
+ */
+ public void setReverseConnection(boolean reverseConnection) {
+ this.reverseConnection = reverseConnection;
+ }
+
+ /**
+ * Getter for sSLEnabled
+ *
+ * @return the sSLEnabled
+ */
+ public boolean getSSLEnabled() {
+ return this.SSLEnabled;
+ }
+
+ /**
+ * Setter for the sSLEnabled
+ *
+ * @param sSLEnabled
+ * the sSLEnabled to set
+ */
+ public void setSSLEnabled(boolean sSLEnabled) {
+ this.SSLEnabled = sSLEnabled;
+ }
+
+ /**
+ * Getter for sSLProtocol
+ *
+ * @return the sSLProtocol
+ */
+ public String getSSLProtocol() {
+ return this.SSLProtocol;
+ }
+
+ /**
+ * Setter for the sSLProtocol
+ *
+ * @param sSLProtocol
+ * the sSLProtocol to set
+ */
+ public void setSSLProtocol(String sSLProtocol) {
+ this.SSLProtocol = sSLProtocol;
+ }
+
+ /**
+ * Getter for sSLPassword
+ *
+ * @return the sSLPassword
+ */
+ public String getSSLPassword() {
+ return this.SSLPassword;
+ }
+
+ /**
+ * Setter for the sSLPassword
+ *
+ * @param sSLPassword
+ * the sSLPassword to set
+ */
+ public void setSSLPassword(String sSLPassword) {
+ this.SSLPassword = sSLPassword;
+ }
+
+ /**
+ * Getter for sSLCipherSuite
+ *
+ * @return the sSLCipherSuite
+ */
+ public String getSSLCipherSuite() {
+ return this.SSLCipherSuite;
+ }
+
+ /**
+ * Setter for the sSLCipherSuite
+ *
+ * @param sSLCipherSuite
+ * the sSLCipherSuite to set
+ */
+ public void setSSLCipherSuite(String sSLCipherSuite) {
+ this.SSLCipherSuite = sSLCipherSuite;
+ }
+
+ /**
+ * Getter for sSLCertificateFile
+ *
+ * @return the sSLCertificateFile
+ */
+ public String getSSLCertificateFile() {
+ return this.SSLCertificateFile;
+ }
+
+ /**
+ * Setter for the sSLCertificateFile
+ *
+ * @param sSLCertificateFile
+ * the sSLCertificateFile to set
+ */
+ public void setSSLCertificateFile(String sSLCertificateFile) {
+ this.SSLCertificateFile = sSLCertificateFile;
+ }
+
+ /**
+ * Getter for sSLCertificateChainFile
+ *
+ * @return the sSLCertificateChainFile
+ */
+ public String getSSLCertificateChainFile() {
+ return this.SSLCertificateChainFile;
+ }
+
+ /**
+ * Setter for the sSLCertificateChainFile
+ *
+ * @param sSLCertificateChainFile
+ * the sSLCertificateChainFile to set
+ */
+ public void setSSLCertificateChainFile(String sSLCertificateChainFile) {
+ this.SSLCertificateChainFile = sSLCertificateChainFile;
+ }
+
+ /**
+ * Getter for sSLCACertificatePath
+ *
+ * @return the sSLCACertificatePath
+ */
+ public String getSSLCACertificatePath() {
+ return this.SSLCACertificatePath;
+ }
+
+ /**
+ * Setter for the sSLCACertificatePath
+ *
+ * @param sSLCACertificatePath
+ * the sSLCACertificatePath to set
+ */
+ public void setSSLCACertificatePath(String sSLCACertificatePath) {
+ this.SSLCACertificatePath = sSLCACertificatePath;
+ }
+
+ /**
+ * Getter for sSLCACertificateFile
+ *
+ * @return the sSLCACertificateFile
+ */
+ public String getSSLCACertificateFile() {
+ return this.SSLCACertificateFile;
+ }
+
+ /**
+ * Setter for the sSLCACertificateFile
+ *
+ * @param sSLCACertificateFile
+ * the sSLCACertificateFile to set
+ */
+ public void setSSLCACertificateFile(String sSLCACertificateFile) {
+ this.SSLCACertificateFile = sSLCACertificateFile;
+ }
+
+ /**
+ * Getter for sSLCARevocationPath
+ *
+ * @return the sSLCARevocationPath
+ */
+ public String getSSLCARevocationPath() {
+ return this.SSLCARevocationPath;
+ }
+
+ /**
+ * Setter for the sSLCARevocationPath
+ *
+ * @param sSLCARevocationPath
+ * the sSLCARevocationPath to set
+ */
+ public void setSSLCARevocationPath(String sSLCARevocationPath) {
+ this.SSLCARevocationPath = sSLCARevocationPath;
+ }
+
+ /**
+ * Getter for sSLCARevocationFile
+ *
+ * @return the sSLCARevocationFile
+ */
+ public String getSSLCARevocationFile() {
+ return this.SSLCARevocationFile;
+ }
+
+ /**
+ * Setter for the sSLCARevocationFile
+ *
+ * @param sSLCARevocationFile
+ * the sSLCARevocationFile to set
+ */
+ public void setSSLCARevocationFile(String sSLCARevocationFile) {
+ this.SSLCARevocationFile = sSLCARevocationFile;
+ }
+
+ /**
+ * Getter for sSLVerifyClient
+ *
+ * @return the sSLVerifyClient
+ */
+ public String getSSLVerifyClient() {
+ return this.SSLVerifyClient;
+ }
+
+ /**
+ * Setter for the sSLVerifyClient
+ *
+ * @param sSLVerifyClient
+ * the sSLVerifyClient to set
+ */
+ public void setSSLVerifyClient(String sSLVerifyClient) {
+ this.SSLVerifyClient = sSLVerifyClient;
+ }
+
+ /**
+ * Getter for sSLVerifyDepth
+ *
+ * @return the sSLVerifyDepth
+ */
+ public int getSSLVerifyDepth() {
+ return this.SSLVerifyDepth;
+ }
+
+ /**
+ * Setter for the sSLVerifyDepth
+ *
+ * @param sSLVerifyDepth
+ * the sSLVerifyDepth to set
+ */
+ public void setSSLVerifyDepth(int sSLVerifyDepth) {
+ this.SSLVerifyDepth = sSLVerifyDepth;
+ }
+
+ /**
+ * Getter for sSLInsecureRenegotiation
+ *
+ * @return the sSLInsecureRenegotiation
+ */
+ public boolean getSSLInsecureRenegotiation() {
+ return this.SSLInsecureRenegotiation;
+ }
+
+ /**
+ * Setter for the sSLInsecureRenegotiation
+ *
+ * @param sSLInsecureRenegotiation
+ * the sSLInsecureRenegotiation to set
+ */
+ public void setSSLInsecureRenegotiation(boolean sSLInsecureRenegotiation) {
+ this.SSLInsecureRenegotiation = sSLInsecureRenegotiation;
+ }
+
+ /**
+ * Getter for sSLCertificateKeyFile
+ *
+ * @return the sSLCertificateKeyFile
+ */
+ public String getSSLCertificateKeyFile() {
+ return this.SSLCertificateKeyFile;
+ }
+
+ /**
+ * Setter for the sSLCertificateKeyFile
+ *
+ * @param sSLCertificateKeyFile
+ * the sSLCertificateKeyFile to set
+ */
+ public void setSSLCertificateKeyFile(String sSLCertificateKeyFile) {
+ this.SSLCertificateKeyFile = sSLCertificateKeyFile;
+ }
+
+ /**
+ * Getter for reuseAddress
+ *
+ * @return the reuseAddress
+ */
+ public boolean isReuseAddress() {
+ return this.reuseAddress;
+ }
+
+ /**
+ * Setter for the reuseAddress
+ *
+ * @param reuseAddress
+ * the reuseAddress to set
+ */
+ public void setReuseAddress(boolean reuseAddress) {
+ this.reuseAddress = reuseAddress;
+ }
+
+ /**
+ * Getter for acceptorThreadCount
+ *
+ * @return the acceptorThreadCount
+ */
+ public int getAcceptorThreadCount() {
+ return this.acceptorThreadCount;
+ }
+
+ /**
+ * Setter for the acceptorThreadCount
+ *
+ * @param acceptorThreadCount
+ * the acceptorThreadCount to set
+ */
+ public void setAcceptorThreadCount(int acceptorThreadCount) {
+ this.acceptorThreadCount = acceptorThreadCount;
+ }
+
+}
Added: trunk/java/org/apache/tomcat/util/net/CompletionHandlerAdapter.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/CompletionHandlerAdapter.java
(rev 0)
+++ trunk/java/org/apache/tomcat/util/net/CompletionHandlerAdapter.java 2012-06-13
08:50:17 UTC (rev 2041)
@@ -0,0 +1,112 @@
+/**
+ * JBoss, Home of Professional Open Source. Copyright 2012, Red Hat, Inc., and
+ * individual contributors as indicated by the @author tags. See the
+ * copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
+ * site:
http://www.fsf.org.
+ */
+package
org.apache.tomcat.util.net;
+
+import java.nio.channels.CompletionHandler;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * {@code CompletionHandlerAdapter}
+ * <p>
+ * A handler adapter for consuming the result of an asynchronous I/O operation.
+ * <br/>
+ *
+ * The asynchronous channels defined in this package allow a completion handler
+ * to be specified to consume the result of an asynchronous operation. The
+ * {@link #completed completed} method is invoked when the I/O operation
+ * completes successfully. The {@link #failed failed} method is invoked if the
+ * I/O operations fails. The implementations of these methods should complete in
+ * a timely manner so as to avoid keeping the invoking thread from dispatching
+ * to other completion handlers.
+ * </p>
+ *
+ * @param <V>
+ * The result type of the I/O operation
+ * @param <A>
+ * The type of the object attached to the I/O operation
+ *
+ * Created on Mar 27, 2012 at 10:27:33 AM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public class CompletionHandlerAdapter<V, A> implements CompletionHandler<V,
A> {
+
+ private Collection<CompletionHandler<? extends V, ? extends A>> handlers;
+
+ /**
+ * Create a new instance of {@code CompletionHandlerAdapter}
+ */
+ public CompletionHandlerAdapter() {
+ super();
+ }
+
+ /**
+ * Create a new instance of {@code CompletionHandlerAdapter}
+ *
+ * @param handler
+ */
+ public CompletionHandlerAdapter(CompletionHandler<? extends V, ? extends A>
handler) {
+ this.handlers = new ArrayList<CompletionHandler<? extends V, ? extends
A>>();
+ this.handlers.add(handler);
+ }
+
+ /**
+ * Create a new instance of {@code CompletionHandlerAdapter}
+ *
+ * @param handlers
+ */
+ public CompletionHandlerAdapter(Collection<CompletionHandler<? extends V, ?
extends A>> handlers) {
+ this.handlers = handlers;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.nio.channels.CompletionHandler#completed(java.lang.Object,
+ * java.lang.Object)
+ */
+ @Override
+ public void completed(V result, A attachment) {
+ if (this.handlers != null) {
+ for (CompletionHandler handler : this.handlers) {
+ handler.completed(result, attachment);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.nio.channels.CompletionHandler#failed(java.lang.Throwable,
+ * java.lang.Object)
+ */
+ @Override
+ public void failed(Throwable exc, A attachment) {
+ if (this.handlers != null) {
+ for (CompletionHandler handler : this.handlers) {
+ handler.failed(exc, attachment);
+ }
+ }
+ }
+
+}
Added: trunk/java/org/apache/tomcat/util/net/DefaultNioServerSocketChannelFactory.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/DefaultNioServerSocketChannelFactory.java
(rev 0)
+++
trunk/java/org/apache/tomcat/util/net/DefaultNioServerSocketChannelFactory.java 2012-06-13
08:50:17 UTC (rev 2041)
@@ -0,0 +1,129 @@
+/**
+ * JBoss, Home of Professional Open Source. Copyright 2012, Red Hat, Inc., and
+ * individual contributors as indicated by the @author tags. See the
+ * copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
+ * site:
http://www.fsf.org.
+ */
+package
org.apache.tomcat.util.net;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.StandardSocketOptions;
+import java.nio.channels.AsynchronousChannelGroup;
+import java.nio.channels.AsynchronousServerSocketChannel;
+
+/**
+ * {@code DefaultNioServerSocketChannelFactory}
+ *
+ * Created on Jan 3, 2012 at 12:12:02 PM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public class DefaultNioServerSocketChannelFactory extends NioServerSocketChannelFactory
{
+
+ /**
+ * Create a new instance of {@code DefaultNioServerSocketChannelFactory}
+ */
+ public DefaultNioServerSocketChannelFactory() {
+ super();
+ }
+
+ /**
+ * Create a new instance of {@code DefaultNioServerSocketChannelFactory}
+ *
+ * @param threadGroup
+ */
+ public DefaultNioServerSocketChannelFactory(AsynchronousChannelGroup threadGroup) {
+ super(threadGroup);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.NioServerSocketChannelFactory#init()
+ */
+ @Override
+ public void init() throws IOException {
+ // NOOP
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.NioServerSocketChannelFactory#destroy()
+ */
+ public void destroy() throws IOException {
+ this.threadGroup = null;
+ this.attributes.clear();
+ this.attributes = null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.NioServerSocketChannelFactory#initChannel(
+ * org.apache.tomcat.util.net.NioChannel)
+ */
+ public void initChannel(NioChannel channel) throws Exception {
+ // NOOP
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.NioServerSocketChannelFactory#handshake(org
+ * .apache.tomcat.util.net.NioChannel)
+ */
+ @Override
+ public void handshake(NioChannel channel) throws IOException {
+ // NOOP
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.NioServerSocketChannelFactory#createServerChannel
+ * (int, int, java.net.InetAddress)
+ */
+ @Override
+ public AsynchronousServerSocketChannel createServerChannel(int port, int backlog,
+ InetAddress ifAddress, boolean reuseAddress) throws IOException {
+ return open().setOption(StandardSocketOptions.SO_REUSEADDR, reuseAddress).bind(
+ new InetSocketAddress(ifAddress, port), backlog);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.NioServerSocketChannelFactory#acceptChannel
+ * (java.nio.channels.AsynchronousServerSocketChannel)
+ */
+ @Override
+ public NioChannel acceptChannel(AsynchronousServerSocketChannel listener) throws
IOException {
+ try {
+ return new NioChannel(listener.accept().get());
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+}
Added: trunk/java/org/apache/tomcat/util/net/NioChannel.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/NioChannel.java (rev 0)
+++ trunk/java/org/apache/tomcat/util/net/NioChannel.java 2012-06-13 08:50:17 UTC (rev
2041)
@@ -0,0 +1,1191 @@
+/**
+ * JBoss, Home of Professional Open Source. Copyright 2011, Red Hat, Inc., and
+ * individual contributors as indicated by the @author tags. See the
+ * copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
+ * site:
http://www.fsf.org.
+ */
+package
org.apache.tomcat.util.net;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.SocketOption;
+import java.nio.ByteBuffer;
+import java.nio.channels.AlreadyBoundException;
+import java.nio.channels.AlreadyConnectedException;
+import java.nio.channels.AsynchronousByteChannel;
+import java.nio.channels.AsynchronousChannelGroup;
+import java.nio.channels.AsynchronousCloseException;
+import java.nio.channels.AsynchronousServerSocketChannel;
+import java.nio.channels.AsynchronousSocketChannel;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.CompletionHandler;
+import java.nio.channels.ConnectionPendingException;
+import java.nio.channels.InterruptedByTimeoutException;
+import java.nio.channels.NetworkChannel;
+import java.nio.channels.NotYetConnectedException;
+import java.nio.channels.ReadPendingException;
+import java.nio.channels.ShutdownChannelGroupException;
+import java.nio.channels.UnresolvedAddressException;
+import java.nio.channels.UnsupportedAddressTypeException;
+import java.nio.channels.WritePendingException;
+import java.nio.channels.spi.AsynchronousChannelProvider;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * {@code NioChannel}
+ *
+ * <p>
+ * An asynchronous channel for stream-oriented connecting sockets.
+ * </p>
+ * <p>
+ * NIO Asynchronous socket channels are created in one of two ways. A
+ * newly-created {@code NioChannel} is created by invoking one of the
+ * {@link #open open} methods defined by this class. A newly-created channel is
+ * open but not yet connected. A connected {@code NioChannel} is created when a
+ * connection is made to the socket of an
+ * {@link AsynchronousServerSocketChannel}. It is not possible to create an
+ * asynchronous socket channel for an arbitrary, pre-existing
+ * {@link java.net.Socket socket}.
+ * </p>
+ * <p>
+ * A newly-created channel is connected by invoking its {@link #connect connect}
+ * method; once connected, a channel remains connected until it is closed.
+ * Whether or not a socket channel is connected may be determined by invoking
+ * its {@link #getRemoteAddress getRemoteAddress} method. An attempt to invoke
+ * an I/O operation upon an unconnected channel will cause a
+ * {@link NotYetConnectedException} to be thrown.
+ * </p>
+ * <p>
+ * Channels of this type are safe for use by multiple concurrent threads. They
+ * support concurrent reading and writing, though at most one read operation and
+ * one write operation can be outstanding at any time. If a thread initiates a
+ * read operation before a previous read operation has completed then a
+ * {@link ReadPendingException} will be thrown. Similarly, an attempt to
+ * initiate a write operation before a previous write has completed will throw a
+ * {@link WritePendingException}.
+ * </p>
+ * <p>
+ * Socket options are configured using the
+ * {@link #setOption(SocketOption,Object) setOption} method. NIO Asynchronous
+ * socket channels support the following options:
+ * </p>
+ * <blockquote>
+ * <table border>
+ * <tr>
+ * <th>Option Name</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOptions#SO_SNDBUF SO_SNDBUF}</td>
+ * <td>The size of the socket send buffer</td>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOptions#SO_RCVBUF SO_RCVBUF}</td>
+ * <td>The size of the socket receive buffer</td>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOptions#SO_KEEPALIVE
SO_KEEPALIVE}</td>
+ * <td>Keep connection alive</td>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOptions#SO_REUSEADDR
SO_REUSEADDR}</td>
+ * <td>Re-use address</td>
+ * </tr>
+ * <tr>
+ * <td> {@link java.net.StandardSocketOptions#TCP_NODELAY TCP_NODELAY}</td>
+ * <td>Disable the Nagle algorithm</td>
+ * </tr>
+ * </table>
+ * </blockquote> Additional (implementation specific) options may also be
+ * supported.
+ *
+ * <h4>Timeouts</h4>
+ *
+ * <p>
+ * The {@link #read(ByteBuffer,long,TimeUnit,Object,CompletionHandler) read} and
+ * {@link #write(ByteBuffer,long,TimeUnit,Object,CompletionHandler) write}
+ * methods defined by this class allow a timeout to be specified when initiating
+ * a read or write operation. If the timeout elapses before an operation
+ * completes then the operation completes with the exception
+ * {@link java.nio.channels.InterruptedByTimeoutException}. A timeout may leave
+ * the channel, or the underlying connection, in an inconsistent state. Where
+ * the implementation cannot guarantee that bytes have not been read from the
+ * channel then it puts the channel into an implementation specific
+ * <em>error state</em>. A subsequent attempt to initiate a {@code read}
+ * operation causes an unspecified runtime exception to be thrown. Similarly if
+ * a {@code write} operation times out and the implementation cannot guarantee
+ * bytes have not been written to the channel then further attempts to
+ * {@code write} to the channel cause an unspecified runtime exception to be
+ * thrown. When a timeout elapses then the state of the
+ * {@link java.nio.ByteBuffer}, or the sequence of buffers, for the I/O
+ * operation is not defined. Buffers should be discarded or at least care must
+ * be taken to ensure that the buffers are not accessed while the channel
+ * remains open. All methods that accept timeout parameters treat values less
+ * than or equal to zero to mean that the I/O operation does not timeout.
+ * </p>
+ *
+ * Created on Dec 19, 2011 at 11:40:18 AM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public class NioChannel implements AsynchronousByteChannel, NetworkChannel {
+
+ /**
+ *
+ */
+ public static final int OP_STATUS_NORMAL = 0;
+
+ /**
+ * Channel closed status code
+ */
+ public static final int OP_STATUS_CLOSED = -1;
+ /**
+ * Read timeout status code
+ */
+ public static final int OP_STATUS_READ_TIMEOUT = -2;
+ /**
+ * Write timeout status code
+ */
+ public static final int OP_STATUS_WRITE_TIMEOUT = -3;
+
+ /**
+ *
+ */
+ public static final int OP_STATUS_READ_KILLED = -4;
+
+ /**
+ *
+ */
+ public static final int OP_STATUS_WRITE_KILLED = -5;
+
+ /**
+ * Read/write operation error code
+ */
+ public static final int OP_STATUS_ERROR = -17;
+
+ /**
+ *
+ */
+ private static final AtomicLong counter = new AtomicLong(0);
+ protected AsynchronousSocketChannel channel;
+ private long id;
+ private ByteBuffer buffer;
+
+ /**
+ * Create a new instance of {@code NioChannel}
+ *
+ * @param channel
+ * The {@link java.nio.channels.AsynchronousSocketChannel}
+ * attached to this channel
+ * @throws NullPointerException
+ * if the channel parameter is null
+ */
+ protected NioChannel(AsynchronousSocketChannel channel) {
+ if (channel == null) {
+ throw new NullPointerException("null channel parameter");
+ }
+ this.channel = channel;
+ this.id = counter.getAndIncrement();
+ this.buffer = ByteBuffer.allocateDirect(1);
+ }
+
+ /**
+ * Opens an asynchronous socket channel.
+ *
+ * <p>
+ * The new channel is created by invoking the
+ * {@link AsynchronousChannelProvider#openAsynchronousSocketChannel
+ * openAsynchronousSocketChannel} method on the
+ * {@link AsynchronousChannelProvider} that created the group. If the group
+ * parameter is {@code null} then the resulting channel is created by the
+ * system-wide default provider, and bound to the <em>default group</em>.
+ *
+ * @param group
+ * The group to which the newly constructed channel should be
+ * bound, or {@code null} for the default group
+ *
+ * @return A new asynchronous socket channel
+ *
+ * @throws ShutdownChannelGroupException
+ * If the channel group is shutdown
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public static NioChannel open(AsynchronousChannelGroup group) throws IOException {
+ AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
+ return new NioChannel(channel);
+ }
+
+ /**
+ * Opens a {@code NioChannel}.
+ *
+ * <p>
+ * This method returns an {@code NioChannel} that is bound to the
+ * <em>default group</em>.This method is equivalent to evaluating the
+ * expression: <blockquote>
+ *
+ * <pre>
+ * open((AsynchronousChannelGroup) null);
+ * </pre>
+ *
+ * </blockquote>
+ *
+ * @return A new {@code NioChannel}
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ public static NioChannel open() throws IOException {
+ return open(null);
+ }
+
+ /**
+ * Binds the channel's socket to a local address.
+ *
+ * <p>
+ * This method is used to establish an association between the socket and a
+ * local address. Once an association is established then the socket remains
+ * bound until the channel is closed. If the {@code local} parameter has the
+ * value {@code null} then the socket will be bound to an address that is
+ * assigned automatically.
+ *
+ * @param local
+ * The address to bind the socket, or {@code null} to bind the
+ * socket to an automatically assigned socket address
+ *
+ * @return This channel
+ *
+ * @throws AlreadyBoundException
+ * If the socket is already bound
+ * @throws UnsupportedAddressTypeException
+ * If the type of the given address is not supported
+ * @throws ClosedChannelException
+ * If the channel is closed
+ * @throws IOException
+ * If some other I/O error occurs
+ * @throws SecurityException
+ * If a security manager is installed and it denies an
+ * unspecified permission. An implementation of this interface
+ * should specify any required permissions.
+ *
+ * @see #getLocalAddress
+ */
+ public NioChannel bind(SocketAddress local) throws IOException {
+ this.channel.bind(local);
+ return this;
+ }
+
+ /**
+ * Binds the channel's socket to a local address.
+ *
+ * <p>
+ * This method is used to establish an association between the socket and a
+ * local address. Once an association is established then the socket remains
+ * bound until the channel is closed.
+ *
+ * @param hostname
+ * the Host name
+ * @param port
+ * The port number
+ *
+ * @return This channel
+ * @throws AlreadyBoundException
+ * If the socket is already bound
+ * @throws UnsupportedAddressTypeException
+ * If the type of the given address is not supported
+ * @throws ClosedChannelException
+ * If the channel is closed
+ * @throws IOException
+ * If some other I/O error occurs
+ * @throws IllegalArgumentException
+ * if the port parameter is outside the range of valid port
+ * values, or if the hostname parameter is <TT>null</TT>.
+ * @throws SecurityException
+ * If a security manager is installed and it denies an
+ * unspecified permission. An implementation of this interface
+ * should specify any required permissions.
+ *
+ * @see #bind(SocketAddress)
+ */
+ public NioChannel bind(String hostname, int port) throws IOException {
+ return bind(new InetSocketAddress(hostname, port));
+ }
+
+ /**
+ * Connects this channel.
+ *
+ * <p>
+ * This method initiates an operation to connect this channel. The
+ * {@code handler} parameter is a completion handler that is invoked when
+ * the connection is successfully established or connection cannot be
+ * established. If the connection cannot be established then the channel is
+ * closed.
+ *
+ * <p>
+ * This method performs exactly the same security checks as the
+ * {@link java.net.Socket} class. That is, if a security manager has been
+ * installed then this method verifies that its
+ * {@link java.lang.SecurityManager#checkConnect checkConnect} method
+ * permits connecting to the address and port number of the given remote
+ * endpoint.
+ *
+ * @param remote
+ * The remote address to which this channel is to be connected
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result
+ *
+ * @throws UnresolvedAddressException
+ * If the given remote address is not fully resolved
+ * @throws UnsupportedAddressTypeException
+ * If the type of the given remote address is not supported
+ * @throws AlreadyConnectedException
+ * If this channel is already connected
+ * @throws ConnectionPendingException
+ * If a connection operation is already in progress on this
+ * channel
+ * @throws ShutdownChannelGroupException
+ * If the channel group has terminated
+ * @throws SecurityException
+ * If a security manager has been installed and it does not
+ * permit access to the given remote endpoint
+ *
+ * @see #getRemoteAddress
+ */
+ public <A> void connect(SocketAddress remote, A attachment,
+ CompletionHandler<Void, ? super A> handler) {
+ this.channel.connect(remote, attachment, handler);
+ }
+
+ /**
+ * Connects this channel.
+ *
+ * <p>
+ * This method initiates an operation to connect this channel. This method
+ * behaves in exactly the same manner as the
+ * {@link #connect(SocketAddress, Object, CompletionHandler)} method except
+ * that instead of specifying a completion handler, this method returns a
+ * {@code Future} representing the pending result. The {@code Future}'s
+ * {@link Future#get() get} method returns {@code null} on successful
+ * completion.
+ *
+ * @param remote
+ * The remote address to which this channel is to be connected
+ *
+ * @return A {@code Future} object representing the pending result
+ *
+ * @throws UnresolvedAddressException
+ * If the given remote address is not fully resolved
+ * @throws UnsupportedAddressTypeException
+ * If the type of the given remote address is not supported
+ * @throws AlreadyConnectedException
+ * If this channel is already connected
+ * @throws ConnectionPendingException
+ * If a connection operation is already in progress on this
+ * channel
+ * @throws SecurityException
+ * If a security manager has been installed and it does not
+ * permit access to the given remote endpoint
+ */
+ public Future<Void> connect(SocketAddress remote) {
+ return this.channel.connect(remote);
+ }
+
+ /**
+ * Reset the internal buffer
+ */
+ protected void reset() {
+ this.buffer.clear();
+ }
+
+ /**
+ * Retrieve data, if any, from the internal buffer, put it in the
+ * <tt>dst</tt> buffer and clear the internal buffer
+ *
+ * @param dst
+ * the destination buffer
+ * @return the number of bytes retrieved
+ */
+ protected int reset(final ByteBuffer dst) {
+ this.buffer.flip();
+ int x = this.buffer.remaining();
+ dst.put(this.buffer);
+ this.buffer.clear();
+
+ return x;
+ }
+
+ /**
+ * @return <tt>TRUE</tt> if the channel is secure (i.e., use SSL), else
+ * <tt>FALSE</tt>
+ */
+ public boolean isSecure() {
+ return false;
+ }
+
+ /**
+ * @return the internal buffer
+ */
+ protected ByteBuffer getBuffer() {
+ return this.buffer;
+ }
+
+ /**
+ * @return the channel id
+ */
+ public long getId() {
+ return this.id;
+ }
+
+ /**
+ * Getter for name
+ *
+ * @return the name
+ */
+ public String getName() {
+ return getClass().getName() + "[" + getId() + "]";
+ }
+
+ /**
+ * Returns the provider that created this channel.
+ *
+ * @return the channel provider
+ */
+ public final AsynchronousChannelProvider provider() {
+ return this.channel.provider();
+ }
+
+ /**
+ * <p>
+ * Tells whether or not this channel is open.
+ * </p>
+ *
+ * @return <tt>true</tt> if, and only if, this channel is open
+ */
+ @Override
+ public boolean isOpen() {
+ return this.channel.isOpen();
+ }
+
+ /**
+ * <p>
+ * Tells whether or not this channel is closed.
+ * </p>
+ *
+ * @return <tt>true</tt> if, and only if, this channel is closed
+ */
+ public boolean isClosed() {
+ return !isOpen();
+ }
+
+ /**
+ * Close this channel.
+ *
+ * <p>
+ * Any outstanding asynchronous operations upon this channel will complete
+ * with the exception {@link AsynchronousCloseException}. After a channel is
+ * closed, further attempts to initiate asynchronous I/O operations complete
+ * immediately with cause {@link ClosedChannelException}.
+ * </p>
+ * <p>
+ * This method otherwise behaves exactly as specified by the
+ * {@link java.nio.channels.Channel} interface.
+ * </p>
+ *
+ * @throws IOException
+ * If an I/O error occurs
+ */
+ @Override
+ public synchronized void close() throws IOException {
+ if (isClosed()) {
+ return;
+ }
+ // Closing the channel
+ this.channel.close();
+ }
+
+ /**
+ * Try to close this channel. If the channel is already closed or the
+ * {@code force} parameter is false, nothing will happen. This method has an
+ * impact only if the channel is open and the {@code force} parameter is
+ * <tt>true</tt>
+ *
+ * @param force
+ * a boolean value indicating if we need to force closing the
+ * channel
+ * @throws IOException
+ */
+ public void close(boolean force) throws IOException {
+ if (isOpen() && force) {
+ this.close();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.nio.channels.AsynchronousByteChannel#read(java.nio.ByteBuffer)
+ *
+ * @deprecated (use readBytes(...) instead)
+ */
+ @Deprecated
+ @Override
+ public Future<Integer> read(ByteBuffer dst) {
+ this.reset(dst);
+ return this.channel.read(dst);
+ }
+
+ /**
+ * Read a sequence of bytes in blocking mode. This method works exactly in
+ * the same way as
+ * <code>readBytes(dst, Integer.MAX_VALUE, TimeUnit.MILLISECONDS)</code>.
+ *
+ * @param dst
+ * the buffer containing the read bytes
+ * @return the number of bytes read
+ * @throws Exception
+ * @throws ExecutionException
+ * @throws InterruptedException
+ */
+ public int readBytes(ByteBuffer dst) throws Exception {
+ return this.readBytes(dst, Integer.MAX_VALUE, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Read a sequence of bytes in blocking mode
+ *
+ * @param dst
+ * the buffer containing the read bytes
+ * @param timeout
+ * the read timeout
+ * @param unit
+ * the timeout unit
+ * @return The possible returned values are :
+ * <ul>
+ * <li>The number of bytes read if the operation was succeed</li>
+ * <li>{@link NioChannel#OP_STATUS_CLOSED} if the channel is
closed</li>
+ * <li>{@link NioChannel#OP_STATUS_READ_TIMEOUT} if the operation
+ * was timed out</li>
+ * </ul>
+ * @throws Exception
+ * @throws ExecutionException
+ * @throws InterruptedException
+ */
+ public int readBytes(final ByteBuffer dst, final long timeout, final TimeUnit unit)
+ throws Exception {
+ try {
+ int x = this.reset(dst);
+ return (x + this.channel.read(dst).get(timeout, unit));
+ } catch (Exception exp) {
+ if (exp instanceof ClosedChannelException) {
+ return OP_STATUS_CLOSED;
+ } else if (exp instanceof TimeoutException) {
+ return OP_STATUS_READ_TIMEOUT;
+ } else {
+ throw exp;
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.nio.channels.AsynchronousByteChannel#read(java.nio.ByteBuffer,
+ * java.lang.Object, java.nio.channels.CompletionHandler)
+ */
+ @Override
+ public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ?
super A> handler) {
+ this.read(dst, 0L, TimeUnit.MILLISECONDS, attachment, handler);
+ }
+
+ /**
+ * Reads a sequence of bytes from this channel into the given buffer.
+ *
+ * <p>
+ * This method initiates an asynchronous read operation to read a sequence
+ * of bytes from this channel into the given buffer. The {@code handler}
+ * parameter is a completion handler that is invoked when the read operation
+ * completes (or fails). The result passed to the completion handler is the
+ * number of bytes read or {@code -1} if no bytes could be read because the
+ * channel has reached end-of-stream.
+ *
+ * <p>
+ * If a timeout is specified and the timeout elapses before the operation
+ * completes then the operation completes with the exception
+ * {@link InterruptedByTimeoutException}. Where a timeout occurs, and the
+ * implementation cannot guarantee that bytes have not been read, or will
+ * not be read from the channel into the given buffer, then further attempts
+ * to read from the channel will cause an unspecific runtime exception to be
+ * thrown.
+ *
+ * <p>
+ * Otherwise this method works in the same manner as the
+ * {@link NioChannel#read(ByteBuffer,Object,CompletionHandler)} method.
+ *
+ * @param dst
+ * The buffer into which bytes are to be transferred
+ * @param timeout
+ * The maximum time for the I/O operation to complete
+ * @param unit
+ * The time unit of the {@code timeout} argument
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result
+ *
+ * @throws IllegalArgumentException
+ * If the buffer is read-only
+ * @throws ReadPendingException
+ * If a read operation is already in progress on this channel
+ * @throws NotYetConnectedException
+ * If this channel is not yet connected
+ * @throws ShutdownChannelGroupException
+ * If the channel group has terminated
+ */
+ public <A> void read(ByteBuffer dst, long timeout, TimeUnit unit, A attachment,
+ final CompletionHandler<Integer, ? super A> handler) {
+ final int x = this.reset(dst);
+ this.channel.read(dst, timeout, unit, attachment, new CompletionHandler<Integer,
A>() {
+
+ @Override
+ public void completed(Integer result, A attach) {
+ handler.completed(result + x, attach);
+ }
+
+ @Override
+ public void failed(Throwable exc, A attach) {
+ handler.failed(exc, attach);
+ }
+ });
+ }
+
+ /**
+ * Reads a sequence of bytes from this channel into a subsequence of the
+ * given buffers. This operation, sometimes called a
+ * <em>scattering read</em>, is often useful when implementing network
+ * protocols that group data into segments consisting of one or more
+ * fixed-length headers followed by a variable-length body. The
+ * {@code handler} parameter is a completion handler that is invoked when
+ * the read operation completes (or fails). The result passed to the
+ * completion handler is the number of bytes read or {@code -1} if no bytes
+ * could be read because the channel has reached end-of-stream.
+ *
+ * <p>
+ * This method initiates a read of up to <i>r</i> bytes from this channel,
+ * where <i>r</i> is the total number of bytes remaining in the specified
+ * subsequence of the given buffer array, that is,
+ *
+ * <blockquote>
+ *
+ * <pre>
+ * dsts[offset].remaining()
+ * + dsts[offset+1].remaining()
+ * + ... + dsts[offset+length-1].remaining()
+ * </pre>
+ *
+ * </blockquote>
+ *
+ * at the moment that the read is attempted.
+ *
+ * <p>
+ * Suppose that a byte sequence of length <i>n</i> is read, where
<tt>0</tt>
+ *
<tt><</tt> <i>n</i> <tt><=</tt> <i>r</i>.
Up
+ * to the first <tt>dsts[offset].remaining()</tt> bytes of this sequence
are
+ * transferred into buffer <tt>dsts[offset]</tt>, up to the next
+ * <tt>dsts[offset+1].remaining()</tt> bytes are transferred into buffer
+ * <tt>dsts[offset+1]</tt>, and so forth, until the entire byte sequence is
+ * transferred into the given buffers. As many bytes as possible are
+ * transferred into each buffer, hence the final position of each updated
+ * buffer, except the last updated buffer, is guaranteed to be equal to that
+ * buffer's limit. The underlying operating system may impose a limit on the
+ * number of buffers that may be used in an I/O operation. Where the number
+ * of buffers (with bytes remaining), exceeds this limit, then the I/O
+ * operation is performed with the maximum number of buffers allowed by the
+ * operating system.
+ *
+ * <p>
+ * If a timeout is specified and the timeout elapses before the operation
+ * completes then it completes with the exception
+ * {@link InterruptedByTimeoutException}. Where a timeout occurs, and the
+ * implementation cannot guarantee that bytes have not been read, or will
+ * not be read from the channel into the given buffers, then further
+ * attempts to read from the channel will cause an unspecific runtime
+ * exception to be thrown.
+ *
+ * @param dsts
+ * The buffers into which bytes are to be transferred
+ * @param offset
+ * The offset within the buffer array of the first buffer into
+ * which bytes are to be transferred; must be non-negative and no
+ * larger than {@code dsts.length}
+ * @param length
+ * The maximum number of buffers to be accessed; must be
+ * non-negative and no larger than {@code dsts.length - offset}
+ * @param timeout
+ * The maximum time for the I/O operation to complete
+ * @param unit
+ * The time unit of the {@code timeout} argument
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result
+ *
+ * @throws IndexOutOfBoundsException
+ * If the pre-conditions for the {@code offset} and
+ * {@code length} parameter aren't met
+ * @throws IllegalArgumentException
+ * If the buffer is read-only
+ * @throws ReadPendingException
+ * If a read operation is already in progress on this channel
+ * @throws NotYetConnectedException
+ * If this channel is not yet connected
+ * @throws ShutdownChannelGroupException
+ * If the channel group has terminated
+ */
+ public <A> void read(ByteBuffer[] dsts, int offset, int length, long timeout,
TimeUnit unit,
+ A attachment, final CompletionHandler<Long, ? super A> handler) {
+
+ // Retrieve bytes, if any, from the internal buffer
+ final int x = this.reset(dsts[0]);
+
+ this.channel.read(dsts, offset, length, timeout, unit, attachment,
+ new CompletionHandler<Long, A>() {
+
+ @Override
+ public void completed(Long result, A attach) {
+ handler.completed(result + x, attach);
+ }
+
+ @Override
+ public void failed(Throwable exc, A attach) {
+ handler.failed(exc, attach);
+ }
+ });
+ }
+
+ /**
+ * <p>
+ * Wait for incoming data in a non-blocking mode. The received data will be
+ * stored by default in the internal buffer (By default, just one byte).
+ * </p>
+ * <p>
+ * The byte read using this method will be available for the next read
+ * operation. That is, When attempting to read after receiving a read
+ * notification saying that there is data available on this stream, the byte
+ * will be first copied in the destination byte buffer and then perform the
+ * read operation which may be a blocking or non-blocking operation. So the
+ * user does not have to do that manually.
+ * </p>
+ *
+ * @param timeout
+ * The maximum time for the I/O operation to complete
+ * @param unit
+ * The time unit of the {@code timeout} argument
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result
+ * @throws ReadPendingException
+ * If a read operation is already in progress on this channel
+ * @throws NotYetConnectedException
+ * If this channel is not yet connected
+ * @throws ShutdownChannelGroupException
+ * If the channel group has terminated
+ */
+ public <A> void awaitRead(long timeout, TimeUnit unit, final A attachment,
+ final CompletionHandler<Integer, ? super A> handler) {
+
+ // Clear the internal buffer
+ this.buffer.clear();
+ // Perform an asynchronous read operation using the internal buffer
+ this.channel.read(this.buffer, timeout, unit, attachment, handler);
+ }
+
+ /**
+ * <p>
+ * Wait for incoming data in a non-blocking mode. The received data will be
+ * stored by default in the internal buffer (By default, just one byte).
+ * </p>
+ *
+ * <p>
+ * This method behaves exactly in the same manner as
+ * {@link #awaitRead(long, TimeUnit, Object, CompletionHandler)} but without
+ * a timeout defined.<br/>
+ * <code>awaitRead(0L, TimeUnit.MILLISECONDS, attachment, handler);</code>
+ * </p>
+ *
+ * @param attachment
+ * @param handler
+ * @see #awaitRead(long, TimeUnit, Object, CompletionHandler)
+ */
+ public <A> void awaitRead(final A attachment,
+ final CompletionHandler<Integer, ? super A> handler) {
+ awaitRead(0L, TimeUnit.MILLISECONDS, attachment, handler);
+ }
+
+ /**
+ * Write a sequence of bytes to this channel from the given buffer.
+ *
+ * @param src
+ * The buffer from which bytes are to be retrieved
+ * @return an instance of {@link java.util.concurrent.Future} containing the
+ * number of bytes written
+ * @see java.nio.channels.AsynchronousByteChannel#write(java.nio.ByteBuffer)
+ * @deprecated (use writeBytes(...) instead)
+ */
+ @Deprecated
+ @Override
+ public Future<Integer> write(ByteBuffer src) {
+ return this.channel.write(src);
+ }
+
+ /**
+ * Writes a sequence of bytes to this channel from the given buffer.
+ *
+ * <p>
+ * This method initiates an asynchronous write operation to write a sequence
+ * of bytes to this channel from the given buffer. The method behaves in
+ * exactly the same manner as the
+ * {@link #writeBytes(ByteBuffer, long, TimeUnit)} with
+ * {@link java.lang.Integer#MAX_VALUE} as a timeout and
+ * {@link java.util.concurrent.TimeUnit#MILLISECONDS} as a time unit.
+ *
+ * @param src
+ * The buffer from which bytes are to be retrieved
+ *
+ * @return The number of bytes written
+ * @see #writeBytes(ByteBuffer, long, TimeUnit)
+ * @throws WritePendingException
+ * If the channel does not allow more than one write to be
+ * outstanding and a previous write has not completed
+ * @throws ExecutionException
+ * @throws InterruptedException
+ * @throws Exception
+ * If any other type of errors occurs
+ */
+ public int writeBytes(ByteBuffer src) throws Exception {
+ return writeBytes(src, Integer.MAX_VALUE, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Write a sequence of bytes in blocking mode.
+ *
+ * @param src
+ * the buffer containing the bytes to write
+ * @param timeout
+ * the read timeout
+ * @param unit
+ * the timeout unit
+ * @return The possible returned values are :
+ * <ul>
+ * <li>The number of bytes written if the operation was
succeed</li>
+ * <li>{@link NioChannel#OP_STATUS_CLOSED} if the channel is
closed</li>
+ * <li>{@link NioChannel#OP_STATUS_WRITE_TIMEOUT} if the operation
+ * was timed out</li>
+ * </ul>
+ * @throws Exception
+ * @throws ExecutionException
+ * @throws InterruptedException
+ */
+ public int writeBytes(ByteBuffer src, long timeout, TimeUnit unit) throws Exception {
+ return this.channel.write(src).get(timeout, unit);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.nio.channels.AsynchronousByteChannel#write(java.nio.ByteBuffer,
+ * java.lang.Object, java.nio.channels.CompletionHandler)
+ */
+ @Override
+ public <A> void write(ByteBuffer src, A attachment,
+ CompletionHandler<Integer, ? super A> handler) {
+ write(src, 0L, TimeUnit.MILLISECONDS, attachment, handler);
+ }
+
+ /**
+ * Writes a sequence of bytes to this channel from the given buffer.
+ *
+ * <p>
+ * This method initiates an asynchronous write operation to write a sequence
+ * of bytes to this channel from the given buffer. The {@code handler}
+ * parameter is a completion handler that is invoked when the write
+ * operation completes (or fails). The result passed to the completion
+ * handler is the number of bytes written.
+ *
+ * <p>
+ * If a timeout is specified and the timeout elapses before the operation
+ * completes then it completes with the exception
+ * {@link InterruptedByTimeoutException}. Where a timeout occurs, and the
+ * implementation cannot guarantee that bytes have not been written, or will
+ * not be written to the channel from the given buffer, then further
+ * attempts to write to the channel will cause an unspecific runtime
+ * exception to be thrown.
+ *
+ * <p>
+ * Otherwise this method works in the same manner as the
+ * {@link NioChannel#write(ByteBuffer,Object,CompletionHandler)} method.
+ *
+ * @param src
+ * The buffer from which bytes are to be retrieved
+ * @param timeout
+ * The maximum time for the I/O operation to complete
+ * @param unit
+ * The time unit of the {@code timeout} argument
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result
+ *
+ * @throws WritePendingException
+ * If a write operation is already in progress on this channel
+ * @throws NotYetConnectedException
+ * If this channel is not yet connected
+ * @throws ShutdownChannelGroupException
+ * If the channel group has terminated
+ */
+ public <A> void write(ByteBuffer src, long timeout, TimeUnit unit, A attachment,
+ final CompletionHandler<Integer, ? super A> handler) {
+
+ this.channel.write(src, timeout, unit, attachment, handler);
+ }
+
+ /**
+ * Writes a sequence of bytes to this channel from a subsequence of the
+ * given buffers. This operation, sometimes called a
+ * <em>gathering write</em>, is often useful when implementing network
+ * protocols that group data into segments consisting of one or more
+ * fixed-length headers followed by a variable-length body. The
+ * {@code handler} parameter is a completion handler that is invoked when
+ * the write operation completes (or fails). The result passed to the
+ * completion handler is the number of bytes written.
+ *
+ * <p>
+ * This method initiates a write of up to <i>r</i> bytes to this channel,
+ * where <i>r</i> is the total number of bytes remaining in the specified
+ * subsequence of the given buffer array, that is,
+ *
+ * <blockquote>
+ *
+ * <pre>
+ * srcs[offset].remaining()
+ * + srcs[offset+1].remaining()
+ * + ... + srcs[offset+length-1].remaining()
+ * </pre>
+ *
+ * </blockquote>
+ *
+ * at the moment that the write is attempted.
+ *
+ * <p>
+ * Suppose that a byte sequence of length <i>n</i> is written, where
+ *
<tt>0</tt> <tt><</tt> <i>n</i> <tt><=</tt>
+ * <i>r</i>. Up to the first
<tt>srcs[offset].remaining()</tt> bytes
+ * of this sequence are written from buffer <tt>srcs[offset]</tt>, up to
the
+ * next <tt>srcs[offset+1].remaining()</tt> bytes are written from buffer
+ * <tt>srcs[offset+1]</tt>, and so forth, until the entire byte sequence is
+ * written. As many bytes as possible are written from each buffer, hence
+ * the final position of each updated buffer, except the last updated
+ * buffer, is guaranteed to be equal to that buffer's limit. The underlying
+ * operating system may impose a limit on the number of buffers that may be
+ * used in an I/O operation. Where the number of buffers (with bytes
+ * remaining), exceeds this limit, then the I/O operation is performed with
+ * the maximum number of buffers allowed by the operating system.
+ *
+ * <p>
+ * If a timeout is specified and the timeout elapses before the operation
+ * completes then it completes with the exception
+ * {@link InterruptedByTimeoutException}. Where a timeout occurs, and the
+ * implementation cannot guarantee that bytes have not been written, or will
+ * not be written to the channel from the given buffers, then further
+ * attempts to write to the channel will cause an unspecific runtime
+ * exception to be thrown.
+ *
+ * @param srcs
+ * The buffers from which bytes are to be retrieved
+ * @param offset
+ * The offset within the buffer array of the first buffer from
+ * which bytes are to be retrieved; must be non-negative and no
+ * larger than {@code srcs.length}
+ * @param length
+ * The maximum number of buffers to be accessed; must be
+ * non-negative and no larger than {@code srcs.length - offset}
+ * @param timeout
+ * The maximum time for the I/O operation to complete
+ * @param unit
+ * The time unit of the {@code timeout} argument
+ * @param attachment
+ * The object to attach to the I/O operation; can be {@code null}
+ * @param handler
+ * The handler for consuming the result
+ *
+ * @throws IndexOutOfBoundsException
+ * If the pre-conditions for the {@code offset} and
+ * {@code length} parameter aren't met
+ * @throws WritePendingException
+ * If a write operation is already in progress on this channel
+ * @throws NotYetConnectedException
+ * If this channel is not yet connected
+ * @throws ShutdownChannelGroupException
+ * If the channel group has terminated
+ */
+ public <A> void write(ByteBuffer[] srcs, int offset, int length, long timeout,
TimeUnit unit,
+ A attachment, final CompletionHandler<Long, ? super A> handler) {
+ this.channel.write(srcs, offset, length, timeout, unit, attachment, handler);
+ }
+
+ /**
+ * @return the local address
+ * @throws IOException
+ */
+ public SocketAddress getLocalAddress() throws IOException {
+ return this.channel.getLocalAddress();
+ }
+
+ /**
+ * @return the remote address
+ * @throws IOException
+ */
+ public SocketAddress getRemoteAddress() throws IOException {
+ return this.channel.getRemoteAddress();
+ }
+
+ /**
+ * Shutdown the connection for reading without closing the channel.
+ *
+ * <p>
+ * Once shutdown for reading then further reads on the channel will return
+ * {@code -1}, the end-of-stream indication. If the input side of the
+ * connection is already shutdown then invoking this method has no effect.
+ * The effect on an outstanding read operation is system dependent and
+ * therefore not specified. The effect, if any, when there is data in the
+ * socket receive buffer that has not been read, or data arrives
+ * subsequently, is also system dependent.
+ *
+ * @return This channel
+ *
+ * @throws NotYetConnectedException
+ * If this channel is not yet connected
+ * @throws ClosedChannelException
+ * If this channel is closed
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public final NioChannel shutdownInput() throws IOException {
+ this.channel.shutdownInput();
+ return this;
+ }
+
+ /**
+ * Shutdown the connection for writing without closing the channel.
+ *
+ * <p>
+ * Once shutdown for writing then further attempts to write to the channel
+ * will throw {@link ClosedChannelException}. If the output side of the
+ * connection is already shutdown then invoking this method has no effect.
+ * The effect on an outstanding write operation is system dependent and
+ * therefore not specified.
+ *
+ * @return This channel
+ *
+ * @throws NotYetConnectedException
+ * If this channel is not yet connected
+ * @throws ClosedChannelException
+ * If this channel is closed
+ * @throws IOException
+ * If some other I/O error occurs
+ */
+ public final NioChannel shutdownOutput() throws IOException {
+ this.channel.shutdownOutput();
+ return this;
+ }
+
+ /**
+ * Sets the value of a socket option.
+ *
+ * @param name
+ * The socket option name
+ * @param value
+ * The value of the socket option. A value of {@code null} may be
+ * a valid value for some socket options.
+ *
+ * @return This channel
+ *
+ * @throws UnsupportedOperationException
+ * If the socket option is not supported by this channel
+ * @throws IllegalArgumentException
+ * If the value is not a valid value for this socket option
+ * @throws ClosedChannelException
+ * If this channel is closed
+ * @throws IOException
+ * If an I/O error occurs
+ *
+ * @see java.net.StandardSocketOptions
+ */
+ public <T> NioChannel setOption(SocketOption<T> name, T value) throws
IOException {
+ this.channel.setOption(name, value);
+ return this;
+ }
+
+ /**
+ * Returns the value of a socket option.
+ *
+ * @param name
+ * The socket option
+ *
+ * @return The value of the socket option. A value of {@code null} may be a
+ * valid value for some socket options.
+ *
+ * @throws UnsupportedOperationException
+ * If the socket option is not supported by this channel
+ * @throws ClosedChannelException
+ * If this channel is closed
+ * @throws IOException
+ * If an I/O error occurs
+ *
+ * @see java.net.StandardSocketOptions
+ */
+ public <T> T getOption(SocketOption<T> name) throws IOException {
+ return this.channel.getOption(name);
+ }
+
+ /**
+ * Returns a set of the socket options supported by this channel.
+ *
+ * <p>
+ * This method will continue to return the set of options even after the
+ * channel has been closed.
+ *
+ * @return A set of the socket options supported by this channel
+ */
+ public Set<SocketOption<?>> supportedOptions() {
+ return this.channel.supportedOptions();
+ }
+
+ @Override
+ public String toString() {
+ return getName();
+ }
+}
Added: trunk/java/org/apache/tomcat/util/net/NioEndpoint.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/NioEndpoint.java (rev
0)
+++ trunk/java/org/apache/tomcat/util/net/NioEndpoint.java 2012-06-13 08:50:17 UTC (rev
2041)
@@ -0,0 +1,1853 @@
+/**
+ * JBoss, Home of Professional Open Source. Copyright 2011, Red Hat, Inc., and
+ * individual contributors as indicated by the @author tags. See the
+ * copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
+ * site:
http://www.fsf.org.
+ */
+
+package
org.apache.tomcat.util.net;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.BindException;
+import java.net.StandardSocketOptions;
+import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousChannelGroup;
+import java.nio.channels.AsynchronousServerSocketChannel;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.CompletionHandler;
+import java.nio.channels.WritePendingException;
+import java.nio.file.StandardOpenOption;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLHandshakeException;
+
+import org.apache.tomcat.util.net.NioEndpoint.Handler.SocketState;
+import org.apache.tomcat.util.net.jsse.NioJSSESocketChannelFactory;
+import org.jboss.logging.Logger;
+
+/**
+ * {@code NioEndpoint} NIO2 endpoint, providing the following services:
+ * <ul>
+ * <li>Socket channel acceptor thread</li>
+ * <li>Simple Worker thread pool, with possible use of executors</li>
+ * </ul>
+ *
+ * Created on Dec 13, 2011 at 9:41:53 AM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public class NioEndpoint extends AbstractEndpoint {
+
+ protected static Logger logger = Logger.getLogger(NioEndpoint.class);
+
+ private AsynchronousServerSocketChannel listener;
+ private ConcurrentHashMap<Long, NioChannel> connections;
+ private ConcurrentLinkedQueue<ChannelProcessor> recycledChannelProcessors;
+ private ConcurrentLinkedQueue<HandshakeHandler> recycledHandshakeProcessors;
+
+ /**
+ * Handling of accepted sockets.
+ */
+ protected Handler handler = null;
+
+ /**
+ * The event poller
+ */
+ private EventPoller eventPoller;
+
+ /**
+ *
+ */
+ protected NioServerSocketChannelFactory serverSocketChannelFactory = null;
+
+ /**
+ * SSL context.
+ */
+ protected SSLContext sslContext;
+
+ /**
+ * The static file sender.
+ */
+ protected Sendfile sendfile;
+
+ /**
+ * Create a new instance of {@code NioEndpoint}
+ */
+ public NioEndpoint() {
+ super();
+ }
+
+ /**
+ * @param handler
+ */
+ public void setHandler(Handler handler) {
+ this.handler = handler;
+ }
+
+ /**
+ * @return the handler
+ */
+ public Handler getHandler() {
+ return handler;
+ }
+
+ /**
+ * Number of keep-alive channels.
+ *
+ * @return the number of connection
+ */
+ public int getKeepAliveCount() {
+ return this.eventPoller.channelList.size();
+ }
+
+ /**
+ * Return the amount of threads that are managed by the pool.
+ *
+ * @return the amount of threads that are managed by the pool
+ */
+ public int getCurrentThreadCount() {
+ return curThreads;
+ }
+
+ /**
+ * Return the amount of threads currently busy.
+ *
+ * @return the amount of threads currently busy
+ */
+ public int getCurrentThreadsBusy() {
+ return curThreadsBusy;
+ }
+
+ /**
+ * Getter for sslContext
+ *
+ * @return the sslContext
+ */
+ public SSLContext getSslContext() {
+ return this.sslContext;
+ }
+
+ /**
+ * Setter for the sslContext
+ *
+ * @param sslContext
+ * the sslContext to set
+ */
+ public void setSslContext(SSLContext sslContext) {
+ this.sslContext = sslContext;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.AbstractEndpoint#init()
+ */
+ @Override
+ public void init() throws Exception {
+ if (initialized) {
+ return;
+ }
+
+ if (this.soTimeout < 0) {
+ this.soTimeout = DEFAULT_SO_TIMEOUT;
+ }
+
+ if (this.keepAliveTimeout < 0) {
+ this.keepAliveTimeout = this.soTimeout;
+ }
+
+ // Initialize thread count defaults for acceptor
+ if (acceptorThreadCount <= 0) {
+ acceptorThreadCount = 1;
+ }
+
+ // Create the thread factory
+ if (this.threadFactory == null) {
+ this.threadFactory = new DefaultThreadFactory(getName() + "-",
threadPriority);
+ }
+
+ if (this.connections == null) {
+ this.connections = new ConcurrentHashMap<>();
+ }
+
+ if (this.recycledChannelProcessors == null) {
+ this.recycledChannelProcessors = new ConcurrentLinkedQueue<>();
+ }
+
+ if (this.recycledHandshakeProcessors == null) {
+ this.recycledHandshakeProcessors = new ConcurrentLinkedQueue<>();
+ }
+
+ // If the executor is not set, create it with a fixed thread pool
+ if (this.executor == null) {
+ this.executor = Executors.newFixedThreadPool(this.maxThreads, this.threadFactory);
+ }
+
+ ExecutorService executorService = (ExecutorService) this.executor;
+ AsynchronousChannelGroup threadGroup = AsynchronousChannelGroup
+ .withThreadPool(executorService);
+
+ if (this.serverSocketChannelFactory == null) {
+ this.serverSocketChannelFactory = NioServerSocketChannelFactory
+ .createServerSocketChannelFactory(threadGroup, SSLEnabled);
+ } else {
+ this.serverSocketChannelFactory.threadGroup = threadGroup;
+ }
+
+ // Initialize the SSL context if the SSL mode is enabled
+ if (SSLEnabled) {
+ NioJSSESocketChannelFactory factory = (NioJSSESocketChannelFactory)
this.serverSocketChannelFactory;
+ sslContext = factory.getSslContext();
+ }
+
+ // Initialize the channel factory
+ this.serverSocketChannelFactory.init();
+
+ if (listener == null) {
+ try {
+ listener = this.serverSocketChannelFactory.createServerChannel(port, backlog,
+ address, reuseAddress);
+ } catch (BindException be) {
+ logger.fatal(be.getMessage(), be);
+ throw new BindException(be.getMessage() + " "
+ + (address == null ? "<null>" : address.toString()) + ":"
+ port);
+ }
+ }
+
+ initialized = true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.AbstractEndpoint#start()
+ */
+ @Override
+ public void start() throws Exception {
+ // Initialize channel if not done before
+ if (!initialized) {
+ init();
+ }
+ if (!running) {
+ running = true;
+ paused = false;
+
+ // Start acceptor threads
+ for (int i = 0; i < acceptorThreadCount; i++) {
+ Thread acceptorThread = newThread(new Acceptor(), "Acceptor", daemon);
+ acceptorThread.start();
+ }
+
+ // Start sendfile thread
+ if (useSendfile) {
+ this.sendfile = new Sendfile();
+ this.sendfile.init();
+ Thread sendfileThread = newThread(this.sendfile, "SendFile", true);
+ sendfileThread.start();
+ }
+
+ // Starting the event poller
+ this.eventPoller = new EventPoller(this.maxThreads);
+ this.eventPoller.init();
+ Thread eventPollerThread = newThread(this.eventPoller, "EventPoller",
true);
+ eventPollerThread.start();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.AbstractEndpoint#stop()
+ */
+ @Override
+ public void stop() {
+ if (running) {
+ running = false;
+ unlockAccept();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.AbstractEndpoint#destroy()
+ */
+ @Override
+ public void destroy() throws Exception {
+ if (running) {
+ stop();
+ }
+ if (listener != null) {
+ try {
+ listener.close();
+ } catch (IOException e) {
+ logger.error(sm.getString("endpoint.err.close"), e);
+ } finally {
+ listener = null;
+ }
+ }
+
+ // Destroy the send file thread
+ if (this.sendfile != null) {
+ sendfile.destroy();
+ }
+
+ // destroy the send file thread
+ if (this.eventPoller != null) {
+ this.eventPoller.destroy();
+ }
+
+ // Closing all alive connections
+ for (NioChannel ch : this.connections.values()) {
+ try {
+ ch.close();
+ } catch (Throwable t) {
+ // Nothing to do
+ }
+ }
+ // Remove all connections
+ this.connections.clear();
+ // Destroy the server socket channel factory
+ this.serverSocketChannelFactory.destroy();
+ this.serverSocketChannelFactory = null;
+ // Destroy all recycled channel processors
+ this.recycledChannelProcessors.clear();
+ this.recycledChannelProcessors = null;
+ // Destroy all recycled handshake processors
+ this.recycledHandshakeProcessors.clear();
+ this.recycledHandshakeProcessors = null;
+
+ // Shut down the executor
+ ((ExecutorService) this.executor).shutdown();
+
+ initialized = false;
+ }
+
+ /**
+ * Configure the channel options before being processed
+ */
+ protected boolean setChannelOptions(NioChannel channel) {
+ // Process the connection
+ try {
+ // Set channel options: timeout, linger, etc
+ if (keepAliveTimeout > 0) {
+ channel.setOption(StandardSocketOptions.SO_KEEPALIVE, Boolean.TRUE);
+ }
+ if (soLinger >= 0) {
+ channel.setOption(StandardSocketOptions.SO_LINGER, soLinger);
+ }
+ if (tcpNoDelay) {
+ channel.setOption(StandardSocketOptions.TCP_NODELAY, tcpNoDelay);
+ }
+
+ // Initialize the channel
+ serverSocketChannelFactory.initChannel(channel);
+ return true;
+ } catch (Throwable t) {
+ // logger.error(t.getMessage(), t);
+ if (logger.isDebugEnabled()) {
+ if (t instanceof SSLHandshakeException) {
+ logger.debug(sm.getString("endpoint.err.handshake"), t);
+ } else {
+ logger.debug(sm.getString("endpoint.err.unexpected"), t);
+ }
+ }
+
+ return false;
+ }
+ }
+
+ /**
+ * Add specified channel and associated pool to the poller. The added will
+ * be added to a temporary array, and polled first after a maximum amount of
+ * time equal to pollTime (in most cases, latency will be much lower,
+ * however). Note: If both read and write are false, the socket will only be
+ * checked for timeout; if the socket was already present in the poller, a
+ * callback event will be generated and the socket will be removed from the
+ * poller.
+ *
+ * @param channel
+ * the channel to add to the poller
+ * @param timeout
+ * to use for this connection
+ * @param read
+ * to do read polling
+ * @param write
+ * to do write polling
+ * @param resume
+ * to send a callback event
+ * @param wakeup
+ *
+ * @see #addEventChannel(NioChannel, long, int)
+ */
+ public void addEventChannel(NioChannel channel, long timeout, boolean read, boolean
write,
+ boolean resume, boolean wakeup) {
+
+ int flags = (read ? ChannelInfo.READ : 0) | (write ? ChannelInfo.WRITE : 0)
+ | (resume ? ChannelInfo.RESUME : 0) | (wakeup ? ChannelInfo.WAKEUP : 0);
+
+ addEventChannel(channel, timeout, flags);
+ }
+
+ /**
+ * Same as
+ * {@link #addEventChannel(NioChannel, long, boolean, boolean, boolean, boolean)}
+ *
+ * @param channel
+ * the channel to add to the poller
+ * @param timeout
+ * the channel timeout
+ * @param flags
+ * a merge of read, write, resume and wake up event types
+ *
+ * @see #addEventChannel(NioChannel, long, boolean, boolean, boolean,
+ * boolean)
+ */
+ public void addEventChannel(NioChannel channel, long timeout, int flags) {
+
+ long eventTimeout = timeout <= 0 ? keepAliveTimeout : timeout;
+
+ if (eventTimeout <= 0) {
+ // Always put a timeout in
+ eventTimeout = soTimeout > 0 ? soTimeout : Integer.MAX_VALUE;
+ }
+
+ if (!this.eventPoller.add(channel, eventTimeout, flags)) {
+ closeChannel(channel);
+ }
+ }
+
+ /**
+ * Remove the channel from the list of venet channels
+ *
+ * @param channel
+ */
+ public void removeEventChannel(NioChannel channel) {
+ this.eventPoller.remove(channel);
+ }
+
+ /**
+ * Add a send file data to the queue of static files
+ *
+ * @param data
+ * @return <tt>TRUE</tt> if the object is added successfully to the list of
+ * {@code SendfileData}, else <tt>FALSE</tt>
+ */
+ public boolean addSendfileData(SendfileData data) {
+ if (this.sendfile != null) {
+ return this.sendfile.add(data);
+ }
+
+ return false;
+ }
+
+ /**
+ * Process given channel.
+ */
+ protected boolean processChannelWithOptions(NioChannel channel) {
+ try {
+ executor.execute(new ChannelWithOptionsProcessor(channel));
+ } catch (Throwable t) {
+ // This means we got an OOM or similar creating a thread, or that
+ // the pool and its queue are full
+ logger.error(sm.getString("endpoint.process.fail"), t);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Process given channel for an event.
+ *
+ * @param channel
+ * @param status
+ * @return <tt>true</tt> if the processing of the channel finish
+ * successfully else <tt>false</tt>
+ */
+ public boolean processChannel(NioChannel channel, SocketStatus status) {
+ if (channel.isClosed()) {
+ return false;
+ }
+ try {
+ ChannelProcessor processor = getChannelProcessor(channel, status);
+ this.executor.execute(processor);
+ return true;
+ } catch (Throwable t) {
+ // This means we got an OOM or similar creating a thread, or that
+ // the pool and its queue are full
+ logger.error(sm.getString("endpoint.process.fail"), t);
+ return false;
+ }
+ }
+
+ /**
+ * @param channel
+ * @return
+ */
+ private boolean handshake(NioChannel channel) {
+ try {
+ HandshakeHandler processor = getHandshakeProcessor(channel);
+ this.executor.execute(processor);
+ return true;
+ } catch (Throwable t) {
+ // This means we got an OOM or similar creating a thread, or that
+ // the pool and its queue are full
+ logger.error(sm.getString("endpoint.process.fail"), t);
+ return false;
+ }
+ }
+
+ /**
+ * @return peek a processor from the recycled processors list
+ */
+ private ChannelProcessor getChannelProcessor(NioChannel channel, SocketStatus status) {
+ ChannelProcessor processor = this.recycledChannelProcessors.poll();
+ if (processor == null) {
+ processor = new ChannelProcessor(channel, status);
+ } else {
+ processor.setChannel(channel);
+ processor.setStatus(status);
+ }
+ return processor;
+ }
+
+ /**
+ * @return peek a handshake processor from the recycled processors list
+ */
+ private HandshakeHandler getHandshakeProcessor(NioChannel channel) {
+ HandshakeHandler processor = this.recycledHandshakeProcessors.poll();
+ if (processor == null) {
+ processor = new HandshakeHandler(channel);
+ } else {
+ processor.setChannel(channel);
+ }
+ return processor;
+ }
+
+ /**
+ * Try to add the specified channel to the list of connections.
+ *
+ * @param channel
+ * the channel to be added
+ * @return <tt>true</tt> if the channel is added successfully, else
+ * <tt>false</tt>
+ */
+ private boolean addChannel(NioChannel channel) {
+ if (this.counter.get() < this.maxConnections && channel.isOpen()) {
+ if (this.connections.get(channel.getId()) == null
+ || this.connections.get(channel.getId()).isClosed()) {
+ this.connections.put(channel.getId(), channel);
+ this.counter.incrementAndGet();
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Getter for serverSocketChannelFactory
+ *
+ * @return the serverSocketChannelFactory
+ */
+ public NioServerSocketChannelFactory getServerSocketChannelFactory() {
+ return this.serverSocketChannelFactory;
+ }
+
+ /**
+ * Setter for the serverSocketChannelFactory
+ *
+ * @param serverSocketChannelFactory
+ * the serverSocketChannelFactory to set
+ */
+ public void setServerSocketChannelFactory(
+ NioServerSocketChannelFactory serverSocketChannelFactory) {
+ this.serverSocketChannelFactory = serverSocketChannelFactory;
+ }
+
+ /**
+ * Close the specified channel and remove it from the list of open
+ * connections
+ *
+ * @param channel
+ * the channel to be closed
+ */
+ public void closeChannel(NioChannel channel) {
+ if (channel != null) {
+ try {
+ channel.close();
+ } catch (IOException e) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(e.getMessage(), e);
+ }
+ } finally {
+ if (this.connections.remove(channel.getId()) != null) {
+ this.counter.decrementAndGet();
+ }
+ }
+ }
+ }
+
+ /**
+ * @return if the send file is supported, peek up a {@link SendfileData}
+ * from the pool, else <tt>null</tt>
+ */
+ public SendfileData getSendfileData() {
+ return this.sendfile != null ? this.sendfile.getSendfileData() : new SendfileData();
+ }
+
+ /**
+ * {@code Acceptor}
+ *
+ * <p>
+ * Server socket acceptor thread.
+ * </p>
+ * Created on Mar 6, 2012 at 9:13:34 AM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+ protected class Acceptor implements Runnable {
+
+ /**
+ * The background thread that listens for incoming TCP/IP connections
+ * and hands them off to an appropriate processor.
+ */
+ public void run() {
+
+ // Loop until we receive a shutdown command
+ while (running) {
+ // Loop if end point is paused
+ while (paused) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+
+ try {
+ // Accept the next incoming connection from the server
+ // channel
+ final NioChannel channel = serverSocketChannelFactory.acceptChannel(listener);
+ boolean ok = false;
+ if (addChannel(channel) && setChannelOptions(channel) &&
channel.isOpen()) {
+ if (channel.isSecure()) {
+ handshake(channel);
+ ok = true;
+ } else {
+ ok = processChannel(channel, null);
+ }
+ }
+ // If a problem occurs, close the channel right away
+ if (!ok) {
+ logger.info("Fail processing the channel");
+ closeChannel(channel);
+ }
+ } catch (Exception exp) {
+ if (running) {
+ logger.error(sm.getString("endpoint.accept.fail"), exp);
+ }
+ } catch (Throwable t) {
+ logger.error(sm.getString("endpoint.accept.fail"), t);
+ }
+ }
+ }
+ }
+
+ /**
+ * {@code HandshakeHandler}
+ * <p>
+ * Asynchronous handler for the secure channel handshake. Since the
+ * handshake for the secure channels may take awhile, if several new
+ * connections are received at the same time, the non-blocking handshake
+ * aims to avoid connections to be timed out. Note that this does not
+ * guarantee that no connection will be timed out, this depends to the
+ * socket SO_TIMEOUT in the client side.
+ * </p>
+ *
+ * Created on May 23, 2012 at 11:48:45 AM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+ protected class HandshakeHandler implements Runnable {
+
+ private NioChannel channel;
+
+ /**
+ * Create a new instance of {@code HandshakeProcessor}
+ *
+ * @param channel
+ */
+ public HandshakeHandler(NioChannel channel) {
+ this.channel = channel;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Runnable#run()
+ */
+ @Override
+ public void run() {
+ try {
+ serverSocketChannelFactory.handshake(channel);
+
+ if (!processChannel(channel, null)) {
+ logger.info("Fail processing the channel");
+ closeChannel(channel);
+ }
+ } catch (Exception exp) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(exp.getMessage(), exp);
+ }
+
+ closeChannel(channel);
+ } finally {
+ this.recycle();
+ }
+ }
+
+ /**
+ * Recycle the handshake processor
+ */
+ private void recycle() {
+ this.channel = null;
+ if (recycledHandshakeProcessors != null) {
+ recycledHandshakeProcessors.offer(this);
+ }
+ }
+
+ /**
+ * Setter for the channel
+ *
+ * @param channel
+ */
+ public void setChannel(NioChannel channel) {
+ this.channel = channel;
+ }
+ }
+
+ /**
+ * {@code ChannelInfo}
+ * <p>
+ * Channel list class, used to avoid using a possibly large amount of
+ * objects with very little actual use.
+ * </p>
+ * Created on Apr 13, 2012 at 11:13:13 AM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+ public static class ChannelInfo {
+ /**
+ *
+ */
+ public static final int READ = 1;
+ /**
+ *
+ */
+ public static final int WRITE = 2;
+ /**
+ *
+ */
+ public static final int RESUME = 4;
+ /**
+ *
+ */
+ public static final int WAKEUP = 8;
+
+ protected NioChannel channel;
+ protected long timeout;
+ protected int flags;
+
+ /**
+ * Create a new instance of {@code ChannelInfo}
+ */
+ public ChannelInfo() {
+ this(null, 0, 0);
+ }
+
+ /**
+ * Create a new instance of {@code ChannelInfo}
+ *
+ * @param channel
+ * the channel
+ * @param timeout
+ * the channel timeout. The default time unit is
+ * {@code java.util.concurrent.TimeUnit.MILLISECONDS}
+ * @param flags
+ */
+ public ChannelInfo(NioChannel channel, long timeout, int flags) {
+ this.channel = channel;
+ this.timeout = timeout;
+ this.flags = flags;
+ }
+
+ /**
+ * Create a new instance of {@code ChannelInfo}
+ *
+ * @param channel
+ * @param timeout
+ * @param unit
+ * @param flags
+ */
+ public ChannelInfo(NioChannel channel, long timeout, TimeUnit unit, int flags) {
+ this(channel, TimeUnit.MILLISECONDS.convert(timeout, unit), flags);
+ }
+
+ /**
+ * Recycle this channel info for next use
+ */
+ public void recycle() {
+ this.channel = null;
+ this.timeout = 0;
+ this.flags = 0;
+ }
+
+ /**
+ * @return the read flag
+ */
+ public boolean read() {
+ return (flags & READ) == READ;
+ }
+
+ /**
+ * Set the <code>read</code> flag. If the parameter is true, the read
+ * flag will have the value 1 else 0.
+ *
+ * @param read
+ */
+ public void read(boolean read) {
+ this.flags = (read ? (this.flags | READ) : (this.flags & 0xE));
+ }
+
+ /**
+ * @return the write flag
+ */
+ public boolean write() {
+ return (flags & WRITE) == WRITE;
+ }
+
+ /**
+ * Set the <code>write</code> flag. If the parameter is true, the write
+ * flag will have the value 1 else 0.
+ *
+ * @param write
+ */
+ public void write(boolean write) {
+ this.flags = (write ? (this.flags | WRITE) : (this.flags & 0xD));
+ }
+
+ /**
+ * @return the resume flag
+ */
+ public boolean resume() {
+ return (flags & RESUME) == RESUME;
+ }
+
+ /**
+ * Set the <code>resume</code> flag. If the parameter is true, the
+ * resume flag will have the value 1 else 0.
+ *
+ * @param resume
+ */
+ public void resume(boolean resume) {
+ this.flags = (resume ? (this.flags | RESUME) : (this.flags & 0xB));
+ }
+
+ /**
+ * @return the wake up flag
+ */
+ public boolean wakeup() {
+ return (flags & WAKEUP) == WAKEUP;
+ }
+
+ /**
+ * Set the <code>wakeup</code> flag. If the parameter is true, the
+ * wakeup flag will have the value 1 else 0.
+ *
+ * @param wakeup
+ */
+ public void wakeup(boolean wakeup) {
+ this.flags = (wakeup ? (this.flags | WAKEUP) : (this.flags & 0x7));
+ }
+
+ /**
+ * Merge the tow flags
+ *
+ * @param flag1
+ * @param flag2
+ * @return the result of merging the tow flags
+ */
+ public static int merge(int flag1, int flag2) {
+ return ((flag1 & READ) | (flag2 & READ)) | ((flag1 & WRITE) | (flag2 &
WRITE))
+ | ((flag1 & RESUME) | (flag2 & RESUME)) | ((flag1 & WAKEUP) & (flag2
& WAKEUP));
+ }
+ }
+
+ /**
+ * {@code Handler}
+ *
+ * <p>
+ * Bare bones interface used for socket processing. Per thread data is to be
+ * stored in the ThreadWithAttributes extra folders, or alternately in
+ * thread local fields.
+ * </p>
+ *
+ * Created on Mar 6, 2012 at 9:13:07 AM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+ public interface Handler {
+ /**
+ * {@code ChannelState}
+ *
+ * Created on Dec 12, 2011 at 9:41:06 AM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+ public enum SocketState {
+ /**
+ *
+ */
+ OPEN,
+ /**
+ *
+ */
+ CLOSED,
+ /**
+ *
+ */
+ LONG
+ }
+
+ /**
+ * Process the specified {@code org.apache.tomcat.util.net.NioChannel}
+ *
+ * @param channel
+ * the {@code org.apache.tomcat.util.net.NioChannel}
+ * @return a channel state
+ */
+ public SocketState process(NioChannel channel);
+
+ /**
+ * Process the specified {@code org.apache.tomcat.util.net.NioChannel}
+ *
+ * @param channel
+ * @param status
+ * @return a channel state
+ */
+ public SocketState event(NioChannel channel, SocketStatus status);
+
+ }
+
+ /**
+ * {@code ChannelWithOptionsProcessor}
+ * <p>
+ * This class is the equivalent of the Worker, but will simply use in an
+ * external Executor thread pool. This will also set the channel options and
+ * do the handshake.
+ * </p>
+ * Created on Mar 6, 2012 at 9:09:43 AM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+ protected class ChannelWithOptionsProcessor extends ChannelProcessor {
+
+ /**
+ * Create a new instance of {@code ChannelWithOptionsProcessor}
+ *
+ * @param channel
+ */
+ public ChannelWithOptionsProcessor(NioChannel channel) {
+ super(channel);
+ }
+
+ @Override
+ public void run() {
+ boolean ok = true;
+
+ if (!deferAccept) {
+ ok = setChannelOptions(channel);
+ } else {
+ // Process the request from this channel
+ ok = setChannelOptions(channel)
+ && handler.process(channel) != Handler.SocketState.CLOSED;
+ }
+
+ if (!ok) {
+ // Close the channel
+ closeChannel(channel);
+ }
+
+ channel = null;
+ }
+ }
+
+ /**
+ * {@code ChannelProcessor}
+ * <p>
+ * This class is the equivalent of the Worker, but will simply use in an
+ * external Executor thread pool.
+ * </p>
+ * Created on Mar 6, 2012 at 9:10:06 AM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+ protected class ChannelProcessor implements Runnable {
+
+ protected NioChannel channel;
+ protected SocketStatus status = null;
+
+ /**
+ * Create a new instance of {@code ChannelProcessor}
+ *
+ * @param channel
+ */
+ public ChannelProcessor(NioChannel channel) {
+ this.channel = channel;
+ }
+
+ /**
+ * Create a new instance of {@code ChannelProcessor}
+ *
+ * @param channel
+ * @param status
+ */
+ public ChannelProcessor(NioChannel channel, SocketStatus status) {
+ this(channel);
+ this.status = status;
+ }
+
+ @Override
+ public void run() {
+ try {
+ Handler.SocketState state = ((status == null) ? handler.process(channel) : handler
+ .event(channel, status));
+
+ if (state == SocketState.CLOSED) {
+ closeChannel(channel);
+ }
+ } catch (Throwable th) {
+ logger.error(th.getMessage(), th);
+ if (logger.isDebugEnabled()) {
+ logger.debug(th.getMessage(), th);
+ }
+ } finally {
+ this.recycle();
+ }
+ }
+
+ /**
+ * Reset this channel processor
+ */
+ protected void recycle() {
+ this.channel = null;
+ this.status = null;
+ if (recycledChannelProcessors != null) {
+ recycledChannelProcessors.offer(this);
+ }
+ }
+
+ /**
+ *
+ * @param channel
+ * @param status
+ */
+ protected void setup(NioChannel channel, SocketStatus status) {
+ this.channel = channel;
+ this.status = status;
+ }
+
+ /**
+ * @param status
+ */
+ public void setStatus(SocketStatus status) {
+ this.status = status;
+ }
+
+ /**
+ * @param channel
+ */
+ public void setChannel(NioChannel channel) {
+ this.channel = channel;
+ }
+ }
+
+ /**
+ * {@code EventPoller}
+ *
+ * Created on Mar 26, 2012 at 12:51:53 PM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+ public class EventPoller implements Runnable {
+
+ /**
+ * Last run of maintain. Maintain will run usually every 5s.
+ */
+ protected long lastMaintain = System.currentTimeMillis();
+
+ protected ConcurrentHashMap<Long, ChannelInfo> channelList;
+ protected ConcurrentLinkedQueue<ChannelInfo> recycledChannelList;
+ private ConcurrentLinkedQueue<CompletionHandler<Integer, NioChannel>>
recycledCompletionHandlers;
+ private Object mutex;
+ private int size;
+
+ /**
+ * Create a new instance of {@code EventPoller}
+ *
+ * @param size
+ */
+ public EventPoller(int size) {
+ this.size = size;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Runnable#run()
+ */
+ @Override
+ public void run() {
+ while (running) {
+ // Loop if endpoint is paused
+ while (paused) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+
+ while (this.channelList.size() < 1 && running) {
+ synchronized (this.mutex) {
+ try {
+ this.mutex.wait(10000);
+ } catch (InterruptedException e) {
+ // NOPE
+ }
+ }
+ }
+
+ while (this.channelList.size() > 0 && running) {
+ maintain();
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ // NOPE
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Check timeouts and raise timeout event
+ */
+ public void maintain() {
+ long date = System.currentTimeMillis();
+ // Maintain runs at most once every 5s, although it will likely get
+ // called more
+ if ((date - lastMaintain) < 5000L) {
+ return;
+ }
+
+ // Update the last maintain time
+ lastMaintain = date;
+
+ for (ChannelInfo info : this.channelList.values()) {
+ if (date >= info.timeout) {
+ NioChannel ch = info.channel;
+ remove(info);
+ if (!processChannel(ch, SocketStatus.TIMEOUT)) {
+ closeChannel(ch);
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove the channel having the specified id
+ *
+ * @param id
+ */
+ protected boolean remove(long id) {
+ ChannelInfo info = this.channelList.remove(id);
+ return offer(info);
+ }
+
+ /**
+ * @param channel
+ * @return true if the channel is removed successfully else false
+ */
+ public boolean remove(NioChannel channel) {
+ return channel != null ? remove(channel.getId()) : false;
+ }
+
+ /**
+ * @param info
+ * @return true if the channel-info is removed successfully else false
+ */
+ public boolean remove(ChannelInfo info) {
+ return info != null ? remove(info.channel) : false;
+ }
+
+ /**
+ * Initialize the event poller
+ */
+ public void init() {
+ this.mutex = new Object();
+ this.channelList = new ConcurrentHashMap<>(this.size);
+ this.recycledChannelList = new ConcurrentLinkedQueue<>();
+ this.recycledCompletionHandlers = new ConcurrentLinkedQueue<>();
+ }
+
+ /**
+ * Destroy the event poller
+ */
+ public void destroy() {
+ synchronized (this.mutex) {
+ this.channelList.clear();
+ this.recycledChannelList.clear();
+ this.recycledCompletionHandlers.clear();
+ this.mutex.notifyAll();
+ }
+ }
+
+ /**
+ *
+ * @return
+ */
+ protected ChannelInfo poll() {
+ ChannelInfo info = this.recycledChannelList.poll();
+ if (info == null) {
+ info = new ChannelInfo();
+ }
+ return info;
+ }
+
+ /**
+ * Recycle the the {@link ChannelInfo}
+ *
+ * @param info
+ */
+ protected boolean offer(ChannelInfo info) {
+ if (info != null) {
+ info.recycle();
+ return this.recycledChannelList.offer(info);
+ }
+ return false;
+ }
+
+ /**
+ * Peek a {@link java.nio.CompletionHandler} from the list of recycled
+ * handlers. If the list is empty, create a new one and return it.
+ *
+ * @return a reference of {@link java.nio.CompletionHandler}
+ */
+ private CompletionHandler<Integer, NioChannel> getCompletionHandler() {
+ CompletionHandler<Integer, NioChannel> handler =
this.recycledCompletionHandlers.poll();
+ if (handler == null) {
+ handler = new CompletionHandler<Integer, NioChannel>() {
+
+ @Override
+ public void completed(Integer nBytes, NioChannel attach) {
+ if (nBytes < 0) {
+ failed(new ClosedChannelException(), attach);
+ } else {
+ remove(attach);
+ if (!processChannel(attach, SocketStatus.OPEN_READ)) {
+ closeChannel(attach);
+ }
+ // Recycle the completion handler
+ recycleHanlder(this);
+ }
+ }
+
+ @Override
+ public void failed(Throwable exc, NioChannel attach) {
+ remove(attach);
+ processChannel(attach, SocketStatus.ERROR);
+ // Recycle the completion handler
+ recycleHanlder(this);
+ }
+ };
+ }
+
+ return handler;
+ }
+
+ /**
+ * Recycle the {@link java.nio.CompletionHandler}
+ *
+ * @param handler
+ */
+ private void recycleHanlder(CompletionHandler<Integer, NioChannel> handler) {
+ this.recycledCompletionHandlers.offer(handler);
+ }
+
+ /**
+ * Add the channel to the list of channels
+ *
+ * @param channel
+ * @param timeout
+ * @param flag
+ * @return <tt>true</tt> if the channel is added successfully else
+ * <tt>false</tt>
+ */
+ public boolean add(final NioChannel channel, long timeout, int flag) {
+ if (this.channelList.size() > this.size) {
+ return false;
+ }
+
+ long date = timeout + System.currentTimeMillis();
+ ChannelInfo info = this.channelList.get(channel.getId());
+
+ if (info == null) {
+ info = poll();
+ info.channel = channel;
+ info.flags = flag;
+ this.channelList.put(channel.getId(), info);
+ } else {
+ info.flags = ChannelInfo.merge(info.flags, flag);
+ }
+ // Setting the channel timeout
+ info.timeout = date;
+
+ final NioChannel ch = channel;
+
+ if (info.resume()) {
+ remove(info);
+ if (!processChannel(ch, SocketStatus.OPEN_CALLBACK)) {
+ closeChannel(ch);
+ }
+ } else if (info.read()) {
+ try {
+ // Trying awaiting for read event
+ ch.awaitRead(ch, getCompletionHandler());
+ } catch (Exception e) {
+ // Ignore
+ if (logger.isDebugEnabled()) {
+ logger.debug(e.getMessage(), e);
+ }
+ }
+ } else if (info.write()) {
+ remove(info);
+ if (!processChannel(ch, SocketStatus.OPEN_WRITE)) {
+ closeChannel(ch);
+ }
+ } else if (info.wakeup()) {
+ remove(info);
+ // TODO
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Unknown Event");
+ }
+ remove(info);
+ if (!processChannel(ch, SocketStatus.ERROR)) {
+ closeChannel(ch);
+ }
+ }
+
+ // Wake up all waiting threads
+ synchronized (this.mutex) {
+ this.mutex.notifyAll();
+ }
+ return true;
+ }
+ }
+
+ /**
+ * {@code DefaultThreadFactory}
+ *
+ * The default thread factory
+ *
+ * Created on Mar 6, 2012 at 9:11:20 AM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+ protected static class DefaultThreadFactory implements ThreadFactory {
+ private static final AtomicInteger poolNumber = new AtomicInteger(1);
+ private final ThreadGroup group;
+ private final AtomicInteger threadNumber = new AtomicInteger(1);
+ private final String namePrefix;
+ private final int threadPriority;
+
+ /**
+ * Create a new instance of {@code DefaultThreadFactory}
+ *
+ * @param namePrefix
+ * @param threadPriority
+ */
+ public DefaultThreadFactory(String namePrefix, int threadPriority) {
+ SecurityManager s = System.getSecurityManager();
+ group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
+ this.namePrefix = namePrefix;
+ this.threadPriority = threadPriority;
+ }
+
+ /**
+ *
+ * Create a new instance of {@code DefaultThreadFactory}
+ *
+ * @param threadPriority
+ */
+ public DefaultThreadFactory(int threadPriority) {
+ this("pool-" + poolNumber.getAndIncrement() + "-thread-",
threadPriority);
+ }
+
+ /**
+ * Create and return a new thread
+ */
+ public Thread newThread(Runnable r) {
+ Thread thread = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
+ if (thread.isDaemon())
+ thread.setDaemon(false);
+
+ if (thread.getPriority() != this.threadPriority)
+ thread.setPriority(this.threadPriority);
+ return thread;
+ }
+ }
+
+ /**
+ * SendfileData class.
+ */
+ public static class SendfileData {
+ // File
+ protected String fileName;
+ // Range information
+ protected long start;
+ protected long end;
+ // The channel
+ protected NioChannel channel;
+ // The file channel
+ protected java.nio.channels.FileChannel fileChannel;
+ // Position
+ protected long pos;
+ // KeepAlive flag
+ protected boolean keepAlive;
+
+ /**
+ * Prepare the {@code SendfileData}
+ *
+ * @throws IOException
+ *
+ * @throws Exception
+ */
+ protected void setup() throws IOException {
+ this.pos = this.start;
+ if (this.fileChannel == null || !this.fileChannel.isOpen()) {
+ java.nio.file.Path path = new File(this.fileName).toPath();
+ this.fileChannel = java.nio.channels.FileChannel
+ .open(path, StandardOpenOption.READ).position(this.pos);
+ }
+ }
+
+ /**
+ * Recycle this {@code SendfileData}
+ */
+ protected void recycle() {
+ this.start = 0;
+ this.end = 0;
+ this.pos = 0;
+ this.channel = null;
+ this.keepAlive = false;
+ if (this.fileChannel != null && this.fileChannel.isOpen()) {
+ try {
+ this.fileChannel.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ this.fileChannel = null;
+ }
+
+ /**
+ * Getter for fileName
+ *
+ * @return the fileName
+ */
+ public String getFileName() {
+ return this.fileName;
+ }
+
+ /**
+ * Setter for the fileName
+ *
+ * @param fileName
+ * the fileName to set
+ */
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ /**
+ * Getter for start
+ *
+ * @return the start
+ */
+ public long getStart() {
+ return this.start;
+ }
+
+ /**
+ * Setter for the start
+ *
+ * @param start
+ * the start to set
+ */
+ public void setStart(long start) {
+ this.start = start;
+ }
+
+ /**
+ * Getter for end
+ *
+ * @return the end
+ */
+ public long getEnd() {
+ return this.end;
+ }
+
+ /**
+ * Setter for the end
+ *
+ * @param end
+ * the end to set
+ */
+ public void setEnd(long end) {
+ this.end = end;
+ }
+
+ /**
+ * Getter for channel
+ *
+ * @return the channel
+ */
+ public NioChannel getChannel() {
+ return this.channel;
+ }
+
+ /**
+ * Setter for the channel
+ *
+ * @param channel
+ * the channel to set
+ */
+ public void setChannel(NioChannel channel) {
+ this.channel = channel;
+ }
+
+ /**
+ * Getter for pos
+ *
+ * @return the pos
+ */
+ public long getPos() {
+ return this.pos;
+ }
+
+ /**
+ * Setter for the pos
+ *
+ * @param pos
+ * the pos to set
+ */
+ public void setPos(long pos) {
+ this.pos = pos;
+ }
+
+ /**
+ * Getter for keepAlive
+ *
+ * @return the keepAlive
+ */
+ public boolean isKeepAlive() {
+ return this.keepAlive;
+ }
+
+ /**
+ * Setter for the keepAlive
+ *
+ * @param keepAlive
+ * the keepAlive to set
+ */
+ public void setKeepAlive(boolean keepAlive) {
+ this.keepAlive = keepAlive;
+ }
+ }
+
+ /**
+ * {@code Sendfile}
+ *
+ * Created on Mar 7, 2012 at 4:04:59 PM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+ public class Sendfile implements Runnable {
+
+ protected int size;
+ protected ConcurrentLinkedQueue<SendfileData> fileDatas;
+ protected ConcurrentLinkedQueue<SendfileData> recycledFileDatas;
+ protected AtomicInteger counter;
+ private Object mutex;
+
+ /**
+ * @return the number of send file
+ */
+ public int getSendfileCount() {
+ return this.counter.get();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Runnable#run()
+ */
+ @Override
+ public void run() {
+
+ while (running) {
+ // Loop if endpoint is paused
+ while (paused) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ // Loop while poller is empty
+ while (this.counter.get() < 1 && running && !paused) {
+ try {
+ synchronized (this.mutex) {
+ this.mutex.wait();
+ }
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+
+ if (running && !paused) {
+ try {
+ SendfileData data = this.poll();
+ if (data != null) {
+ sendFile(data);
+ }
+ } catch (Throwable th) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+ /**
+ * Initialize the {@code Sendfile}
+ */
+ protected void init() {
+ this.size = maxThreads;
+ this.mutex = new Object();
+ this.counter = new AtomicInteger(0);
+ this.fileDatas = new ConcurrentLinkedQueue<>();
+ this.recycledFileDatas = new ConcurrentLinkedQueue<>();
+ }
+
+ /**
+ * Destroy the SendFile
+ */
+ protected void destroy() {
+ synchronized (this.mutex) {
+ // To unlock the
+ this.counter.incrementAndGet();
+ this.fileDatas.clear();
+ this.recycledFileDatas.clear();
+ // Unlock threads waiting for this monitor
+ this.mutex.notifyAll();
+ }
+ }
+
+ /**
+ * @param data
+ */
+ public void recycleSendfileData(SendfileData data) {
+ data.recycle();
+ this.recycledFileDatas.offer(data);
+ }
+
+ /**
+ * Poll the head of the recycled object list if it is not empty, else
+ * create a new one.
+ *
+ * @return a {@code SendfileData}
+ */
+ public SendfileData getSendfileData() {
+ SendfileData data = this.recycledFileDatas.poll();
+ return data == null ? new SendfileData() : data;
+ }
+
+ /**
+ *
+ * @param data
+ * @throws Exception
+ */
+ private void sendFile(final SendfileData data) throws Exception {
+
+ // Configure the send file data
+ data.setup();
+
+ final NioChannel channel = data.channel;
+ final int BUFFER_SIZE = channel.getOption(StandardSocketOptions.SO_SNDBUF);
+ final ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
+ int nr = data.fileChannel.read(buffer);
+
+ if (nr >= 0) {
+ buffer.flip();
+ try {
+ channel.write(buffer, data, new CompletionHandler<Integer, SendfileData>() {
+
+ @Override
+ public void completed(Integer nw, SendfileData attachment) {
+ if (nw < 0) { // Reach the end of stream
+ closeChannel(channel);
+ closeFile(attachment.fileChannel);
+ return;
+ }
+
+ attachment.pos += nw;
+
+ if (attachment.pos >= attachment.end) {
+ // All requested bytes were sent, recycle it
+ recycleSendfileData(attachment);
+ return;
+ }
+
+ boolean ok = true;
+
+ if (!buffer.hasRemaining()) {
+ // This means that all data in the buffer has
+ // been
+ // written => Empty the buffer and read again
+ buffer.clear();
+ try {
+ if (attachment.fileChannel.read(buffer) >= 0) {
+ buffer.flip();
+ } else {
+ // Reach the EOF
+ ok = false;
+ }
+ } catch (Throwable th) {
+ ok = false;
+ }
+ }
+
+ if (ok) {
+ channel.write(buffer, attachment, this);
+ } else {
+ closeFile(attachment.fileChannel);
+ }
+ }
+
+ @Override
+ public void failed(Throwable exc, SendfileData attachment) {
+ // Closing channels
+ closeChannel(channel);
+ closeFile(data.fileChannel);
+ }
+
+ /**
+ *
+ * @param closeable
+ */
+ private void closeFile(java.io.Closeable closeable) {
+ try {
+ closeable.close();
+ } catch (IOException e) {
+ // NOPE
+ }
+ }
+ });
+ } catch (WritePendingException exp) {
+ data.fileChannel.close();
+ add(data);
+ }
+ } else {
+ recycleSendfileData(data);
+ }
+ }
+
+ /**
+ * Add the sendfile data to the sendfile poller. Note that in most
+ * cases, the initial non blocking calls to sendfile will return right
+ * away, and will be handled asynchronously inside the kernel. As a
+ * result, the poller will never be used.
+ *
+ * @param data
+ * containing the reference to the data which should be sent
+ * @return true if all the data has been sent right away, and false
+ * otherwise
+ */
+ public boolean add(SendfileData data) {
+ if (data != null && this.counter.get() < this.size) {
+ synchronized (this.mutex) {
+ if (this.fileDatas.offer(data)) {
+ this.counter.incrementAndGet();
+ this.mutex.notifyAll();
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Retrieves and removes the head of this queue, or returns
+ * <tt>null</tt> if this queue is empty.
+ *
+ * @return the head of this queue, or <tt>null</tt> if this queue is
+ * empty
+ */
+ protected SendfileData poll() {
+ SendfileData data = this.fileDatas.poll();
+ if (data != null) {
+ this.counter.decrementAndGet();
+ }
+ return data;
+ }
+
+ /**
+ * Remove socket from the poller.
+ *
+ * @param data
+ * the sendfile data which should be removed
+ */
+ protected void remove(SendfileData data) {
+ if (this.fileDatas.remove(data)) {
+ this.counter.decrementAndGet();
+ }
+ }
+ }
+}
Added: trunk/java/org/apache/tomcat/util/net/NioServerSocketChannelFactory.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/NioServerSocketChannelFactory.java
(rev 0)
+++ trunk/java/org/apache/tomcat/util/net/NioServerSocketChannelFactory.java 2012-06-13
08:50:17 UTC (rev 2041)
@@ -0,0 +1,260 @@
+/**
+ * JBoss, Home of Professional Open Source. Copyright 2012, Red Hat, Inc., and
+ * individual contributors as indicated by the @author tags. See the
+ * copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
+ * site:
http://www.fsf.org.
+ */
+package
org.apache.tomcat.util.net;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.nio.channels.AsynchronousChannelGroup;
+import java.nio.channels.AsynchronousServerSocketChannel;
+import java.util.Hashtable;
+
+import org.apache.tomcat.util.net.jsse.NioJSSESocketChannelFactory;
+
+/**
+ * {@code NioServerSocketChannelFactory}
+ *
+ * Created on Jan 3, 2012 at 12:08:30 PM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public abstract class NioServerSocketChannelFactory implements Cloneable {
+
+ protected static org.jboss.logging.Logger log = org.jboss.logging.Logger
+ .getLogger(NioServerSocketChannelFactory.class);
+
+ private static NioServerSocketChannelFactory theFactory;
+ protected Hashtable<String, Object> attributes = new Hashtable<String,
Object>();
+
+ protected AsynchronousChannelGroup threadGroup;
+
+ /**
+ * Create a new instance of {@code NioServerSocketChannelFactory}
+ */
+ protected NioServerSocketChannelFactory() {
+ /* NOTHING */
+ }
+
+ /**
+ * Create a new instance of {@code NioServerSocketChannelFactory}
+ *
+ * @param threadGroup
+ */
+ protected NioServerSocketChannelFactory(AsynchronousChannelGroup threadGroup) {
+ this.threadGroup = threadGroup;
+ }
+
+ /**
+ * Initialize the factory
+ *
+ * @throws IOException
+ */
+ public abstract void init() throws IOException;
+
+ /**
+ * Destroy the factory
+ *
+ * @throws IOException
+ */
+ public abstract void destroy() throws IOException;
+
+ /**
+ * General mechanism to pass attributes from the ServerConnector to the
+ * socket factory.
+ *
+ * Note that the "preferred" mechanism is to use bean setters and explicit
+ * methods, but this allows easy configuration via server.xml or simple
+ * Properties
+ *
+ * @param name
+ * @param value
+ */
+ public void setAttribute(String name, Object value) {
+ if (name != null && value != null) {
+ attributes.put(name, value);
+ }
+ }
+
+ /**
+ * Returns a copy of the environment's default socket factory.
+ *
+ * @return the default factory
+ */
+ public static synchronized NioServerSocketChannelFactory getDefault() {
+ return getDefault(null);
+ }
+
+ /**
+ * Returns a copy of the environment's default socket factory.
+ *
+ * @param threadGroup
+ * @return the default factory
+ */
+ public static synchronized NioServerSocketChannelFactory getDefault(
+ AsynchronousChannelGroup threadGroup) {
+ //
+ // optimize typical case: no synch needed
+ //
+
+ if (theFactory == null) {
+ //
+ // Different implementations of this method could
+ // work rather differently. For example, driving
+ // this from a system property, or using a different
+ // implementation than JavaSoft's.
+ //
+
+ theFactory = new DefaultNioServerSocketChannelFactory(threadGroup);
+ }
+
+ try {
+ return (NioServerSocketChannelFactory) theFactory.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+ /**
+ * Create and configure a new secure {@code NioServerSocketChannelFactory}
+ * instance
+ *
+ * @param threadGroup
+ * the thread group that will be used with the server sockets
+ * @return a new secure {@code NioServerSocketChannelFactory} instance
+ */
+ public static synchronized NioServerSocketChannelFactory createSecureFactory(
+ AsynchronousChannelGroup threadGroup) {
+ return new NioJSSESocketChannelFactory(threadGroup);
+ }
+
+ /**
+ * Create a new {@code NioServerSocketChannelFactory} instance
+ *
+ * @param threadGroup
+ * the thread group that will be used with the server sockets
+ * @param secure
+ * @return a new secure {@code NioServerSocketChannelFactory} instance
+ */
+ public static synchronized NioServerSocketChannelFactory
createServerSocketChannelFactory(
+ AsynchronousChannelGroup threadGroup, boolean secure) {
+ return secure ? createSecureFactory(threadGroup) : getDefault(threadGroup);
+ }
+
+ /**
+ * Returns a server socket channel which uses all network interfaces on the
+ * host, and is bound to a the specified port. The socket is configured with
+ * the socket options (such as accept timeout) given to this factory.
+ *
+ * @param port
+ * the port to listen to
+ * @return an instance of
+ * {@link java.nio.channels.AsynchronousServerSocketChannel}
+ * @exception IOException
+ * for networking errors
+ */
+ public AsynchronousServerSocketChannel createServerChannel(int port) throws IOException
{
+ return createServerChannel(port, -1);
+ }
+
+ /**
+ * Returns a server socket which uses all network interfaces on the host, is
+ * bound to a the specified port, and uses the specified connection backlog.
+ * The socket is configured with the socket options (such as accept timeout)
+ * given to this factory.
+ *
+ * @param port
+ * the port to listen to
+ * @param backlog
+ * how many connections are queued
+ * @return an instance of
+ * {@link java.nio.channels.AsynchronousServerSocketChannel}
+ * @exception IOException
+ * for networking errors
+ */
+ public AsynchronousServerSocketChannel createServerChannel(int port, int backlog)
+ throws IOException {
+ return createServerChannel(port, backlog, null, false);
+ }
+
+ /**
+ * Returns a server socket channel which uses only the specified network
+ * interface on the local host, is bound to a the specified port, and uses
+ * the specified connection backlog. The socket is configured with the
+ * socket options (such as accept timeout) given to this factory.
+ *
+ * @param port
+ * the port to listen to
+ * @param backlog
+ * how many connections are queued
+ * @param ifAddress
+ * the network interface address to use
+ * @param reuseAddress
+ * @return an instance of
+ * {@link java.nio.channels.AsynchronousServerSocketChannel}
+ * @exception IOException
+ * for networking errors
+ */
+ public abstract AsynchronousServerSocketChannel createServerChannel(int port, int
backlog,
+ InetAddress ifAddress, boolean reuseAddress) throws IOException;
+
+ /**
+ * Initialize the specified {@code NioChannel}
+ *
+ * @param channel
+ * The channel to be initialized
+ * @throws Exception
+ */
+ public abstract void initChannel(NioChannel channel) throws Exception;
+
+ /**
+ * Wrapper function for accept(). This allows us to trap and translate
+ * exceptions if necessary
+ *
+ * @param listener
+ * The Asynchronous Server Socket channel that will accept a new
+ * connection
+ * @return an instance of {@link NioChannel} representing the new connection
+ *
+ * @exception IOException
+ */
+ public abstract NioChannel acceptChannel(AsynchronousServerSocketChannel listener)
+ throws IOException;
+
+ /**
+ * Extra function to initiate the handshake. Sometimes necessary for SSL
+ *
+ * @param channel
+ *
+ * @exception IOException
+ */
+ public abstract void handshake(NioChannel channel) throws IOException;
+
+ /**
+ * Open an {@link java.nio.channels.AsynchronousServerSocketChannel}
+ *
+ * @return an instance of
+ * {@link java.nio.channels.AsynchronousServerSocketChannel}
+ * @throws IOException
+ */
+ protected AsynchronousServerSocketChannel open() throws IOException {
+ return AsynchronousServerSocketChannel.open(threadGroup);
+ }
+}
Modified: trunk/java/org/apache/tomcat/util/net/SSLImplementation.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/SSLImplementation.java 2012-06-07 13:48:42 UTC
(rev 2040)
+++ trunk/java/org/apache/tomcat/util/net/SSLImplementation.java 2012-06-13 08:50:17 UTC
(rev 2041)
@@ -1,18 +1,18 @@
/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF 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.
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * 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.apache.tomcat.util.net;
@@ -20,67 +20,109 @@
import java.net.Socket;
import javax.net.ssl.SSLSession;
-/* SSLImplementation:
+import org.apache.tomcat.util.net.jsse.NioJSSESocketChannelFactory;
- Abstract factory and base class for all SSL implementations.
-
- @author EKR
-*/
+/**
+ * {@code SSLImplementation}
+ * <p>
+ * Abstract factory and base class for all SSL implementations.
+ * </p>
+ *
+ *
+ * Created on Feb 22, 2012 at 12:55:17 PM
+ *
+ * @author EKR & <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
abstract public class SSLImplementation {
- private static org.jboss.logging.Logger logger =
- org.jboss.logging.Logger.getLogger(SSLImplementation.class);
+ private static org.jboss.logging.Logger logger = org.jboss.logging.Logger
+ .getLogger(SSLImplementation.class);
// The default implementations in our search path
- private static final String JSSEImplementationClass=
- "org.apache.tomcat.util.net.jsse.JSSEImplementation";
-
- private static final String[] implementations=
- {
- JSSEImplementationClass
- };
+ private static final String JSSEImplementationClass =
"org.apache.tomcat.util.net.jsse.JSSEImplementation";
- public static SSLImplementation getInstance() throws ClassNotFoundException
- {
- for(int i=0;i<implementations.length;i++){
- try {
- SSLImplementation impl=
- getInstance(implementations[i]);
- return impl;
- } catch (Exception e) {
- if(logger.isTraceEnabled())
- logger.trace("Error creating " + implementations[i],e);
- }
- }
+ private static final String[] implementations = { JSSEImplementationClass,
+ "org.apache.tomcat.util.net.jsse.NioJSSEImplementation" };
- // If we can't instantiate any of these
- throw new ClassNotFoundException("Can't find any SSL implementation");
+ /**
+ * @return the default implementation of {@code SSLImplementation}
+ * @throws ClassNotFoundException
+ */
+ public static SSLImplementation getInstance() throws ClassNotFoundException {
+ for (int i = 0; i < implementations.length; i++) {
+ try {
+ SSLImplementation impl = getInstance(implementations[i]);
+ return impl;
+ } catch (Exception e) {
+ if (logger.isTraceEnabled())
+ logger.trace("Error creating " + implementations[i], e);
+ }
+ }
+
+ // If we can't instantiate any of these
+ throw new ClassNotFoundException("Can't find any SSL
implementation");
}
- public static SSLImplementation getInstance(String className)
- throws ClassNotFoundException
- {
- if(className==null) return getInstance();
+ /**
+ * Returns the {@code SSLImplementation} specified by the name of it's class
+ *
+ * @param className
+ * @return a new instance of the {@code SSLImplementation} given by it's name
+ * @throws ClassNotFoundException
+ */
+ public static SSLImplementation getInstance(String className) throws
ClassNotFoundException {
+ if (className == null)
+ return getInstance();
- try {
- // Workaround for the J2SE 1.4.x classloading problem (under Solaris).
- // Class.forName(..) fails without creating class using new.
- // This is an ugly workaround.
- if( JSSEImplementationClass.equals(className) ) {
- return new org.apache.tomcat.util.net.jsse.JSSEImplementation();
- }
- Class clazz=Class.forName(className);
- return (SSLImplementation)clazz.newInstance();
- } catch (Exception e){
- if(logger.isDebugEnabled())
- logger.debug("Error loading SSL Implementation "
- +className, e);
- throw new ClassNotFoundException("Error loading SSL Implementation "
- +className+ " :" +e.toString());
- }
+ try {
+ // Workaround for the J2SE 1.4.x classloading problem (under
+ // Solaris).
+ // Class.forName(..) fails without creating class using new.
+ // This is an ugly workaround.
+ if (JSSEImplementationClass.equals(className)) {
+ return new org.apache.tomcat.util.net.jsse.JSSEImplementation();
+ }
+ Class<?> clazz = Class.forName(className);
+ return (SSLImplementation) clazz.newInstance();
+ } catch (Exception e) {
+ if (logger.isDebugEnabled())
+ logger.debug("Error loading SSL Implementation " + className,
e);
+ throw new ClassNotFoundException("Error loading SSL Implementation
" + className + " :"
+ + e.toString());
+ }
}
+ /**
+ * @return the implementation name
+ */
abstract public String getImplementationName();
+
+ /**
+ * @return a new instance of {@link ServerSocketFactory}
+ */
abstract public ServerSocketFactory getServerSocketFactory();
+
+ /**
+ *
+ * @return a new instance of {@link NioJSSESocketChannelFactory}
+ */
+ public abstract NioJSSESocketChannelFactory getServerSocketChannelFactory();
+
+ /**
+ * Return a {@link SSLSupport} attached to the socket
+ * @param sock
+ * @return a {@link SSLSupport} attached to the socket
+ */
abstract public SSLSupport getSSLSupport(Socket sock);
+
+ /**
+ * @param channel
+ * @return the {@link SSLSupport} attached to this channel
+ */
+ public abstract SSLSupport getSSLSupport(NioChannel channel);
+
+ /**
+ * @param session
+ * @return the {@link SSLSupport} attached to this session
+ */
abstract public SSLSupport getSSLSupport(SSLSession session);
-}
+}
\ No newline at end of file
Modified: trunk/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java 2012-06-07 13:48:42
UTC (rev 2040)
+++ trunk/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java 2012-06-13 08:50:17
UTC (rev 2041)
@@ -1,69 +1,126 @@
/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF 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.
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * 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.apache.tomcat.util.net.jsse;
import java.net.Socket;
+import org.apache.tomcat.util.net.NioChannel;
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:
-
- Concrete implementation class for JSSE
-
- @author EKR
-*/
-
-public class JSSEImplementation extends SSLImplementation
-{
+/**
+ * {@code JSSEImplementation}
+ * <p>
+ * Concrete implementation class for JSSE
+ * </p>
+ *
+ *
+ * Created on Feb 22, 2012 at 12:53:14 PM
+ *
+ * @author EKR & <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public class JSSEImplementation extends SSLImplementation {
static final String SSLSocketClass = "javax.net.ssl.SSLSocket";
- static org.jboss.logging.Logger logger =
- org.jboss.logging.Logger.getLogger(JSSEImplementation.class);
+ static org.jboss.logging.Logger logger = org.jboss.logging.Logger
+ .getLogger(JSSEImplementation.class);
private JSSEFactory factory = null;
+ /**
+ * Create a new instance of {@code JSSEImplementation}
+ *
+ * @throws ClassNotFoundException
+ */
public JSSEImplementation() throws ClassNotFoundException {
// Check to see if JSSE is floating around somewhere
Class.forName(SSLSocketClass);
factory = new JSSEFactory();
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.SSLImplementation#getImplementationName()
+ */
+ public String getImplementationName() {
+ return "JSSE";
+ }
- public String getImplementationName(){
- return "JSSE";
- }
-
- public ServerSocketFactory getServerSocketFactory() {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.SSLImplementation#getServerSocketFactory()
+ */
+ public ServerSocketFactory getServerSocketFactory() {
ServerSocketFactory ssf = factory.getSocketFactory();
return ssf;
- }
+ }
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.SSLImplementation#getSSLSupport(java.net.Socket
+ * )
+ */
public SSLSupport getSSLSupport(Socket s) {
SSLSupport ssls = factory.getSSLSupport(s);
return ssls;
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ *
org.apache.tomcat.util.net.SSLImplementation#getSSLSupport(javax.net.
+ * ssl.SSLSession)
+ */
public SSLSupport getSSLSupport(SSLSession session) {
SSLSupport ssls = factory.getSSLSupport(session);
return ssls;
}
-}
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.SSLImplementation#getServerSocketChannelFactory
+ * ()
+ */
+ @Override
+ public NioJSSESocketChannelFactory getServerSocketChannelFactory() {
+ throw new RuntimeException("Not supported for class " +
NioJSSESocketChannelFactory.class.getName());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.SSLImplementation#getSSLSupport(org.apache
+ * .tomcat.util.net.NioChannel)
+ */
+ @Override
+ public SSLSupport getSSLSupport(NioChannel channel) {
+ throw new RuntimeException("Not supported for class " +
NioChannel.class.getName());
+ }
+
+}
\ No newline at end of file
Added: trunk/java/org/apache/tomcat/util/net/jsse/NioJSSEFactory.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/jsse/NioJSSEFactory.java
(rev 0)
+++ trunk/java/org/apache/tomcat/util/net/jsse/NioJSSEFactory.java 2012-06-13 08:50:17 UTC
(rev 2041)
@@ -0,0 +1,65 @@
+/**
+ * JBoss, Home of Professional Open Source. Copyright 2012, Red Hat, Inc., and
+ * individual contributors as indicated by the @author tags. See the
+ * copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
+ * site:
http://www.fsf.org.
+ */
+package org.apache.tomcat.util.net.jsse;
+
+import javax.net.ssl.SSLSession;
+import org.apache.tomcat.util.net.NioChannel;
+
+/**
+ * {@code NioJSSEFactory}
+ *
+ * Created on Feb 22, 2012 at 12:10:48 PM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public class NioJSSEFactory {
+
+ /**
+ * Returns the NioSocketChannelFactory to use.
+ *
+ * @return the NioSocketChannelFactory to use.
+ */
+ public NioJSSESocketChannelFactory getSocketChannelFactory() {
+ return new NioJSSESocketChannelFactory();
+ }
+
+ /**
+ * Returns the SSLSupport attached to this channel.
+ *
+ * @param channel
+ * @return the SSLSupport attached to this channel
+ */
+ public NioJSSESupport getSSLSupport(NioChannel channel) {
+ return new NioJSSESupport((SecureNioChannel) channel);
+ }
+
+ /**
+ * Return the SSLSupport attached to this session
+ *
+ * @param session
+ * @return the SSLSupport attached to this session
+ */
+ public NioJSSESupport getSSLSupport(SSLSession session) {
+ return new NioJSSESupport(session);
+ }
+
+}
Added: trunk/java/org/apache/tomcat/util/net/jsse/NioJSSEImplementation.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/jsse/NioJSSEImplementation.java
(rev 0)
+++ trunk/java/org/apache/tomcat/util/net/jsse/NioJSSEImplementation.java 2012-06-13
08:50:17 UTC (rev 2041)
@@ -0,0 +1,126 @@
+/**
+ * JBoss, Home of Professional Open Source. Copyright 2012, Red Hat, Inc., and
+ * individual contributors as indicated by the @author tags. See the
+ * copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
+ * site:
http://www.fsf.org.
+ */
+package org.apache.tomcat.util.net.jsse;
+
+import java.net.Socket;
+
+import javax.net.ssl.SSLSession;
+
+import org.apache.tomcat.util.net.NioChannel;
+import org.apache.tomcat.util.net.SSLImplementation;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.ServerSocketFactory;
+
+/**
+ * {@code NioJSSEImplementation}
+ *
+ * Created on Feb 22, 2012 at 12:41:08 PM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public class NioJSSEImplementation extends SSLImplementation {
+
+ static final String SSLClass = "javax.net.ssl.SSLEngine";
+
+ static org.jboss.logging.Logger logger = org.jboss.logging.Logger
+ .getLogger(NioJSSEImplementation.class);
+
+ private NioJSSEFactory factory = null;
+
+ /**
+ * Create a new instance of {@code NioJSSEImplementation}
+ *
+ * @throws ClassNotFoundException
+ */
+ public NioJSSEImplementation() throws ClassNotFoundException {
+ // Check to see if JSSE is floating around somewhere
+ Class.forName(SSLClass);
+ factory = new NioJSSEFactory();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.SSLImplementation#getImplementationName()
+ */
+ public String getImplementationName() {
+ return "JSSE";
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.SSLImplementation#getServerSocketChannelFactory
+ * ()
+ */
+ public NioJSSESocketChannelFactory getServerSocketChannelFactory() {
+ NioJSSESocketChannelFactory ssf = factory.getSocketChannelFactory();
+ return ssf;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.SSLImplementation#getSSLSupport(org.apache
+ * .tomcat.util.net.NioChannel)
+ */
+ public SSLSupport getSSLSupport(NioChannel channel) {
+ return factory.getSSLSupport(channel);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ *
org.apache.tomcat.util.net.SSLImplementation#getSSLSupport(javax.net.
+ * ssl.SSLSession)
+ */
+ public SSLSupport getSSLSupport(SSLSession session) {
+ return factory.getSSLSupport(session);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.SSLImplementation#getServerSocketFactory()
+ */
+ @Override
+ public ServerSocketFactory getServerSocketFactory() {
+ throw new RuntimeException("Not supported for class " +
ServerSocketFactory.class.getName());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.SSLImplementation#getSSLSupport(java.net.Socket
+ * )
+ */
+ @Override
+ public SSLSupport getSSLSupport(Socket sock) {
+ throw new RuntimeException("Not supported for class " +
Socket.class.getName());
+ }
+
+}
Added: trunk/java/org/apache/tomcat/util/net/jsse/NioJSSESocketChannelFactory.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/jsse/NioJSSESocketChannelFactory.java
(rev 0)
+++ trunk/java/org/apache/tomcat/util/net/jsse/NioJSSESocketChannelFactory.java 2012-06-13
08:50:17 UTC (rev 2041)
@@ -0,0 +1,831 @@
+/**
+ * JBoss, Home of Professional Open Source. Copyright 2012, Red Hat, Inc., and
+ * individual contributors as indicated by the @author tags. See the
+ * copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
+ * site:
http://www.fsf.org.
+ */
+package org.apache.tomcat.util.net.jsse;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.nio.channels.AsynchronousChannelGroup;
+import java.nio.channels.AsynchronousServerSocketChannel;
+import java.nio.channels.AsynchronousSocketChannel;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.cert.CRL;
+import java.security.cert.CRLException;
+import java.security.cert.CertPathParameters;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreParameters;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.X509CertSelector;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.Vector;
+
+import javax.net.ssl.CertPathTrustManagerParameters;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.ManagerFactoryParameters;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509KeyManager;
+
+import org.apache.tomcat.util.net.DefaultNioServerSocketChannelFactory;
+import org.apache.tomcat.util.net.NioChannel;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * {@code NioJSSESocketChannelFactory}
+ *
+ * <p>
+ * SSL server socket factory. It _requires_ a valid RSA key and JSSE.
+ * </p>
+ * <p>
+ * 1. Make the JSSE's jars available, either as an installed extension (copy
+ * them into jre/lib/ext) or by adding them to the Tomcat classpath. 2. keytool
+ * -genkey -alias tomcat -keyalg RSA Use "changeit" as password ( this is the
+ * default we use )
+ * </p>
+ *
+ * Created on Jan 3, 2012 at 2:07:00 PM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public class NioJSSESocketChannelFactory extends DefaultNioServerSocketChannelFactory {
+
+ private static StringManager sm = StringManager
+ .getManager("org.apache.tomcat.util.net.jsse.res");
+ private static final boolean RFC_5746_SUPPORTED;
+ // defaults
+ private static final String defaultProtocol = "TLS";
+ static boolean defaultClientAuth = false;
+ static String defaultKeystoreType = "JKS";
+ private static final String defaultKeystoreFile =
System.getProperty("user.home")
+ + "/.keystore";
+ private static final String defaultKeyPass = "changeit";
+ private static final int defaultSessionCacheSize = 0;
+ private static final int defaultSessionTimeout = 86400;
+
+ // private static SSLContext context;
+ static {
+ boolean result = false;
+ try {
+ SSLContext context = SSLContext.getInstance(defaultProtocol);
+ context.init(null, null, new SecureRandom());
+ SSLServerSocketFactory ssf = context.getServerSocketFactory();
+ String ciphers[] = ssf.getSupportedCipherSuites();
+ for (String cipher : ciphers) {
+ if ("TLS_EMPTY_RENEGOTIATION_INFO_SCSV".equals(cipher)) {
+ result = true;
+ break;
+ }
+ }
+ } catch (NoSuchAlgorithmException | KeyManagementException e) {
+ // Assume no RFC 5746 support
+ }
+ RFC_5746_SUPPORTED = result;
+ }
+
+ protected boolean initialized;
+ private SSLContext sslContext;
+ protected String clientAuth = "false";
+ protected String[] enabledCiphers;
+ protected boolean allowUnsafeLegacyRenegotiation = false;
+
+ /**
+ * Flag to state that we require client authentication.
+ */
+ protected boolean requireClientAuth = false;
+
+ /**
+ * Flag to state that we would like client authentication.
+ */
+ protected boolean wantClientAuth = false;
+
+ /**
+ * Create a new instance of {@code NioJSSESocketChannelFactory}
+ */
+ public NioJSSESocketChannelFactory() {
+ super();
+ }
+
+ /**
+ * Create a new instance of {@code NioJSSESocketChannelFactory}
+ *
+ * @param threadGroup
+ */
+ public NioJSSESocketChannelFactory(AsynchronousChannelGroup threadGroup) {
+ super(threadGroup);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.DefaultNioServerSocketChannelFactory#acceptChannel
+ * (java.nio.channels.AsynchronousServerSocketChannel)
+ */
+ @Override
+ public NioChannel acceptChannel(AsynchronousServerSocketChannel listener) throws
IOException {
+ try {
+ AsynchronousSocketChannel asyncChannel = listener.accept().get();
+ InetSocketAddress addr = (InetSocketAddress) asyncChannel.getRemoteAddress();
+ SSLEngine engine = sslContext.createSSLEngine(addr.getHostString(), addr.getPort());
+ NioChannel channel = new SecureNioChannel(asyncChannel, engine);
+ return channel;
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.NioServerSocketChannelFactory#initChannel(
+ * org.apache.tomcat.util.net.NioChannel)
+ */
+ public void initChannel(NioChannel channel) throws Exception {
+ SecureNioChannel sslChannel = (SecureNioChannel) channel;
+ initSSLEngine(sslChannel.getSslEngine());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.NioServerSocketChannelFactory#handshake(java
+ * .nio.channels.AsynchronousSocketChannel)
+ */
+ @Override
+ public void handshake(NioChannel channel) throws IOException {
+ // We do getSession instead of startHandshake() so we can call this
+ // multiple times
+ SecureNioChannel sslChannel = (SecureNioChannel) channel;
+
+ if (sslChannel.handshakeComplete()) {
+ // The handshake was already done
+ return;
+ }
+
+ SSLEngine engine = sslChannel.getSslEngine();
+
+ if (!allowUnsafeLegacyRenegotiation && !RFC_5746_SUPPORTED) {
+ // Prevent further handshakes by removing all cipher suites
+ engine.setEnabledCipherSuites(new String[0]);
+ }
+ sslChannel.handshake();
+
+ if
(sslChannel.getSSLSession().getCipherSuite().equals("SSL_NULL_WITH_NULL_NULL"))
{
+ throw new IOException(
+ "SSL handshake failed. Ciper suite in SSL Session is
SSL_NULL_WITH_NULL_NULL");
+ }
+ }
+
+ /**
+ *
+ * @param ctx
+ */
+ public void setSslContext(SSLContext ctx) {
+ sslContext = ctx;
+ }
+
+ /**
+ * @return the SSLContext
+ */
+ public SSLContext getSslContext() {
+ return sslContext;
+ }
+
+ /**
+ * Reads the keystore and initializes the SSL socket factory.
+ *
+ * @throws IOException
+ */
+ @Override
+ public void init() throws IOException {
+ try {
+ String clientAuthStr = (String) attributes.get("clientauth");
+ if ("true".equalsIgnoreCase(clientAuthStr) ||
"yes".equalsIgnoreCase(clientAuthStr)) {
+ requireClientAuth = true;
+ } else if ("want".equalsIgnoreCase(clientAuthStr)) {
+ wantClientAuth = true;
+ }
+
+ // SSL protocol variant (e.g., TLS, SSL v3, etc.)
+ String protocol = (String) attributes.get("protocol");
+ if (protocol == null) {
+ protocol = defaultProtocol;
+ }
+
+ // Certificate encoding algorithm (e.g., SunX509)
+ String algorithm = (String) attributes.get("algorithm");
+ if (algorithm == null) {
+ algorithm = KeyManagerFactory.getDefaultAlgorithm();
+ }
+
+ String keystoreType = (String) attributes.get("keystoreType");
+ if (keystoreType == null) {
+ keystoreType = defaultKeystoreType;
+ }
+
+ String keystoreProvider = (String) attributes.get("keystoreProvider");
+ String trustAlgorithm = (String) attributes.get("truststoreAlgorithm");
+
+ if (trustAlgorithm == null) {
+ trustAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
+ }
+
+ // Create and initialize SSLContext
+ sslContext = (SSLContext) attributes.get("SSLContext");
+ if (sslContext == null) {
+ sslContext = SSLContext.getInstance(protocol);
+ sslContext.init(
+ getKeyManagers(keystoreType, keystoreProvider, algorithm,
+ (String) attributes.get("keyAlias")),
+ getTrustManagers(keystoreType, keystoreProvider, trustAlgorithm),
+ new SecureRandom());
+ }
+
+ // Configure SSL session cache
+ int sessionCacheSize;
+ if (attributes.get("sessionCacheSize") != null) {
+ sessionCacheSize = Integer.parseInt((String)
attributes.get("sessionCacheSize"));
+ } else {
+ sessionCacheSize = defaultSessionCacheSize;
+ }
+ int sessionCacheTimeout;
+ if (attributes.get("sessionCacheTimeout") != null) {
+ sessionCacheTimeout = Integer.parseInt((String) attributes
+ .get("sessionCacheTimeout"));
+ } else {
+ sessionCacheTimeout = defaultSessionTimeout;
+ }
+ SSLSessionContext sessionContext = sslContext.getServerSessionContext();
+ if (sessionContext != null) {
+ sessionContext.setSessionCacheSize(sessionCacheSize);
+ sessionContext.setSessionTimeout(sessionCacheTimeout);
+ }
+
+ // create proxy
+ SSLServerSocketFactory sslProxy = sslContext.getServerSocketFactory();
+
+ // Determine which cipher suites to enable
+ String requestedCiphers = (String) attributes.get("ciphers");
+ enabledCiphers = getEnabledCiphers(requestedCiphers,
+ sslProxy.getSupportedCipherSuites());
+
+ allowUnsafeLegacyRenegotiation = "true".equals(attributes
+ .get("allowUnsafeLegacyRenegotiation"));
+
+ // Check that the SSL configuration is OK
+ checkConfig();
+
+ } catch (Exception e) {
+ throw new IOException(e.getMessage(), e);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.DefaultNioServerSocketChannelFactory#destroy()
+ */
+ @Override
+ public void destroy() throws IOException {
+ super.destroy();
+ this.sslContext = null;
+ }
+
+ /**
+ * Determines the SSL cipher suites to be enabled.
+ *
+ * @param requestedCiphers
+ * Comma-separated list of requested ciphers
+ *
+ * @param supportedCiphers
+ * Array of supported ciphers
+ *
+ * @return Array of SSL cipher suites to be enabled, or null if none of the
+ * requested ciphers are supported
+ */
+ protected String[] getEnabledCiphers(String requestedCiphers, String[] supportedCiphers)
{
+
+ String[] enabledCiphers = null;
+ SSLServerSocketFactory sslProxy = sslContext.getServerSocketFactory();
+ if (requestedCiphers != null) {
+ Vector<Object> vec = null;
+ String cipher = requestedCiphers;
+ int index = requestedCiphers.indexOf(',');
+ if (index != -1) {
+ int fromIndex = 0;
+ while (index != -1) {
+ cipher = requestedCiphers.substring(fromIndex, index).trim();
+ if (cipher.length() > 0) {
+ /*
+ * Check to see if the requested cipher is among the
+ * supported ciphers, i.e., may be enabled
+ */
+ for (int i = 0; supportedCiphers != null && i < supportedCiphers.length;
i++) {
+ if (supportedCiphers[i].equals(cipher)) {
+ if (vec == null) {
+ vec = new Vector<>();
+ }
+ vec.addElement(cipher);
+ break;
+ }
+ }
+ }
+ fromIndex = index + 1;
+ index = requestedCiphers.indexOf(',', fromIndex);
+ } // while
+ cipher = requestedCiphers.substring(fromIndex);
+ }
+
+ if (cipher != null) {
+ cipher = cipher.trim();
+ if (cipher.length() > 0) {
+ /*
+ * Check to see if the requested cipher is among the
+ * supported ciphers, i.e., may be enabled
+ */
+ for (int i = 0; supportedCiphers != null && i < supportedCiphers.length;
i++) {
+ if (supportedCiphers[i].equals(cipher)) {
+ if (vec == null) {
+ vec = new Vector<>();
+ }
+ vec.addElement(cipher);
+ break;
+ }
+ }
+ }
+ }
+
+ if (vec != null) {
+ enabledCiphers = new String[vec.size()];
+ vec.copyInto(enabledCiphers);
+ }
+ } else {
+ enabledCiphers = sslProxy.getDefaultCipherSuites();
+ }
+
+ return enabledCiphers;
+ }
+
+ /**
+ * Gets the SSL server's keystore.
+ *
+ * @param type
+ * the type of the keystore
+ * @param provider
+ * the keystore provider
+ * @param pass
+ * the keystore password
+ * @return the SSL server's keystore
+ * @throws IOException
+ */
+ protected KeyStore getKeystore(String type, String provider, String pass) throws
IOException {
+
+ String keystoreFile = (String) attributes.get("keystore");
+ if (keystoreFile == null)
+ keystoreFile = defaultKeystoreFile;
+
+ return getStore(type, provider, keystoreFile, pass);
+ }
+
+ /**
+ * Gets the SSL server's keystore password.
+ *
+ * @return the keystore password
+ */
+ protected String getKeystorePassword() {
+ String keyPass = (String) attributes.get("keypass");
+ if (keyPass == null) {
+ keyPass = defaultKeyPass;
+ }
+ String keystorePass = (String) attributes.get("keystorePass");
+ if (keystorePass == null) {
+ keystorePass = keyPass;
+ }
+ return keystorePass;
+ }
+
+ /**
+ * Gets the key- or truststore with the specified type, path, and password.
+ *
+ * @param type
+ * @param provider
+ * @param path
+ * @param pass
+ * @return
+ * @throws IOException
+ */
+ private KeyStore getStore(String type, String provider, String path, String pass)
+ throws IOException {
+
+ KeyStore ks = null;
+ InputStream istream = null;
+ try {
+ if (provider == null) {
+ ks = KeyStore.getInstance(type);
+ } else {
+ ks = KeyStore.getInstance(type, provider);
+ }
+ if (!("PKCS11".equalsIgnoreCase(type) ||
"".equalsIgnoreCase(path))) {
+ File keyStoreFile = new File(path);
+ if (!keyStoreFile.isAbsolute()) {
+ keyStoreFile = new File(System.getProperty("catalina.base"), path);
+ }
+ istream = new FileInputStream(keyStoreFile);
+ }
+
+ char[] storePass = null;
+ if (pass != null) {
+ storePass = pass.toCharArray();
+ }
+ ks.load(istream, storePass);
+ } catch (FileNotFoundException fnfe) {
+ log.error(sm.getString("jsse.keystore_load_failed", type, path,
fnfe.getMessage()),
+ fnfe);
+ throw fnfe;
+ } catch (IOException ioe) {
+ log.error(sm.getString("jsse.keystore_load_failed", type, path,
ioe.getMessage()), ioe);
+ throw ioe;
+ } catch (Exception ex) {
+ String msg = sm.getString("jsse.keystore_load_failed", type, path,
ex.getMessage());
+ log.error(msg, ex);
+ throw new IOException(msg);
+ } finally {
+ if (istream != null) {
+ try {
+ istream.close();
+ } catch (IOException ioe) {
+ // Do nothing
+ }
+ }
+ }
+
+ return ks;
+ }
+
+ /**
+ * Gets the initialized key managers.
+ *
+ * @param keystoreType
+ * @param keystoreProvider
+ * @param algorithm
+ * @param keyAlias
+ * @return
+ * @throws Exception
+ */
+ protected KeyManager[] getKeyManagers(String keystoreType, String keystoreProvider,
+ String algorithm, String keyAlias) throws Exception {
+
+ KeyManager[] kms = null;
+
+ String keystorePass = getKeystorePassword();
+
+ KeyStore ks = getKeystore(keystoreType, keystoreProvider, keystorePass);
+ if (keyAlias != null && !ks.isKeyEntry(keyAlias)) {
+ throw new IOException(sm.getString("jsse.alias_no_key_entry", keyAlias));
+ }
+
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
+ kmf.init(ks, keystorePass.toCharArray());
+
+ kms = kmf.getKeyManagers();
+ if (keyAlias != null) {
+ if (defaultKeystoreType.equals(keystoreType)) {
+ keyAlias = keyAlias.toLowerCase(Locale.ENGLISH);
+ }
+ for (int i = 0; i < kms.length; i++) {
+ kms[i] = new JSSEKeyManager((X509KeyManager) kms[i], keyAlias);
+ }
+ }
+
+ return kms;
+ }
+
+ /**
+ * Gets the initialized trust managers.
+ *
+ * @param keystoreType
+ * @param keystoreProvider
+ * @param algorithm
+ * @return
+ * @throws Exception
+ */
+ protected TrustManager[] getTrustManagers(String keystoreType, String keystoreProvider,
+ String algorithm) throws Exception {
+ String crlf = (String) attributes.get("crlFile");
+
+ TrustManager[] tms = null;
+
+ KeyStore trustStore = getTrustStore(keystoreType, keystoreProvider);
+ if (trustStore != null) {
+ if (crlf == null) {
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
+ tmf.init(trustStore);
+ tms = tmf.getTrustManagers();
+ } else {
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
+ CertPathParameters params = getParameters(algorithm, crlf, trustStore);
+ ManagerFactoryParameters mfp = new CertPathTrustManagerParameters(params);
+ tmf.init(mfp);
+ tms = tmf.getTrustManagers();
+ }
+ }
+
+ return tms;
+ }
+
+ /**
+ * Gets the SSL server's truststore.
+ *
+ * @param keystoreType
+ * @param keystoreProvider
+ * @return the SSL server's truststore.
+ * @throws IOException
+ */
+ protected KeyStore getTrustStore(String keystoreType, String keystoreProvider)
+ throws IOException {
+ KeyStore trustStore = null;
+
+ String truststoreFile = (String) attributes.get("truststoreFile");
+ if (truststoreFile == null) {
+ truststoreFile = System.getProperty("javax.net.ssl.trustStore");
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("Truststore = " + truststoreFile);
+ }
+ String truststorePassword = (String) attributes.get("truststorePass");
+ if (truststorePassword == null) {
+ truststorePassword =
System.getProperty("javax.net.ssl.trustStorePassword");
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("TrustPass = " + truststorePassword);
+ }
+ String truststoreType = (String) attributes.get("truststoreType");
+ if (truststoreType == null) {
+ truststoreType = System.getProperty("javax.net.ssl.trustStoreType");
+ }
+ if (truststoreType == null) {
+ truststoreType = keystoreType;
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("trustType = " + truststoreType);
+ }
+ String truststoreProvider = (String) attributes.get("truststoreProvider");
+ if (truststoreProvider == null) {
+ truststoreProvider =
System.getProperty("javax.net.ssl.trustStoreProvider");
+ }
+ if (truststoreProvider == null) {
+ truststoreProvider = keystoreProvider;
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("trustProvider = " + truststoreProvider);
+ }
+
+ if (truststoreFile != null) {
+ trustStore = getStore(truststoreType, truststoreProvider, truststoreFile,
+ truststorePassword);
+ }
+
+ return trustStore;
+ }
+
+ /**
+ * Return the initialization parameters for the TrustManager. Currently,
+ * only the default <code>PKIX</code> is supported.
+ *
+ * @param algorithm
+ * The algorithm to get parameters for.
+ * @param crlf
+ * The path to the CRL file.
+ * @param trustStore
+ * The configured TrustStore.
+ * @return The parameters including the CRLs and TrustStore.
+ */
+ protected CertPathParameters getParameters(String algorithm, String crlf, KeyStore
trustStore)
+ throws Exception {
+ CertPathParameters params = null;
+ if ("PKIX".equalsIgnoreCase(algorithm)) {
+ PKIXBuilderParameters xparams = new PKIXBuilderParameters(trustStore,
+ new X509CertSelector());
+ Collection<?> crls = getCRLs(crlf);
+ CertStoreParameters csp = new CollectionCertStoreParameters(crls);
+ CertStore store = CertStore.getInstance("Collection", csp);
+ xparams.addCertStore(store);
+ xparams.setRevocationEnabled(true);
+ String trustLength = (String) attributes.get("trustMaxCertLength");
+ if (trustLength != null) {
+ try {
+ xparams.setMaxPathLength(Integer.parseInt(trustLength));
+ } catch (Exception ex) {
+ log.warn("Bad maxCertLength: " + trustLength);
+ }
+ }
+
+ params = xparams;
+ } else {
+ throw new CRLException("CRLs not supported for type: " + algorithm);
+ }
+ return params;
+ }
+
+ /**
+ * Load the collection of CRLs.
+ *
+ * @param crlf
+ * @return a collection of {@code java.security.cert.CRL}
+ * @throws IOException
+ * @throws CRLException
+ * @throws CertificateException
+ */
+ protected Collection<? extends CRL> getCRLs(String crlf) throws IOException,
CRLException,
+ CertificateException {
+
+ File crlFile = new File(crlf);
+ if (!crlFile.isAbsolute()) {
+ crlFile = new File(System.getProperty("catalina.base"), crlf);
+ }
+ Collection<? extends CRL> crls = null;
+ InputStream is = null;
+ try {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ is = new FileInputStream(crlFile);
+ crls = cf.generateCRLs(is);
+ } catch (IOException iex) {
+ throw iex;
+ } catch (CRLException crle) {
+ throw crle;
+ } catch (CertificateException ce) {
+ throw ce;
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (Exception ex) {
+ }
+ }
+ }
+ return crls;
+ }
+
+ /**
+ * Set the SSL protocol variants to be enabled.
+ *
+ * @param engine
+ * the SSLEngine.
+ * @param protocols
+ * the protocols to use.
+ */
+ protected void setEnabledProtocols(SSLEngine engine, String[] protocols) {
+ if (protocols != null) {
+ engine.setEnabledProtocols(protocols);
+ }
+ }
+
+ /**
+ * Determines the SSL protocol variants to be enabled.
+ *
+ * @param engine
+ * The SSLEngine to get supported list from.
+ * @param requestedProtocols
+ * Comma-separated list of requested SSL protocol variants
+ *
+ * @return Array of SSL protocol variants to be enabled, or null if none of
+ * the requested protocol variants are supported
+ */
+ protected String[] getEnabledProtocols(SSLEngine engine, String requestedProtocols) {
+ String[] supportedProtocols = engine.getSupportedProtocols();
+
+ String[] enabledProtocols = null;
+
+ if (requestedProtocols != null) {
+ Vector<Object> vec = null;
+ String tab[] = requestedProtocols.trim().split("\\s*,\\s*");
+ if (tab.length > 0) {
+ vec = new Vector<Object>(tab.length);
+ }
+ for (String s : tab) {
+ if (s.length() > 0) {
+ /*
+ * Check to see if the requested protocol is among the
+ * supported protocols, i.e., may be already enabled
+ */
+ for (int i = 0; supportedProtocols != null && i <
supportedProtocols.length; i++) {
+ if (supportedProtocols[i].equals(s)) {
+ vec.addElement(s);
+ break;
+ }
+ }
+ }
+ }
+
+ if (vec != null && !vec.isEmpty()) {
+ enabledProtocols = new String[vec.size()];
+ vec.copyInto(enabledProtocols);
+ }
+ }
+
+ return enabledProtocols;
+ }
+
+ /**
+ * Configure the given SSL server socket with the requested cipher suites,
+ * protocol versions, and need for client authentication
+ *
+ * @param engine
+ */
+ private void initSSLEngine(SSLEngine engine) {
+ if (enabledCiphers != null) {
+ engine.setEnabledCipherSuites(enabledCiphers);
+ }
+
+ engine.setUseClientMode(false);
+ String requestedProtocols = (String) attributes.get("protocols");
+
+ setEnabledProtocols(engine, getEnabledProtocols(engine, requestedProtocols));
+
+ // we don't know if client authentication is needed -
+ // after parsing the request we may re-handshake
+ engine.setWantClientAuth(wantClientAuth);
+ engine.setNeedClientAuth(requireClientAuth);
+ }
+
+ /**
+ * Checks that the certificate is compatible with the enabled cipher suites.
+ * If we don't check now, the JIoEndpoint can enter a nasty logging loop.
+ * See bug 45528.
+ */
+ private void checkConfig() throws IOException {
+ // Create an unbound server socket
+ SSLServerSocketFactory sslProxy = sslContext.getServerSocketFactory();
+ ServerSocket socket = sslProxy.createServerSocket();
+ // SSLEngine engine = sslContext.createSSLEngine();
+ // initSSLEngine(engine);
+
+ try {
+ // Set the timeout to 1ms as all we care about is if it throws an
+ // SSLException on accept.
+ socket.setSoTimeout(1);
+ socket.accept();
+ // Will never get here - no client can connect to an unbound port
+ } catch (SSLException ssle) {
+ // SSL configuration is invalid. Possibly cert doesn't match ciphers
+ IOException ioe = new IOException(sm.getString("jsse.invalid_ssl_conf",
+ ssle.getMessage()));
+ ioe.initCause(ssle);
+ throw ioe;
+ } catch (Exception e) {
+ /*
+ * Possible ways of getting here socket.accept() throws a
+ * SecurityException socket.setSoTimeout() throws a SocketException
+ * socket.accept() throws some other exception (after a JDK change)
+ * In these cases the test won't work so carry on - essentially the
+ * Behavior before this patch socket.accept() throws a
+ * SocketTimeoutException In this case all is well so carry on
+ */
+ } finally {
+ // Should be open here but just in case
+ if (!socket.isClosed()) {
+ socket.close();
+ }
+ }
+
+ }
+}
Added: trunk/java/org/apache/tomcat/util/net/jsse/NioJSSESupport.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/jsse/NioJSSESupport.java
(rev 0)
+++ trunk/java/org/apache/tomcat/util/net/jsse/NioJSSESupport.java 2012-06-13 08:50:17 UTC
(rev 2041)
@@ -0,0 +1,233 @@
+/**
+ * JBoss, Home of Professional Open Source. Copyright 2012, Red Hat, Inc., and
+ * individual contributors as indicated by the @author tags. See the
+ * copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
+ * site:
http://www.fsf.org.
+ */
+package org.apache.tomcat.util.net.jsse;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+
+import javax.net.ssl.SSLSession;
+import javax.security.cert.X509Certificate;
+
+import org.apache.tomcat.util.net.Constants;
+import org.apache.tomcat.util.net.SSLSupport;
+
+/**
+ * {@code NioJSSESupport}
+ *
+ * Created on Jan 5, 2012 at 1:28:34 PM
+ *
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+class NioJSSESupport implements SSLSupport {
+
+ private static org.jboss.logging.Logger log = org.jboss.logging.Logger
+ .getLogger(NioJSSESupport.class);
+
+ protected SecureNioChannel channel;
+ protected SSLSession session;
+
+ /**
+ * Create a new instance of {@code NioJSSESupport}
+ *
+ * @param channel
+ */
+ NioJSSESupport(SecureNioChannel channel) {
+ this.channel = channel;
+ this.session = channel.getSSLSession();
+ }
+
+ /**
+ * Create a new instance of {@code NioJSSESupport}
+ *
+ * @param session
+ */
+ NioJSSESupport(SSLSession session) {
+ this.session = session;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.SSLSupport#getCipherSuite()
+ */
+ @Override
+ public String getCipherSuite() throws IOException {
+ // Look up the current SSLSession
+ return this.session == null ? null : this.session.getCipherSuite();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.SSLSupport#getPeerCertificateChain()
+ */
+ @Override
+ public Object[] getPeerCertificateChain() throws IOException {
+ return getPeerCertificateChain(false);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.SSLSupport#getPeerCertificateChain(boolean)
+ */
+ @Override
+ public Object[] getPeerCertificateChain(boolean force) throws IOException {
+ // Look up the current SSLSession
+ if (session == null) {
+ return null;
+ }
+
+ // Convert JSSE's certificate format to the ones we need
+ X509Certificate[] jsseCerts = null;
+ try {
+ jsseCerts = session.getPeerCertificateChain();
+ } catch (Exception bex) {
+ // ignore.
+ }
+ if (jsseCerts == null)
+ jsseCerts = new X509Certificate[0];
+ if (jsseCerts.length <= 0 && force) {
+ session.invalidate();
+ handShake();
+ session = channel.getSSLSession();
+ }
+ return getX509Certificates(session);
+ }
+
+ /**
+ *
+ * @param session
+ * @return
+ * @throws IOException
+ */
+ protected java.security.cert.X509Certificate[] getX509Certificates(SSLSession session)
+ throws IOException {
+ Certificate[] certs = null;
+ try {
+ certs = session.getPeerCertificates();
+ } catch (Throwable t) {
+ log.debug("Error getting client certs", t);
+ return null;
+ }
+ if (certs == null)
+ return null;
+
+ java.security.cert.X509Certificate[] x509Certs = new
java.security.cert.X509Certificate[certs.length];
+ for (int i = 0; i < certs.length; i++) {
+ if (certs[i] instanceof java.security.cert.X509Certificate) {
+ // always currently true with the JSSE 1.1.x
+ x509Certs[i] = (java.security.cert.X509Certificate) certs[i];
+ } else {
+ try {
+ byte[] buffer = certs[i].getEncoded();
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ ByteArrayInputStream stream = new ByteArrayInputStream(buffer);
+ x509Certs[i] = (java.security.cert.X509Certificate) cf
+ .generateCertificate(stream);
+ } catch (Exception ex) {
+ log.info("Error translating cert " + certs[i], ex);
+ return null;
+ }
+ }
+ if (log.isTraceEnabled())
+ log.trace("Cert #" + i + " = " + x509Certs[i]);
+ }
+ if (x509Certs.length < 1)
+ return null;
+ return x509Certs;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.SSLSupport#getKeySize()
+ */
+ @Override
+ public Integer getKeySize() throws IOException {
+ // Look up the current SSLSession
+ SSLSupport.CipherData c_aux[] = ciphers;
+ if (session == null)
+ return null;
+ Integer keySize = (Integer) session.getValue(Constants.KEY_SIZE_KEY);
+ if (keySize == null) {
+ int size = 0;
+ String cipherSuite = session.getCipherSuite();
+ for (int i = 0; i < c_aux.length; i++) {
+ if (cipherSuite.indexOf(c_aux[i].phrase) >= 0) {
+ size = c_aux[i].keySize;
+ break;
+ }
+ }
+ keySize = new Integer(size);
+ session.putValue(Constants.KEY_SIZE_KEY, keySize);
+ }
+ return keySize;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.SSLSupport#getSessionId()
+ */
+ @Override
+ public String getSessionId() throws IOException {
+ // Look up the current SSLSession
+ if (session == null) {
+ return null;
+ }
+ // Expose ssl_session (getId)
+ byte[] ssl_session = session.getId();
+ if (ssl_session == null) {
+ return null;
+ }
+ StringBuilder buf = new StringBuilder("");
+ for (int x = 0; x < ssl_session.length; x++) {
+ String digit = Integer.toHexString((int) ssl_session[x]);
+ if (digit.length() < 2) {
+ buf.append('0');
+ }
+ if (digit.length() > 2) {
+ digit = digit.substring(digit.length() - 2);
+ }
+ buf.append(digit);
+ }
+ return buf.toString();
+ }
+
+ /**
+ *
+ * @throws IOException
+ */
+ protected void handShake() throws IOException {
+ if (channel != null && channel.handshakeComplete) {
+ return;
+ }
+
+ if (channel != null) {
+ channel.handshake();
+ }
+ }
+}
Added: trunk/java/org/apache/tomcat/util/net/jsse/SecureNioChannel.java
===================================================================
--- trunk/java/org/apache/tomcat/util/net/jsse/SecureNioChannel.java
(rev 0)
+++ trunk/java/org/apache/tomcat/util/net/jsse/SecureNioChannel.java 2012-06-13 08:50:17
UTC (rev 2041)
@@ -0,0 +1,835 @@
+/**
+ * JBoss, Home of Professional Open Source. Copyright 2012, Red Hat, Inc., and
+ * individual contributors as indicated by the @author tags. See the
+ * copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this software; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
+ * site:
http://www.fsf.org.
+ */
+package org.apache.tomcat.util.net.jsse;
+
+import java.io.IOException;
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousSocketChannel;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.CompletionHandler;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLEngineResult.Status;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLSession;
+
+import org.apache.tomcat.util.net.NioChannel;
+
+/**
+ * {@code SecureNioChannel}
+ * <p>
+ * This class is an extension of the class {@link NioChannel} to allow using
+ * secure communication channels.
+ * </p>
+ * Created on Jan 3, 2012 at 3:43:44 PM
+ *
+ * @see javax.net.ssl.SSLEngine
+ * @author <a href="mailto:nbenothm@redhat.com">Nabil
Benothman</a>
+ */
+public class SecureNioChannel extends NioChannel {
+
+ private static final int MIN_BUFFER_SIZE = 16 * 1024;
+
+ protected SSLEngine sslEngine;
+ private ByteBuffer netInBuffer;
+ private ByteBuffer netOutBuffer;
+ protected boolean handshakeComplete = false;
+ // To save the handshake status for each operation
+ protected HandshakeStatus handshakeStatus;
+
+ /**
+ * Create a new instance of {@code SecureNioChannel}
+ *
+ * @param channel
+ * the {@link java.nio.channels.AsynchronousSocketChannel}
+ * @param engine
+ * The {@link javax.net.ssl.SSLEngine} linked to this channel
+ * @throws NullPointerException
+ * if the one at least one of the parameters is null
+ */
+ protected SecureNioChannel(AsynchronousSocketChannel channel, SSLEngine engine) {
+ super(channel);
+ if (engine == null) {
+ throw new NullPointerException("null SSLEngine parameter");
+ }
+
+ this.sslEngine = engine;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.NioChannel#isSecure()
+ */
+ public boolean isSecure() {
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.NioChannel#read(java.nio.ByteBuffer)
+ *
+ * @deprecated (use readBytes(...) instead)
+ */
+ @Deprecated
+ @Override
+ public Future<Integer> read(ByteBuffer dst) {
+ throw new RuntimeException("Operation not supported for class " +
getClass().getName()
+ + ". Use method readBytes(...) instead");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.NioChannel#readBytes(java.nio.ByteBuffer)
+ */
+ public int readBytes(ByteBuffer dst) throws Exception {
+ return readBytes(dst, Integer.MAX_VALUE, TimeUnit.SECONDS);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.NioChannel#readBytes(java.nio.ByteBuffer,
+ * long, java.util.concurrent.TimeUnit)
+ */
+ public int readBytes(ByteBuffer dst, long timeout, TimeUnit unit) throws Exception {
+
+ // The handshake is completed
+ checkHandshake();
+
+ if (this.netInBuffer.position() == 0) {
+ this.reset(this.netInBuffer);
+ int x = this.channel.read(this.netInBuffer).get(timeout, unit);
+ if (x < 0) {
+ throw new ClosedChannelException();
+ }
+ }
+ // Unwrap the data read, and return the number of unwrapped bytes
+ return this.unwrap(this.netInBuffer, dst);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.NioChannel#read(java.nio.ByteBuffer,
+ * java.lang.Object, java.nio.channels.CompletionHandler)
+ */
+ @Override
+ public <A> void read(final ByteBuffer dst, A attachment,
+ CompletionHandler<Integer, ? super A> handler) {
+ this.read(dst, Integer.MAX_VALUE, TimeUnit.MILLISECONDS, attachment, handler);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.NioChannel#read(java.nio.ByteBuffer,
+ * long, java.util.concurrent.TimeUnit, java.lang.Object,
+ * java.nio.channels.CompletionHandler)
+ */
+ @Override
+ public <A> void read(final ByteBuffer dst, long timeout, TimeUnit unit, A
attachment,
+ final CompletionHandler<Integer, ? super A> handler) {
+
+ // The handshake is completed
+ checkHandshake();
+ // Retrieve bytes in the internal buffer
+ this.reset(this.netInBuffer);
+ // perform read operation
+ this.channel.read(this.netInBuffer, timeout, unit, attachment,
+ new CompletionHandler<Integer, A>() {
+
+ @Override
+ public void completed(Integer nBytes, A attach) {
+ if (nBytes < 0) {
+ handler.failed(new ClosedChannelException(), attach);
+ return;
+ }
+
+ try {
+ // Unwrap the data
+ int read = unwrap(netInBuffer, dst);
+ // If everything is OK, so complete
+ handler.completed(read, attach);
+ } catch (Exception e) {
+ // The operation must fails
+ handler.failed(e, attach);
+ }
+ }
+
+ @Override
+ public void failed(Throwable exc, A attach) {
+ handler.failed(exc, attach);
+ }
+ });
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.NioChannel#read(java.nio.ByteBuffer[],
+ * int, int, long, java.util.concurrent.TimeUnit, java.lang.Object,
+ * java.nio.channels.CompletionHandler)
+ */
+ @Override
+ public <A> void read(final ByteBuffer[] dsts, final int offset, final int length,
long timeout,
+ TimeUnit unit, A attachment, final CompletionHandler<Long, ? super A> handler)
{
+
+ // The handshake is completed
+ checkHandshake();
+
+ if (handler == null) {
+ throw new NullPointerException("'handler' parameter is null");
+ }
+ if ((offset < 0) || (length < 0) || (offset > dsts.length - length)) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ final ByteBuffer netInBuffers[] = new ByteBuffer[length];
+ for (int i = 0; i < length; i++) {
+ netInBuffers[i] = ByteBuffer.allocateDirect(getSSLSession().getPacketBufferSize());
+ }
+
+ this.reset(netInBuffers[0]);
+
+ this.channel.read(netInBuffers, 0, length, timeout, unit, attachment,
+ new CompletionHandler<Long, A>() {
+
+ @Override
+ public void completed(Long nBytes, A attach) {
+ if (nBytes < 0) {
+ failed(new ClosedChannelException(), attach);
+ return;
+ }
+
+ long read = 0;
+ for (int i = 0; i < length; i++) {
+ try {
+ read += unwrap(netInBuffers[i], dsts[offset + i]);
+ } catch (Exception e) {
+ handler.failed(e, attach);
+ return;
+ }
+ }
+
+ handler.completed(read, attach);
+ }
+
+ @Override
+ public void failed(Throwable exc, A attach) {
+ handler.failed(exc, attach);
+ }
+ });
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.NioChannel#write(java.nio.ByteBuffer)
+ *
+ * @deprecated (use writeBytes(...) instead)
+ */
+ @Deprecated
+ @Override
+ public Future<Integer> write(ByteBuffer src) {
+ throw new RuntimeException("Operation not supported for class " +
getClass().getName()
+ + ". Use method writeBytes(...) instead");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.NioChannel#writeBytes(java.nio.ByteBuffer)
+ */
+ public int writeBytes(ByteBuffer src) throws Exception {
+ return writeBytes(src, Integer.MAX_VALUE, TimeUnit.MILLISECONDS);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.tomcat.util.net.NioChannel#writeBytes(java.nio.ByteBuffer,
+ * long, java.util.concurrent.TimeUnit)
+ */
+ public int writeBytes(ByteBuffer src, long timeout, TimeUnit unit) throws Exception {
+
+ // The handshake is completed
+ checkHandshake();
+
+ // Clear the output buffer
+ this.netOutBuffer.compact();
+ // the number of bytes written
+ int written = wrap(src, this.netOutBuffer);
+ this.netOutBuffer.flip();
+
+ // write bytes to the channel
+ while (this.netOutBuffer.hasRemaining()) {
+ int x = this.channel.write(netOutBuffer).get(timeout, unit);
+ if (x < 0) {
+ throw new ClosedChannelException();
+ }
+ }
+
+ return written;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.NioChannel#write(java.nio.ByteBuffer,
+ * java.lang.Object, java.nio.channels.CompletionHandler)
+ */
+ @Override
+ public <A> void write(ByteBuffer src, A attachment,
+ CompletionHandler<Integer, ? super A> handler) {
+ this.write(src, Integer.MAX_VALUE, TimeUnit.MILLISECONDS, attachment, handler);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.NioChannel#write(java.nio.ByteBuffer,
+ * long, java.util.concurrent.TimeUnit, java.lang.Object,
+ * java.nio.channels.CompletionHandler)
+ */
+ @Override
+ public <A> void write(final ByteBuffer src, long timeout, TimeUnit unit, final A
attachment,
+ final CompletionHandler<Integer, ? super A> handler) {
+
+ // The handshake is completed
+ checkHandshake();
+
+ try {
+ // Prepare the output buffer
+ this.netOutBuffer.clear();
+ // Wrap the source data into the internal buffer
+ final int written = wrap(src, this.netOutBuffer);
+ this.netOutBuffer.flip();
+
+ // Write data to the channel
+ this.channel.write(this.netOutBuffer, timeout, unit, attachment,
+ new CompletionHandler<Integer, A>() {
+
+ @Override
+ public void completed(Integer nBytes, A attach) {
+ if (nBytes < 0) {
+ handler.failed(new ClosedChannelException(), attach);
+ } else {
+ // Call the handler completed method with the
+ // consumed bytes number
+ handler.completed(written, attach);
+ }
+ }
+
+ @Override
+ public void failed(Throwable exc, A attach) {
+ handler.failed(exc, attach);
+ }
+ });
+
+ } catch (Throwable exp) {
+ handler.failed(exp, attachment);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.NioChannel#write(java.nio.ByteBuffer[],
+ * int, int, long, java.util.concurrent.TimeUnit, java.lang.Object,
+ * java.nio.channels.CompletionHandler)
+ */
+ @Override
+ public <A> void write(final ByteBuffer[] srcs, int offset, int length, long
timeout,
+ TimeUnit unit, A attachment, final CompletionHandler<Long, ? super A> handler)
{
+
+ // The handshake is completed
+ checkHandshake();
+
+ if (handler == null) {
+ throw new NullPointerException("'handler' parameter is null");
+ }
+ if ((offset < 0) || (length < 0) || (offset > srcs.length - length)) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ ByteBuffer[] netOutBuffers = new ByteBuffer[length];
+ int size = getSSLSession().getPacketBufferSize();
+ long written = 0;
+ for (int i = 0; i < length; i++) {
+ try {
+ // Prepare the output buffer
+ netOutBuffers[i] = ByteBuffer.allocateDirect(size);
+ // Wrap the source data into the internal buffer
+ written += wrap(srcs[offset + i], netOutBuffers[i]);
+ netOutBuffers[i].flip();
+ } catch (Throwable exp) {
+ handler.failed(exp, attachment);
+ return;
+ }
+ }
+
+ final long res = written;
+
+ this.channel.write(netOutBuffers, 0, length, timeout, unit, attachment,
+ new CompletionHandler<Long, A>() {
+
+ @Override
+ public void completed(Long nBytes, A attach) {
+ if (nBytes < 0) {
+ handler.failed(new ClosedChannelException(), attach);
+ } else {
+ // If everything is OK, so complete
+ handler.completed(res, attach);
+ }
+ }
+
+ @Override
+ public void failed(Throwable exc, A attach) {
+ handler.failed(exc, attach);
+ }
+ });
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.tomcat.util.net.NioChannel#close()
+ */
+ @Override
+ public synchronized void close() throws IOException {
+ if (isClosed()) {
+ return;
+ }
+ try {
+ // Handle closing the SSL Engine
+ handleClose();
+ this.channel.close();
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ }
+
+ /**
+ * Close the {@link SSLEngine} attached to this channel
+ *
+ * @throws Exception
+ */
+ private void handleClose() throws Exception {
+ if (sslEngine.isOutboundDone()) {
+ return;
+ }
+ sslEngine.closeOutbound();
+
+ SSLSession session = getSSLSession();
+ int packetBufferSize = Math.max(session.getPacketBufferSize(), MIN_BUFFER_SIZE);
+
+ this.netOutBuffer = (this.netOutBuffer == null) ? ByteBuffer
+ .allocateDirect(packetBufferSize) : this.netOutBuffer.compact();
+ this.netInBuffer = (this.netInBuffer == null) ?
ByteBuffer.allocateDirect(packetBufferSize)
+ : this.netInBuffer.compact();
+
+ while (!sslEngine.isOutboundDone()) {
+ // Get close message
+ SSLEngineResult res = sslEngine.wrap(this.netInBuffer, this.netOutBuffer);
+
+ switch (res.getStatus()) {
+ case OK:
+ // Execute tasks if we need to
+ tryTasks();
+ while (this.netOutBuffer.hasRemaining()) {
+ if (this.channel.write(this.netOutBuffer).get() < 0) {
+ break;
+ }
+ this.netOutBuffer.compact();
+ }
+ break;
+ case BUFFER_OVERFLOW:
+ ByteBuffer tmp = ByteBuffer.allocateDirect(packetBufferSize
+ + this.netOutBuffer.capacity());
+ this.netOutBuffer.flip();
+ tmp.put(this.netOutBuffer);
+ this.netOutBuffer = tmp;
+
+ break;
+ case BUFFER_UNDERFLOW:
+ // Cannot happens in case of wrap
+ case CLOSED:
+ // Already closed, so return
+ break;
+ }
+ }
+ }
+
+ /**
+ * Getter for sslEngine
+ *
+ * @return the sslEngine
+ */
+ public SSLEngine getSslEngine() {
+ return this.sslEngine;
+ }
+
+ /**
+ * Setter for the sslEngine
+ *
+ * @param sslEngine
+ * the sslEngine to set
+ */
+ protected void setSslEngine(SSLEngine sslEngine) {
+ this.sslEngine = sslEngine;
+ }
+
+ /**
+ * Attempts to encode a buffer of plaintext application data into SSL/TLS
+ * network data.
+ *
+ * @param src
+ * a ByteBuffer containing outbound application data
+ * @param dst
+ * a ByteBuffer to hold outbound network data
+ * @return the number of bytes consumed
+ * @throws Exception
+ * if the wrap status is not <tt>OK</tt>
+ */
+ private int wrap(ByteBuffer src, ByteBuffer dst) throws Exception {
+ // Wrap the source data into the destination buffer
+ SSLEngineResult result = sslEngine.wrap(src, dst);
+ // the number of bytes written
+ int written = result.bytesConsumed();
+ this.handshakeStatus = result.getHandshakeStatus();
+
+ switch (result.getStatus()) {
+ case OK:
+ // Execute tasks if we need to
+ tryTasks();
+ break;
+ case CLOSED:
+ // We can't do encryption any more
+ written = -1;
+ case BUFFER_OVERFLOW:
+ throw new BufferOverflowException();
+ case BUFFER_UNDERFLOW:
+ // This case should not happen for a wrap method
+ break;
+ }
+
+ return written;
+ }
+
+ /**
+ * Attempts to decode SSL/TLS network data into a plaintext application data
+ * buffer.
+ *
+ * @param src
+ * a ByteBuffer containing inbound network data.
+ * @param dst
+ * a ByteBuffer to hold inbound application data.
+ * @return the number of bytes produced
+ * @throws Exception
+ */
+ private int unwrap(ByteBuffer src, ByteBuffer dst) throws Exception {
+ SSLEngineResult result;
+ int read = 0;
+ do {
+ // prepare the input buffer
+ src.flip();
+ // unwrap the data
+ result = sslEngine.unwrap(src, dst);
+ // compact the buffer
+ src.compact();
+
+ if (result.getStatus() == Status.OK || result.getStatus() == Status.BUFFER_UNDERFLOW)
{
+ // we did receive some data, add it to our total
+ read += result.bytesProduced();
+
+ handshakeStatus = result.getHandshakeStatus();
+ // perform any tasks if needed
+ tryTasks();
+ // if we need more network data, then bail out for now.
+ if (result.getStatus() == Status.BUFFER_UNDERFLOW) {
+ break;
+ }
+ } else if (result.getStatus() == Status.BUFFER_OVERFLOW && read > 0) {
+ // buffer overflow can happen, if we have read data, then
+ // empty out the destination buffer before we do another read
+ break;
+ } else if (result.getStatus() == Status.CLOSED) {
+ return -1;
+ } 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(this + " Unable to unwrap data, invalid status: "
+ + result.getStatus());
+ }
+ // continue to unwrapping as long as the input buffer has stuff
+ } while (src.position() != 0);
+
+ return read;
+ }
+
+ /**
+ * Initiates handshaking (initial or renegotiation) on this SSLEngine.
+ * <P>
+ * This method is not needed for the initial handshake, as the
+ * <code>wrap()</code> and <code>unwrap()</code> methods will
implicitly
+ * call this method if handshaking has not already begun.
+ * <P>
+ * Note that the client may also request a session renegotiation with this
+ * <code>SSLEngine</code> by sending the appropriate session renegotiate
+ * handshake message.
+ * <P>
+ * Unlike the {@link SSLSocket#startHandshake() SSLSocket#startHandshake()}
+ * method, this method does not block until handshaking is completed.
+ * <P>
+ * To force a complete SSL/TLS session renegotiation, the current session
+ * should be invalidated prior to calling this method.
+ * <P>
+ * Some protocols may not support multiple handshakes on an existing engine
+ * and may throw an <code>SSLException</code>.
+ *
+ * @throws SSLException
+ * if a problem was encountered while signaling the
+ * <code>SSLEngine</code> to begin a new handshake. See the
+ * class description for more information on engine closure.
+ * @throws IllegalStateException
+ * if the client/server mode has not yet been set.
+ * @see javax.net.ssl.SSLEngine#beginHandshake()
+ * @see javax.net.ssl.SSLSession#invalidate()
+ */
+ protected void handshake() throws SSLException {
+ if (handshakeComplete) {
+ return;
+ }
+
+ try {
+ doHandshake();
+ } catch (Exception e) {
+ throw new SSLException(e);
+ }
+ }
+
+ /**
+ * Check whether the handshake is already complete or not
+ */
+ private void checkHandshake() {
+ if (!handshakeComplete) {
+ throw new IllegalStateException(
+ "Handshake incomplete, you must complete handshake before read/write
data.");
+ }
+ }
+
+ /**
+ * Start a new handshake operation for this channel.
+ *
+ * @see #handshake()
+ * @throws SSLException
+ */
+ protected void reHandshake() throws SSLException {
+ handshakeComplete = false;
+ handshakeStatus = sslEngine.getHandshakeStatus();
+ try {
+ doHandshake();
+ } catch (Exception e) {
+ throw new SSLException(e);
+ }
+ }
+
+ /**
+ * Execute a handshake with the client socket channel
+ *
+ * @throws Exception
+ */
+ private void doHandshake() throws Exception {
+
+ SSLSession session = getSSLSession();
+ int packetBufferSize = Math.max(session.getPacketBufferSize(), MIN_BUFFER_SIZE);
+ // Create byte buffers to use for holding application data
+ initBuffers(packetBufferSize);
+
+ ByteBuffer clientNetData = ByteBuffer.allocateDirect(packetBufferSize);
+ ByteBuffer clientAppData = ByteBuffer.allocateDirect(packetBufferSize);
+
+ // Begin handshake
+ sslEngine.beginHandshake();
+ handshakeStatus = sslEngine.getHandshakeStatus();
+ int i = 1;
+ boolean read = true;
+ // Process handshaking message
+ while (!handshakeComplete) {
+
+ switch (handshakeStatus) {
+ case NEED_UNWRAP:
+ int nBytes = 0;
+ if (read) {
+ clientAppData.clear();
+ nBytes = this.channel.read(this.netInBuffer).get();
+ }
+ if (nBytes < 0) {
+ throw new IOException(this + " : EOF encountered during handshake
UNWRAP.");
+ } else {
+ boolean cont = false;
+ // Loop while we can perform pure SSLEngine data
+ do {
+ // Prepare the buffer with the incoming data
+ this.netInBuffer.flip();
+ // Call unwrap
+ SSLEngineResult res = sslEngine.unwrap(this.netInBuffer, clientAppData);
+ // Compact the buffer, this is an optional method,
+ // wonder what would happen if we didn't
+ this.netInBuffer.compact();
+ // Read in the status
+ handshakeStatus = res.getHandshakeStatus();
+ if (res.getStatus() == SSLEngineResult.Status.OK) {
+ // Execute tasks if we need to
+ tryTasks();
+ read = true;
+ } else if (res.getStatus() == Status.BUFFER_UNDERFLOW) {
+ read = true;
+ } else if (res.getStatus() == Status.BUFFER_OVERFLOW) {
+ ByteBuffer tmp = ByteBuffer.allocateDirect(packetBufferSize * (++i));
+
+ if (clientAppData.position() > 0) {
+ clientAppData.flip();
+ }
+ tmp.put(clientAppData);
+ clientAppData = tmp;
+ read = false;
+ }
+ // Perform another unwrap?
+ cont = res.getStatus() == SSLEngineResult.Status.OK
+ && handshakeStatus == HandshakeStatus.NEED_UNWRAP;
+ } while (cont);
+ }
+
+ break;
+ case NEED_WRAP:
+ clientNetData.compact();
+ this.netOutBuffer.clear();
+ SSLEngineResult res = sslEngine.wrap(clientNetData, this.netOutBuffer);
+ handshakeStatus = res.getHandshakeStatus();
+ this.netOutBuffer.flip();
+
+ if (res.getStatus() == Status.OK) {
+ // Execute tasks if we need to
+ tryTasks();
+ // Send the handshaking data to client
+ while (this.netOutBuffer.hasRemaining()) {
+ if (this.channel.write(this.netOutBuffer).get() < 0) {
+ // Handle closed channel
+ throw new IOException(this
+ + " : EOF encountered during handshake WRAP.");
+ }
+ }
+ } else {
+ // Wrap should always work with our buffers
+ throw new IOException("Unexpected status:" + res.getStatus()
+ + " during handshake WRAP.");
+ }
+
+ break;
+ case NEED_TASK:
+ handshakeStatus = tasks();
+
+ break;
+ case NOT_HANDSHAKING:
+ throw new SSLHandshakeException("NOT_HANDSHAKING during handshake");
+ case FINISHED:
+ handshakeComplete = true;
+ break;
+ }
+ }
+
+ this.handshakeComplete = (handshakeStatus == HandshakeStatus.FINISHED);
+ }
+
+ /**
+ * Perform tasks, if any, during the handshake phase
+ *
+ * @return The handshake status (
+ * {@link javax.net.ssl.SSLEngineResult.HandshakeStatus})
+ */
+ private SSLEngineResult.HandshakeStatus tasks() {
+ Runnable task = null;
+ while ((task = sslEngine.getDelegatedTask()) != null) {
+ // Run the task in blocking mode
+ task.run();
+ }
+
+ return sslEngine.getHandshakeStatus();
+ }
+
+ /**
+ * Try to run tasks if any.
+ */
+ private void tryTasks() {
+ if (handshakeStatus == HandshakeStatus.NEED_TASK) {
+ handshakeStatus = tasks();
+ }
+ }
+
+ /**
+ *
+ * @param capacity
+ */
+ private void initBuffers(int capacity) {
+ if (this.netInBuffer == null) {
+ this.netInBuffer = ByteBuffer.allocateDirect(capacity);
+ } else {
+ this.netInBuffer.clear();
+ }
+ if (this.netOutBuffer == null) {
+ this.netOutBuffer = ByteBuffer.allocateDirect(capacity);
+ } else {
+ this.netOutBuffer.clear();
+ }
+ }
+
+ /**
+ * Check if the handshake was done or not yet
+ *
+ * @return <tt>true</tt> if the handshake was already done, else
+ * <tt>false</tt>
+ */
+ protected boolean handshakeComplete() {
+ return this.handshakeComplete;
+ }
+
+ /**
+ * @return The SSL Session of the channel
+ */
+ public SSLSession getSSLSession() {
+ return this.sslEngine.getSession();
+ }
+}
Modified: trunk/webapps/docs/changelog.xml
===================================================================
--- trunk/webapps/docs/changelog.xml 2012-06-07 13:48:42 UTC (rev 2040)
+++ trunk/webapps/docs/changelog.xml 2012-06-13 08:50:17 UTC (rev 2041)
@@ -59,6 +59,9 @@
<fix>
<jboss-jira>AS7-4566</jboss-jira>: Add system property to configure
connection timeout. (remm)
</fix>
+ <add>
+ NIO 2 HTTP connector. (benothman)
+ </add>
</changelog>
</subsection>
<subsection name="Jasper">